usearch 2.9.2 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +39 -38
  2. package/javascript/{types → dist/cjs}/usearch.d.ts +90 -115
  3. package/javascript/dist/cjs/usearch.js +531 -0
  4. package/javascript/dist/esm/usearch.d.ts +212 -0
  5. package/javascript/{usearch.js → dist/esm/usearch.js} +199 -153
  6. package/package.json +25 -23
  7. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  8. package/prebuilds/linux-arm64/usearch.node +0 -0
  9. package/prebuilds/linux-x64/usearch.node +0 -0
  10. package/prebuilds/win32-ia32/usearch.node +0 -0
  11. package/prebuilds/win32-x64/usearch.node +0 -0
  12. package/.clang-format +0 -10
  13. package/.clang-tidy +0 -60
  14. package/.cmake-format.py +0 -19
  15. package/.dockerignore +0 -93
  16. package/.gitattributes +0 -9
  17. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
  18. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  19. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -64
  20. package/.github/workflows/Dockerfile.libs +0 -36
  21. package/.github/workflows/prerelease.yml +0 -331
  22. package/.github/workflows/release.yml +0 -722
  23. package/.github/workflows/update_version.sh +0 -15
  24. package/.gitmodules +0 -9
  25. package/.releaserc +0 -90
  26. package/CITATION.cff +0 -11
  27. package/CMakeLists.txt +0 -324
  28. package/CODE_OF_CONDUCT.md +0 -128
  29. package/CONTRIBUTING.md +0 -361
  30. package/Cargo.toml +0 -19
  31. package/Dockerfile +0 -16
  32. package/VERSION +0 -1
  33. package/assets/unum.png +0 -0
  34. package/assets/usearch-approaches-transparent.png +0 -0
  35. package/assets/usearch-approaches-white.png +0 -0
  36. package/assets/usearch-neighbor-types.png +0 -0
  37. package/binding.gyp +0 -87
  38. package/build.gradle +0 -204
  39. package/build.rs +0 -18
  40. package/c/CMakeLists.txt +0 -25
  41. package/c/README.md +0 -34
  42. package/c/lib.cpp +0 -358
  43. package/c/test.c +0 -382
  44. package/c/usearch.h +0 -311
  45. package/cmake/clipp.cmake +0 -7
  46. package/cmake/config.cmake.in +0 -15
  47. package/cmake/emscripten.cmake +0 -8
  48. package/cmake/jemalloc.cmake +0 -62
  49. package/cmake/pkg-config.pc.in +0 -4
  50. package/cmake/usearchConfigVersion.cmake.in +0 -20
  51. package/conanfile.py +0 -26
  52. package/cpp/CMakeLists.txt +0 -14
  53. package/cpp/README.md +0 -161
  54. package/cpp/bench.cpp +0 -618
  55. package/cpp/test.cpp +0 -323
  56. package/fp16/CMakeLists.txt +0 -176
  57. package/fp16/LICENSE +0 -11
  58. package/fp16/README.md +0 -20
  59. package/fp16/bench/alt-element.cc +0 -89
  60. package/fp16/bench/from-alt-array.cc +0 -175
  61. package/fp16/bench/from-ieee-array.cc +0 -399
  62. package/fp16/bench/ieee-element.cc +0 -272
  63. package/fp16/bench/to-alt-array.cc +0 -91
  64. package/fp16/bench/to-ieee-array.cc +0 -303
  65. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  66. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  67. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  68. package/fp16/configure.py +0 -65
  69. package/fp16/confu.yaml +0 -8
  70. package/fp16/include/fp16/__init__.py +0 -0
  71. package/fp16/include/fp16/avx.py +0 -50
  72. package/fp16/include/fp16/avx2.py +0 -53
  73. package/fp16/include/fp16/bitcasts.h +0 -92
  74. package/fp16/include/fp16/fp16.h +0 -451
  75. package/fp16/include/fp16/psimd.h +0 -131
  76. package/fp16/include/fp16.h +0 -11
  77. package/fp16/jni/Android.mk +0 -6
  78. package/fp16/jni/Application.mk +0 -4
  79. package/fp16/test/alt-from-fp32-value.cc +0 -498
  80. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  81. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  82. package/fp16/test/alt-to-fp32-value.cc +0 -348
  83. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  84. package/fp16/test/bitcasts.cc +0 -56
  85. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  86. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  87. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  88. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  89. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  90. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  91. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  92. package/fp16/test/peachpy/stubs.py +0 -38
  93. package/fp16/test/tables.cc +0 -530
  94. package/fp16/test/tables.h +0 -13
  95. package/fp16/third-party/THHalf.h +0 -92
  96. package/fp16/third-party/eigen-half.h +0 -170
  97. package/fp16/third-party/float16-compressor.h +0 -80
  98. package/fp16/third-party/half.hpp +0 -2916
  99. package/fp16/third-party/npy-halffloat.h +0 -328
  100. package/include/usearch/index.hpp +0 -3836
  101. package/include/usearch/index_dense.hpp +0 -2004
  102. package/include/usearch/index_plugins.hpp +0 -2213
  103. package/javascript/lib.cpp +0 -341
  104. package/javascript/types/usearch.d.ts.map +0 -1
  105. package/javascript/usearch.test.js +0 -74
  106. package/package-ci.json +0 -10
  107. package/pyproject.toml +0 -51
  108. package/setup.py +0 -201
  109. package/simsimd/.releaserc +0 -86
  110. package/simsimd/CONTRIBUTING.md +0 -88
  111. package/simsimd/Cargo.lock +0 -709
  112. package/simsimd/Cargo.toml +0 -44
  113. package/simsimd/LICENSE +0 -201
  114. package/simsimd/README.md +0 -317
  115. package/simsimd/binding.gyp +0 -36
  116. package/simsimd/build.rs +0 -20
  117. package/simsimd/include/simsimd/binary.h +0 -245
  118. package/simsimd/include/simsimd/complex.h +0 -174
  119. package/simsimd/include/simsimd/probability.h +0 -548
  120. package/simsimd/include/simsimd/simsimd.h +0 -465
  121. package/simsimd/include/simsimd/spatial.h +0 -1267
  122. package/simsimd/include/simsimd/types.h +0 -208
  123. package/simsimd/javascript/bench.js +0 -127
  124. package/simsimd/javascript/fallback.ts +0 -193
  125. package/simsimd/javascript/lib.c +0 -102
  126. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  127. package/simsimd/javascript/simsimd.ts +0 -124
  128. package/simsimd/javascript/test.mjs +0 -153
  129. package/simsimd/javascript/tsconfig-base.json +0 -25
  130. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  131. package/simsimd/javascript/tsconfig-esm.json +0 -8
  132. package/simsimd/package-lock.json +0 -1688
  133. package/simsimd/package.json +0 -61
  134. package/simsimd/rust/benches/cosine.rs +0 -27
  135. package/simsimd/rust/benches/native.rs +0 -30
  136. package/simsimd/rust/benches/sqeuclidean.rs +0 -27
  137. package/simsimd/rust/lib.c +0 -45
  138. package/simsimd/rust/lib.rs +0 -550
  139. package/sqlite/README.md +0 -178
  140. package/sqlite/lib.cpp +0 -325
  141. package/stringzilla/CONTRIBUTING.md +0 -446
  142. package/stringzilla/Cargo.lock +0 -25
  143. package/stringzilla/Cargo.toml +0 -26
  144. package/stringzilla/LICENSE +0 -201
  145. package/stringzilla/README.md +0 -1422
  146. package/stringzilla/VERSION +0 -1
  147. package/stringzilla/assets/cover-strinzilla.jpeg +0 -0
  148. package/stringzilla/assets/meme-stringzilla-v2.jpeg +0 -0
  149. package/stringzilla/assets/meme-stringzilla-v3.jpeg +0 -0
  150. package/stringzilla/binding.gyp +0 -16
  151. package/stringzilla/build.rs +0 -87
  152. package/stringzilla/c/lib.c +0 -337
  153. package/stringzilla/cli/README.md +0 -65
  154. package/stringzilla/cli/__init__.py +0 -0
  155. package/stringzilla/cli/split.py +0 -105
  156. package/stringzilla/cli/wc.py +0 -117
  157. package/stringzilla/include/stringzilla/experimental.h +0 -845
  158. package/stringzilla/include/stringzilla/module.modulemap +0 -9
  159. package/stringzilla/include/stringzilla/stringzilla.h +0 -5370
  160. package/stringzilla/include/stringzilla/stringzilla.hpp +0 -3838
  161. package/stringzilla/javascript/lib.c +0 -125
  162. package/stringzilla/javascript/stringzilla.js +0 -22
  163. package/stringzilla/package-ci.json +0 -10
  164. package/stringzilla/package.json +0 -32
  165. package/stringzilla/rust/lib.rs +0 -873
  166. package/stringzilla/swift/StringProtocol+StringZilla.swift +0 -280
  167. package/stringzilla/swift/Test.swift +0 -60
  168. package/tsconfig.json +0 -21
  169. package/wasmer.toml +0 -12
  170. package/winlibinstaller.bat +0 -42
  171. /package/{simsimd/javascript/dist-package-cjs.json → javascript/dist/cjs/package.json} +0 -0
  172. /package/{simsimd/javascript/dist-package-esm.json → javascript/dist/esm/package.json} +0 -0
