Repository: unum-cloud/usearch (language: rust)

Issue:
Bug: Java bindings throws Out of memory error
### Describe the bug

Out of memory error with Java bindings.

### Steps to reproduce

Follow the Java [README](https://unum-cloud.github.io/usearch/java/) code:
```
Index index = new Index.Config().metric("cos").dimensions(2).build();
float vec[] = {10, 20};
index.add(42, vec);
int[] keys = index.search(vec, 5);
```
throws this error:

> Exception in thread "main" java.lang.Error: Out of memory!
> at cloud.unum.usearch.Index.c_add(Native Method)
> at cloud.unum.usearch.Index.add(Index.java:107)
> at TestUsearch.main(TestUsearch.java:14)

### Expected behavior

No error occurs.

### USearch version

2.8.10

### Operating System

Ubuntu 22.04

### Hardware architecture

x86

### Which interface are you using?

Other bindings

### Contact Details

_No response_

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml
index 6873845d..210cb6ab 100644
--- a/.github/workflows/prerelease.yml
+++ b/.github/workflows/prerelease.yml
@@ -127,7 +127,7 @@ jobs:
           wget https://apt.llvm.org/llvm.sh
           chmod +x llvm.sh
           sudo ./llvm.sh 16
-          cmake -B build_artifacts -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSEARCH_BUILD_TEST_CPP=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_BUILD_LIB_C=1 -DDUSEARCH_USE_OPENMP=0 -DUSEARCH_USE_SIMSIMD=1 -DUSEARCH_USE_JEMALLOC=1
+          cmake -B build_artifacts -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSEARCH_BUILD_TEST_CPP=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_BUILD_LIB_C=1 -DUSEARCH_USE_OPENMP=0 -DUSEARCH_USE_SIMSIMD=1 -DUSEARCH_USE_JEMALLOC=1
           cmake --build build_artifacts --config RelWithDebInfo
       - name: Test C++
         run: ./build_artifacts/test_cpp
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 736c6177..99d4b16c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -432,6 +432,7 @@ jobs:
           npm install
           npm ci
           npm test
+          npm run typegen
 
       - name: Publish
         uses: JS-DevTools/npm-publish@v2
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0663405b..eeb45763 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -232,21 +232,30 @@ Afterwards, in a new terminal:
 
 ```sh
 gradle clean build
-java -cp . -Djava.library.path="$(pwd)/build/libs/usearch/shared" java/cloud/unum/usearch/Index.java
+gradle test
+```
+
+Alternatively, to run the `Index.main`:
+
+```sh
+java -cp "$(pwd)/build/classes/java/main" -Djava.library.path="$(pwd)/build/libs/usearch/shared" java/cloud/unum/usearch/Index.java
 ```
 
 Or step by-step:
 
 ```sh
-cs java/unum/cloud/usearch
-javac -h . Index.java
+cd java/cloud/unum/usearch
+javac -h . Index.java NativeUtils.java
+
+# Ensure JAVA_HOME system environment variable has been set
+# e.g. export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
 
 # Ubuntu:
-g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
-g++ -shared -fPIC -o libusearch_c.so cloud_unum_usearch_Index.o -lc
+g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -I../../../../include cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
+g++ -shared -fPIC -o libusearch.so cloud_unum_usearch_Index.o -lc
 
 # Windows
-g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
+g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 cloud_unum_usearch_Index.cpp -I..\..\..\..\include -o cloud_unum_usearch_Index.o
 g++ -shared -o USearchJNI.dll cloud_unum_usearch_Index.o -Wl,--add-stdcall-alias
 
 # MacOS
@@ -256,9 +265,11 @@ g++ -std=c++11 -c -fPIC \
     -I../../../../simsimd/include \
     -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
 g++ -dynamiclib -o libusearch.dylib cloud_unum_usearch_Index.o -lc
+
 # Run linking to that directory
-java -cp . -Djava.library.path="$(pwd)/java/cloud/unum/usearch/" Index.java
-java -cp . -Djava.library.path="$(pwd)/java" cloud.unum.usearch.Index
+cd ../../../..
+cp cloud/unum/usearch/libusearch.* .
+java -cp . -Djava.library.path="$(pwd)" cloud.unum.usearch.Index
 ```
 
 ## Wolfram
diff --git a/build.gradle b/build.gradle
index 59cd142f..36d758a3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,6 +23,11 @@ sourceSets {
             srcDir "java/"
             exclude "test/"
         }
+        // Ensure native libraries are included in the main source set
+        resources {
+            srcDirs "$buildDir/libs"
+            include "usearch/**"
+        }
     }
     test {
         java {
@@ -98,15 +103,25 @@ test {
 
     // Rearrange test classpath, add compiled JAR instead of main classes
     classpath = project.sourceSets.test.output + configurations.testRuntimeClasspath + files(jar.archiveFile)
+
+    // Enable detailed test logging
+    testLogging {
+        // Ensure that we get output from all tests, not just failing ones
+        showStandardStreams = true
+        exceptionFormat = 'full' // Can be 'short', 'full', or 'summary'
+
+        // More detailed test result logging
+        events "passed", "skipped", "failed"
+    }
 }
 
 jar {
     dependsOn("usearchSharedLibrary")
-    into("usearch") {
-        from("$buildDir/libs/usearch/shared/libusearch.dylib")
-        from("$buildDir/libs/usearch/shared/libusearch.dll")
-        from("$buildDir/libs/usearch/shared/libusearch.so")
+    from("$buildDir/libs/usearch/shared") {
+        include "libusearch.so", "libusearch.dylib", "libusearch.dll"
+        into "usearch"
     }
+    from sourceSets.main.output
 }
 
 signing {
@@ -180,4 +195,10 @@ publishing {
             }
         }
     }
-}
\ No newline at end of file
+}
+
+// Ensure that the native library is built before the Java code
+compileJava.dependsOn tasks.withType(SharedLibraryBinarySpec)
+tasks.named("processResources").configure {
+    dependsOn tasks.named("linkUsearchSharedLibrary")
+}
diff --git a/include/usearch/index_dense.hpp b/include/usearch/index_dense.hpp
index 50c6d79f..e638d00c 100644
--- a/include/usearch/index_dense.hpp
+++ b/include/usearch/index_dense.hpp
@@ -108,13 +108,10 @@ struct index_dense_config_t : public index_config_t {
 
     index_dense_config_t(index_config_t base) noexcept : index_config_t(base) {}
 
-    index_dense_config_t(                                  //
-        std::size_t connectivity = default_connectivity(), //
-        std::size_t expansion_add = default_expansion_add(),
-        std::size_t expansion_search = default_expansion_search()) noexcept
-        : index_config_t(connectivity), //
-          expansion_add(expansion_add ? expansion_add : default_expansion_add()),
-          expansion_search(expansion_search ? expansion_search : default_expansion_search()) {}
+    index_dense_config_t(std::size_t c = default_connectivity(), std::size_t ea = default_expansion_add(),
+                         std::size_t es = default_expansion_search()) noexcept
+        : index_config_t(c), expansion_add(ea ? ea : default_expansion_add()),
+          expansion_search(es ? es : default_expansion_search()) {}
 };
 
 struct index_dense_clustering_config_t {
diff --git a/include/usearch/index_plugins.hpp b/include/usearch/index_plugins.hpp
index 42c5c423..061fc5f5 100644
--- a/include/usearch/index_plugins.hpp
+++ b/include/usearch/index_plugins.hpp
@@ -1343,11 +1343,13 @@ class metric_punned_t {
           scalar_kind_(scalar_kind) {
 
 #if USEARCH_USE_SIMSIMD
-        if (configure_with_simsimd())
-            return;
+        if (!configure_with_simsimd())
+            configure_with_auto_vectorized();
 #endif
-
         configure_with_auto_vectorized();
+
+        if (scalar_kind == scalar_kind_t::b1x8_k)
+            raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_);
     }
 
     inline metric_punned_t(                                                 //
@@ -1499,18 +1501,9 @@ class metric_punned_t {
             break;
         }
         case metric_kind_t::jaccard_k: // Equivalent to Tanimoto
-        case metric_kind_t::tanimoto_k:
-            raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_tanimoto_gt<b1x8_t>>,
-            raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
-            break;
-        case metric_kind_t::hamming_k:
-            raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_hamming_gt<b1x8_t>>,
-            raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
-            break;
-        case metric_kind_t::sorensen_k:
-            raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_sorensen_gt<b1x8_t>>,
-            raw_arg3_ = raw_arg4_ = divide_round_up<CHAR_BIT>(dimensions_), scalar_kind_ = scalar_kind_t::b1x8_k;
-            break;
+        case metric_kind_t::tanimoto_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_tanimoto_gt<b1x8_t>>; break;
+        case metric_kind_t::hamming_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_hamming_gt<b1x8_t>>; break;
+        case metric_kind_t::sorensen_k: raw_ptr_ = (punned_ptr_t)&equidimensional_<metric_sorensen_gt<b1x8_t>>; break;
         default: return;
         }
     }
