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

Issue:
Feature: optional ucall for python
### Describe what you are looking for

Since using usearch does not require ucall AFAIK, please make it optional, so it is not installed/required while: 
`pip install usearch` 

### Can you contribute to the implementation?

- [ ] I can contribute

### Is your feature request specific to a certain interface?

It applies to everything

### Contact Details

mtasic85@gmail.com

### 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:
Already did that on the `main-dev` a while ago, just a matter of polishing the #318 and releasing 🤗 
I see https://github.com/unum-cloud/usearch/releases/tag/v2.8.16 but it is not yet on pypi.
Can you upload it there so it is the official release?

Fix patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index cbcec460..506b075e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -105,7 +105,22 @@
     "span": "cpp",
     "format": "cpp",
     "charconv": "cpp",
-    "strstream": "cpp"
+    "strstream": "cpp",
+    "xfacet": "cpp",
+    "xhash": "cpp",
+    "xiosbase": "cpp",
+    "xlocale": "cpp",
+    "xlocbuf": "cpp",
+    "xlocinfo": "cpp",
+    "xlocmes": "cpp",
+    "xlocmon": "cpp",
+    "xlocnum": "cpp",
+    "xloctime": "cpp",
+    "xmemory": "cpp",
+    "xstring": "cpp",
+    "xtr1common": "cpp",
+    "xtree": "cpp",
+    "xutility": "cpp"
   },
   "cSpell.words": [
     "allclose",
@@ -169,7 +184,9 @@
   ],
   "autoDocstring.docstringFormat": "sphinx",
   "java.configuration.updateBuildConfiguration": "interactive",
-  "python.testing.pytestArgs": ["python"],
+  "python.testing.pytestArgs": [
+    "python"
+  ],
   "python.analysis.diagnosticSeverityOverrides": {
     "reportMissingImports": "none"
   },
@@ -181,4 +198,4 @@
     "editor.defaultFormatter": "golang.go"
   },
   "dotnet.defaultSolution": "csharp/Cloud.Unum.USearch.sln"
-}
+}
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2fedc08c..1af23ede 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -291,6 +291,32 @@ cp cloud/unum/usearch/libusearch.* .
 java -cp . -Djava.library.path="$(pwd)" cloud.unum.usearch.Index
 ```
 
+## C#
+
+Setup the .NET environment:
+
+```sh
+dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
+```
+
+USearch provides CSharp bindings, that depend on the C library that must be installed beforehand.
+So one should first compile the C library, link it with CSharp, and only then run tests.
+
+```sh
+cmake -B ./build_artifacts -DUSEARCH_BUILD_LIB_C=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_USE_OPENMP=1 -DUSEARCH_USE_SIMSIMD=1 
+cmake --build ./build_artifacts --config Release -j
+```
+
+Then, on Windows, copy the library to the CSharp project and run the tests:
+
+```sh
+mkdir -p ".\csharp\lib\runtimes\win-x64\native"
+cp ".\build_artifacts\libusearch_c.dll" ".\csharp\lib\runtimes\win-x64\native"
+cd csharp
+dotnet test -c Debug --logger "console;verbosity=detailed"
+dotnet test -c Release
+```
+
 ## Wolfram
 
 ```sh