@@ -1,873 +0,0 @@
1
- #![cfg_attr(not(test), no_std)]
2
-
3
- /// The `sz` module provides a collection of string searching and manipulation functionality,
4
- /// designed for high efficiency and compatibility with no_std environments. This module offers
5
- /// various utilities for byte string manipulation, including search, reverse search, and
6
- /// edit-distance calculations, suitable for a wide range of applications from basic string
7
- /// processing to complex text analysis tasks.
8
-
9
- pub mod sz {
10
-
11
- use core::ffi::c_void;
12
-
13
- // Import the functions from the StringZilla C library.
14
- extern "C" {
15
- fn sz_find(
16
- haystack: *const c_void,
17
- haystack_length: usize,
18
- needle: *const c_void,
19
- needle_length: usize,
20
- ) -> *const c_void;
21
-
22
- fn sz_rfind(
23
- haystack: *const c_void,
24
- haystack_length: usize,
25
- needle: *const c_void,
26
- needle_length: usize,
27
- ) -> *const c_void;
28
-
29
- fn sz_find_char_from(
30
- haystack: *const c_void,
31
- haystack_length: usize,
32
- needle: *const c_void,
33
- needle_length: usize,
34
- ) -> *const c_void;
35
-
36
- fn sz_rfind_char_from(
37
- haystack: *const c_void,
38
- haystack_length: usize,
39
- needle: *const c_void,
40
- needle_length: usize,
41
- ) -> *const c_void;
42
-
43
- fn sz_find_char_not_from(
44
- haystack: *const c_void,
45
- haystack_length: usize,
46
- needle: *const c_void,
47
- needle_length: usize,
48
- ) -> *const c_void;
49
-
50
- fn sz_rfind_char_not_from(
51
- haystack: *const c_void,
52
- haystack_length: usize,
53
- needle: *const c_void,
54
- needle_length: usize,
55
- ) -> *const c_void;
56
-
57
- fn sz_edit_distance(
58
- haystack1: *const c_void,
59
- haystack1_length: usize,
60
- haystack2: *const c_void,
61
- haystack2_length: usize,
62
- bound: usize,
63
- allocator: *const c_void,
64
- ) -> usize;
65
-
66
- fn sz_edit_distance_utf8(
67
- haystack1: *const c_void,
68
- haystack1_length: usize,
69
- haystack2: *const c_void,
70
- haystack2_length: usize,
71
- bound: usize,
72
- allocator: *const c_void,
73
- ) -> usize;
74
-
75
- fn sz_hamming_distance(
76
- haystack1: *const c_void,
77
- haystack1_length: usize,
78
- haystack2: *const c_void,
79
- haystack2_length: usize,
80
- bound: usize,
81
- ) -> usize;
82
-
83
- fn sz_hamming_distance_utf8(
84
- haystack1: *const c_void,
85
- haystack1_length: usize,
86
- haystack2: *const c_void,
87
- haystack2_length: usize,
88
- bound: usize,
89
- ) -> usize;
90
-
91
- fn sz_alignment_score(
92
- haystack1: *const c_void,
93
- haystack1_length: usize,
94
- haystack2: *const c_void,
95
- haystack2_length: usize,
96
- matrix: *const c_void,
97
- gap: i8,
98
- allocator: *const c_void,
99
- ) -> isize;
100
-
101
- // type RandomGeneratorT = fn(*mut c_void) -> u64;
102
-
103
- fn sz_generate(
104
- alphabet: *const c_void,
105
- alphabet_size: usize,
106
- text: *mut c_void,
107
- length: usize,
108
- generate: *const c_void,
109
- generator: *mut c_void,
110
- );
111
- }
112
-
113
- /// Locates the first matching substring within `haystack` that equals `needle`.
114
- /// This function is similar to the `memmem()` function in LibC, but, unlike `strstr()`,
115
- /// it requires the length of both haystack and needle to be known beforehand.
116
- ///
117
- /// # Arguments
118
- ///
119
- /// * `haystack`: The byte slice to search.
120
- /// * `needle`: The byte slice to find within the haystack.
121
- ///
122
- /// # Returns
123
- ///
124
- /// An `Option<usize>` representing the starting index of the first occurrence of `needle`
125
- /// within `haystack` if found, otherwise `None`.
126
- pub fn find<H, N>(haystack: H, needle: N) -> Option<usize>
127
- where
128
- H: AsRef<[u8]>,
129
- N: AsRef<[u8]>,
130
- {
131
- let haystack_ref = haystack.as_ref();
132
- let needle_ref = needle.as_ref();
133
- let haystack_pointer = haystack_ref.as_ptr() as _;
134
- let haystack_length = haystack_ref.len();
135
- let needle_pointer = needle_ref.as_ptr() as _;
136
- let needle_length = needle_ref.len();
137
- let result = unsafe {
138
- sz_find(
139
- haystack_pointer,
140
- haystack_length,
141
- needle_pointer,
142
- needle_length,
143
- )
144
- };
145
-
146
- if result.is_null() {
147
- None
148
- } else {
149
- Some(unsafe { result.offset_from(haystack_pointer) } as usize)
150
- }
151
- }
152
-
153
- /// Locates the last matching substring within `haystack` that equals `needle`.
154
- /// This function is useful for finding the most recent or last occurrence of a pattern
155
- /// within a byte slice.
156
- ///
157
- /// # Arguments
158
- ///
159
- /// * `haystack`: The byte slice to search.
160
- /// * `needle`: The byte slice to find within the haystack.
161
- ///
162
- /// # Returns
163
- ///
164
- /// An `Option<usize>` representing the starting index of the last occurrence of `needle`
165
- /// within `haystack` if found, otherwise `None`.
166
- pub fn rfind<H, N>(haystack: H, needle: N) -> Option<usize>
167
- where
168
- H: AsRef<[u8]>,
169
- N: AsRef<[u8]>,
170
- {
171
- let haystack_ref = haystack.as_ref();
172
- let needle_ref = needle.as_ref();
173
- let haystack_pointer = haystack_ref.as_ptr() as _;
174
- let haystack_length = haystack_ref.len();
175
- let needle_pointer = needle_ref.as_ptr() as _;
176
- let needle_length = needle_ref.len();
177
- let result = unsafe {
178
- sz_rfind(
179
- haystack_pointer,
180
- haystack_length,
181
- needle_pointer,
182
- needle_length,
183
- )
184
- };
185
-
186
- if result.is_null() {
187
- None
188
- } else {
189
- Some(unsafe { result.offset_from(haystack_pointer) } as usize)
190
- }
191
- }
192
-
193
- /// Finds the index of the first character in `haystack` that is also present in `needles`.
194
- /// This function is particularly useful for parsing and tokenization tasks where a set of
195
- /// delimiter characters is used.
196
- ///
197
- /// # Arguments
198
- ///
199
- /// * `haystack`: The byte slice to search.
200
- /// * `needles`: The set of bytes to search for within the haystack.
201
- ///
202
- /// # Returns
203
- ///
204
- /// An `Option<usize>` representing the index of the first occurrence of any byte from
205
- /// `needles` within `haystack`, if found, otherwise `None`.
206
- pub fn find_char_from<H, N>(haystack: H, needles: N) -> Option<usize>
207
- where
208
- H: AsRef<[u8]>,
209
- N: AsRef<[u8]>,
210
- {
211
- let haystack_ref = haystack.as_ref();
212
- let needles_ref = needles.as_ref();
213
- let haystack_pointer = haystack_ref.as_ptr() as _;
214
- let haystack_length = haystack_ref.len();
215
- let needles_pointer = needles_ref.as_ptr() as _;
216
- let needles_length = needles_ref.len();
217
- let result = unsafe {
218
- sz_find_char_from(
219
- haystack_pointer,
220
- haystack_length,
221
- needles_pointer,
222
- needles_length,
223
- )
224
- };
225
- if result.is_null() {
226
- None
227
- } else {
228
- Some(unsafe { result.offset_from(haystack_pointer) } as usize)
229
- }
230
- }
231
-
232
- /// Finds the index of the last character in `haystack` that is also present in `needles`.
233
- /// This can be used to find the last occurrence of any character from a specified set,
234
- /// useful in parsing scenarios such as finding the last delimiter in a string.
235
- ///
236
- /// # Arguments
237
- ///
238
- /// * `haystack`: The byte slice to search.
239
- /// * `needles`: The set of bytes to search for within the haystack.
240
- ///
241
- /// # Returns
242
- ///
243
- /// An `Option<usize>` representing the index of the last occurrence of any byte from
244
- /// `needles` within `haystack`, if found, otherwise `None`.
245
- pub fn rfind_char_from<H, N>(haystack: H, needles: N) -> Option<usize>
246
- where
247
- H: AsRef<[u8]>,
248
- N: AsRef<[u8]>,
249
- {
250
- let haystack_ref = haystack.as_ref();
251
- let needles_ref = needles.as_ref();
252
- let haystack_pointer = haystack_ref.as_ptr() as _;
253
- let haystack_length = haystack_ref.len();
254
- let needles_pointer = needles_ref.as_ptr() as _;
255
- let needles_length = needles_ref.len();
256
- let result = unsafe {
257
- sz_rfind_char_from(
258
- haystack_pointer,
259
- haystack_length,
260
- needles_pointer,
261
- needles_length,
262
- )
263
- };
264
- if result.is_null() {
265
- None
266
- } else {
267
- Some(unsafe { result.offset_from(haystack_pointer) } as usize)
268
- }
269
- }
270
-
271
- /// Finds the index of the first character in `haystack` that is not present in `needles`.
272
- /// This function is useful for skipping over a known set of characters and finding the
273
- /// first character that does not belong to that set.
274
- ///
275
- /// # Arguments
276
- ///
277
- /// * `haystack`: The byte slice to search.
278
- /// * `needles`: The set of bytes that should not be matched within the haystack.
279
- ///
280
- /// # Returns
281
- ///
282
- /// An `Option<usize>` representing the index of the first occurrence of any byte not in
283
- /// `needles` within `haystack`, if found, otherwise `None`.
284
- pub fn find_char_not_from<H, N>(haystack: H, needles: N) -> Option<usize>
285
- where
286
- H: AsRef<[u8]>,
287
- N: AsRef<[u8]>,
288
- {
289
- let haystack_ref = haystack.as_ref();
290
- let needles_ref = needles.as_ref();
291
- let haystack_pointer = haystack_ref.as_ptr() as _;
292
- let haystack_length = haystack_ref.len();
293
- let needles_pointer = needles_ref.as_ptr() as _;
294
- let needles_length = needles_ref.len();
295
- let result = unsafe {
296
- sz_find_char_not_from(
297
- haystack_pointer,
298
- haystack_length,
299
- needles_pointer,
300
- needles_length,
301
- )
302
- };
303
- if result.is_null() {
304
- None
305
- } else {
306
- Some(unsafe { result.offset_from(haystack_pointer) } as usize)
307
- }
308
- }
309
-
310
- /// Finds the index of the last character in `haystack` that is not present in `needles`.
311
- /// Useful for text processing tasks such as trimming trailing characters that belong to
312
- /// a specified set.
313
- ///
314
- /// # Arguments
315
- ///
316
- /// * `haystack`: The byte slice to search.
317
- /// * `needles`: The set of bytes that should not be matched within the haystack.
318
- ///
319
- /// # Returns
320
- ///
321
- /// An `Option<usize>` representing the index of the last occurrence of any byte not in
322
- /// `needles` within `haystack`, if found, otherwise `None`.
323
- pub fn rfind_char_not_from<H, N>(haystack: H, needles: N) -> Option<usize>
324
- where
325
- H: AsRef<[u8]>,
326
- N: AsRef<[u8]>,
327
- {
328
- let haystack_ref = haystack.as_ref();
329
- let needles_ref = needles.as_ref();
330
- let haystack_pointer = haystack_ref.as_ptr() as _;
331
- let haystack_length = haystack_ref.len();
332
- let needles_pointer = needles_ref.as_ptr() as _;
333
- let needles_length = needles_ref.len();
334
- let result = unsafe {
335
- sz_rfind_char_not_from(
336
- haystack_pointer,
337
- haystack_length,
338
- needles_pointer,
339
- needles_length,
340
- )
341
- };
342
- if result.is_null() {
343
- None
344
- } else {
345
- Some(unsafe { result.offset_from(haystack_pointer) } as usize)
346
- }
347
- }
348
-
349
- /// Computes the Levenshtein edit distance between two strings, using the Wagner-Fisher
350
- /// algorithm. This measure is widely used in applications like spell-checking, DNA sequence
351
- /// analysis.
352
- ///
353
- /// # Arguments
354
- ///
355
- /// * `first`: The first byte slice.
356
- /// * `second`: The second byte slice.
357
- /// * `bound`: The maximum distance to compute, allowing for early exit.
358
- ///
359
- /// # Returns
360
- ///
361
- /// A `usize` representing the minimum number of single-character edits (insertions,
362
- /// deletions, or substitutions) required to change `first` into `second`.
363
- pub fn edit_distance_bounded<F, S>(first: F, second: S, bound: usize) -> usize
364
- where
365
- F: AsRef<[u8]>,
366
- S: AsRef<[u8]>,
367
- {
368
- let first_ref = first.as_ref();
369
- let second_ref = second.as_ref();
370
- let first_length = first_ref.len();
371
- let second_length = second_ref.len();
372
- let first_pointer = first_ref.as_ptr() as _;
373
- let second_pointer = second_ref.as_ptr() as _;
374
- unsafe {
375
- sz_edit_distance(
376
- first_pointer,
377
- first_length,
378
- second_pointer,
379
- second_length,
380
- // Upper bound on the distance, that allows us to exit early. If zero is
381
- // passed, the maximum possible distance will be equal to the length of
382
- // the longer input.
383
- bound,
384
- // Uses the default allocator
385
- core::ptr::null(),
386
- )
387
- }
388
- }
389
-
390
- /// Computes the Levenshtein edit distance between two UTF8 strings, using the Wagner-Fisher
391
- /// algorithm. This measure is widely used in applications like spell-checking.
392
- ///
393
- /// # Arguments
394
- ///
395
- /// * `first`: The first byte slice.
396
- /// * `second`: The second byte slice.
397
- /// * `bound`: The maximum distance to compute, allowing for early exit.
398
- ///
399
- /// # Returns
400
- ///
401
- /// A `usize` representing the minimum number of single-character edits (insertions,
402
- /// deletions, or substitutions) required to change `first` into `second`.
403
- pub fn edit_distance_utf8_bounded<F, S>(first: F, second: S, bound: usize) -> usize
404
- where
405
- F: AsRef<[u8]>,
406
- S: AsRef<[u8]>,
407
- {
408
- let first_ref = first.as_ref();
409
- let second_ref = second.as_ref();
410
- let first_length = first_ref.len();
411
- let second_length = second_ref.len();
412
- let first_pointer = first_ref.as_ptr() as _;
413
- let second_pointer = second_ref.as_ptr() as _;
414
- unsafe {
415
- sz_edit_distance_utf8(
416
- first_pointer,
417
- first_length,
418
- second_pointer,
419
- second_length,
420
- // Upper bound on the distance, that allows us to exit early. If zero is
421
- // passed, the maximum possible distance will be equal to the length of
422
- // the longer input.
423
- bound,
424
- // Uses the default allocator
425
- core::ptr::null(),
426
- )
427
- }
428
- }
429
-
430
- /// Computes the Levenshtein edit distance between two strings, using the Wagner-Fisher
431
- /// algorithm. This measure is widely used in applications like spell-checking, DNA sequence
432
- /// analysis.
433
- ///
434
- /// # Arguments
435
- ///
436
- /// * `first`: The first byte slice.
437
- /// * `second`: The second byte slice.
438
- ///
439
- /// # Returns
440
- ///
441
- /// A `usize` representing the minimum number of single-character edits (insertions,
442
- /// deletions, or substitutions) required to change `first` into `second`.
443
- pub fn edit_distance<F, S>(first: F, second: S) -> usize
444
- where
445
- F: AsRef<[u8]>,
446
- S: AsRef<[u8]>,
447
- {
448
- edit_distance_bounded(first, second, 0)
449
- }
450
-
451
- /// Computes the Levenshtein edit distance between two UTF8 strings, using the Wagner-Fisher
452
- /// algorithm. This measure is widely used in applications like spell-checking.
453
- ///
454
- /// # Arguments
455
- ///
456
- /// * `first`: The first byte slice.
457
- /// * `second`: The second byte slice.
458
- ///
459
- /// # Returns
460
- ///
461
- /// A `usize` representing the minimum number of single-character edits (insertions,
462
- /// deletions, or substitutions) required to change `first` into `second`.
463
- pub fn edit_distance_utf8<F, S>(first: F, second: S) -> usize
464
- where
465
- F: AsRef<[u8]>,
466
- S: AsRef<[u8]>,
467
- {
468
- edit_distance_utf8_bounded(first, second, 0)
469
- }
470
-
471
- /// Computes the Hamming edit distance between two strings, counting the number of substituted characters.
472
- /// Difference in length is added to the result as well.
473
- ///
474
- /// # Arguments
475
- ///
476
- /// * `first`: The first byte slice.
477
- /// * `second`: The second byte slice.
478
- /// * `bound`: The maximum distance to compute, allowing for early exit.
479
- ///
480
- /// # Returns
481
- ///
482
- /// A `usize` representing the minimum number of single-character edits (substitutions) required to
483
- /// change `first` into `second`.
484
- pub fn hamming_distance_bounded<F, S>(first: F, second: S, bound: usize) -> usize
485
- where
486
- F: AsRef<[u8]>,
487
- S: AsRef<[u8]>,
488
- {
489
- let first_ref = first.as_ref();
490
- let second_ref = second.as_ref();
491
- let first_length = first_ref.len();
492
- let second_length = second_ref.len();
493
- let first_pointer = first_ref.as_ptr() as _;
494
- let second_pointer = second_ref.as_ptr() as _;
495
- unsafe {
496
- sz_hamming_distance(
497
- first_pointer,
498
- first_length,
499
- second_pointer,
500
- second_length,
501
- // Upper bound on the distance, that allows us to exit early. If zero is
502
- // passed, the maximum possible distance will be equal to the length of
503
- // the longer input.
504
- bound,
505
- )
506
- }
507
- }
508
-
509
- /// Computes the Hamming edit distance between two UTF8 strings, counting the number of substituted
510
- /// variable-length characters. Difference in length is added to the result as well.
511
- ///
512
- /// # Arguments
513
- ///
514
- /// * `first`: The first byte slice.
515
- /// * `second`: The second byte slice.
516
- /// * `bound`: The maximum distance to compute, allowing for early exit.
517
- ///
518
- /// # Returns
519
- ///
520
- /// A `usize` representing the minimum number of single-character edits (substitutions) required to
521
- /// change `first` into `second`.
522
- pub fn hamming_distance_utf8_bounded<F, S>(first: F, second: S, bound: usize) -> usize
523
- where
524
- F: AsRef<[u8]>,
525
- S: AsRef<[u8]>,
526
- {
527
- let first_ref = first.as_ref();
528
- let second_ref = second.as_ref();
529
- let first_length = first_ref.len();
530
- let second_length = second_ref.len();
531
- let first_pointer = first_ref.as_ptr() as _;
532
- let second_pointer = second_ref.as_ptr() as _;
533
- unsafe {
534
- sz_hamming_distance_utf8(
535
- first_pointer,
536
- first_length,
537
- second_pointer,
538
- second_length,
539
- // Upper bound on the distance, that allows us to exit early. If zero is
540
- // passed, the maximum possible distance will be equal to the length of
541
- // the longer input.
542
- bound,
543
- )
544
- }
545
- }
546
-
547
- /// Computes the Hamming edit distance between two strings, counting the number of substituted characters.
548
- /// Difference in length is added to the result as well.
549
- ///
550
- /// # Arguments
551
- ///
552
- /// * `first`: The first byte slice.
553
- /// * `second`: The second byte slice.
554
- ///
555
- /// # Returns
556
- ///
557
- /// A `usize` representing the minimum number of single-character edits (substitutions) required to
558
- /// change `first` into `second`.
559
- pub fn hamming_distance<F, S>(first: F, second: S) -> usize
560
- where
561
- F: AsRef<[u8]>,
562
- S: AsRef<[u8]>,
563
- {
564
- hamming_distance_bounded(first, second, 0)
565
- }
566
-
567
- /// Computes the Hamming edit distance between two UTF8 strings, counting the number of substituted
568
- /// variable-length characters. Difference in length is added to the result as well.
569
- ///
570
- /// # Arguments
571
- ///
572
- /// * `first`: The first byte slice.
573
- /// * `second`: The second byte slice.
574
- ///
575
- /// # Returns
576
- ///
577
- /// A `usize` representing the minimum number of single-character edits (substitutions) required to
578
- /// change `first` into `second`.
579
- pub fn hamming_distance_utf8<F, S>(first: F, second: S) -> usize
580
- where
581
- F: AsRef<[u8]>,
582
- S: AsRef<[u8]>,
583
- {
584
- hamming_distance_utf8_bounded(first, second, 0)
585
- }
586
-
587
- /// Computes the Needleman-Wunsch alignment score for two strings. This function is
588
- /// particularly used in bioinformatics for sequence alignment but is also applicable in
589
- /// other domains requiring detailed comparison between two strings, including gap and
590
- /// substitution penalties.
591
- ///
592
- /// # Arguments
593
- ///
594
- /// * `first`: The first byte slice to align.
595
- /// * `second`: The second byte slice to align.
596
- /// * `matrix`: The substitution matrix used for scoring.
597
- /// * `gap`: The penalty for each gap introduced during alignment.
598
- ///
599
- /// # Returns
600
- ///
601
- /// An `isize` representing the total alignment score, where higher scores indicate better
602
- /// alignment between the two strings, considering the specified gap penalties and
603
- /// substitution matrix.
604
- pub fn alignment_score<F, S>(first: F, second: S, matrix: [[i8; 256]; 256], gap: i8) -> isize
605
- where
606
- F: AsRef<[u8]>,
607
- S: AsRef<[u8]>,
608
- {
609
- let first_ref = first.as_ref();
610
- let second_ref = second.as_ref();
611
- let first_length = first_ref.len();
612
- let second_length = second_ref.len();
613
- let first_pointer = first_ref.as_ptr() as _;
614
- let second_pointer = second_ref.as_ptr() as _;
615
- unsafe {
616
- sz_alignment_score(
617
- first_pointer,
618
- first_length,
619
- second_pointer,
620
- second_length,
621
- matrix.as_ptr() as _,
622
- gap,
623
- core::ptr::null(),
624
- )
625
- }
626
- }
627
-
628
- /// Generates a default substitution matrix for use with the Needleman-Wunsch
629
- /// alignment algorithm. This matrix is initialized such that diagonal entries
630
- /// (representing matching characters) are zero, and off-diagonal entries
631
- /// (representing mismatches) are -1. This setup effectively produces distances
632
- /// equal to the negative Levenshtein edit distance, suitable for basic sequence
633
- /// alignment tasks where all mismatches are penalized equally and there are no
634
- /// rewards for matches.
635
- ///
636
- /// # Returns
637
- ///
638
- /// A 256x256 array of `i8`, where each element represents the substitution cost
639
- /// between two characters (byte values). Matching characters are assigned a cost
640
- /// of 0, and non-matching characters are assigned a cost of -1.
641
- pub fn unary_substitution_costs() -> [[i8; 256]; 256] {
642
- let mut result = [[0; 256]; 256];
643
-
644
- for i in 0..256 {
645
- for j in 0..256 {
646
- result[i][j] = if i == j { 0 } else { -1 };
647
- }
648
- }
649
-
650
- result
651
- }
652
-
653
- /// Randomizes the contents of a given byte slice `text` using characters from
654
- /// a specified `alphabet`. This function mutates `text` in place, replacing each
655
- /// byte with a random one from `alphabet`. It is designed for situations where
656
- /// you need to generate random strings or data sequences based on a specific set
657
- /// of characters, such as generating random DNA sequences or testing inputs.
658
- ///
659
- /// # Type Parameters
660
- ///
661
- /// * `T`: The type of the text to be randomized. Must be mutable and convertible to a byte slice.
662
- /// * `A`: The type of the alphabet. Must be convertible to a byte slice.
663
- ///
664
- /// # Arguments
665
- ///
666
- /// * `text`: A mutable reference to the data to randomize. This data will be mutated in place.
667
- /// * `alphabet`: A reference to the byte slice representing the alphabet to use for randomization.
668
- ///
669
- /// # Examples
670
- ///
671
- /// ```
672
- /// use stringzilla::sz;
673
- /// let mut my_text = vec![0; 10]; // A buffer to randomize
674
- /// let alphabet = b"ACTG"; // Using a DNA alphabet
675
- /// sz::randomize(&mut my_text, &alphabet);
676
- /// ```
677
- ///
678
- /// After than, `my_text` is filled with random 'A', 'C', 'T', or 'G' values.
679
- pub fn randomize<T, A>(text: &mut T, alphabet: &A)
680
- where
681
- T: AsMut<[u8]> + ?Sized, // Allows for mutable references to dynamically sized types.
682
- A: AsRef<[u8]> + ?Sized, // Allows for references to dynamically sized types.
683
- {
684
- let text_slice = text.as_mut();
685
- let alphabet_slice = alphabet.as_ref();
686
- unsafe {
687
- sz_generate(
688
- alphabet_slice.as_ptr() as *const c_void,
689
- alphabet_slice.len(),
690
- text_slice.as_mut_ptr() as *mut c_void,
691
- text_slice.len(),
692
- core::ptr::null(),
693
- core::ptr::null_mut(),
694
- );
695
- }
696
- }
697
- }
698
-
699
- /// The [StringZilla] trait provides a collection of string searching and manipulation functionalities.
700
- pub trait StringZilla<N>
701
- where
702
- N: AsRef<[u8]>,
703
- {
704
- fn sz_find(&self, needle: N) -> Option<usize>;
705
- fn sz_rfind(&self, needle: N) -> Option<usize>;
706
- fn sz_find_char_from(&self, needles: N) -> Option<usize>;
707
- fn sz_rfind_char_from(&self, needles: N) -> Option<usize>;
708
- fn sz_find_char_not_from(&self, needles: N) -> Option<usize>;
709
- fn sz_rfind_char_not_from(&self, needles: N) -> Option<usize>;
710
- fn sz_edit_distance(&self, other: N) -> usize;
711
- fn sz_alignment_score(&self, other: N, matrix: [[i8; 256]; 256], gap: i8) -> isize;
712
- }
713
-
714
- impl<T, N> StringZilla<N> for T
715
- where
716
- T: AsRef<[u8]>,
717
- N: AsRef<[u8]>,
718
- {
719
- fn sz_find(&self, needle: N) -> Option<usize> {
720
- sz::find(self, needle)
721
- }
722
-
723
- fn sz_rfind(&self, needle: N) -> Option<usize> {
724
- sz::rfind(self, needle)
725
- }
726
-
727
- fn sz_find_char_from(&self, needles: N) -> Option<usize> {
728
- sz::find_char_from(self, needles)
729
- }
730
-
731
- fn sz_rfind_char_from(&self, needles: N) -> Option<usize> {
732
- sz::rfind_char_from(self, needles)
733
- }
734
-
735
- fn sz_find_char_not_from(&self, needles: N) -> Option<usize> {
736
- sz::find_char_not_from(self, needles)
737
- }
738
-
739
- fn sz_rfind_char_not_from(&self, needles: N) -> Option<usize> {
740
- sz::rfind_char_not_from(self, needles)
741
- }
742
-
743
- fn sz_edit_distance(&self, other: N) -> usize {
744
- sz::edit_distance(self, other)
745
- }
746
-
747
- fn sz_alignment_score(&self, other: N, matrix: [[i8; 256]; 256], gap: i8) -> isize {
748
- sz::alignment_score(self, other, matrix, gap)
749
- }
750
- }
751
-
752
- /// The [MutableStringZilla] trait provides tools for updating strings.
753
- trait MutableStringZilla<A>
754
- where
755
- A: AsRef<[u8]>,
756
- {
757
- /// Generates a random string for a given alphabet.
758
- /// Replaces the buffer with a random string of the same length.
759
- fn sz_randomize(&mut self, alphabet: A);
760
- }
761
-
762
- impl<T, A> MutableStringZilla<A> for T
763
- where
764
- T: AsMut<[u8]>,
765
- A: AsRef<[u8]>,
766
- {
767
- fn sz_randomize(&mut self, alphabet: A) {
768
- let self_mut = self.as_mut();
769
- let alphabet_ref = alphabet.as_ref();
770
- sz::randomize(self_mut, alphabet_ref);
771
- }
772
- }
773
-
774
- #[cfg(test)]
775
- mod tests {
776
- use std::borrow::Cow;
777
-
778
- use crate::sz;
779
- use crate::MutableStringZilla;
780
- use crate::StringZilla;
781
-
782
- #[test]
783
- fn hamming() {
784
- assert_eq!(sz::hamming_distance("hello", "hello"), 0);
785
- assert_eq!(sz::hamming_distance("hello", "hell"), 1);
786
- assert_eq!(sz::hamming_distance("abc", "adc"), 1);
787
-
788
- assert_eq!(sz::hamming_distance_bounded("abcdefgh", "ABCDEFGH", 2), 2);
789
- assert_eq!(sz::hamming_distance_utf8("αβγδ", "αγγδ"), 1);
790
- }
791
-
792
- #[test]
793
- fn levenshtein() {
794
- assert_eq!(sz::edit_distance("hello", "hell"), 1);
795
- assert_eq!(sz::edit_distance("hello", "hell"), 1);
796
- assert_eq!(sz::edit_distance("abc", ""), 3);
797
- assert_eq!(sz::edit_distance("abc", "ac"), 1);
798
- assert_eq!(sz::edit_distance("abc", "a_bc"), 1);
799
- assert_eq!(sz::edit_distance("abc", "adc"), 1);
800
- assert_eq!(sz::edit_distance("fitting", "kitty"), 4);
801
- assert_eq!(sz::edit_distance("smitten", "mitten"), 1);
802
- assert_eq!(sz::edit_distance("ggbuzgjux{}l", "gbuzgjux{}l"), 1);
803
- assert_eq!(sz::edit_distance("abcdefgABCDEFG", "ABCDEFGabcdefg"), 14);
804
-
805
- assert_eq!(sz::edit_distance_bounded("fitting", "kitty", 2), 2);
806
- assert_eq!(sz::edit_distance_utf8("façade", "facade"), 1);
807
- }
808
-
809
- #[test]
810
- fn needleman() {
811
- let costs_vector = sz::unary_substitution_costs();
812
- assert_eq!(
813
- sz::alignment_score("listen", "silent", costs_vector, -1),
814
- -4
815
- );
816
- assert_eq!(
817
- sz::alignment_score("abcdefgABCDEFG", "ABCDEFGabcdefg", costs_vector, -1),
818
- -14
819
- );
820
- assert_eq!(sz::alignment_score("hello", "hello", costs_vector, -1), 0);
821
- assert_eq!(sz::alignment_score("hello", "hell", costs_vector, -1), -1);
822
- }
823
-
824
- #[test]
825
- fn search() {
826
- let my_string: String = String::from("Hello, world!");
827
- let my_str: &str = my_string.as_str();
828
- let my_cow_str: Cow<'_, str> = Cow::from(&my_string);
829
-
830
- // Identical to `memchr::memmem::find` and `memchr::memmem::rfind` functions
831
- assert_eq!(sz::find("Hello, world!", "world"), Some(7));
832
- assert_eq!(sz::rfind("Hello, world!", "world"), Some(7));
833
-
834
- // Use the generic function with a String
835
- assert_eq!(my_string.sz_find("world"), Some(7));
836
- assert_eq!(my_string.sz_rfind("world"), Some(7));
837
- assert_eq!(my_string.sz_find_char_from("world"), Some(2));
838
- assert_eq!(my_string.sz_rfind_char_from("world"), Some(11));
839
- assert_eq!(my_string.sz_find_char_not_from("world"), Some(0));
840
- assert_eq!(my_string.sz_rfind_char_not_from("world"), Some(12));
841
-
842
- // Use the generic function with a &str
843
- assert_eq!(my_str.sz_find("world"), Some(7));
844
- assert_eq!(my_str.sz_find("world"), Some(7));
845
- assert_eq!(my_str.sz_find_char_from("world"), Some(2));
846
- assert_eq!(my_str.sz_rfind_char_from("world"), Some(11));
847
- assert_eq!(my_str.sz_find_char_not_from("world"), Some(0));
848
- assert_eq!(my_str.sz_rfind_char_not_from("world"), Some(12));
849
-
850
- // Use the generic function with a Cow<'_, str>
851
- assert_eq!(my_cow_str.as_ref().sz_find("world"), Some(7));
852
- assert_eq!(my_cow_str.as_ref().sz_find("world"), Some(7));
853
- assert_eq!(my_cow_str.as_ref().sz_find_char_from("world"), Some(2));
854
- assert_eq!(my_cow_str.as_ref().sz_rfind_char_from("world"), Some(11));
855
- assert_eq!(my_cow_str.as_ref().sz_find_char_not_from("world"), Some(0));
856
- assert_eq!(
857
- my_cow_str.as_ref().sz_rfind_char_not_from("world"),
858
- Some(12)
859
- );
860
- }
861
-
862
- #[test]
863
- fn randomize() {
864
- let mut text: Vec<u8> = vec![0; 10]; // A buffer of ten zeros
865
- let alphabet: &[u8] = b"abcd"; // A byte slice alphabet
866
- text.sz_randomize(alphabet);
867
-
868
- // Iterate throught text and check that it only contains letters from the alphabet
869
- assert!(text
870
- .iter()
871
- .all(|&b| b == b'd' || b == b'c' || b == b'b' || b == b'a'));
872
- }
873
- }