usearch 2.10.3 → 2.11.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 (114) hide show
  1. package/README.md +28 -8
  2. package/binding.gyp +101 -0
  3. package/c/CMakeLists.txt +27 -0
  4. package/c/README.md +244 -0
  5. package/c/lib.cpp +459 -0
  6. package/c/test.c +386 -0
  7. package/c/usearch.h +451 -0
  8. package/cmake/clipp.cmake +7 -0
  9. package/cmake/config.cmake.in +15 -0
  10. package/cmake/emscripten.cmake +8 -0
  11. package/cmake/jemalloc.cmake +62 -0
  12. package/cmake/pkg-config.pc.in +4 -0
  13. package/cmake/usearchConfigVersion.cmake.in +20 -0
  14. package/cpp/CMakeLists.txt +14 -0
  15. package/cpp/README.md +161 -0
  16. package/cpp/bench.cpp +616 -0
  17. package/cpp/test.cpp +335 -0
  18. package/fp16/CMakeLists.txt +176 -0
  19. package/fp16/LICENSE +11 -0
  20. package/fp16/README.md +20 -0
  21. package/fp16/bench/alt-element.cc +89 -0
  22. package/fp16/bench/from-alt-array.cc +175 -0
  23. package/fp16/bench/from-ieee-array.cc +399 -0
  24. package/fp16/bench/ieee-element.cc +272 -0
  25. package/fp16/bench/to-alt-array.cc +91 -0
  26. package/fp16/bench/to-ieee-array.cc +303 -0
  27. package/fp16/cmake/DownloadGoogleBenchmark.cmake +15 -0
  28. package/fp16/cmake/DownloadGoogleTest.cmake +15 -0
  29. package/fp16/cmake/DownloadPSimd.cmake +15 -0
  30. package/fp16/configure.py +65 -0
  31. package/fp16/confu.yaml +8 -0
  32. package/fp16/include/fp16/__init__.py +0 -0
  33. package/fp16/include/fp16/avx.py +50 -0
  34. package/fp16/include/fp16/avx2.py +53 -0
  35. package/fp16/include/fp16/bitcasts.h +92 -0
  36. package/fp16/include/fp16/fp16.h +451 -0
  37. package/fp16/include/fp16/psimd.h +131 -0
  38. package/fp16/include/fp16.h +11 -0
  39. package/fp16/jni/Android.mk +6 -0
  40. package/fp16/jni/Application.mk +4 -0
  41. package/fp16/test/alt-from-fp32-value.cc +498 -0
  42. package/fp16/test/alt-to-fp32-bits.cc +263 -0
  43. package/fp16/test/alt-to-fp32-psimd.cc +145 -0
  44. package/fp16/test/alt-to-fp32-value.cc +348 -0
  45. package/fp16/test/alt-to-fp32x2-psimd.cc +245 -0
  46. package/fp16/test/bitcasts.cc +56 -0
  47. package/fp16/test/ieee-from-fp32-value.cc +523 -0
  48. package/fp16/test/ieee-to-fp32-bits.cc +333 -0
  49. package/fp16/test/ieee-to-fp32-psimd.cc +312 -0
  50. package/fp16/test/ieee-to-fp32-value.cc +420 -0
  51. package/fp16/test/ieee-to-fp32x2-psimd.cc +541 -0
  52. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +96 -0
  53. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +96 -0
  54. package/fp16/test/peachpy/stubs.py +38 -0
  55. package/fp16/test/tables.cc +530 -0
  56. package/fp16/test/tables.h +13 -0
  57. package/fp16/third-party/THHalf.h +92 -0
  58. package/fp16/third-party/eigen-half.h +170 -0
  59. package/fp16/third-party/float16-compressor.h +80 -0
  60. package/fp16/third-party/half.hpp +2916 -0
  61. package/fp16/third-party/npy-halffloat.h +328 -0
  62. package/include/usearch/index.hpp +3838 -0
  63. package/include/usearch/index_dense.hpp +2022 -0
  64. package/include/usearch/index_plugins.hpp +2317 -0
  65. package/javascript/dist/cjs/usearch.js +5 -5
  66. package/javascript/dist/esm/usearch.js +2 -2
  67. package/javascript/dist-package-cjs.json +3 -0
  68. package/javascript/dist-package-esm.json +3 -0
  69. package/javascript/lib.cpp +350 -0
  70. package/javascript/node-gyp-build.d.ts +1 -0
  71. package/javascript/tsconfig-base.json +25 -0
  72. package/javascript/tsconfig-cjs.json +8 -0
  73. package/javascript/tsconfig-esm.json +8 -0
  74. package/javascript/usearch.test.js +84 -0
  75. package/javascript/usearch.ts +640 -0
  76. package/package.json +14 -7
  77. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  78. package/prebuilds/linux-arm64/usearch.node +0 -0
  79. package/prebuilds/linux-x64/usearch.node +0 -0
  80. package/prebuilds/win32-ia32/usearch.node +0 -0
  81. package/prebuilds/win32-x64/usearch.node +0 -0
  82. package/simsimd/.releaserc +87 -0
  83. package/simsimd/CONTRIBUTING.md +114 -0
  84. package/simsimd/Cargo.lock +709 -0
  85. package/simsimd/Cargo.toml +44 -0
  86. package/simsimd/LICENSE +201 -0
  87. package/simsimd/README.md +708 -0
  88. package/simsimd/binding.gyp +36 -0
  89. package/simsimd/build.rs +51 -0
  90. package/simsimd/c/lib.c +110 -0
  91. package/simsimd/include/simsimd/binary.h +258 -0
  92. package/simsimd/include/simsimd/dot.h +1237 -0
  93. package/simsimd/include/simsimd/geospatial.h +37 -0
  94. package/simsimd/include/simsimd/probability.h +563 -0
  95. package/simsimd/include/simsimd/simsimd.h +1129 -0
  96. package/simsimd/include/simsimd/spatial.h +1019 -0
  97. package/simsimd/include/simsimd/types.h +309 -0
  98. package/simsimd/javascript/bench.js +127 -0
  99. package/simsimd/javascript/dist-package-cjs.json +3 -0
  100. package/simsimd/javascript/dist-package-esm.json +3 -0
  101. package/simsimd/javascript/fallback.ts +203 -0
  102. package/simsimd/javascript/lib.c +107 -0
  103. package/simsimd/javascript/node-gyp-build.d.ts +1 -0
  104. package/simsimd/javascript/simsimd.ts +135 -0
  105. package/simsimd/javascript/test.mjs +179 -0
  106. package/simsimd/javascript/tsconfig-base.json +37 -0
  107. package/simsimd/javascript/tsconfig-cjs.json +8 -0
  108. package/simsimd/javascript/tsconfig-esm.json +8 -0
  109. package/simsimd/package-lock.json +1709 -0
  110. package/simsimd/package.json +61 -0
  111. package/simsimd/rust/benches/cosine.rs +27 -0
  112. package/simsimd/rust/benches/native.rs +30 -0
  113. package/simsimd/rust/benches/sqeuclidean.rs +27 -0
  114. package/simsimd/rust/lib.rs +778 -0