diff --git a/c/lib.cpp b/c/lib.cpp
index 1ba12764..0642a22f 100644
--- a/c/lib.cpp
+++ b/c/lib.cpp
@@ -106,7 +106,7 @@ extern "C" {
 
 USEARCH_EXPORT usearch_index_t usearch_init(usearch_init_options_t* options, usearch_error_t* error) {
 
-    assert(options && error);
+    assert(options && error && "Missing arguments");
 
     index_dense_config_t config(options->connectivity, options->expansion_add, options->expansion_search);
     config.multi = options->multi;
@@ -132,13 +132,13 @@ USEARCH_EXPORT void usearch_free(usearch_index_t index, usearch_error_t*) {
 }
 
 USEARCH_EXPORT size_t usearch_serialized_length(usearch_index_t index, usearch_error_t*) {
-    assert(index);
+    assert(index && "Missing arguments");
     return reinterpret_cast<index_dense_t*>(index)->serialized_length();
 }
 
 USEARCH_EXPORT void usearch_save(usearch_index_t index, char const* path, usearch_error_t* error) {
 
-    assert(index && path && error);
+    assert(index && path && error && "Missing arguments");
     serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->save(path);
     if (!result)
         *error = result.error.release();
@@ -146,7 +146,7 @@ USEARCH_EXPORT void usearch_save(usearch_index_t index, char const* path, usearc
 
 USEARCH_EXPORT void usearch_load(usearch_index_t index, char const* path, usearch_error_t* error) {
 
-    assert(index && path && error);
+    assert(index && path && error && "Missing arguments");
     serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->load(path);
     if (!result)
         *error = result.error.release();
@@ -154,7 +154,7 @@ USEARCH_EXPORT void usearch_load(usearch_index_t index, char const* path, usearc
 
 USEARCH_EXPORT void usearch_view(usearch_index_t index, char const* path, usearch_error_t* error) {
 
-    assert(index && path && error);
+    assert(index && path && error && "Missing arguments");
     serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->view(path);
     if (!result)
         *error = result.error.release();
@@ -162,7 +162,7 @@ USEARCH_EXPORT void usearch_view(usearch_index_t index, char const* path, usearc
 
 USEARCH_EXPORT void usearch_metadata(char const* path, usearch_init_options_t* options, usearch_error_t* error) {
 
-    assert(path && options && error);
+    assert(path && options && error && "Missing arguments");
     index_dense_metadata_result_t result = index_dense_metadata_from_path(path);
     if (!result)
         *error = result.error.release();
@@ -180,7 +180,7 @@ USEARCH_EXPORT void usearch_metadata(char const* path, usearch_init_options_t* o
 
 USEARCH_EXPORT void usearch_save_buffer(usearch_index_t index, void* buffer, size_t length, usearch_error_t* error) {
 
-    assert(index && buffer && length && error);
+    assert(index && buffer && length && error && "Missing arguments");
     memory_mapped_file_t memory_map((byte_t*)buffer, length);
     serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->save(std::move(memory_map));
     if (!result)
@@ -190,7 +190,7 @@ USEARCH_EXPORT void usearch_save_buffer(usearch_index_t index, void* buffer, siz
 USEARCH_EXPORT void usearch_load_buffer(usearch_index_t index, void const* buffer, size_t length,
                                         usearch_error_t* error) {
 
-    assert(index && buffer && length && error);
+    assert(index && buffer && length && error && "Missing arguments");
     memory_mapped_file_t memory_map((byte_t*)buffer, length);
     serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->load(std::move(memory_map));
     if (!result)
@@ -200,7 +200,7 @@ USEARCH_EXPORT void usearch_load_buffer(usearch_index_t index, void const* buffe
 USEARCH_EXPORT void usearch_view_buffer(usearch_index_t index, void const* buffer, size_t length,
                                         usearch_error_t* error) {
 
-    assert(index && buffer && length && error);
+    assert(index && buffer && length && error && "Missing arguments");
     memory_mapped_file_t memory_map((byte_t*)buffer, length);
     serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->view(std::move(memory_map));
     if (!result)
@@ -210,7 +210,7 @@ USEARCH_EXPORT void usearch_view_buffer(usearch_index_t index, void const* buffe
 USEARCH_EXPORT void usearch_metadata_buffer(void const* buffer, size_t length, usearch_init_options_t* options,
                                             usearch_error_t* error) {
 
-    assert(buffer && length && options && error);
+    assert(buffer && length && options && error && "Missing arguments");
     index_dense_metadata_result_t result =
         index_dense_metadata_from_buffer(memory_mapped_file_t((byte_t*)(buffer), length));
     if (!result)
@@ -244,7 +244,7 @@ USEARCH_EXPORT size_t usearch_connectivity(usearch_index_t index, usearch_error_
 }
 
 USEARCH_EXPORT void usearch_reserve(usearch_index_t index, size_t capacity, usearch_error_t* error) {
-    assert(index && error);
+    assert(index && error && "Missing arguments");
     if (!reinterpret_cast<index_dense_t*>(index)->reserve(capacity))
         *error = "Out of memory!";
 }
@@ -253,19 +253,19 @@ USEARCH_EXPORT void usearch_add(
     usearch_index_t index, usearch_key_t key, void const* vector, usearch_scalar_kind_t kind, //
     usearch_error_t* error) {
 
-    assert(index && vector && error);
+    assert(index && vector && error && "Missing arguments");
     add_result_t result = add_(reinterpret_cast<index_dense_t*>(index), key, vector, scalar_kind_to_cpp(kind));
     if (!result)
         *error = result.error.release();
 }
 
 USEARCH_EXPORT bool usearch_contains(usearch_index_t index, usearch_key_t key, usearch_error_t*) {
-    assert(index);
+    assert(index && "Missing arguments");
     return reinterpret_cast<index_dense_t*>(index)->contains(key);
 }
 
 USEARCH_EXPORT size_t usearch_count(usearch_index_t index, usearch_key_t key, usearch_error_t*) {
-    assert(index);
+    assert(index && "Missing arguments");
     return reinterpret_cast<index_dense_t*>(index)->count(key);
 }
 
@@ -273,7 +273,7 @@ USEARCH_EXPORT size_t usearch_search(
     usearch_index_t index, void const* vector, usearch_scalar_kind_t kind, size_t results_limit, //
     usearch_key_t* found_keys, usearch_distance_t* found_distances, usearch_error_t* error) {
 
-    assert(index && vector && error);
+    assert(index && vector && error && "Missing arguments");
     search_result_t result =
         search_(reinterpret_cast<index_dense_t*>(index), vector, scalar_kind_to_cpp(kind), results_limit);
     if (!result) {
@@ -294,7 +294,7 @@ USEARCH_EXPORT size_t usearch_get(                          //
 
 USEARCH_EXPORT size_t usearch_remove(usearch_index_t index, usearch_key_t key, usearch_error_t* error) {
 
-    assert(index && error);
+    assert(index && error && "Missing arguments");
     labeling_result_t result = reinterpret_cast<index_dense_t*>(index)->remove(key);
     if (!result)
         *error = result.error.release();
@@ -304,7 +304,7 @@ USEARCH_EXPORT size_t usearch_remove(usearch_index_t index, usearch_key_t key, u
 USEARCH_EXPORT size_t usearch_rename( //
     usearch_index_t index, usearch_key_t from, usearch_key_t to, usearch_error_t* error) {
 
-    assert(index && error);
+    assert(index && error && "Missing arguments");
     labeling_result_t result = reinterpret_cast<index_dense_t*>(index)->rename(from, to);
     if (!result)
         *error = result.error.release();
@@ -330,6 +330,8 @@ USEARCH_EXPORT void usearch_exact_search(                             //
     usearch_distance_t* distances, size_t distances_stride,           //
     usearch_error_t* error) {
 
+    assert(dataset && queries && keys && distances && error && "Missing arguments");
+
     metric_punned_t metric(dimensions, metric_kind_to_cpp(metric_kind), scalar_kind_to_cpp(scalar_kind));
     executor_default_t executor(threads);
     static exact_search_t search;
diff --git a/c/usearch.h b/c/usearch.h
index 68bc2261..c6ce27a1 100644
--- a/c/usearch.h
+++ b/c/usearch.h
@@ -39,25 +39,25 @@ USEARCH_EXPORT typedef usearch_distance_t (*usearch_metric_t)(void const*, void
  */
 USEARCH_EXPORT typedef enum usearch_metric_kind_t {
     usearch_metric_unknown_k = 0,
-    usearch_metric_cos_k,
-    usearch_metric_ip_k,
-    usearch_metric_l2sq_k,
-    usearch_metric_haversine_k,
-    usearch_metric_divergence_k,
-    usearch_metric_pearson_k,
-    usearch_metric_jaccard_k,
-    usearch_metric_hamming_k,
-    usearch_metric_tanimoto_k,
-    usearch_metric_sorensen_k,
+    usearch_metric_cos_k = 1,
+    usearch_metric_ip_k = 2,
+    usearch_metric_l2sq_k = 3,
+    usearch_metric_haversine_k = 4,
+    usearch_metric_divergence_k = 5,
+    usearch_metric_pearson_k = 6,
+    usearch_metric_jaccard_k = 7,
+    usearch_metric_hamming_k = 8,
+    usearch_metric_tanimoto_k = 9,
+    usearch_metric_sorensen_k = 10,
 } usearch_metric_kind_t;
 
 USEARCH_EXPORT typedef enum usearch_scalar_kind_t {
     usearch_scalar_unknown_k = 0,
-    usearch_scalar_f32_k,
-    usearch_scalar_f64_k,
-    usearch_scalar_f16_k,
-    usearch_scalar_i8_k,
-    usearch_scalar_b1_k,
+    usearch_scalar_f32_k = 1,
+    usearch_scalar_f64_k = 2,
+    usearch_scalar_f16_k = 3,
+    usearch_scalar_i8_k = 4,
+    usearch_scalar_b1_k = 5,
 } usearch_scalar_kind_t;
 
 USEARCH_EXPORT typedef struct usearch_init_options_t {
diff --git a/csharp/DEV_SETUP.md b/csharp/DEV_SETUP.md
deleted file mode 100644
index 9c0490bf..00000000
--- a/csharp/DEV_SETUP.md
+++ /dev/null
@@ -1,51 +0,0 @@
-This document outlines the steps to build, test, and publish a package to NuGet.
-
-# System setup
-To begin, install the .NET SDK: https://dotnet.microsoft.com/en-us/download
-
-# Compile USearch
-
-Collect the USearch libraries for all target runtimes: Linux, Windows, macOS.  Place these libraries in the ./lib/runtimes/{RID}/native/ directory, where RID stands for Runtime Identifier. For more information on RIDs, visit [official documentation](https://learn.microsoft.com/en-us/dotnet/core/rid-catalog).
-
-To obtain all libraries, download the artifacts generated by the GitHub workflow and place them in the `lib` folder.
-
-To get a library for your current platform (Linux or Macos), execute: `./eng/build_lib.sh`.
-
-For more details, refer to the [compilation guide](https://github.com/unum-cloud/usearch/blob/main/CONTRIBUTING.md).
-
-# Build and Test
-
-## Using dotnet CLI:
-
-    dotnet build -c Release
-    dotnet test -c Release --no-build
-
-## Using script:
-
-For Linux/macOS, run: `./build_and_test.sh`
-
-For Windows, run: `build_and_test.cmd`
-
-# Push to NuGet
-
-ℹ️ **Note** 
-
-For test, use the NuGet test server: https://apiint.nugettest.org/v3/index.json 
-
-For release, use: https://api.nuget.org/v3/index.json
-
-## Using dotnet CLI
-
-To pack specify `PACKAGE_VERSION` and run:
-
-    dotnet pack -c Release -p:Version="${PACKAGE_VERSION}"
-
-To publish, set `PACKAGE_VERSION`, `NUGET_APIKEY`, and `NUGET_SERVER`, then run:
-
-    dotnet nuget push "./packages/Cloud.Unum.USearch.${PACKAGE_VERSION}.nupkg" -k ${NUGET_APIKEY} -s ${NUGET_SERVER}
-
-## Using script
-
-Run the script to clear `packages` folder of older artifacts, then builds and pushes project:
-
-    PACKAGE_ID= PACKAGE_VERSION= NUGET_APIKEY= NUGET_SERVER= ./pack.sh
diff --git a/csharp/pack.sh b/csharp/pack.sh
deleted file mode 100755
index 04f1ed89..00000000
--- a/csharp/pack.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/bash
-
-check_task_status() {
-  local task_name=$1
-  local exit_code=$2
-
-  if [ $exit_code -ne 0 ]; then
-    echo "$task_name failed!"
-    exit 1
-  else
-    echo "$task_name finished successfully!"
-  fi
-}
-
-# Check vars for pack and push are set
-required_vars=("PACKAGE_ID" "PACKAGE_VERSION" "NUGET_APIKEY" "NUGET_SERVER")
-unset_vars=""
-
-for var in "${required_vars[@]}"; do
-  if [ -z "${!var}" ]; then
-    unset_vars="$unset_vars $var"
-  fi
-done
-
-if [ -n "$unset_vars" ]; then
-  echo "The following variables are not set:$unset_vars"
-  exit 1
-fi
-
-# Get absolute path to the usearch dir
-# FIX: realpath is not available on OSX, so use pwd
-SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
-SCRIPT_DIR="${SCRIPT_DIR}/"
-
-# Define project paths
-REPO_ROOT=`cd -P "$SCRIPT_DIR../" && pwd`
-REPO_ROOT="${REPO_ROOT}/"
-
-# Clear package output folder
-rm -rf ./packages/*
-
-# Clean previous outputs
-dotnet clean -c Release -p:RepoRoot="$REPO_ROOT"
-
-# Build
-dotnet build -c Release -p:Version=${PACKAGE_VERSION} -p:RepoRoot="$REPO_ROOT"
-
-# Pack
-dotnet pack -c Release -p:Version=${PACKAGE_VERSION} -p:RepoRoot="$REPO_ROOT"
-EXIT_CODE=$?
-check_task_status "Pack" $EXIT_CODE
-
-# Push
-dotnet nuget push "./packages/${PACKAGE_ID}.*.nupkg" -k ${NUGET_APIKEY} -s ${NUGET_SERVER}
-EXIT_CODE=$?
-check_task_status "NuGet Push" $EXIT_CODE
diff --git a/csharp/src/Cloud.Unum.USearch.Tests/USearchIndexTests.cs b/csharp/src/Cloud.Unum.USearch.Tests/USearchIndexTests.cs
index dd830cd6..0ad98edd 100644
--- a/csharp/src/Cloud.Unum.USearch.Tests/USearchIndexTests.cs
+++ b/csharp/src/Cloud.Unum.USearch.Tests/USearchIndexTests.cs
@@ -1,3 +1,4 @@
+﻿using System.Diagnostics;
 using System.Numerics;
 using System.Runtime.InteropServices;
 using Cloud.Unum.USearch;
@@ -39,6 +40,45 @@ public void IndexOptions_InitializesEmptyArguments_ConstructedWithMinimalOrUnkno
         Assert.Equal(controlIndexOptions, indexOptions);
     }
 
+    [Fact]
+    public void PersistAndRestore()
+    {
+
+        string pathUsearch = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "savedVectorFolder");
+        if (!Directory.Exists(pathUsearch))
+        {
+            Directory.CreateDirectory(pathUsearch);
+        };
+
+        var savedPath = Path.Combine(pathUsearch, "tmp.usearch");
+
+        using var index = new USearchIndex(
+            metricKind: MetricKind.Pearson, // Overwrite the default metric
+            quantization: ScalarKind.Float64, // Don't quantize at all - max precision
+            dimensions: 3,  // Define the number of dimensions in input vectors
+            connectivity: 11, // How frequent should the connections in the graph be, optional
+            expansionAdd: 15, // Control the recall of indexing, optional
+            expansionSearch: 19 // Control the quality of search, optional
+        );
+
+        var vector = new float[] { 0.2f, 0.6f, 0.4f };
+        index.Add(42, vector);
+        index.Save(savedPath);
+
+        Trace.Assert(File.Exists(savedPath));
+        Trace.Assert(File.Exists(Path.Combine(pathUsearch, "tmp.usearch")));
+
+        using var indexRestored = new USearchIndex(savedPath);
+        int matches = indexRestored.Search(vector, 10, out ulong[] keys, out float[] distances);
+        Trace.Assert(indexRestored.Size() == 1);
+        Trace.Assert(matches == 1);
+        Trace.Assert(keys[0] == 42);
+        Trace.Assert(distances[0] <= 0.001f);
+
+        // Clean-up
+        File.Delete(savedPath);
+    }
+
     [Fact]
     public void USearch_InitializesWithRequiredArgumentsSetToMinimalOrUnknownValue_UpdatesIndexOptions()
     {
diff --git a/csharp/src/Cloud.Unum.USearch/USearchTypes.cs b/csharp/src/Cloud.Unum.USearch/USearchTypes.cs
index 2757ff8c..4e492693 100644
--- a/csharp/src/Cloud.Unum.USearch/USearchTypes.cs
+++ b/csharp/src/Cloud.Unum.USearch/USearchTypes.cs
@@ -6,25 +6,26 @@ namespace Cloud.Unum.USearch;
 public enum MetricKind : uint
 {
     Unknown = 0,
-    Cos,
-    Ip,
-    L2sq,
-    Haversine,
-    Pearson,
-    Jaccard,
-    Hamming,
-    Tanimoto,
-    Sorensen,
+    Cos = 1,
+    Ip = 2,
+    L2sq = 3,
+    Haversine = 4,
+    Divergence = 5,
+    Pearson = 6,
+    Jaccard = 7,
+    Hamming = 8,
+    Tanimoto = 9,
+    Sorensen = 10,
 }
 
 public enum ScalarKind : uint
 {
     Unknown = 0,
-    Float32,
-    Float64,
-    Float16,
-    Int8,
-    Byte1,
+    Float32 = 1,
+    Float64 = 2,
+    Float16 = 3,
+    Int8 = 4,
+    Bits1 = 5,
 }
 
 // TODO: implement custom metric delegate
diff --git a/include/usearch/index.hpp b/include/usearch/index.hpp
index 4e318ede..7c99a8b0 100644
--- a/include/usearch/index.hpp
+++ b/include/usearch/index.hpp
@@ -2543,8 +2543,8 @@ class index_gt {
         if (!next.reserve(expansion))
             return result.failed("Out of memory!");
 
-        result.cluster.member =
-            at(search_for_one_(query, metric, prefetch, entry_slot_, max_level_, level - 1, context));
+        result.cluster.member = at(search_for_one_(query, metric, prefetch, entry_slot_, max_level_,
+                                                   static_cast<level_t>(level - 1), context));
         result.cluster.distance = context.measure(query, result.cluster.member, metric);
 
         // Normalize stats
@@ -3422,7 +3422,7 @@ class index_gt {
         distance_t radius = context.measure(query, citerator_at(start_slot), metric);
         next.insert_reserved({-radius, static_cast<compressed_slot_t>(start_slot)});
         top.insert_reserved({radius, static_cast<compressed_slot_t>(start_slot)});
-        visits.set(start_slot);
+        visits.set(static_cast<compressed_slot_t>(start_slot));
 
         while (!next.empty()) {
 
@@ -3496,7 +3496,7 @@ class index_gt {
         distance_t radius = context.measure(query, citerator_at(start_slot), metric);
         next.insert_reserved({-radius, static_cast<compressed_slot_t>(start_slot)});
         top.insert_reserved({radius, static_cast<compressed_slot_t>(start_slot)});
-        visits.set(start_slot);
+        visits.set(static_cast<compressed_slot_t>(start_slot));
 
         while (!next.empty()) {
 
diff --git a/include/usearch/index_dense.hpp b/include/usearch/index_dense.hpp
index e151b929..95dbb68e 100644
--- a/include/usearch/index_dense.hpp
+++ b/include/usearch/index_dense.hpp
@@ -199,7 +199,7 @@ inline index_dense_metadata_result_t index_dense_metadata_from_path(char const*
 
     // Check if it starts with 32-bit
     if (offset_if_u32 + sizeof(index_dense_head_buffer_t) < file_size) {
-        if (std::fseek(file.get(), offset_if_u32, SEEK_SET) != 0)
+        if (std::fseek(file.get(), static_cast<long>(offset_if_u32), SEEK_SET) != 0)
             return result.failed(std::strerror(errno));
         read = std::fread(result.head_buffer, sizeof(index_dense_head_buffer_t), 1, file.get());
         if (!read)
@@ -213,7 +213,7 @@ inline index_dense_metadata_result_t index_dense_metadata_from_path(char const*
 
     // Check if it starts with 64-bit
     if (offset_if_u64 + sizeof(index_dense_head_buffer_t) < file_size) {
-        if (std::fseek(file.get(), offset_if_u64, SEEK_SET) != 0)
+        if (std::fseek(file.get(), static_cast<long>(offset_if_u64), SEEK_SET) != 0)
             return result.failed(std::strerror(errno));
         read = std::fread(result.head_buffer, sizeof(index_dense_head_buffer_t), 1, file.get());
         if (!read)
@@ -929,8 +929,10 @@ class index_dense_gt {
             if (head.kind_compressed_slot != unum::usearch::scalar_kind<compressed_slot_t>())
                 return result.failed("Slot type doesn't match, consider rebuilding");
 
-            metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
             config_.multi = head.multi;
+            metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
+            cast_buffer_.resize(available_threads_.size() * metric_.bytes_per_vector());
+            casts_ = make_casts_(head.kind_scalar);
         }
 
         // Pull the actual proximity graph
@@ -1013,8 +1015,10 @@ class index_dense_gt {
             if (head.kind_compressed_slot != unum::usearch::scalar_kind<compressed_slot_t>())
                 return result.failed("Slot type doesn't match, consider rebuilding");
 
-            metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
             config_.multi = head.multi;
+            metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
+            cast_buffer_.resize(available_threads_.size() * metric_.bytes_per_vector());
+            casts_ = make_casts_(head.kind_scalar);
             offset += sizeof(buffer);
         }
 
diff --git a/include/usearch/index_plugins.hpp b/include/usearch/index_plugins.hpp
index c2ecec24..0f9c3739 100644
--- a/include/usearch/index_plugins.hpp
+++ b/include/usearch/index_plugins.hpp
@@ -1006,10 +1006,11 @@ template <typename scalar_at = float, typename result_at = scalar_at> struct met
 #elif defined(USEARCH_DEFINED_GCC)
 #pragma GCC ivdep
 #endif
-        for (std::size_t i = 0; i != dim; ++i)
-            ab += result_t(a[i]) * result_t(b[i]), //
-                a2 += square<result_t>(a[i]),      //
-                b2 += square<result_t>(b[i]);
+        for (std::size_t i = 0; i != dim; ++i) {
+            result_t ai = static_cast<result_t>(a[i]);
+            result_t bi = static_cast<result_t>(b[i]);
+            ab += ai * bi, a2 += square(ai), b2 += square(bi);
+        }
 
         result_t result_if_zero[2][2];
         result_if_zero[0][0] = 1 - ab / (std::sqrt(a2) * std::sqrt(b2));
@@ -1036,8 +1037,11 @@ template <typename scalar_at = float, typename result_at = scalar_at> struct met
 #elif defined(USEARCH_DEFINED_GCC)
 #pragma GCC ivdep
 #endif
-        for (std::size_t i = 0; i != dim; ++i)
-            ab_deltas_sq += square(result_t(a[i]) - result_t(b[i]));
+        for (std::size_t i = 0; i != dim; ++i) {
+            result_t ai = static_cast<result_t>(a[i]);
+            result_t bi = static_cast<result_t>(b[i]);
+            ab_deltas_sq += square(ai - bi);
+        }
         return ab_deltas_sq;
     }
 };
@@ -1095,9 +1099,10 @@ template <typename scalar_at = std::uint64_t, typename result_at = float> struct
 #elif defined(USEARCH_DEFINED_GCC)
 #pragma GCC ivdep
 #endif
-        for (std::size_t i = 0; i != words; ++i)
-            and_count += std::bitset<bits_per_word_k>(a[i] & b[i]).count(),
-                or_count += std::bitset<bits_per_word_k>(a[i] | b[i]).count();
+        for (std::size_t i = 0; i != words; ++i) {
+            and_count += std::bitset<bits_per_word_k>(a[i] & b[i]).count();
+            or_count += std::bitset<bits_per_word_k>(a[i] | b[i]).count();
+        }
         return 1 - result_t(and_count) / or_count;
     }
 };
@@ -1126,9 +1131,10 @@ template <typename scalar_at = std::uint64_t, typename result_at = float> struct
 #elif defined(USEARCH_DEFINED_GCC)
 #pragma GCC ivdep
 #endif
-        for (std::size_t i = 0; i != words; ++i)
-            and_count += std::bitset<bits_per_word_k>(a[i] & b[i]).count(),
-                any_count += std::bitset<bits_per_word_k>(a[i]).count() + std::bitset<bits_per_word_k>(b[i]).count();
+        for (std::size_t i = 0; i != words; ++i) {
+            and_count += std::bitset<bits_per_word_k>(a[i] & b[i]).count();
+            any_count += std::bitset<bits_per_word_k>(a[i]).count() + std::bitset<bits_per_word_k>(b[i]).count();
+        }
         return 1 - 2 * result_t(and_count) / any_count;
     }
 };
@@ -1176,11 +1182,13 @@ template <typename scalar_at = float, typename result_at = float> struct metric_
 #pragma GCC ivdep
 #endif
         for (std::size_t i = 0; i != dim; ++i) {
-            a_sum += result_t(a[i]);
-            b_sum += result_t(b[i]);
-            ab_sum += result_t(a[i]) * result_t(b[i]);
-            a_sq_sum += result_t(a[i]) * result_t(a[i]);
-            b_sq_sum += result_t(b[i]) * result_t(b[i]);
+            result_t ai = static_cast<result_t>(a[i]);
+            result_t bi = static_cast<result_t>(b[i]);
+            a_sum += ai;
+            b_sum += bi;
+            ab_sum += ai * bi;
+            a_sq_sum += ai * ai;
+            b_sq_sum += bi * bi;
         }
         result_t denom = std::sqrt((dim * a_sq_sum - a_sum * a_sum) * (dim * b_sq_sum - b_sum * b_sum));
         result_t corr = (dim * ab_sum - a_sum * b_sum) / denom;
@@ -1197,7 +1205,7 @@ template <typename scalar_at = float, typename result_at = float> struct metric_
 
     inline result_t operator()(scalar_t const* p, scalar_t const* q, std::size_t dim) const noexcept {
         result_t kld_pm{}, kld_qm{};
-        scalar_t epsilon = std::numeric_limits<scalar_t>::epsilon();
+        result_t epsilon = std::numeric_limits<result_t>::epsilon();
 #if USEARCH_USE_OPENMP
 #pragma omp simd reduction(+ : kld_pm, kld_qm)
 #elif defined(USEARCH_DEFINED_CLANG)
@@ -1206,9 +1214,11 @@ template <typename scalar_at = float, typename result_at = float> struct metric_
 #pragma GCC ivdep
 #endif
         for (std::size_t i = 0; i != dim; ++i) {
-            result_t mi = result_t(p[i] + q[i]) / 2 + epsilon;
-            kld_pm += p[i] * std::log((p[i] + epsilon) / mi);
-            kld_qm += q[i] * std::log((q[i] + epsilon) / mi);
+            result_t pi = static_cast<result_t>(p[i]);
+            result_t qi = static_cast<result_t>(q[i]);
+            result_t mi = (pi + qi) / 2 + epsilon;
+            kld_pm += pi * std::log((pi + epsilon) / mi);
+            kld_qm += qi * std::log((qi + epsilon) / mi);
         }
         return (kld_pm + kld_qm) / 2;
     }
@@ -1253,7 +1263,7 @@ struct l2sq_i8_t {
 #endif
         for (std::size_t i = 0; i != dim; i++)
             ab_deltas_sq += square(std::int16_t(a[i]) - std::int16_t(b[i]));
-        return ab_deltas_sq;
+        return static_cast<result_t>(ab_deltas_sq);
     }
 };
 
diff --git a/package.json b/package.json
index 3b2299ca..eefbe03b 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,8 @@
     "exports": {
         ".": {
             "types": "./javascript/types/usearch.d.ts",
-            "require": "./javascript/usearch.js"
+            "require": "./javascript/usearch.js",
+            "import": "./javascript/usearch.js"
         },
         "./package.json": "./package.json"
     },


Test patch:
diff --git a/c/test.c b/c/test.c
index 96e08212..919ee117 100644
--- a/c/test.c
+++ b/c/test.c
@@ -1,5 +1,5 @@
 #include <errno.h>
-#include <stdio.h>
+#include <stdio.h> // `remove`
 #include <stdlib.h>
 #include <sys/stat.h>
 
@@ -236,7 +236,13 @@ void test_save_load(size_t const collection_size, size_t const dimensions) {
 
     usearch_error_t error = NULL;
     usearch_init_options_t opts = create_options(dimensions);
-    usearch_index_t index = usearch_init(&opts, &error);
+    usearch_init_options_t weird_ops = opts;
+    weird_ops.connectivity = 11;
+    weird_ops.expansion_add = 15;
+    weird_ops.expansion_search = 19;
+    weird_ops.metric_kind = usearch_metric_pearson_k;
+    weird_ops.quantization = usearch_scalar_f64_k;
+    usearch_index_t index = usearch_init(&weird_ops, &error);
     usearch_reserve(index, collection_size, &error);
 
     // Add vectors
@@ -248,23 +254,32 @@ void test_save_load(size_t const collection_size, size_t const dimensions) {
     }
 
     // Save and free the index
-    usearch_save(index, "usearch_index.bin", &error);
+    usearch_save(index, "tmp.usearch", &error);
     ASSERT(!error, error);
     usearch_free(index, &error);
     ASSERT(!error, error);
 
+    // Reset the options
+    opts.connectivity = 0;
+    opts.dimensions = 0;
+    opts.expansion_add = 0;
+    opts.expansion_search = 0;
+    opts.metric = NULL;
+    opts.quantization = usearch_scalar_unknown_k;
+    opts.metric_kind = usearch_metric_unknown_k;
+
     // Reinit
     index = usearch_init(&opts, &error);
     ASSERT(!error, error);
     ASSERT(usearch_size(index, &error) == 0, error);
 
     // Load
-    usearch_load(index, "usearch_index.bin", &error);
+    usearch_load(index, "tmp.usearch", &error);
     ASSERT(!error, error);
     ASSERT(usearch_size(index, &error) == collection_size, error);
     ASSERT(usearch_capacity(index, &error) == collection_size, error);
     ASSERT(usearch_dimensions(index, &error) == dimensions, error);
-    ASSERT(usearch_connectivity(index, &error) == opts.connectivity, error);
+    ASSERT(usearch_connectivity(index, &error) == weird_ops.connectivity, error);
 
     // Check vectors in the index
     for (size_t i = 0; i < collection_size; ++i) {
@@ -272,8 +287,27 @@ void test_save_load(size_t const collection_size, size_t const dimensions) {
         ASSERT(usearch_contains(index, key, &error), error);
     }
 
+    // Create result buffers
+    usearch_key_t* keys = (usearch_key_t*)malloc(collection_size * sizeof(usearch_key_t));
+    float* distances = (float*)malloc(collection_size * sizeof(float));
+    ASSERT(keys && distances, "Failed to allocate memory");
+
+    // Find the vectors
+    for (size_t i = 0; i < collection_size; i++) {
+        size_t found_count = usearch_search(index, data + i * dimensions, usearch_scalar_f32_k, collection_size, keys,
+                                            distances, &error);
+        ASSERT(!error, error);
+        ASSERT(found_count >= 1 && found_count <= collection_size, "Vector is missing");
+    }
+
     free(data);
+    free(keys);
+    free(distances);
     usearch_free(index, &error);
+
+    // Remove the file from disk
+    remove("tmp.usearch");
+
     printf("Test: Save/Load - PASSED\n");
 }
 
@@ -300,7 +334,7 @@ void test_view(size_t const collection_size, size_t const dimensions) {
     }
 
     // Save and free the index
-    usearch_save(index, "usearch_index.bin", &error);
+    usearch_save(index, "tmp.usearch", &error);
     ASSERT(!error, error);
     usearch_free(index, &error);
     ASSERT(!error, error);
@@ -310,7 +344,7 @@ void test_view(size_t const collection_size, size_t const dimensions) {
     ASSERT(!error, error);
 
     // View
-    usearch_view(index, "usearch_index.bin", &error);
+    usearch_view(index, "tmp.usearch", &error);
     ASSERT(!error, error);
 
     free(data);
diff --git a/csharp/build_and_test.cmd b/csharp/build_and_test.cmd
deleted file mode 100644
index 77f73b12..00000000
--- a/csharp/build_and_test.cmd
+++ /dev/null
@@ -1,31 +0,0 @@
-@echo off
-setlocal EnableDelayedExpansion
-
-:: Get the absolute paths of the directory where the script is located and repo root
-set SCRIPT_DIR=%~dp0
-set REPO_ROOT=%SCRIPT_DIR%..\\
-
-:: Switch to the C# directory
-cd %REPO_ROOT%csharp\
-
-:: Clean and build the .NET project
-dotnet clean -c Release -p:RepoRoot="%REPO_ROOT%"
-dotnet build -c Release -p:RepoRoot="%REPO_ROOT%"
-if %ERRORLEVEL% neq 0 (
-    echo Build failed!
-    exit /b 1
-)
-
-:: Run the tests
-dotnet test -c Release -p:RepoRoot="%REPO_ROOT%" --no-build
-set EXIT_CODE=%ERRORLEVEL%
-
-if %EXIT_CODE% neq 0 (
-    echo Tests failed!
-    exit /b 1
-) else (
-    echo Tests passed successfully!
-    exit /b 0
-)
-
-endlocal
diff --git a/csharp/build_and_test.sh b/csharp/build_and_test.sh
deleted file mode 100755
index 50cf22ce..00000000
--- a/csharp/build_and_test.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-
-# Get absolute path to the usearch dir
-# FIX: realpath is not available on OSX, so use pwd
-SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
-SCRIPT_DIR="${SCRIPT_DIR}/"
-
-# Define project paths
-REPO_ROOT=`cd -P "$SCRIPT_DIR../" && pwd`
-REPO_ROOT="${REPO_ROOT}/"
-
-# Clean and build the .NET project with RepoRoot property
-dotnet clean -c Release -p:RepoRoot="$REPO_ROOT"
-dotnet build -c Release -p:RepoRoot="$REPO_ROOT"
-if [ $? -ne 0 ]; then
-  echo "Build failed!"
-  exit 1
-fi
-
-# Run the tests
-dotnet test -c Release -p:RepoRoot="$REPO_ROOT" --no-build
-EXIT_CODE=$?
-
-if [ $EXIT_CODE -ne 0 ]; then
-  echo "Tests failed!"
-  exit 1
-else
-  echo "Tests passed successfully!"
-  exit 0
-fi