diff --git a/java/README.md b/java/README.md
index 489d9953..e03e0225 100644
--- a/java/README.md
+++ b/java/README.md
@@ -6,7 +6,7 @@
 <dependency>
   <groupId>cloud.unum</groupId>
   <artifactId>usearch</artifactId>
-  <version>0.2.3</version>
+  <version>2.8.11</version>
 </dependency>
 ```
 
diff --git a/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp b/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp
index 9bca7000..34fccf7b 100644
--- a/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp
+++ b/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp
@@ -22,18 +22,14 @@ JNIEXPORT jlong JNICALL Java_cloud_unum_usearch_Index_c_1create( //
     char const* quantization_cstr{};
     try {
 
-        std::size_t connectivity = static_cast<std::size_t>(connectivity);
-        std::size_t expansion_add = static_cast<std::size_t>(expansion_add);
-        std::size_t expansion_search = static_cast<std::size_t>(expansion_search);
-
         metric_cstr = (*env).GetStringUTFChars(metric, 0);
         std::size_t metric_length = (*env).GetStringUTFLength(metric);
         quantization_cstr = (*env).GetStringUTFChars(quantization, 0);
         std::size_t quantization_length = (*env).GetStringUTFLength(quantization);
-
         metric_kind_t metric_kind = metric_from_name(metric_cstr, metric_length);
         scalar_kind_t quantization = scalar_kind_from_name(quantization_cstr, quantization_length);
-        index_dense_config_t config(connectivity, expansion_add, expansion_search);
+        index_dense_config_t config(static_cast<std::size_t>(connectivity), static_cast<std::size_t>(expansion_add),
+                                    static_cast<std::size_t>(expansion_search));
         metric_punned_t metric(static_cast<std::size_t>(dimensions), metric_kind, quantization);
         index_dense_t index = index_dense_t::make(metric, config);
         if (!index.reserve(static_cast<std::size_t>(capacity))) {
@@ -126,6 +122,7 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1add( //
 
     using vector_key_t = typename index_dense_t::vector_key_t;
     using add_result_t = typename index_dense_t::add_result_t;
+    printf("Adding %zu dims \n", (size_t)vector_dims);
 
     add_result_t result = reinterpret_cast<index_dense_t*>(c_ptr)->add(static_cast<vector_key_t>(key), vector_span);
     if (!result) {
@@ -158,7 +155,7 @@ JNIEXPORT jintArray JNICALL Java_cloud_unum_usearch_Index_c_1search( //
     search_result_t result =
         reinterpret_cast<index_dense_t*>(c_ptr)->search(vector_span, static_cast<std::size_t>(wanted));
     if (result) {
-        std::size_t found = result.dump_to(reinterpret_cast<vector_key_t*>(matches_data), NULL);
+        std::size_t found = result.dump_to(reinterpret_cast<vector_key_t*>(matches_data));
         (*env).SetIntArrayRegion(matches, 0, found, matches_data);
     } else {
         jclass jc = (*env).FindClass("java/lang/Error");
@@ -171,7 +168,7 @@ JNIEXPORT jintArray JNICALL Java_cloud_unum_usearch_Index_c_1search( //
     return matches;
 }
 
-JNIEXPORT bool JNICALL Java_cloud_unum_usearch_Index_c_1remove(JNIEnv* env, jclass, jlong c_ptr, jlong key) {
+JNIEXPORT jboolean JNICALL Java_cloud_unum_usearch_Index_c_1remove(JNIEnv* env, jclass, jlong c_ptr, jint key) {
     using vector_key_t = typename index_dense_t::vector_key_t;
     using labeling_result_t = typename index_dense_t::labeling_result_t;
     labeling_result_t result = reinterpret_cast<index_dense_t*>(c_ptr)->remove(static_cast<vector_key_t>(key));
@@ -183,7 +180,8 @@ JNIEXPORT bool JNICALL Java_cloud_unum_usearch_Index_c_1remove(JNIEnv* env, jcla
     return result.completed;
 }
 
-JNIEXPORT bool JNICALL Java_cloud_unum_usearch_Index_c_1rename(JNIEnv* env, jclass, jlong c_ptr, jlong from, jlong to) {
+JNIEXPORT jboolean JNICALL Java_cloud_unum_usearch_Index_c_1rename(JNIEnv* env, jclass, jlong c_ptr, jint from,
+                                                                   jint to) {
     using vector_key_t = typename index_dense_t::vector_key_t;
     using labeling_result_t = typename index_dense_t::labeling_result_t;
     labeling_result_t result =
diff --git a/java/cloud/unum/usearch/cloud_unum_usearch_Index.h b/java/cloud/unum/usearch/cloud_unum_usearch_Index.h
index a79a87f2..554b7669 100644
--- a/java/cloud/unum/usearch/cloud_unum_usearch_Index.h
+++ b/java/cloud/unum/usearch/cloud_unum_usearch_Index.h
@@ -106,19 +106,19 @@ JNIEXPORT void JNICALL Java_cloud_unum_usearch_Index_c_1view
 /*
  * Class:     cloud_unum_usearch_Index
  * Method:    c_remove
- * Signature: (JLjava/lang/String;)V
+ * Signature: (JI)Z
  */
-JNIEXPORT bool JNICALL Java_cloud_unum_usearch_Index_c_1remove
-  (JNIEnv *, jclass, jlong);
+JNIEXPORT jboolean JNICALL Java_cloud_unum_usearch_Index_c_1remove
+  (JNIEnv *, jclass, jlong, jint);
 
 /*
  * Class:     cloud_unum_usearch_Index
  * Method:    c_rename
- * Signature: (JLjava/lang/String;)V
+ * Signature: (JII)Z
  */
-JNIEXPORT bool JNICALL Java_cloud_unum_usearch_Index_c_1rename
-  (JNIEnv *, jclass, jlong, jlong);
-  
+JNIEXPORT jboolean JNICALL Java_cloud_unum_usearch_Index_c_1rename
+  (JNIEnv *, jclass, jlong, jint, jint);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/javascript/usearch.js b/javascript/usearch.js
index 3c6f7292..2366967d 100644
--- a/javascript/usearch.js
+++ b/javascript/usearch.js
@@ -137,7 +137,7 @@ class Index {
     /**
      * Constructs a new index.
      * 
-     * @param {number} dimensionsOrConfigs
+     * @param {(number | {dimensions: number, metric: MetricKind = MetricKind.Cos, quantization: ScalarKind = ScalarKind.F32, connectivity: number = 0, expansion_add: number = 0, expansion_search: number = 0, multi: boolean = false})} dimensionsOrConfigs
      * @param {MetricKind} [metric=MetricKind.Cos] - Optional, default is 'cos'.
      * @param {ScalarKind} [quantization=ScalarKind.F32] - Optional, default is 'f32'.
      * @param {number} [connectivity=0] - Optional, default is 0.
diff --git a/package.json b/package.json
index 0533f9e1..44026a4b 100644
--- a/package.json
+++ b/package.json
@@ -25,9 +25,24 @@
     },
     "scripts": {
         "test": "node --test ./javascript/usearch.test.js",
-        "install": "node-gyp rebuild"
+        "install": "node-gyp rebuild",
+        "typegen": "npx tsc"
     },
     "devDependencies": {
         "typescript": "^5.1.6"
-    }
+    },
+    "exports": {
+        ".": {
+            "types": "./javascript/types/usearch.d.ts",
+            "require": "./javascript/usearch.js"
+        },
+        "./package.json": "./package.json"
+    },
+    "typesVersions": {
+      "*": {
+          "*": [
+              "./javascript/types/usearch.d.ts"
+          ]
+      }
+  }
 }
diff --git a/setup.py b/setup.py
index a604abf7..4983c088 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
 import os
 import sys
-import platform
+import subprocess
 from setuptools import setup
 
 from pybind11.setup_helpers import Pybind11Extension
@@ -19,11 +19,24 @@ def get_bool_env_w_name(name: str, preference: bool) -> tuple:
 
 
 # Check the environment variables
-is_gcc = "GCC" in platform.python_compiler().upper()
 is_linux: bool = sys.platform == "linux"
 is_macos: bool = sys.platform == "darwin"
 is_windows: bool = sys.platform == "win32"
 
+
+is_gcc = False
+if is_linux:
+    cxx = os.environ.get("CXX")
+    if cxx:
+        try:
+            command = "where" if os.name == "nt" else "which"
+            full_path = subprocess.check_output([command, cxx], text=True).strip()
+            compiler_name = os.path.basename(full_path)
+            is_gcc = ("g++" in compiler_name) and ("clang++" not in compiler_name)
+        except subprocess.CalledProcessError:
+            pass
+
+
 prefer_simsimd: bool = is_linux or is_macos
 prefer_fp16lib: bool = True
 prefer_openmp: bool = is_linux and is_gcc
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..f48dae3d
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,21 @@
+{
+  // Change this to match your project
+  "include": ["javascript/usearch.js"],
+  "compilerOptions": {
+    // Tells TypeScript to read JS files, as
+    // normally they are ignored as source files
+    "allowJs": true,
+    // Generate d.ts files
+    "declaration": true,
+    // This compiler run should
+    // only output d.ts files
+    "emitDeclarationOnly": true,
+    // Types should go into this directory.
+    // Removing this would place the .d.ts files
+    // next to the .js files
+    "outDir": "javascript/types",
+    // go to js file when using IDE functions like
+    // "Go to Definition" in VSCode
+    "declarationMap": true
+  }
+}


Test patch:
diff --git a/java/test/IndexTest.java b/java/test/IndexTest.java
index 2bda8892..e0e1de05 100644
--- a/java/test/IndexTest.java
+++ b/java/test/IndexTest.java
@@ -20,10 +20,11 @@ public void test() {
         String path = "./tmp/";
         deleteDirectoryFiles(path);
 
-        // Index index = new Index.Config().metric("cos").dimensions(2).build();
-        // float vec[] = {10, 20};
-        // index.add(42, vec);
-        // int[] keys = index.search(vec, 5);
+        Index index = new Index.Config().metric("cos").dimensions(2).build();
+        float vec[] = { 10, 20 };
+        index.reserve(10);
+        index.add(42, vec);
+        int[] keys = index.search(vec, 5);
 
         System.out.println("Java Tests Passed!");
     }