package/c/test.c ADDED
@@ -0,0 +1,386 @@
1
+ #include <errno.h>
2
+ #include <stdio.h> // `remove`
3
+ #include <stdlib.h>
4
+ #include <sys/stat.h>
5
+
6
+ #include "usearch.h"
7
+
8
+ #define ASSERT(must_be_true, message) \
9
+ if (!(must_be_true)) { \
10
+ printf("Assert: %s\n", message); \
11
+ exit(-1); \
12
+ }
13
+
14
+ /**
15
+ * @brief Creates and initializes vectors with random float values.
16
+ *
17
+ * @param count The number of vectors.
18
+ * @param dimensions The number of dimensions per vector.
19
+ * @return A pointer to the first element of the vectors, that must be @b free-ed afterwards.
20
+ */
21
+ float* create_vectors(size_t const count, size_t const dimensions) {
22
+ float* data = (float*)malloc(count * dimensions * sizeof(float));
23
+ ASSERT(data, "Failed to allocate memory");
24
+ for (size_t index = 0; index < count * dimensions; ++index)
25
+ data[index] = (float)rand() / (float)RAND_MAX;
26
+ return data;
27
+ }
28
+
29
+ usearch_init_options_t create_options(size_t const dimensions) {
30
+ usearch_init_options_t opts;
31
+ opts.connectivity = 3; // 32 in faiss
32
+ opts.dimensions = dimensions;
33
+ opts.expansion_add = 40; // 40 in faiss
34
+ opts.expansion_search = 16; // 10 in faiss
35
+ opts.metric_kind = usearch_metric_ip_k;
36
+ opts.metric = NULL;
37
+ opts.quantization = usearch_scalar_f32_k;
38
+ opts.multi = false;
39
+ return opts;
40
+ }
41
+
42
+ /**
43
+ * This test is designed to verify the initialization of the index with specific dimensions and ensures that the
44
+ * associated properties are set correctly. It initializes the index twice, checking for errors at each step, and
45
+ * performs a reserve operation to pre-allocate space in the index, verifying the correct settings of size, capacity,
46
+ * dimensions, and connectivity after each operation.
47
+ */
48
+ void test_init(size_t const collection_size, size_t const dimensions) {
49
+ printf("Test: Index Initialization... %zu vectors, %zu dimensions \n", collection_size, dimensions);
50
+
51
+ // Init index
52
+ usearch_error_t error = NULL;
53
+ usearch_init_options_t opts = create_options(dimensions);
54
+ usearch_index_t index = usearch_init(&opts, &error);
55
+ ASSERT(!error, error);
56
+ usearch_free(index, &error);
57
+ ASSERT(!error, error);
58
+
59
+ // Init second time
60
+ index = usearch_init(&opts, &error);
61
+ ASSERT(!error, error);
62
+
63
+ ASSERT(usearch_size(index, &error) == 0, error);
64
+ ASSERT(usearch_capacity(index, &error) == 0, error);
65
+ ASSERT(usearch_dimensions(index, &error) == dimensions, error);
66
+ ASSERT(usearch_connectivity(index, &error) == opts.connectivity, error);
67
+
68
+ // Reserve
69
+ usearch_reserve(index, collection_size, &error);
70
+ ASSERT(!error, error);
71
+ ASSERT(usearch_size(index, &error) == 0, error);
72
+ ASSERT(usearch_capacity(index, &error) == collection_size, error);
73
+ ASSERT(usearch_dimensions(index, &error) == dimensions, error);
74
+ ASSERT(usearch_connectivity(index, &error) == opts.connectivity, error);
75
+ ASSERT(usearch_hardware_acceleration(index, &error), error);
76
+ ASSERT(usearch_memory_usage(index, &error), error);
77
+
78
+ usearch_free(index, &error);
79
+ ASSERT(!error, error);
80
+
81
+ printf("Test: Index Initialization - PASSED\n");
82
+ }
83
+
84
+ /**
85
+ * This test validates the addition of vectors to the index. It initializes the index and reserves space for vectors.
86
+ * It then iteratively adds vectors to the index and checks if the index contains the added vectors by verifying the
87
+ * size, capacity, and presence of each vector in the index.
88
+ */
89
+ void test_add_vector(size_t const collection_size, size_t const dimensions) {
90
+ printf("Test: Add Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
91
+
92
+ usearch_error_t error = NULL;
93
+ usearch_init_options_t opts = create_options(dimensions);
94
+ usearch_index_t index = usearch_init(&opts, &error);
95
+ usearch_reserve(index, collection_size, &error);
96
+
97
+ // Add vectors
98
+ float* data = create_vectors(collection_size, dimensions);
99
+ for (size_t i = 0; i < collection_size; ++i) {
100
+ usearch_key_t key = i;
101
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
102
+ ASSERT(!error, error);
103
+ }
104
+
105
+ ASSERT(usearch_size(index, &error) == collection_size, error);
106
+ ASSERT(usearch_capacity(index, &error) == collection_size, error);
107
+
108
+ // Check vectors in the index
109
+ for (size_t i = 0; i < collection_size; ++i) {
110
+ usearch_key_t key = i;
111
+ ASSERT(usearch_contains(index, key, &error), error);
112
+ }
113
+ ASSERT(!usearch_contains(index, -1, &error), error); // Non existing key
114
+
115
+ free(data);
116
+ usearch_free(index, &error);
117
+ printf("Test: Add Vector - PASSED\n");
118
+ }
119
+
120
+ /**
121
+ * This test ensures that vectors added to the index can be correctly found. It initializes the index, reserves space,
122
+ * and adds vectors. It then performs a search query for each added vector to ensure that the vectors are correctly
123
+ * found in the index, validating the count of found vectors.
124
+ */
125
+ void test_find_vector(size_t const collection_size, size_t const dimensions) {
126
+ printf("Test: Find Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
127
+
128
+ usearch_error_t error = NULL;
129
+ usearch_init_options_t opts = create_options(dimensions);
130
+ usearch_index_t index = usearch_init(&opts, &error);
131
+ usearch_reserve(index, collection_size, &error);
132
+
133
+ // Create result buffers
134
+ usearch_key_t* keys = (usearch_key_t*)malloc(collection_size * sizeof(usearch_key_t));
135
+ float* distances = (float*)malloc(collection_size * sizeof(float));
136
+ ASSERT(keys && distances, "Failed to allocate memory");
137
+
138
+ // Add vectors
139
+ float* data = create_vectors(collection_size, dimensions);
140
+ for (size_t i = 0; i < collection_size; ++i) {
141
+ usearch_key_t key = i;
142
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
143
+ ASSERT(!error, error);
144
+ }
145
+
146
+ // Find the vectors
147
+ for (size_t i = 0; i < collection_size; i++) {
148
+ size_t found_count = usearch_search(index, data + i * dimensions, usearch_scalar_f32_k, collection_size, keys,
149
+ distances, &error);
150
+ ASSERT(!error, error);
151
+ ASSERT(found_count >= 1 && found_count <= collection_size, "Vector is missing");
152
+ }
153
+
154
+ free(data);
155
+ free(keys);
156
+ free(distances);
157
+ usearch_free(index, &error);
158
+ printf("Test: Find Vector - PASSED\n");
159
+ }
160
+
161
+ /**
162
+ * This test checks the ability of the index to handle multiple vectors associated with the same key. It initializes
163
+ * the index with the multi-option enabled, reserves space, and adds multiple vectors with the same key. The test then
164
+ * retrieves vectors associated with the key from the index and checks the count of retrieved vectors.
165
+ */
166
+ void test_get_vector(size_t const collection_size, size_t const dimensions) {
167
+ printf("Test: Get Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
168
+
169
+ usearch_error_t error = NULL;
170
+ usearch_init_options_t opts = create_options(dimensions);
171
+ opts.multi = true;
172
+ usearch_index_t index = usearch_init(&opts, &error);
173
+ usearch_reserve(index, collection_size, &error);
174
+
175
+ // Create result buffers
176
+ float* vectors = (float*)malloc(collection_size * dimensions * sizeof(float));
177
+ ASSERT(vectors, "Failed to allocate memory");
178
+
179
+ // Add multiple vectors with SAME key
180
+ usearch_key_t const key = 1;
181
+ float* data = create_vectors(collection_size, dimensions);
182
+ for (size_t i = 0; i < collection_size; i++) {
183
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
184
+ ASSERT(!error, error);
185
+ }
186
+
187
+ // Retrieve vectors from index
188
+ size_t found_count = usearch_get(index, key, collection_size, vectors, usearch_scalar_f32_k, &error);
189
+ ASSERT(found_count == collection_size, "Vector is missing");
190
+
191
+ free(vectors);
192
+ free(data);
193
+ usearch_free(index, &error);
194
+
195
+ printf("Test: Get Vector - PASSED\n");
196
+ }
197
+
198
+ /**
199
+ * This test ensures that vectors can be successfully removed from the index. It initializes the index, reserves space,
200
+ * and adds vectors. It then iteratively removes each vector from the index and checks for errors. However, note that
201
+ * the assert in this test expects an error, indicating that the remove functionality is not currently supported.
202
+ */
203
+ void test_remove_vector(size_t const collection_size, size_t const dimensions) {
204
+ printf("Test: Remove Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
205
+
206
+ usearch_error_t error = NULL;
207
+ usearch_init_options_t opts = create_options(dimensions);
208
+ usearch_index_t index = usearch_init(&opts, &error);
209
+ usearch_reserve(index, collection_size, &error);
210
+
211
+ // Add vectors
212
+ float* data = create_vectors(collection_size, dimensions);
213
+ for (size_t i = 0; i < collection_size; ++i) {
214
+ usearch_key_t key = i;
215
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
216
+ ASSERT(!error, error);
217
+ }
218
+
219
+ // Remove the vectors
220
+ for (size_t i = 0; i < collection_size; i++) {
221
+ usearch_key_t key = i;
222
+ usearch_remove(index, key, &error);
223
+ ASSERT(!error, "Currently, Remove is not supported");
224
+ }
225
+
226
+ free(data);
227
+ usearch_free(index, &error);
228
+ printf("Test: Remove Vector - PASSED\n");
229
+ }
230
+
231
+ /**
232
+ * This test validates the save and load functionality of the index. It initializes the index, reserves space, and adds
233
+ * vectors. The index is then saved to a file and freed. A new index is initialized, and the previously saved index is
234
+ * loaded into it. The test then validates the loaded index properties and ensures that it contains all the vectors
235
+ * from the saved index.
236
+ */
237
+ void test_save_load(size_t const collection_size, size_t const dimensions) {
238
+ printf("Test: Save/Load... %zu vectors, %zu dimensions \n", collection_size, dimensions);
239
+ float* data = create_vectors(collection_size, dimensions);
240
+
241
+ usearch_error_t error = NULL;
242
+ usearch_init_options_t opts = create_options(dimensions);
243
+ usearch_init_options_t weird_ops = opts;
244
+ weird_ops.connectivity = 11;
245
+ weird_ops.expansion_add = 15;
246
+ weird_ops.expansion_search = 19;
247
+ weird_ops.metric_kind = usearch_metric_pearson_k;
248
+ weird_ops.quantization = usearch_scalar_f64_k;
249
+
250
+ {
251
+
252
+ usearch_index_t index = usearch_init(&weird_ops, &error);
253
+ usearch_reserve(index, collection_size, &error);
254
+
255
+ // Add vectors
256
+ for (size_t i = 0; i < collection_size; ++i) {
257
+ usearch_key_t key = i;
258
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
259
+ ASSERT(!error, error);
260
+ }
261
+
262
+ // Save and free the index
263
+ usearch_save(index, "tmp.usearch", &error);
264
+ ASSERT(!error, error);
265
+ usearch_free(index, &error);
266
+ ASSERT(!error, error);
267
+ }
268
+
269
+ // Reset the options
270
+ opts.connectivity = 0;
271
+ opts.dimensions = 0;
272
+ opts.expansion_add = 0;
273
+ opts.expansion_search = 0;
274
+ opts.metric = NULL;
275
+ opts.quantization = usearch_scalar_unknown_k;
276
+ opts.metric_kind = usearch_metric_unknown_k;
277
+
278
+ // Reinit
279
+ {
280
+
281
+ usearch_index_t index = usearch_init(&opts, &error);
282
+ ASSERT(!error, error);
283
+ ASSERT(usearch_size(index, &error) == 0, error);
284
+
285
+ // Load
286
+ usearch_load(index, "tmp.usearch", &error);
287
+ ASSERT(!error, error);
288
+ ASSERT(usearch_size(index, &error) == collection_size, error);
289
+ ASSERT(usearch_capacity(index, &error) == collection_size, error);
290
+ ASSERT(usearch_dimensions(index, &error) == dimensions, error);
291
+ ASSERT(usearch_connectivity(index, &error) == weird_ops.connectivity, error);
292
+
293
+ // Check vectors in the index
294
+ for (size_t i = 0; i < collection_size; ++i) {
295
+ usearch_key_t key = i;
296
+ ASSERT(usearch_contains(index, key, &error), error);
297
+ }
298
+
299
+ // Create result buffers
300
+ usearch_key_t* keys = (usearch_key_t*)malloc(collection_size * sizeof(usearch_key_t));
301
+ float* distances = (float*)malloc(collection_size * sizeof(float));
302
+ ASSERT(keys && distances, "Failed to allocate memory");
303
+
304
+ // Find the vectors
305
+ for (size_t i = 0; i < collection_size; i++) {
306
+ size_t found_count = usearch_search(index, data + i * dimensions, usearch_scalar_f32_k, collection_size,
307
+ keys, distances, &error);
308
+ ASSERT(!error, error);
309
+ ASSERT(found_count >= 1 && found_count <= collection_size, "Vector is missing");
310
+ }
311
+
312
+ free(keys);
313
+ free(distances);
314
+ usearch_free(index, &error);
315
+ }
316
+
317
+ free(data);
318
+
319
+ // Remove the file from disk
320
+ remove("tmp.usearch");
321
+ printf("Test: Save/Load - PASSED\n");
322
+ }
323
+
324
+ /**
325
+ * This test is designed to validate the view functionality of the index. It initializes the index, reserves space, and
326
+ * adds vectors. The index is then saved to a file and freed. A new index is initialized and a view is created from the
327
+ * saved index file. The test is mainly focused on ensuring that no errors occur during these operations, but it does
328
+ * not verify the properties or contents of the viewed index.
329
+ */
330
+ void test_view(size_t const collection_size, size_t const dimensions) {
331
+ printf("Test: View... %zu vectors, %zu dimensions \n", collection_size, dimensions);
332
+
333
+ usearch_error_t error = NULL;
334
+ usearch_init_options_t opts = create_options(dimensions);
335
+ usearch_index_t index = usearch_init(&opts, &error);
336
+ usearch_reserve(index, collection_size, &error);
337
+
338
+ // Add vectors
339
+ float* data = create_vectors(collection_size, dimensions);
340
+ for (size_t i = 0; i < collection_size; ++i) {
341
+ usearch_key_t key = i;
342
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
343
+ ASSERT(!error, error);
344
+ }
345
+
346
+ // Save and free the index
347
+ usearch_save(index, "tmp.usearch", &error);
348
+ ASSERT(!error, error);
349
+ usearch_free(index, &error);
350
+ ASSERT(!error, error);
351
+
352
+ // Reinit
353
+ index = usearch_init(&opts, &error);
354
+ ASSERT(!error, error);
355
+
356
+ // View
357
+ usearch_view(index, "tmp.usearch", &error);
358
+ ASSERT(!error, error);
359
+
360
+ free(data);
361
+ usearch_free(index, &error);
362
+ printf("Test: View - PASSED\n");
363
+ }
364
+
365
+ int main(int argc, char const* argv[]) {
366
+ printf("Running tests...\n");
367
+ printf("USearch version: %s\n", usearch_version());
368
+
369
+ size_t collection_sizes[] = {11, 512};
370
+ size_t dimensions[] = {83, 2}; // Not all distance functions make sense for 1 dimensional data
371
+ for (size_t index = 0; index < sizeof(collection_sizes) / sizeof(collection_sizes[0]); ++index) {
372
+ for (size_t jdx = 0; jdx < sizeof(dimensions) / sizeof(dimensions[0]); ++jdx) {
373
+ test_init(collection_sizes[index], dimensions[jdx]);
374
+ test_add_vector(collection_sizes[index], dimensions[jdx]);
375
+ test_find_vector(collection_sizes[index], dimensions[jdx]);
376
+ test_get_vector(collection_sizes[index], dimensions[jdx]);
377
+ test_remove_vector(collection_sizes[index], dimensions[jdx]);
378
+ test_save_load(collection_sizes[index], dimensions[jdx]);
379
+ test_view(collection_sizes[index], dimensions[jdx]);
380
+ }
381
+ }
382
+
383
+ (void)argc;
384
+ (void)argv;
385
+ return 0;
386
+ }