usearch 2.21.4 → 2.25.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 (602) hide show
  1. package/README.md +24 -24
  2. package/binding.gyp +15 -37
  3. package/c/CMakeLists.txt +4 -5
  4. package/c/README.md +7 -7
  5. package/c/lib.cpp +32 -14
  6. package/c/test.c +83 -1
  7. package/c/usearch.h +24 -3
  8. package/cpp/CMakeLists.txt +4 -0
  9. package/cpp/README.md +5 -5
  10. package/cpp/bench.cpp +97 -116
  11. package/cpp/test.cpp +117 -20
  12. package/include/usearch/index.hpp +224 -48
  13. package/include/usearch/index_dense.hpp +105 -40
  14. package/include/usearch/index_plugins.hpp +1387 -204
  15. package/javascript/dist/cjs/usearch.d.ts +8 -3
  16. package/javascript/dist/cjs/usearch.js +12 -4
  17. package/javascript/dist/esm/usearch.d.ts +8 -3
  18. package/javascript/dist/esm/usearch.js +12 -4
  19. package/javascript/lib.cpp +17 -0
  20. package/javascript/usearch.ts +14 -6
  21. package/numkong/.clang-tidy +30 -0
  22. package/{simsimd → numkong}/.cmake-format.py +18 -0
  23. package/numkong/.editorconfig +12 -0
  24. package/numkong/.git-blame-ignore-revs +123 -0
  25. package/numkong/.mailmap +15 -0
  26. package/numkong/.spi.yml +11 -0
  27. package/numkong/.swift-format +14 -0
  28. package/numkong/CITATION.cff +81 -0
  29. package/numkong/CONTRIBUTING.md +500 -0
  30. package/numkong/Cargo.toml +54 -0
  31. package/numkong/MANIFEST.in +8 -0
  32. package/numkong/Package.swift +84 -0
  33. package/numkong/README.md +569 -0
  34. package/numkong/bench/README.md +217 -0
  35. package/numkong/bench/bench-browser.html +323 -0
  36. package/numkong/bench/bench.cpp +344 -0
  37. package/numkong/bench/bench.hpp +816 -0
  38. package/numkong/bench/bench.js +148 -0
  39. package/numkong/bench/bench.mjs +616 -0
  40. package/numkong/bench/bench_attention.cpp +113 -0
  41. package/numkong/bench/bench_cast.cpp +264 -0
  42. package/numkong/bench/bench_cross_amx.cpp +62 -0
  43. package/numkong/bench/bench_cross_arm.cpp +245 -0
  44. package/numkong/bench/bench_cross_blas.cpp +309 -0
  45. package/numkong/bench/bench_cross_loongarch.cpp +106 -0
  46. package/numkong/bench/bench_cross_power.cpp +84 -0
  47. package/numkong/bench/bench_cross_rvv.cpp +121 -0
  48. package/numkong/bench/bench_cross_serial.cpp +151 -0
  49. package/numkong/bench/bench_cross_sme.cpp +163 -0
  50. package/numkong/bench/bench_cross_wasm.cpp +142 -0
  51. package/numkong/bench/bench_cross_x86.cpp +455 -0
  52. package/numkong/bench/bench_curved.cpp +210 -0
  53. package/numkong/bench/bench_dot.cpp +315 -0
  54. package/numkong/bench/bench_each.cpp +503 -0
  55. package/numkong/bench/bench_geospatial.cpp +112 -0
  56. package/numkong/bench/bench_maxsim.cpp +154 -0
  57. package/numkong/bench/bench_mesh.cpp +148 -0
  58. package/numkong/bench/bench_probability.cpp +60 -0
  59. package/numkong/bench/bench_reduce.cpp +371 -0
  60. package/numkong/bench/bench_set.cpp +73 -0
  61. package/numkong/bench/bench_sparse.cpp +198 -0
  62. package/numkong/bench/bench_spatial.cpp +347 -0
  63. package/numkong/bench/bench_trigonometry.cpp +137 -0
  64. package/numkong/bench/swift/Bench.swift +506 -0
  65. package/numkong/binding.gyp +169 -0
  66. package/numkong/build.rs +484 -0
  67. package/numkong/c/dispatch.h +512 -0
  68. package/numkong/c/dispatch_bf16.c +447 -0
  69. package/numkong/c/dispatch_bf16c.c +52 -0
  70. package/numkong/c/dispatch_e2m3.c +296 -0
  71. package/numkong/c/dispatch_e3m2.c +284 -0
  72. package/numkong/c/dispatch_e4m3.c +318 -0
  73. package/numkong/c/dispatch_e5m2.c +316 -0
  74. package/numkong/c/dispatch_f16.c +452 -0
  75. package/numkong/c/dispatch_f16c.c +58 -0
  76. package/numkong/c/dispatch_f32.c +434 -0
  77. package/numkong/c/dispatch_f32c.c +99 -0
  78. package/numkong/c/dispatch_f64.c +348 -0
  79. package/numkong/c/dispatch_f64c.c +98 -0
  80. package/numkong/c/dispatch_i16.c +96 -0
  81. package/numkong/c/dispatch_i32.c +89 -0
  82. package/numkong/c/dispatch_i4.c +153 -0
  83. package/numkong/c/dispatch_i64.c +86 -0
  84. package/numkong/c/dispatch_i8.c +348 -0
  85. package/numkong/c/dispatch_other.c +348 -0
  86. package/numkong/c/dispatch_u1.c +193 -0
  87. package/numkong/c/dispatch_u16.c +124 -0
  88. package/numkong/c/dispatch_u32.c +118 -0
  89. package/numkong/c/dispatch_u4.c +153 -0
  90. package/numkong/c/dispatch_u64.c +102 -0
  91. package/numkong/c/dispatch_u8.c +364 -0
  92. package/numkong/c/numkong.c +940 -0
  93. package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
  94. package/numkong/cmake/nk_isa_probe.cmake +90 -0
  95. package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
  96. package/numkong/cmake/nk_math_lib.cmake +46 -0
  97. package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
  98. package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
  99. package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
  100. package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
  101. package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
  102. package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
  103. package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
  104. package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
  105. package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
  106. package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
  107. package/numkong/cmake/toolchain-wasi.cmake +77 -0
  108. package/numkong/cmake/toolchain-wasm.cmake +70 -0
  109. package/numkong/cmake/toolchain-wasm64.cmake +62 -0
  110. package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
  111. package/numkong/include/README.md +643 -0
  112. package/numkong/include/module.modulemap +129 -0
  113. package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
  114. package/numkong/include/numkong/attention/sme.h +2075 -0
  115. package/numkong/include/numkong/attention.h +49 -0
  116. package/numkong/include/numkong/capabilities.h +930 -0
  117. package/numkong/include/numkong/cast/README.md +262 -0
  118. package/numkong/include/numkong/cast/diamond.h +64 -0
  119. package/numkong/include/numkong/cast/haswell.h +823 -0
  120. package/numkong/include/numkong/cast/icelake.h +475 -0
  121. package/numkong/include/numkong/cast/loongsonasx.h +252 -0
  122. package/numkong/include/numkong/cast/neon.h +1174 -0
  123. package/numkong/include/numkong/cast/powervsx.h +449 -0
  124. package/numkong/include/numkong/cast/rvv.h +970 -0
  125. package/numkong/include/numkong/cast/sapphire.h +262 -0
  126. package/numkong/include/numkong/cast/serial.h +2336 -0
  127. package/numkong/include/numkong/cast/skylake.h +915 -0
  128. package/numkong/include/numkong/cast/v128relaxed.h +537 -0
  129. package/numkong/include/numkong/cast.h +254 -0
  130. package/numkong/include/numkong/cast.hpp +44 -0
  131. package/numkong/include/numkong/curved/README.md +223 -0
  132. package/numkong/include/numkong/curved/genoa.h +182 -0
  133. package/numkong/include/numkong/curved/haswell.h +276 -0
  134. package/numkong/include/numkong/curved/neon.h +329 -0
  135. package/numkong/include/numkong/curved/neonbfdot.h +211 -0
  136. package/numkong/include/numkong/curved/rvv.h +305 -0
  137. package/numkong/include/numkong/curved/serial.h +207 -0
  138. package/numkong/include/numkong/curved/skylake.h +457 -0
  139. package/numkong/include/numkong/curved/smef64.h +530 -0
  140. package/numkong/include/numkong/curved.h +513 -0
  141. package/numkong/include/numkong/curved.hpp +144 -0
  142. package/numkong/include/numkong/dot/README.md +442 -0
  143. package/numkong/include/numkong/dot/alder.h +563 -0
  144. package/numkong/include/numkong/dot/diamond.h +158 -0
  145. package/numkong/include/numkong/dot/genoa.h +289 -0
  146. package/numkong/include/numkong/dot/haswell.h +1723 -0
  147. package/numkong/include/numkong/dot/icelake.h +997 -0
  148. package/numkong/include/numkong/dot/loongsonasx.h +671 -0
  149. package/numkong/include/numkong/dot/neon.h +869 -0
  150. package/numkong/include/numkong/dot/neonbfdot.h +243 -0
  151. package/numkong/include/numkong/dot/neonfhm.h +358 -0
  152. package/numkong/include/numkong/dot/neonfp8.h +323 -0
  153. package/numkong/include/numkong/dot/neonsdot.h +622 -0
  154. package/numkong/include/numkong/dot/powervsx.h +752 -0
  155. package/numkong/include/numkong/dot/rvv.h +722 -0
  156. package/numkong/include/numkong/dot/rvvbb.h +72 -0
  157. package/numkong/include/numkong/dot/rvvbf16.h +123 -0
  158. package/numkong/include/numkong/dot/rvvhalf.h +129 -0
  159. package/numkong/include/numkong/dot/sapphire.h +141 -0
  160. package/numkong/include/numkong/dot/serial.h +874 -0
  161. package/numkong/include/numkong/dot/sierra.h +405 -0
  162. package/numkong/include/numkong/dot/skylake.h +1116 -0
  163. package/numkong/include/numkong/dot/sve.h +420 -0
  164. package/numkong/include/numkong/dot/svebfdot.h +75 -0
  165. package/numkong/include/numkong/dot/svehalf.h +168 -0
  166. package/numkong/include/numkong/dot/svesdot.h +90 -0
  167. package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
  168. package/numkong/include/numkong/dot.h +1136 -0
  169. package/numkong/include/numkong/dot.hpp +94 -0
  170. package/numkong/include/numkong/dots/README.md +496 -0
  171. package/numkong/include/numkong/dots/alder.h +117 -0
  172. package/numkong/include/numkong/dots/diamond.h +86 -0
  173. package/numkong/include/numkong/dots/genoa.h +100 -0
  174. package/numkong/include/numkong/dots/graniteamx.h +733 -0
  175. package/numkong/include/numkong/dots/haswell.h +310 -0
  176. package/numkong/include/numkong/dots/icelake.h +180 -0
  177. package/numkong/include/numkong/dots/loongsonasx.h +176 -0
  178. package/numkong/include/numkong/dots/neon.h +123 -0
  179. package/numkong/include/numkong/dots/neonbfdot.h +59 -0
  180. package/numkong/include/numkong/dots/neonfhm.h +96 -0
  181. package/numkong/include/numkong/dots/neonfp8.h +99 -0
  182. package/numkong/include/numkong/dots/neonsdot.h +144 -0
  183. package/numkong/include/numkong/dots/powervsx.h +194 -0
  184. package/numkong/include/numkong/dots/rvv.h +2593 -0
  185. package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
  186. package/numkong/include/numkong/dots/serial.h +2867 -0
  187. package/numkong/include/numkong/dots/sierra.h +100 -0
  188. package/numkong/include/numkong/dots/skylake.h +207 -0
  189. package/numkong/include/numkong/dots/sme.h +5041 -0
  190. package/numkong/include/numkong/dots/smebi32.h +477 -0
  191. package/numkong/include/numkong/dots/smef64.h +1328 -0
  192. package/numkong/include/numkong/dots/smehalf.h +47 -0
  193. package/numkong/include/numkong/dots/v128relaxed.h +317 -0
  194. package/numkong/include/numkong/dots.h +3110 -0
  195. package/numkong/include/numkong/dots.hpp +641 -0
  196. package/numkong/include/numkong/each/README.md +465 -0
  197. package/numkong/include/numkong/each/haswell.h +1658 -0
  198. package/numkong/include/numkong/each/icelake.h +272 -0
  199. package/numkong/include/numkong/each/neon.h +1138 -0
  200. package/numkong/include/numkong/each/neonbfdot.h +211 -0
  201. package/numkong/include/numkong/each/neonhalf.h +318 -0
  202. package/numkong/include/numkong/each/rvv.h +1121 -0
  203. package/numkong/include/numkong/each/sapphire.h +343 -0
  204. package/numkong/include/numkong/each/serial.h +282 -0
  205. package/numkong/include/numkong/each/skylake.h +1562 -0
  206. package/numkong/include/numkong/each/v128relaxed.h +562 -0
  207. package/numkong/include/numkong/each.h +2232 -0
  208. package/numkong/include/numkong/each.hpp +434 -0
  209. package/numkong/include/numkong/geospatial/README.md +147 -0
  210. package/numkong/include/numkong/geospatial/haswell.h +633 -0
  211. package/numkong/include/numkong/geospatial/neon.h +615 -0
  212. package/numkong/include/numkong/geospatial/rvv.h +701 -0
  213. package/numkong/include/numkong/geospatial/serial.h +309 -0
  214. package/numkong/include/numkong/geospatial/skylake.h +613 -0
  215. package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
  216. package/numkong/include/numkong/geospatial.h +453 -0
  217. package/numkong/include/numkong/geospatial.hpp +235 -0
  218. package/numkong/include/numkong/matrix.hpp +336 -0
  219. package/numkong/include/numkong/maxsim/README.md +187 -0
  220. package/numkong/include/numkong/maxsim/alder.h +511 -0
  221. package/numkong/include/numkong/maxsim/genoa.h +115 -0
  222. package/numkong/include/numkong/maxsim/haswell.h +553 -0
  223. package/numkong/include/numkong/maxsim/icelake.h +480 -0
  224. package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
  225. package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
  226. package/numkong/include/numkong/maxsim/serial.h +490 -0
  227. package/numkong/include/numkong/maxsim/sme.h +993 -0
  228. package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
  229. package/numkong/include/numkong/maxsim.h +571 -0
  230. package/numkong/include/numkong/maxsim.hpp +133 -0
  231. package/numkong/include/numkong/mesh/README.md +219 -0
  232. package/numkong/include/numkong/mesh/haswell.h +2013 -0
  233. package/numkong/include/numkong/mesh/neon.h +1821 -0
  234. package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
  235. package/numkong/include/numkong/mesh/rvv.h +936 -0
  236. package/numkong/include/numkong/mesh/serial.h +721 -0
  237. package/numkong/include/numkong/mesh/skylake.h +2344 -0
  238. package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
  239. package/numkong/include/numkong/mesh.h +666 -0
  240. package/numkong/include/numkong/mesh.hpp +701 -0
  241. package/numkong/include/numkong/numkong.h +78 -0
  242. package/numkong/include/numkong/numkong.hpp +58 -0
  243. package/numkong/include/numkong/probability/README.md +177 -0
  244. package/numkong/include/numkong/probability/haswell.h +267 -0
  245. package/numkong/include/numkong/probability/neon.h +255 -0
  246. package/numkong/include/numkong/probability/rvv.h +414 -0
  247. package/numkong/include/numkong/probability/serial.h +181 -0
  248. package/numkong/include/numkong/probability/skylake.h +324 -0
  249. package/numkong/include/numkong/probability.h +380 -0
  250. package/numkong/include/numkong/probability.hpp +120 -0
  251. package/numkong/include/numkong/random.h +50 -0
  252. package/numkong/include/numkong/random.hpp +285 -0
  253. package/numkong/include/numkong/reduce/README.md +552 -0
  254. package/numkong/include/numkong/reduce/alder.h +636 -0
  255. package/numkong/include/numkong/reduce/genoa.h +201 -0
  256. package/numkong/include/numkong/reduce/haswell.h +3785 -0
  257. package/numkong/include/numkong/reduce/icelake.h +549 -0
  258. package/numkong/include/numkong/reduce/neon.h +3980 -0
  259. package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
  260. package/numkong/include/numkong/reduce/neonfhm.h +293 -0
  261. package/numkong/include/numkong/reduce/neonsdot.h +357 -0
  262. package/numkong/include/numkong/reduce/rvv.h +3533 -0
  263. package/numkong/include/numkong/reduce/serial.h +753 -0
  264. package/numkong/include/numkong/reduce/sierra.h +338 -0
  265. package/numkong/include/numkong/reduce/skylake.h +3794 -0
  266. package/numkong/include/numkong/reduce/sve.h +52 -0
  267. package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
  268. package/numkong/include/numkong/reduce.h +1582 -0
  269. package/numkong/include/numkong/reduce.hpp +778 -0
  270. package/numkong/include/numkong/scalar/README.md +89 -0
  271. package/numkong/include/numkong/scalar/haswell.h +122 -0
  272. package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
  273. package/numkong/include/numkong/scalar/neon.h +122 -0
  274. package/numkong/include/numkong/scalar/neonhalf.h +70 -0
  275. package/numkong/include/numkong/scalar/powervsx.h +96 -0
  276. package/numkong/include/numkong/scalar/rvv.h +211 -0
  277. package/numkong/include/numkong/scalar/sapphire.h +74 -0
  278. package/numkong/include/numkong/scalar/serial.h +332 -0
  279. package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
  280. package/numkong/include/numkong/scalar.h +696 -0
  281. package/numkong/include/numkong/set/README.md +179 -0
  282. package/numkong/include/numkong/set/haswell.h +334 -0
  283. package/numkong/include/numkong/set/icelake.h +485 -0
  284. package/numkong/include/numkong/set/loongsonasx.h +181 -0
  285. package/numkong/include/numkong/set/neon.h +363 -0
  286. package/numkong/include/numkong/set/powervsx.h +326 -0
  287. package/numkong/include/numkong/set/rvv.h +226 -0
  288. package/numkong/include/numkong/set/rvvbb.h +117 -0
  289. package/numkong/include/numkong/set/serial.h +174 -0
  290. package/numkong/include/numkong/set/sve.h +187 -0
  291. package/numkong/include/numkong/set/v128relaxed.h +240 -0
  292. package/numkong/include/numkong/set.h +471 -0
  293. package/numkong/include/numkong/set.hpp +114 -0
  294. package/numkong/include/numkong/sets/README.md +149 -0
  295. package/numkong/include/numkong/sets/haswell.h +63 -0
  296. package/numkong/include/numkong/sets/icelake.h +66 -0
  297. package/numkong/include/numkong/sets/loongsonasx.h +52 -0
  298. package/numkong/include/numkong/sets/neon.h +61 -0
  299. package/numkong/include/numkong/sets/powervsx.h +65 -0
  300. package/numkong/include/numkong/sets/serial.h +43 -0
  301. package/numkong/include/numkong/sets/smebi32.h +1135 -0
  302. package/numkong/include/numkong/sets/v128relaxed.h +58 -0
  303. package/numkong/include/numkong/sets.h +382 -0
  304. package/numkong/include/numkong/sparse/README.md +156 -0
  305. package/numkong/include/numkong/sparse/icelake.h +463 -0
  306. package/numkong/include/numkong/sparse/neon.h +288 -0
  307. package/numkong/include/numkong/sparse/serial.h +117 -0
  308. package/numkong/include/numkong/sparse/sve2.h +504 -0
  309. package/numkong/include/numkong/sparse/turin.h +322 -0
  310. package/numkong/include/numkong/sparse.h +363 -0
  311. package/numkong/include/numkong/sparse.hpp +113 -0
  312. package/numkong/include/numkong/spatial/README.md +439 -0
  313. package/numkong/include/numkong/spatial/alder.h +604 -0
  314. package/numkong/include/numkong/spatial/diamond.h +240 -0
  315. package/numkong/include/numkong/spatial/genoa.h +222 -0
  316. package/numkong/include/numkong/spatial/haswell.h +979 -0
  317. package/numkong/include/numkong/spatial/icelake.h +1067 -0
  318. package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
  319. package/numkong/include/numkong/spatial/neon.h +846 -0
  320. package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
  321. package/numkong/include/numkong/spatial/neonfp8.h +258 -0
  322. package/numkong/include/numkong/spatial/neonsdot.h +429 -0
  323. package/numkong/include/numkong/spatial/powervsx.h +738 -0
  324. package/numkong/include/numkong/spatial/rvv.h +991 -0
  325. package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
  326. package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
  327. package/numkong/include/numkong/spatial/serial.h +347 -0
  328. package/numkong/include/numkong/spatial/sierra.h +516 -0
  329. package/numkong/include/numkong/spatial/skylake.h +605 -0
  330. package/numkong/include/numkong/spatial/sve.h +246 -0
  331. package/numkong/include/numkong/spatial/svebfdot.h +125 -0
  332. package/numkong/include/numkong/spatial/svehalf.h +136 -0
  333. package/numkong/include/numkong/spatial/svesdot.h +143 -0
  334. package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
  335. package/numkong/include/numkong/spatial.h +1681 -0
  336. package/numkong/include/numkong/spatial.hpp +183 -0
  337. package/numkong/include/numkong/spatials/README.md +580 -0
  338. package/numkong/include/numkong/spatials/alder.h +94 -0
  339. package/numkong/include/numkong/spatials/diamond.h +82 -0
  340. package/numkong/include/numkong/spatials/genoa.h +94 -0
  341. package/numkong/include/numkong/spatials/graniteamx.h +173 -0
  342. package/numkong/include/numkong/spatials/haswell.h +219 -0
  343. package/numkong/include/numkong/spatials/icelake.h +113 -0
  344. package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
  345. package/numkong/include/numkong/spatials/neon.h +109 -0
  346. package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
  347. package/numkong/include/numkong/spatials/neonfhm.h +92 -0
  348. package/numkong/include/numkong/spatials/neonfp8.h +111 -0
  349. package/numkong/include/numkong/spatials/neonsdot.h +143 -0
  350. package/numkong/include/numkong/spatials/powervsx.h +153 -0
  351. package/numkong/include/numkong/spatials/rvv.h +1976 -0
  352. package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
  353. package/numkong/include/numkong/spatials/serial.h +248 -0
  354. package/numkong/include/numkong/spatials/sierra.h +96 -0
  355. package/numkong/include/numkong/spatials/skylake.h +184 -0
  356. package/numkong/include/numkong/spatials/sme.h +1927 -0
  357. package/numkong/include/numkong/spatials/smef64.h +483 -0
  358. package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
  359. package/numkong/include/numkong/spatials.h +3374 -0
  360. package/numkong/include/numkong/spatials.hpp +509 -0
  361. package/numkong/include/numkong/tensor.hpp +1981 -0
  362. package/numkong/include/numkong/trigonometry/README.md +184 -0
  363. package/numkong/include/numkong/trigonometry/haswell.h +653 -0
  364. package/numkong/include/numkong/trigonometry/neon.h +638 -0
  365. package/numkong/include/numkong/trigonometry/rvv.h +700 -0
  366. package/numkong/include/numkong/trigonometry/serial.h +703 -0
  367. package/numkong/include/numkong/trigonometry/skylake.h +725 -0
  368. package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
  369. package/numkong/include/numkong/trigonometry.h +467 -0
  370. package/numkong/include/numkong/trigonometry.hpp +166 -0
  371. package/numkong/include/numkong/types.h +1695 -0
  372. package/numkong/include/numkong/types.hpp +6027 -0
  373. package/numkong/include/numkong/vector.hpp +766 -0
  374. package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
  375. package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
  376. package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
  377. package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
  378. package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
  379. package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
  380. package/numkong/javascript/README.md +254 -0
  381. package/numkong/javascript/node-gyp-build.d.ts +4 -0
  382. package/numkong/javascript/numkong-browser.ts +40 -0
  383. package/numkong/javascript/numkong-wasm.ts +750 -0
  384. package/numkong/javascript/numkong.c +712 -0
  385. package/numkong/javascript/numkong.ts +613 -0
  386. package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
  387. package/numkong/javascript/tsconfig-cjs.json +13 -0
  388. package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
  389. package/numkong/javascript/types.ts +784 -0
  390. package/numkong/numkong.gypi +104 -0
  391. package/numkong/package.json +108 -0
  392. package/numkong/probes/arm_neon.c +8 -0
  393. package/numkong/probes/arm_neon_bfdot.c +9 -0
  394. package/numkong/probes/arm_neon_fhm.c +9 -0
  395. package/numkong/probes/arm_neon_half.c +8 -0
  396. package/numkong/probes/arm_neon_sdot.c +9 -0
  397. package/numkong/probes/arm_neonfp8.c +9 -0
  398. package/numkong/probes/arm_sme.c +16 -0
  399. package/numkong/probes/arm_sme2.c +16 -0
  400. package/numkong/probes/arm_sme2p1.c +16 -0
  401. package/numkong/probes/arm_sme_bf16.c +16 -0
  402. package/numkong/probes/arm_sme_bi32.c +16 -0
  403. package/numkong/probes/arm_sme_f64.c +16 -0
  404. package/numkong/probes/arm_sme_fa64.c +14 -0
  405. package/numkong/probes/arm_sme_half.c +16 -0
  406. package/numkong/probes/arm_sme_lut2.c +15 -0
  407. package/numkong/probes/arm_sve.c +18 -0
  408. package/numkong/probes/arm_sve2.c +20 -0
  409. package/numkong/probes/arm_sve2p1.c +18 -0
  410. package/numkong/probes/arm_sve_bfdot.c +20 -0
  411. package/numkong/probes/arm_sve_half.c +18 -0
  412. package/numkong/probes/arm_sve_sdot.c +21 -0
  413. package/numkong/probes/loongarch_lasx.c +12 -0
  414. package/numkong/probes/power_vsx.c +12 -0
  415. package/numkong/probes/probe.js +127 -0
  416. package/numkong/probes/riscv_rvv.c +14 -0
  417. package/numkong/probes/riscv_rvv_bb.c +15 -0
  418. package/numkong/probes/riscv_rvv_bf16.c +17 -0
  419. package/numkong/probes/riscv_rvv_half.c +14 -0
  420. package/numkong/probes/wasm_v128relaxed.c +11 -0
  421. package/numkong/probes/x86_alder.c +17 -0
  422. package/numkong/probes/x86_diamond.c +17 -0
  423. package/numkong/probes/x86_genoa.c +17 -0
  424. package/numkong/probes/x86_graniteamx.c +19 -0
  425. package/numkong/probes/x86_haswell.c +11 -0
  426. package/numkong/probes/x86_icelake.c +17 -0
  427. package/numkong/probes/x86_sapphire.c +16 -0
  428. package/numkong/probes/x86_sapphireamx.c +18 -0
  429. package/numkong/probes/x86_sierra.c +17 -0
  430. package/numkong/probes/x86_skylake.c +15 -0
  431. package/numkong/probes/x86_turin.c +17 -0
  432. package/numkong/rust/README.md +698 -0
  433. package/numkong/rust/capabilities.rs +96 -0
  434. package/numkong/rust/cast.rs +314 -0
  435. package/numkong/rust/curved.rs +391 -0
  436. package/numkong/rust/each.rs +2721 -0
  437. package/numkong/rust/geospatial.rs +343 -0
  438. package/numkong/rust/matrix.rs +5424 -0
  439. package/numkong/rust/maxsim.rs +540 -0
  440. package/numkong/rust/mesh.rs +837 -0
  441. package/numkong/rust/numkong.rs +409 -0
  442. package/numkong/rust/probability.rs +272 -0
  443. package/numkong/rust/reduce.rs +1249 -0
  444. package/numkong/rust/set.rs +194 -0
  445. package/numkong/rust/sparse.rs +513 -0
  446. package/numkong/rust/spatial.rs +1815 -0
  447. package/numkong/rust/tensor.rs +7565 -0
  448. package/numkong/rust/types.rs +3666 -0
  449. package/numkong/rust/vector.rs +1899 -0
  450. package/numkong/rustfmt.toml +2 -0
  451. package/numkong/swift/Matrix.swift +1388 -0
  452. package/numkong/swift/MaxSim.swift +151 -0
  453. package/numkong/swift/NumKong.swift +283 -0
  454. package/numkong/swift/Numerics.swift +329 -0
  455. package/numkong/swift/README.md +433 -0
  456. package/numkong/swift/Scalar.swift +741 -0
  457. package/numkong/swift/Tensor.swift +209 -0
  458. package/numkong/test/README.md +315 -0
  459. package/numkong/test/playwright.config.ts +16 -0
  460. package/numkong/test/swift/Test.swift +534 -0
  461. package/numkong/test/test-browser-bundle-runner.mjs +36 -0
  462. package/numkong/test/test-browser-bundle.html +188 -0
  463. package/numkong/test/test-browser-runner.mjs +43 -0
  464. package/numkong/test/test-browser.html +305 -0
  465. package/numkong/test/test-dtypes.mjs +453 -0
  466. package/numkong/test/test-wasm.mjs +267 -0
  467. package/numkong/test/test.cpp +442 -0
  468. package/numkong/test/test.hpp +580 -0
  469. package/numkong/test/test.mjs +363 -0
  470. package/numkong/test/test_base.py +1041 -0
  471. package/numkong/test/test_cast.cpp +242 -0
  472. package/numkong/test/test_cross.hpp +551 -0
  473. package/numkong/test/test_cross.py +467 -0
  474. package/numkong/test/test_cross_amx.cpp +50 -0
  475. package/numkong/test/test_cross_arm.cpp +225 -0
  476. package/numkong/test/test_cross_blas.cpp +354 -0
  477. package/numkong/test/test_cross_loongarch.cpp +90 -0
  478. package/numkong/test/test_cross_power.cpp +73 -0
  479. package/numkong/test/test_cross_rvv.cpp +109 -0
  480. package/numkong/test/test_cross_serial.cpp +181 -0
  481. package/numkong/test/test_cross_sme.cpp +148 -0
  482. package/numkong/test/test_cross_wasm.cpp +141 -0
  483. package/numkong/test/test_cross_x86.cpp +442 -0
  484. package/numkong/test/test_curved.cpp +182 -0
  485. package/numkong/test/test_curved.py +207 -0
  486. package/numkong/test/test_dot.cpp +353 -0
  487. package/numkong/test/test_dot.py +296 -0
  488. package/numkong/test/test_each.cpp +437 -0
  489. package/numkong/test/test_each.py +756 -0
  490. package/numkong/test/test_geospatial.cpp +155 -0
  491. package/numkong/test/test_geospatial.py +241 -0
  492. package/numkong/test/test_maxsim.cpp +138 -0
  493. package/numkong/test/test_maxsim.py +209 -0
  494. package/numkong/test/test_mesh.cpp +203 -0
  495. package/numkong/test/test_mesh.py +394 -0
  496. package/numkong/test/test_probability.cpp +136 -0
  497. package/numkong/test/test_probability.py +166 -0
  498. package/numkong/test/test_reduce.cpp +563 -0
  499. package/numkong/test/test_reduce.py +781 -0
  500. package/numkong/test/test_set.cpp +151 -0
  501. package/numkong/test/test_set.py +120 -0
  502. package/numkong/test/test_similarities.py +579 -0
  503. package/numkong/test/test_sparse.cpp +121 -0
  504. package/numkong/test/test_sparse.py +118 -0
  505. package/numkong/test/test_spatial.cpp +518 -0
  506. package/numkong/test/test_spatial.py +269 -0
  507. package/numkong/test/test_tensor.cpp +884 -0
  508. package/numkong/test/test_tensor.py +1417 -0
  509. package/numkong/test/test_trigonometry.cpp +156 -0
  510. package/numkong/test/test_trigonometry.py +180 -0
  511. package/package.json +7 -8
  512. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  513. package/prebuilds/linux-arm64/usearch.node +0 -0
  514. package/prebuilds/linux-x64/usearch.node +0 -0
  515. package/fp16/CMakeLists.txt +0 -176
  516. package/fp16/LICENSE +0 -11
  517. package/fp16/README.md +0 -20
  518. package/fp16/bench/alt-element.cc +0 -89
  519. package/fp16/bench/from-alt-array.cc +0 -175
  520. package/fp16/bench/from-ieee-array.cc +0 -399
  521. package/fp16/bench/ieee-element.cc +0 -272
  522. package/fp16/bench/to-alt-array.cc +0 -91
  523. package/fp16/bench/to-ieee-array.cc +0 -303
  524. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  525. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  526. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  527. package/fp16/configure.py +0 -65
  528. package/fp16/confu.yaml +0 -8
  529. package/fp16/include/fp16/__init__.py +0 -0
  530. package/fp16/include/fp16/avx.py +0 -50
  531. package/fp16/include/fp16/avx2.py +0 -53
  532. package/fp16/include/fp16/bitcasts.h +0 -92
  533. package/fp16/include/fp16/fp16.h +0 -451
  534. package/fp16/include/fp16/psimd.h +0 -131
  535. package/fp16/include/fp16.h +0 -11
  536. package/fp16/jni/Android.mk +0 -6
  537. package/fp16/jni/Application.mk +0 -4
  538. package/fp16/test/alt-from-fp32-value.cc +0 -498
  539. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  540. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  541. package/fp16/test/alt-to-fp32-value.cc +0 -348
  542. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  543. package/fp16/test/bitcasts.cc +0 -56
  544. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  545. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  546. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  547. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  548. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  549. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  550. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  551. package/fp16/test/peachpy/stubs.py +0 -38
  552. package/fp16/test/tables.cc +0 -530
  553. package/fp16/test/tables.h +0 -13
  554. package/fp16/third-party/THHalf.h +0 -92
  555. package/fp16/third-party/eigen-half.h +0 -170
  556. package/fp16/third-party/float16-compressor.h +0 -80
  557. package/fp16/third-party/half.hpp +0 -2916
  558. package/fp16/third-party/npy-halffloat.h +0 -328
  559. package/simsimd/.git-blame-ignore-revs +0 -2
  560. package/simsimd/CONTRIBUTING.md +0 -321
  561. package/simsimd/Cargo.toml +0 -49
  562. package/simsimd/MANIFEST.in +0 -4
  563. package/simsimd/Package.swift +0 -28
  564. package/simsimd/README.md +0 -1493
  565. package/simsimd/binding.gyp +0 -35
  566. package/simsimd/build.rs +0 -70
  567. package/simsimd/c/lib.c +0 -374
  568. package/simsimd/include/module.modulemap +0 -4
  569. package/simsimd/include/simsimd/binary.h +0 -486
  570. package/simsimd/include/simsimd/curved.h +0 -1541
  571. package/simsimd/include/simsimd/dot.h +0 -1853
  572. package/simsimd/include/simsimd/elementwise.h +0 -2465
  573. package/simsimd/include/simsimd/geospatial.h +0 -43
  574. package/simsimd/include/simsimd/mesh.h +0 -69
  575. package/simsimd/include/simsimd/probability.h +0 -606
  576. package/simsimd/include/simsimd/simsimd.h +0 -2567
  577. package/simsimd/include/simsimd/sparse.h +0 -1384
  578. package/simsimd/include/simsimd/spatial.h +0 -2335
  579. package/simsimd/include/simsimd/types.h +0 -666
  580. package/simsimd/javascript/fallback.ts +0 -233
  581. package/simsimd/javascript/lib.c +0 -125
  582. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  583. package/simsimd/javascript/simsimd.ts +0 -179
  584. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  585. package/simsimd/package-lock.json +0 -1761
  586. package/simsimd/package.json +0 -61
  587. package/simsimd/rust/lib.rs +0 -1821
  588. package/simsimd/scripts/bench.cxx +0 -1170
  589. package/simsimd/scripts/bench.ipynb +0 -187
  590. package/simsimd/scripts/bench.js +0 -127
  591. package/simsimd/scripts/bench.rs +0 -876
  592. package/simsimd/scripts/bench_vectors.py +0 -734
  593. package/simsimd/scripts/bench_vectors_live.py +0 -213
  594. package/simsimd/scripts/test.c +0 -187
  595. package/simsimd/scripts/test.mjs +0 -220
  596. package/simsimd/scripts/test.py +0 -1505
  597. package/simsimd/swift/SimSIMD.swift +0 -149
  598. package/simsimd/swift/Test.swift +0 -128
  599. /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
  600. /package/{simsimd → numkong}/LICENSE +0 -0
  601. /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
  602. /package/{simsimd → numkong}/javascript/dist-package-esm.json +0 -0
@@ -0,0 +1,615 @@
1
+ /**
2
+ * @brief SIMD-accelerated Geospatial Distances for NEON.
3
+ * @file include/numkong/geospatial/neon.h
4
+ * @author Ash Vardanian
5
+ * @date February 6, 2026
6
+ *
7
+ * @sa include/numkong/geospatial.h
8
+ *
9
+ * @section geospatial_neon_instructions Key NEON Geospatial Instructions
10
+ *
11
+ * Intrinsic Instruction M1 Firestorm Graviton 3 Graviton 4
12
+ * vfmaq_f32 FMLA.S (vec) 4cy @ V0123 4cy @ V0123 4cy @ V0123
13
+ * vfmaq_f64 FMLA.D (vec) 4cy @ V0123 4cy @ V0123 4cy @ V0123
14
+ * vsqrtq_f32 FSQRT.S (vec) 10cy @ V02 10cy @ V02 9cy @ V02
15
+ * vsqrtq_f64 FSQRT.D (vec) 13cy @ V02 16cy @ V02 16cy @ V02
16
+ */
17
+ #ifndef NK_GEOSPATIAL_NEON_H
18
+ #define NK_GEOSPATIAL_NEON_H
19
+
20
+ #if NK_TARGET_ARM64_
21
+ #if NK_TARGET_NEON
22
+
23
+ #include "numkong/types.h"
24
+ #include "numkong/trigonometry/neon.h" // `nk_sin_f64x2_neon_`, `nk_cos_f64x2_neon_`, `nk_atan2_f64x2_neon_`
25
+
26
+ #if defined(__cplusplus)
27
+ extern "C" {
28
+ #endif
29
+
30
+ #if defined(__clang__)
31
+ #pragma clang attribute push(__attribute__((target("arch=armv8-a+simd"))), apply_to = function)
32
+ #elif defined(__GNUC__)
33
+ #pragma GCC push_options
34
+ #pragma GCC target("arch=armv8-a+simd")
35
+ #endif
36
+
37
+ /* NEON implementations using 2-wide f64 and 4-wide f32 SIMD.
38
+ * These require NEON trigonometric kernels from trigonometry/neon.h.
39
+ */
40
+
41
+ NK_INTERNAL float64x2_t nk_haversine_f64x2_neon_( //
42
+ float64x2_t first_latitudes_f64x2, float64x2_t first_longitudes_f64x2, //
43
+ float64x2_t second_latitudes_f64x2, float64x2_t second_longitudes_f64x2) {
44
+
45
+ float64x2_t const earth_radius_f64x2 = vdupq_n_f64(NK_EARTH_MEDIATORIAL_RADIUS);
46
+ float64x2_t const half_f64x2 = vdupq_n_f64(0.5);
47
+ float64x2_t const one_f64x2 = vdupq_n_f64(1.0);
48
+ float64x2_t const two_f64x2 = vdupq_n_f64(2.0);
49
+
50
+ float64x2_t latitude_delta_f64x2 = vsubq_f64(second_latitudes_f64x2, first_latitudes_f64x2);
51
+ float64x2_t longitude_delta_f64x2 = vsubq_f64(second_longitudes_f64x2, first_longitudes_f64x2);
52
+
53
+ // Haversine terms: sin²(Δ/2)
54
+ float64x2_t latitude_delta_half_f64x2 = vmulq_f64(latitude_delta_f64x2, half_f64x2);
55
+ float64x2_t longitude_delta_half_f64x2 = vmulq_f64(longitude_delta_f64x2, half_f64x2);
56
+ float64x2_t sin_latitude_delta_half_f64x2 = nk_sin_f64x2_neon_(latitude_delta_half_f64x2);
57
+ float64x2_t sin_longitude_delta_half_f64x2 = nk_sin_f64x2_neon_(longitude_delta_half_f64x2);
58
+ float64x2_t sin_squared_latitude_delta_half_f64x2 = vmulq_f64(sin_latitude_delta_half_f64x2,
59
+ sin_latitude_delta_half_f64x2);
60
+ float64x2_t sin_squared_longitude_delta_half_f64x2 = vmulq_f64(sin_longitude_delta_half_f64x2,
61
+ sin_longitude_delta_half_f64x2);
62
+
63
+ // Latitude cosine product
64
+ float64x2_t cos_first_latitude_f64x2 = nk_cos_f64x2_neon_(first_latitudes_f64x2);
65
+ float64x2_t cos_second_latitude_f64x2 = nk_cos_f64x2_neon_(second_latitudes_f64x2);
66
+ float64x2_t cos_latitude_product_f64x2 = vmulq_f64(cos_first_latitude_f64x2, cos_second_latitude_f64x2);
67
+
68
+ // a = sin²(Δlat/2) + cos(lat1) × cos(lat2) × sin²(Δlon/2)
69
+ float64x2_t haversine_term_f64x2 = vaddq_f64(
70
+ sin_squared_latitude_delta_half_f64x2,
71
+ vmulq_f64(cos_latitude_product_f64x2, sin_squared_longitude_delta_half_f64x2));
72
+ // Clamp haversine_term_f64x2 to [0, 1] to prevent NaN from sqrt of negative values
73
+ float64x2_t zero_f64x2 = vdupq_n_f64(0.0);
74
+ haversine_term_f64x2 = vmaxq_f64(zero_f64x2, vminq_f64(one_f64x2, haversine_term_f64x2));
75
+
76
+ // Central angle: c = 2 × atan2(√a, √(1-a))
77
+ float64x2_t sqrt_haversine_f64x2 = vsqrtq_f64(haversine_term_f64x2);
78
+ float64x2_t sqrt_complement_f64x2 = vsqrtq_f64(vsubq_f64(one_f64x2, haversine_term_f64x2));
79
+ float64x2_t central_angle_f64x2 = vmulq_f64(two_f64x2,
80
+ nk_atan2_f64x2_neon_(sqrt_haversine_f64x2, sqrt_complement_f64x2));
81
+
82
+ return vmulq_f64(earth_radius_f64x2, central_angle_f64x2);
83
+ }
84
+
85
+ NK_PUBLIC void nk_haversine_f64_neon( //
86
+ nk_f64_t const *a_lats, nk_f64_t const *a_lons, //
87
+ nk_f64_t const *b_lats, nk_f64_t const *b_lons, //
88
+ nk_size_t n, nk_f64_t *results) {
89
+
90
+ while (n >= 2) {
91
+ float64x2_t first_latitudes_f64x2 = vld1q_f64(a_lats);
92
+ float64x2_t first_longitudes_f64x2 = vld1q_f64(a_lons);
93
+ float64x2_t second_latitudes_f64x2 = vld1q_f64(b_lats);
94
+ float64x2_t second_longitudes_f64x2 = vld1q_f64(b_lons);
95
+
96
+ float64x2_t distances_f64x2 = nk_haversine_f64x2_neon_(first_latitudes_f64x2, first_longitudes_f64x2,
97
+ second_latitudes_f64x2, second_longitudes_f64x2);
98
+ vst1q_f64(results, distances_f64x2);
99
+
100
+ a_lats += 2, a_lons += 2, b_lats += 2, b_lons += 2, results += 2, n -= 2;
101
+ }
102
+
103
+ // Handle tail with partial loads (n can only be 0 or 1 here)
104
+ if (n > 0) {
105
+ nk_b128_vec_t a_lat_vec, a_lon_vec, b_lat_vec, b_lon_vec, result_vec;
106
+ nk_partial_load_b64x2_serial_(a_lats, &a_lat_vec, n);
107
+ nk_partial_load_b64x2_serial_(a_lons, &a_lon_vec, n);
108
+ nk_partial_load_b64x2_serial_(b_lats, &b_lat_vec, n);
109
+ nk_partial_load_b64x2_serial_(b_lons, &b_lon_vec, n);
110
+ float64x2_t distances_f64x2 = nk_haversine_f64x2_neon_(a_lat_vec.f64x2, a_lon_vec.f64x2, b_lat_vec.f64x2,
111
+ b_lon_vec.f64x2);
112
+ result_vec.f64x2 = distances_f64x2;
113
+ nk_partial_store_b64x2_serial_(&result_vec, results, n);
114
+ }
115
+ }
116
+
117
+ NK_INTERNAL float32x4_t nk_haversine_f32x4_neon_( //
118
+ float32x4_t first_latitudes_f32x4, float32x4_t first_longitudes_f32x4, //
119
+ float32x4_t second_latitudes_f32x4, float32x4_t second_longitudes_f32x4) {
120
+
121
+ float32x4_t const earth_radius_f32x4 = vdupq_n_f32((float)NK_EARTH_MEDIATORIAL_RADIUS);
122
+ float32x4_t const half_f32x4 = vdupq_n_f32(0.5f);
123
+ float32x4_t const one_f32x4 = vdupq_n_f32(1.0f);
124
+ float32x4_t const two_f32x4 = vdupq_n_f32(2.0f);
125
+
126
+ float32x4_t latitude_delta_f32x4 = vsubq_f32(second_latitudes_f32x4, first_latitudes_f32x4);
127
+ float32x4_t longitude_delta_f32x4 = vsubq_f32(second_longitudes_f32x4, first_longitudes_f32x4);
128
+
129
+ // Haversine terms: sin²(Δ/2)
130
+ float32x4_t latitude_delta_half_f32x4 = vmulq_f32(latitude_delta_f32x4, half_f32x4);
131
+ float32x4_t longitude_delta_half_f32x4 = vmulq_f32(longitude_delta_f32x4, half_f32x4);
132
+ float32x4_t sin_latitude_delta_half_f32x4 = nk_sin_f32x4_neon_(latitude_delta_half_f32x4);
133
+ float32x4_t sin_longitude_delta_half_f32x4 = nk_sin_f32x4_neon_(longitude_delta_half_f32x4);
134
+ float32x4_t sin_squared_latitude_delta_half_f32x4 = vmulq_f32(sin_latitude_delta_half_f32x4,
135
+ sin_latitude_delta_half_f32x4);
136
+ float32x4_t sin_squared_longitude_delta_half_f32x4 = vmulq_f32(sin_longitude_delta_half_f32x4,
137
+ sin_longitude_delta_half_f32x4);
138
+
139
+ // Latitude cosine product
140
+ float32x4_t cos_first_latitude_f32x4 = nk_cos_f32x4_neon_(first_latitudes_f32x4);
141
+ float32x4_t cos_second_latitude_f32x4 = nk_cos_f32x4_neon_(second_latitudes_f32x4);
142
+ float32x4_t cos_latitude_product_f32x4 = vmulq_f32(cos_first_latitude_f32x4, cos_second_latitude_f32x4);
143
+
144
+ // a = sin²(Δlat/2) + cos(lat1) × cos(lat2) × sin²(Δlon/2)
145
+ float32x4_t haversine_term_f32x4 = vaddq_f32(
146
+ sin_squared_latitude_delta_half_f32x4,
147
+ vmulq_f32(cos_latitude_product_f32x4, sin_squared_longitude_delta_half_f32x4));
148
+
149
+ // Clamp to [0, 1] to avoid NaN from sqrt of negative numbers (due to floating point errors)
150
+ float32x4_t zero_f32x4 = vdupq_n_f32(0.0f);
151
+ haversine_term_f32x4 = vmaxq_f32(zero_f32x4, vminq_f32(one_f32x4, haversine_term_f32x4));
152
+
153
+ // Central angle: c = 2 × atan2(√a, √(1-a))
154
+ float32x4_t sqrt_haversine_f32x4 = vsqrtq_f32(haversine_term_f32x4);
155
+ float32x4_t sqrt_complement_f32x4 = vsqrtq_f32(vsubq_f32(one_f32x4, haversine_term_f32x4));
156
+ float32x4_t central_angle_f32x4 = vmulq_f32(two_f32x4,
157
+ nk_atan2_f32x4_neon_(sqrt_haversine_f32x4, sqrt_complement_f32x4));
158
+
159
+ return vmulq_f32(earth_radius_f32x4, central_angle_f32x4);
160
+ }
161
+
162
+ NK_PUBLIC void nk_haversine_f32_neon( //
163
+ nk_f32_t const *a_lats, nk_f32_t const *a_lons, //
164
+ nk_f32_t const *b_lats, nk_f32_t const *b_lons, //
165
+ nk_size_t n, nk_f32_t *results) {
166
+
167
+ while (n >= 4) {
168
+ float32x4_t first_latitudes_f32x4 = vld1q_f32(a_lats);
169
+ float32x4_t first_longitudes_f32x4 = vld1q_f32(a_lons);
170
+ float32x4_t second_latitudes_f32x4 = vld1q_f32(b_lats);
171
+ float32x4_t second_longitudes_f32x4 = vld1q_f32(b_lons);
172
+
173
+ float32x4_t distances_f32x4 = nk_haversine_f32x4_neon_(first_latitudes_f32x4, first_longitudes_f32x4,
174
+ second_latitudes_f32x4, second_longitudes_f32x4);
175
+ vst1q_f32(results, distances_f32x4);
176
+
177
+ a_lats += 4, a_lons += 4, b_lats += 4, b_lons += 4, results += 4, n -= 4;
178
+ }
179
+
180
+ // Handle tail with partial loads (n can be 0-3 here)
181
+ if (n > 0) {
182
+ nk_b128_vec_t a_lat_vec, a_lon_vec, b_lat_vec, b_lon_vec, result_vec;
183
+ nk_partial_load_b32x4_serial_(a_lats, &a_lat_vec, n);
184
+ nk_partial_load_b32x4_serial_(a_lons, &a_lon_vec, n);
185
+ nk_partial_load_b32x4_serial_(b_lats, &b_lat_vec, n);
186
+ nk_partial_load_b32x4_serial_(b_lons, &b_lon_vec, n);
187
+ float32x4_t distances_f32x4 = nk_haversine_f32x4_neon_(a_lat_vec.f32x4, a_lon_vec.f32x4, b_lat_vec.f32x4,
188
+ b_lon_vec.f32x4);
189
+ result_vec.f32x4 = distances_f32x4;
190
+ nk_partial_store_b32x4_serial_(&result_vec, results, n);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * @brief NEON helper for Vincenty's geodesic distance on 2 f64 point pairs.
196
+ * @note This is a true SIMD implementation using masked convergence tracking via blending.
197
+ */
198
+ NK_INTERNAL float64x2_t nk_vincenty_f64x2_neon_( //
199
+ float64x2_t first_latitudes_f64x2, float64x2_t first_longitudes_f64x2, //
200
+ float64x2_t second_latitudes_f64x2, float64x2_t second_longitudes_f64x2) {
201
+
202
+ float64x2_t const equatorial_radius_f64x2 = vdupq_n_f64(NK_EARTH_ELLIPSOID_EQUATORIAL_RADIUS);
203
+ float64x2_t const polar_radius_f64x2 = vdupq_n_f64(NK_EARTH_ELLIPSOID_POLAR_RADIUS);
204
+ float64x2_t const flattening_f64x2 = vdupq_n_f64(1.0 / NK_EARTH_ELLIPSOID_INVERSE_FLATTENING);
205
+ float64x2_t const convergence_threshold_f64x2 = vdupq_n_f64(NK_VINCENTY_CONVERGENCE_THRESHOLD_F64);
206
+ float64x2_t const one_f64x2 = vdupq_n_f64(1.0);
207
+ float64x2_t const two_f64x2 = vdupq_n_f64(2.0);
208
+ float64x2_t const three_f64x2 = vdupq_n_f64(3.0);
209
+ float64x2_t const four_f64x2 = vdupq_n_f64(4.0);
210
+ float64x2_t const six_f64x2 = vdupq_n_f64(6.0);
211
+ float64x2_t const sixteen_f64x2 = vdupq_n_f64(16.0);
212
+ float64x2_t const epsilon_f64x2 = vdupq_n_f64(1e-15);
213
+
214
+ // Longitude difference
215
+ float64x2_t longitude_difference_f64x2 = vsubq_f64(second_longitudes_f64x2, first_longitudes_f64x2);
216
+
217
+ // Reduced latitudes: tan(U) = (1-f) * tan(lat)
218
+ float64x2_t one_minus_f_f64x2 = vsubq_f64(one_f64x2, flattening_f64x2);
219
+ float64x2_t tan_first_f64x2 = vdivq_f64(nk_sin_f64x2_neon_(first_latitudes_f64x2),
220
+ nk_cos_f64x2_neon_(first_latitudes_f64x2));
221
+ float64x2_t tan_second_f64x2 = vdivq_f64(nk_sin_f64x2_neon_(second_latitudes_f64x2),
222
+ nk_cos_f64x2_neon_(second_latitudes_f64x2));
223
+ float64x2_t tan_reduced_first_f64x2 = vmulq_f64(one_minus_f_f64x2, tan_first_f64x2);
224
+ float64x2_t tan_reduced_second_f64x2 = vmulq_f64(one_minus_f_f64x2, tan_second_f64x2);
225
+
226
+ // cos(U) = 1/√(1 + tan²(U)), sin(U) = tan(U) × cos(U)
227
+ float64x2_t cos_reduced_first_f64x2 = vdivq_f64(
228
+ one_f64x2, vsqrtq_f64(vfmaq_f64(one_f64x2, tan_reduced_first_f64x2, tan_reduced_first_f64x2)));
229
+ float64x2_t sin_reduced_first_f64x2 = vmulq_f64(tan_reduced_first_f64x2, cos_reduced_first_f64x2);
230
+ float64x2_t cos_reduced_second_f64x2 = vdivq_f64(
231
+ one_f64x2, vsqrtq_f64(vfmaq_f64(one_f64x2, tan_reduced_second_f64x2, tan_reduced_second_f64x2)));
232
+ float64x2_t sin_reduced_second_f64x2 = vmulq_f64(tan_reduced_second_f64x2, cos_reduced_second_f64x2);
233
+
234
+ // Initialize lambda_f64x2 and tracking variables
235
+ float64x2_t lambda_f64x2 = longitude_difference_f64x2;
236
+ float64x2_t sin_angular_distance_f64x2, cos_angular_distance_f64x2, angular_distance_f64x2;
237
+ float64x2_t sin_azimuth_f64x2, cos_squared_azimuth_f64x2, cos_double_angular_midpoint_f64x2;
238
+
239
+ // Track convergence and coincident points using masks
240
+ uint64x2_t converged_mask_u64x2 = vdupq_n_u64(0);
241
+ uint64x2_t coincident_mask_u64x2 = vdupq_n_u64(0);
242
+
243
+ for (nk_u32_t iteration = 0; iteration < NK_VINCENTY_MAX_ITERATIONS; ++iteration) {
244
+ // Check if all lanes converged
245
+ nk_u64_t converged_bits = vgetq_lane_u64(converged_mask_u64x2, 0) & vgetq_lane_u64(converged_mask_u64x2, 1);
246
+ if (converged_bits) break;
247
+
248
+ float64x2_t sin_lambda_f64x2 = nk_sin_f64x2_neon_(lambda_f64x2);
249
+ float64x2_t cos_lambda_f64x2 = nk_cos_f64x2_neon_(lambda_f64x2);
250
+
251
+ // sin²(angular_distance_f64x2) = (cos(U₂) × sin(λ))² + (cos(U₁) × sin(U₂) - sin(U₁) × cos(U₂) × cos(λ))²
252
+ float64x2_t cross_term_f64x2 = vmulq_f64(cos_reduced_second_f64x2, sin_lambda_f64x2);
253
+ float64x2_t mixed_term_f64x2 = vsubq_f64(
254
+ vmulq_f64(cos_reduced_first_f64x2, sin_reduced_second_f64x2),
255
+ vmulq_f64(vmulq_f64(sin_reduced_first_f64x2, cos_reduced_second_f64x2), cos_lambda_f64x2));
256
+ float64x2_t sin_angular_dist_sq_f64x2 = vfmaq_f64(vmulq_f64(mixed_term_f64x2, mixed_term_f64x2),
257
+ cross_term_f64x2, cross_term_f64x2);
258
+ sin_angular_distance_f64x2 = vsqrtq_f64(sin_angular_dist_sq_f64x2);
259
+
260
+ // Check for coincident points (sin_angular_distance_f64x2 ≈ 0)
261
+ coincident_mask_u64x2 = vcltq_f64(sin_angular_distance_f64x2, epsilon_f64x2);
262
+
263
+ // cos(angular_distance_f64x2) = sin(U₁) × sin(U₂) + cos(U₁) × cos(U₂) × cos(λ)
264
+ cos_angular_distance_f64x2 = vfmaq_f64(vmulq_f64(sin_reduced_first_f64x2, sin_reduced_second_f64x2),
265
+ vmulq_f64(cos_reduced_first_f64x2, cos_reduced_second_f64x2),
266
+ cos_lambda_f64x2);
267
+
268
+ // angular_distance_f64x2 = atan2(sin, cos)
269
+ angular_distance_f64x2 = nk_atan2_f64x2_neon_(sin_angular_distance_f64x2, cos_angular_distance_f64x2);
270
+
271
+ // sin(azimuth) = cos(U₁) × cos(U₂) × sin(λ) / sin(angular_distance_f64x2)
272
+ // Avoid division by zero by using blending
273
+ float64x2_t safe_sin_angular_f64x2 = vbslq_f64(coincident_mask_u64x2, one_f64x2, sin_angular_distance_f64x2);
274
+ sin_azimuth_f64x2 = vdivq_f64(
275
+ vmulq_f64(vmulq_f64(cos_reduced_first_f64x2, cos_reduced_second_f64x2), sin_lambda_f64x2),
276
+ safe_sin_angular_f64x2);
277
+ cos_squared_azimuth_f64x2 = vsubq_f64(one_f64x2, vmulq_f64(sin_azimuth_f64x2, sin_azimuth_f64x2));
278
+
279
+ // Handle equatorial case: cos²α ≈ 0
280
+ uint64x2_t equatorial_mask_u64x2 = vcltq_f64(cos_squared_azimuth_f64x2, epsilon_f64x2);
281
+ float64x2_t safe_cos_sq_azimuth_f64x2 = vbslq_f64(equatorial_mask_u64x2, one_f64x2, cos_squared_azimuth_f64x2);
282
+
283
+ // cos(2σₘ) = cos(σ) - 2 × sin(U₁) × sin(U₂) / cos²(α)
284
+ float64x2_t sin_product_f64x2 = vmulq_f64(sin_reduced_first_f64x2, sin_reduced_second_f64x2);
285
+ cos_double_angular_midpoint_f64x2 = vsubq_f64(
286
+ cos_angular_distance_f64x2, vdivq_f64(vmulq_f64(two_f64x2, sin_product_f64x2), safe_cos_sq_azimuth_f64x2));
287
+ cos_double_angular_midpoint_f64x2 = vbslq_f64(equatorial_mask_u64x2, vdupq_n_f64(0.0),
288
+ cos_double_angular_midpoint_f64x2);
289
+
290
+ // C = f/16 * cos²α * (4 + f*(4 - 3*cos²α))
291
+ float64x2_t correction_factor_f64x2 = vmulq_f64(
292
+ vdivq_f64(flattening_f64x2, sixteen_f64x2),
293
+ vmulq_f64(cos_squared_azimuth_f64x2,
294
+ vfmaq_f64(four_f64x2, flattening_f64x2,
295
+ vfmsq_f64(four_f64x2, three_f64x2, cos_squared_azimuth_f64x2))));
296
+
297
+ // λ' = L + (1-C) × f × sin(α) × (σ + C × sin(σ) × (cos(2σₘ) + C × cos(σ) × (-1 + 2 × cos²(2σₘ))))
298
+ float64x2_t cos_2sm_sq_f64x2 = vmulq_f64(cos_double_angular_midpoint_f64x2, cos_double_angular_midpoint_f64x2);
299
+ // innermost_f64x2 = -1 + 2 × cos²(2σₘ)
300
+ float64x2_t innermost_f64x2 = vfmaq_f64(vdupq_n_f64(-1.0), two_f64x2, cos_2sm_sq_f64x2);
301
+ // middle_f64x2 = cos(2σₘ) + C × cos(σ) × innermost_f64x2
302
+ float64x2_t middle_f64x2 = vfmaq_f64(cos_double_angular_midpoint_f64x2,
303
+ vmulq_f64(correction_factor_f64x2, cos_angular_distance_f64x2),
304
+ innermost_f64x2);
305
+ // inner_f64x2 = C × sin(σ) × middle_f64x2
306
+ float64x2_t inner_f64x2 = vmulq_f64(vmulq_f64(correction_factor_f64x2, sin_angular_distance_f64x2),
307
+ middle_f64x2);
308
+
309
+ // λ' = L + (1-C) * f * sin_α * (σ + inner_f64x2)
310
+ float64x2_t lambda_new_f64x2 = vfmaq_f64(
311
+ longitude_difference_f64x2,
312
+ vmulq_f64(vmulq_f64(vsubq_f64(one_f64x2, correction_factor_f64x2), flattening_f64x2), sin_azimuth_f64x2),
313
+ vaddq_f64(angular_distance_f64x2, inner_f64x2));
314
+
315
+ // Check convergence: |λ - λ'| < threshold
316
+ float64x2_t lambda_diff_f64x2 = vsubq_f64(lambda_new_f64x2, lambda_f64x2);
317
+ float64x2_t lambda_diff_abs_f64x2 = vabsq_f64(lambda_diff_f64x2);
318
+ uint64x2_t newly_converged_u64x2 = vcltq_f64(lambda_diff_abs_f64x2, convergence_threshold_f64x2);
319
+ converged_mask_u64x2 = vorrq_u64(converged_mask_u64x2, newly_converged_u64x2);
320
+
321
+ // Only update lambda_f64x2 for non-converged lanes
322
+ lambda_f64x2 = vbslq_f64(converged_mask_u64x2, lambda_f64x2, lambda_new_f64x2);
323
+ }
324
+
325
+ // Final distance calculation
326
+ // u² = cos²α * (a² - b²) / b²
327
+ float64x2_t a_sq_f64x2 = vmulq_f64(equatorial_radius_f64x2, equatorial_radius_f64x2);
328
+ float64x2_t b_sq_f64x2 = vmulq_f64(polar_radius_f64x2, polar_radius_f64x2);
329
+ float64x2_t u_squared_f64x2 = vdivq_f64(vmulq_f64(cos_squared_azimuth_f64x2, vsubq_f64(a_sq_f64x2, b_sq_f64x2)),
330
+ b_sq_f64x2);
331
+
332
+ // A = 1 + u²/16384 * (4096 + u²*(-768 + u²*(320 - 175*u²)))
333
+ float64x2_t series_a_f64x2 = vfmaq_f64(vdupq_n_f64(320.0), u_squared_f64x2, vdupq_n_f64(-175.0));
334
+ series_a_f64x2 = vfmaq_f64(vdupq_n_f64(-768.0), u_squared_f64x2, series_a_f64x2);
335
+ series_a_f64x2 = vfmaq_f64(vdupq_n_f64(4096.0), u_squared_f64x2, series_a_f64x2);
336
+ series_a_f64x2 = vfmaq_f64(one_f64x2, vdivq_f64(u_squared_f64x2, vdupq_n_f64(16384.0)), series_a_f64x2);
337
+
338
+ // B = u²/1024 * (256 + u²*(-128 + u²*(74 - 47*u²)))
339
+ float64x2_t series_b_f64x2 = vfmaq_f64(vdupq_n_f64(74.0), u_squared_f64x2, vdupq_n_f64(-47.0));
340
+ series_b_f64x2 = vfmaq_f64(vdupq_n_f64(-128.0), u_squared_f64x2, series_b_f64x2);
341
+ series_b_f64x2 = vfmaq_f64(vdupq_n_f64(256.0), u_squared_f64x2, series_b_f64x2);
342
+ series_b_f64x2 = vmulq_f64(vdivq_f64(u_squared_f64x2, vdupq_n_f64(1024.0)), series_b_f64x2);
343
+
344
+ // Δσ = B × sin(σ) × (cos(2σₘ) + B/4 × (cos(σ) × (-1 + 2 × cos²(2σₘ)) - B/6 × cos(2σₘ) × (-3 + 4 × sin²(σ)) × (-3 +
345
+ // 4 × cos²(2σₘ))))
346
+ float64x2_t cos_2sm_sq_f64x2 = vmulq_f64(cos_double_angular_midpoint_f64x2, cos_double_angular_midpoint_f64x2);
347
+ float64x2_t sin_sq_f64x2 = vmulq_f64(sin_angular_distance_f64x2, sin_angular_distance_f64x2);
348
+ float64x2_t term1_f64x2 = vfmaq_f64(vdupq_n_f64(-1.0), two_f64x2, cos_2sm_sq_f64x2);
349
+ term1_f64x2 = vmulq_f64(cos_angular_distance_f64x2, term1_f64x2);
350
+ float64x2_t term2_f64x2 = vfmaq_f64(vdupq_n_f64(-3.0), four_f64x2, sin_sq_f64x2);
351
+ float64x2_t term3_f64x2 = vfmaq_f64(vdupq_n_f64(-3.0), four_f64x2, cos_2sm_sq_f64x2);
352
+ term2_f64x2 = vmulq_f64(vmulq_f64(vdivq_f64(series_b_f64x2, six_f64x2), cos_double_angular_midpoint_f64x2),
353
+ vmulq_f64(term2_f64x2, term3_f64x2));
354
+ float64x2_t delta_sigma_f64x2 = vmulq_f64(
355
+ series_b_f64x2,
356
+ vmulq_f64(sin_angular_distance_f64x2,
357
+ vaddq_f64(cos_double_angular_midpoint_f64x2,
358
+ vmulq_f64(vdivq_f64(series_b_f64x2, four_f64x2), vsubq_f64(term1_f64x2, term2_f64x2)))));
359
+
360
+ // s = b * A * (σ - Δσ)
361
+ float64x2_t distances_f64x2 = vmulq_f64(vmulq_f64(polar_radius_f64x2, series_a_f64x2),
362
+ vsubq_f64(angular_distance_f64x2, delta_sigma_f64x2));
363
+
364
+ // Set coincident points to zero
365
+ distances_f64x2 = vbslq_f64(coincident_mask_u64x2, vdupq_n_f64(0.0), distances_f64x2);
366
+
367
+ return distances_f64x2;
368
+ }
369
+
370
+ NK_PUBLIC void nk_vincenty_f64_neon( //
371
+ nk_f64_t const *a_lats, nk_f64_t const *a_lons, //
372
+ nk_f64_t const *b_lats, nk_f64_t const *b_lons, //
373
+ nk_size_t n, nk_f64_t *results) {
374
+
375
+ while (n >= 2) {
376
+ float64x2_t first_latitudes_f64x2 = vld1q_f64(a_lats);
377
+ float64x2_t first_longitudes_f64x2 = vld1q_f64(a_lons);
378
+ float64x2_t second_latitudes_f64x2 = vld1q_f64(b_lats);
379
+ float64x2_t second_longitudes_f64x2 = vld1q_f64(b_lons);
380
+
381
+ float64x2_t distances_f64x2 = nk_vincenty_f64x2_neon_(first_latitudes_f64x2, first_longitudes_f64x2,
382
+ second_latitudes_f64x2, second_longitudes_f64x2);
383
+ vst1q_f64(results, distances_f64x2);
384
+
385
+ a_lats += 2, a_lons += 2, b_lats += 2, b_lons += 2, results += 2, n -= 2;
386
+ }
387
+
388
+ // Handle remaining elements with partial loads (n can only be 0 or 1 here)
389
+ if (n > 0) {
390
+ nk_b128_vec_t a_lat_vec, a_lon_vec, b_lat_vec, b_lon_vec, result_vec;
391
+ nk_partial_load_b64x2_serial_(a_lats, &a_lat_vec, n);
392
+ nk_partial_load_b64x2_serial_(a_lons, &a_lon_vec, n);
393
+ nk_partial_load_b64x2_serial_(b_lats, &b_lat_vec, n);
394
+ nk_partial_load_b64x2_serial_(b_lons, &b_lon_vec, n);
395
+ float64x2_t distances_f64x2 = nk_vincenty_f64x2_neon_(a_lat_vec.f64x2, a_lon_vec.f64x2, b_lat_vec.f64x2,
396
+ b_lon_vec.f64x2);
397
+ result_vec.f64x2 = distances_f64x2;
398
+ nk_partial_store_b64x2_serial_(&result_vec, results, n);
399
+ }
400
+ }
401
+
402
+ /**
403
+ * @brief NEON helper for Vincenty's geodesic distance on 4 f32 point pairs.
404
+ * @note This is a true SIMD implementation using masked convergence tracking via blending.
405
+ */
406
+ NK_INTERNAL float32x4_t nk_vincenty_f32x4_neon_( //
407
+ float32x4_t first_latitudes_f32x4, float32x4_t first_longitudes_f32x4, //
408
+ float32x4_t second_latitudes_f32x4, float32x4_t second_longitudes_f32x4) {
409
+
410
+ float32x4_t const equatorial_radius_f32x4 = vdupq_n_f32((float)NK_EARTH_ELLIPSOID_EQUATORIAL_RADIUS);
411
+ float32x4_t const polar_radius_f32x4 = vdupq_n_f32((float)NK_EARTH_ELLIPSOID_POLAR_RADIUS);
412
+ float32x4_t const flattening_f32x4 = vdupq_n_f32(1.0f / (float)NK_EARTH_ELLIPSOID_INVERSE_FLATTENING);
413
+ float32x4_t const convergence_threshold_f32x4 = vdupq_n_f32(NK_VINCENTY_CONVERGENCE_THRESHOLD_F32);
414
+ float32x4_t const one_f32x4 = vdupq_n_f32(1.0f);
415
+ float32x4_t const two_f32x4 = vdupq_n_f32(2.0f);
416
+ float32x4_t const three_f32x4 = vdupq_n_f32(3.0f);
417
+ float32x4_t const four_f32x4 = vdupq_n_f32(4.0f);
418
+ float32x4_t const six_f32x4 = vdupq_n_f32(6.0f);
419
+ float32x4_t const sixteen_f32x4 = vdupq_n_f32(16.0f);
420
+ float32x4_t const epsilon_f32x4 = vdupq_n_f32(1e-7f);
421
+
422
+ // Longitude difference
423
+ float32x4_t longitude_difference_f32x4 = vsubq_f32(second_longitudes_f32x4, first_longitudes_f32x4);
424
+
425
+ // Reduced latitudes: tan(U) = (1-f) * tan(lat)
426
+ float32x4_t one_minus_f_f32x4 = vsubq_f32(one_f32x4, flattening_f32x4);
427
+ float32x4_t tan_first_f32x4 = vdivq_f32(nk_sin_f32x4_neon_(first_latitudes_f32x4),
428
+ nk_cos_f32x4_neon_(first_latitudes_f32x4));
429
+ float32x4_t tan_second_f32x4 = vdivq_f32(nk_sin_f32x4_neon_(second_latitudes_f32x4),
430
+ nk_cos_f32x4_neon_(second_latitudes_f32x4));
431
+ float32x4_t tan_reduced_first_f32x4 = vmulq_f32(one_minus_f_f32x4, tan_first_f32x4);
432
+ float32x4_t tan_reduced_second_f32x4 = vmulq_f32(one_minus_f_f32x4, tan_second_f32x4);
433
+
434
+ // cos(U) = 1/√(1 + tan²(U)), sin(U) = tan(U) × cos(U)
435
+ float32x4_t cos_reduced_first_f32x4 = vdivq_f32(
436
+ one_f32x4, vsqrtq_f32(vfmaq_f32(one_f32x4, tan_reduced_first_f32x4, tan_reduced_first_f32x4)));
437
+ float32x4_t sin_reduced_first_f32x4 = vmulq_f32(tan_reduced_first_f32x4, cos_reduced_first_f32x4);
438
+ float32x4_t cos_reduced_second_f32x4 = vdivq_f32(
439
+ one_f32x4, vsqrtq_f32(vfmaq_f32(one_f32x4, tan_reduced_second_f32x4, tan_reduced_second_f32x4)));
440
+ float32x4_t sin_reduced_second_f32x4 = vmulq_f32(tan_reduced_second_f32x4, cos_reduced_second_f32x4);
441
+
442
+ // Initialize lambda_f32x4 and tracking variables
443
+ float32x4_t lambda_f32x4 = longitude_difference_f32x4;
444
+ float32x4_t sin_angular_distance_f32x4, cos_angular_distance_f32x4, angular_distance_f32x4;
445
+ float32x4_t sin_azimuth_f32x4, cos_squared_azimuth_f32x4, cos_double_angular_midpoint_f32x4;
446
+
447
+ // Track convergence and coincident points using masks
448
+ uint32x4_t converged_mask_u32x4 = vdupq_n_u32(0);
449
+ uint32x4_t coincident_mask_u32x4 = vdupq_n_u32(0);
450
+
451
+ for (nk_u32_t iteration = 0; iteration < NK_VINCENTY_MAX_ITERATIONS; ++iteration) {
452
+ // Check if all lanes converged (all bits set = 0xFFFFFFFF per lane)
453
+ nk_u32_t converged_bits = vminvq_u32(converged_mask_u32x4);
454
+ if (converged_bits == 0xFFFFFFFF) break;
455
+
456
+ float32x4_t sin_lambda_f32x4 = nk_sin_f32x4_neon_(lambda_f32x4);
457
+ float32x4_t cos_lambda_f32x4 = nk_cos_f32x4_neon_(lambda_f32x4);
458
+
459
+ // sin²(angular_distance_f32x4) = (cos(U₂) × sin(λ))² + (cos(U₁) × sin(U₂) - sin(U₁) × cos(U₂) × cos(λ))²
460
+ float32x4_t cross_term_f32x4 = vmulq_f32(cos_reduced_second_f32x4, sin_lambda_f32x4);
461
+ float32x4_t mixed_term_f32x4 = vsubq_f32(
462
+ vmulq_f32(cos_reduced_first_f32x4, sin_reduced_second_f32x4),
463
+ vmulq_f32(vmulq_f32(sin_reduced_first_f32x4, cos_reduced_second_f32x4), cos_lambda_f32x4));
464
+ float32x4_t sin_angular_dist_sq_f32x4 = vfmaq_f32(vmulq_f32(mixed_term_f32x4, mixed_term_f32x4),
465
+ cross_term_f32x4, cross_term_f32x4);
466
+ sin_angular_distance_f32x4 = vsqrtq_f32(sin_angular_dist_sq_f32x4);
467
+
468
+ // Check for coincident points (sin_angular_distance_f32x4 ≈ 0)
469
+ coincident_mask_u32x4 = vcltq_f32(sin_angular_distance_f32x4, epsilon_f32x4);
470
+
471
+ // cos(angular_distance_f32x4) = sin(U₁) × sin(U₂) + cos(U₁) × cos(U₂) × cos(λ)
472
+ cos_angular_distance_f32x4 = vfmaq_f32(vmulq_f32(sin_reduced_first_f32x4, sin_reduced_second_f32x4),
473
+ vmulq_f32(cos_reduced_first_f32x4, cos_reduced_second_f32x4),
474
+ cos_lambda_f32x4);
475
+
476
+ // angular_distance_f32x4 = atan2(sin, cos)
477
+ angular_distance_f32x4 = nk_atan2_f32x4_neon_(sin_angular_distance_f32x4, cos_angular_distance_f32x4);
478
+
479
+ // sin(azimuth) = cos(U₁) × cos(U₂) × sin(λ) / sin(angular_distance_f32x4)
480
+ float32x4_t safe_sin_angular_f32x4 = vbslq_f32(coincident_mask_u32x4, one_f32x4, sin_angular_distance_f32x4);
481
+ sin_azimuth_f32x4 = vdivq_f32(
482
+ vmulq_f32(vmulq_f32(cos_reduced_first_f32x4, cos_reduced_second_f32x4), sin_lambda_f32x4),
483
+ safe_sin_angular_f32x4);
484
+ cos_squared_azimuth_f32x4 = vsubq_f32(one_f32x4, vmulq_f32(sin_azimuth_f32x4, sin_azimuth_f32x4));
485
+
486
+ // Handle equatorial case: cos²α ≈ 0
487
+ uint32x4_t equatorial_mask_u32x4 = vcltq_f32(cos_squared_azimuth_f32x4, epsilon_f32x4);
488
+ float32x4_t safe_cos_sq_azimuth_f32x4 = vbslq_f32(equatorial_mask_u32x4, one_f32x4, cos_squared_azimuth_f32x4);
489
+
490
+ // cos(2σₘ) = cos(σ) - 2 × sin(U₁) × sin(U₂) / cos²(α)
491
+ float32x4_t sin_product_f32x4 = vmulq_f32(sin_reduced_first_f32x4, sin_reduced_second_f32x4);
492
+ cos_double_angular_midpoint_f32x4 = vsubq_f32(
493
+ cos_angular_distance_f32x4, vdivq_f32(vmulq_f32(two_f32x4, sin_product_f32x4), safe_cos_sq_azimuth_f32x4));
494
+ cos_double_angular_midpoint_f32x4 = vbslq_f32(equatorial_mask_u32x4, vdupq_n_f32(0.0f),
495
+ cos_double_angular_midpoint_f32x4);
496
+
497
+ // C = f/16 * cos²α * (4 + f*(4 - 3*cos²α))
498
+ float32x4_t correction_factor_f32x4 = vmulq_f32(
499
+ vdivq_f32(flattening_f32x4, sixteen_f32x4),
500
+ vmulq_f32(cos_squared_azimuth_f32x4,
501
+ vfmaq_f32(four_f32x4, flattening_f32x4,
502
+ vfmsq_f32(four_f32x4, three_f32x4, cos_squared_azimuth_f32x4))));
503
+
504
+ // λ' = L + (1-C) × f × sin(α) × (σ + C × sin(σ) × (cos(2σₘ) + C × cos(σ) × (-1 + 2 × cos²(2σₘ))))
505
+ float32x4_t cos_2sm_sq_f32x4 = vmulq_f32(cos_double_angular_midpoint_f32x4, cos_double_angular_midpoint_f32x4);
506
+ float32x4_t innermost_f32x4 = vfmaq_f32(vdupq_n_f32(-1.0f), two_f32x4, cos_2sm_sq_f32x4);
507
+ float32x4_t middle_f32x4 = vfmaq_f32(cos_double_angular_midpoint_f32x4,
508
+ vmulq_f32(correction_factor_f32x4, cos_angular_distance_f32x4),
509
+ innermost_f32x4);
510
+ float32x4_t inner_f32x4 = vmulq_f32(vmulq_f32(correction_factor_f32x4, sin_angular_distance_f32x4),
511
+ middle_f32x4);
512
+
513
+ float32x4_t lambda_new_f32x4 = vfmaq_f32(
514
+ longitude_difference_f32x4,
515
+ vmulq_f32(vmulq_f32(vsubq_f32(one_f32x4, correction_factor_f32x4), flattening_f32x4), sin_azimuth_f32x4),
516
+ vaddq_f32(angular_distance_f32x4, inner_f32x4));
517
+
518
+ // Check convergence: |λ - λ'| < threshold
519
+ float32x4_t lambda_diff_f32x4 = vsubq_f32(lambda_new_f32x4, lambda_f32x4);
520
+ float32x4_t lambda_diff_abs_f32x4 = vabsq_f32(lambda_diff_f32x4);
521
+ uint32x4_t newly_converged_u32x4 = vcltq_f32(lambda_diff_abs_f32x4, convergence_threshold_f32x4);
522
+ converged_mask_u32x4 = vorrq_u32(converged_mask_u32x4, newly_converged_u32x4);
523
+
524
+ // Only update lambda_f32x4 for non-converged lanes
525
+ lambda_f32x4 = vbslq_f32(converged_mask_u32x4, lambda_f32x4, lambda_new_f32x4);
526
+ }
527
+
528
+ // Final distance calculation
529
+ float32x4_t a_sq_f32x4 = vmulq_f32(equatorial_radius_f32x4, equatorial_radius_f32x4);
530
+ float32x4_t b_sq_f32x4 = vmulq_f32(polar_radius_f32x4, polar_radius_f32x4);
531
+ float32x4_t u_squared_f32x4 = vdivq_f32(vmulq_f32(cos_squared_azimuth_f32x4, vsubq_f32(a_sq_f32x4, b_sq_f32x4)),
532
+ b_sq_f32x4);
533
+
534
+ // A = 1 + u²/16384 * (4096 + u²*(-768 + u²*(320 - 175*u²)))
535
+ float32x4_t series_a_f32x4 = vfmaq_f32(vdupq_n_f32(320.0f), u_squared_f32x4, vdupq_n_f32(-175.0f));
536
+ series_a_f32x4 = vfmaq_f32(vdupq_n_f32(-768.0f), u_squared_f32x4, series_a_f32x4);
537
+ series_a_f32x4 = vfmaq_f32(vdupq_n_f32(4096.0f), u_squared_f32x4, series_a_f32x4);
538
+ series_a_f32x4 = vfmaq_f32(one_f32x4, vdivq_f32(u_squared_f32x4, vdupq_n_f32(16384.0f)), series_a_f32x4);
539
+
540
+ // B = u²/1024 * (256 + u²*(-128 + u²*(74 - 47*u²)))
541
+ float32x4_t series_b_f32x4 = vfmaq_f32(vdupq_n_f32(74.0f), u_squared_f32x4, vdupq_n_f32(-47.0f));
542
+ series_b_f32x4 = vfmaq_f32(vdupq_n_f32(-128.0f), u_squared_f32x4, series_b_f32x4);
543
+ series_b_f32x4 = vfmaq_f32(vdupq_n_f32(256.0f), u_squared_f32x4, series_b_f32x4);
544
+ series_b_f32x4 = vmulq_f32(vdivq_f32(u_squared_f32x4, vdupq_n_f32(1024.0f)), series_b_f32x4);
545
+
546
+ // Δσ calculation
547
+ float32x4_t cos_2sm_sq_f32x4 = vmulq_f32(cos_double_angular_midpoint_f32x4, cos_double_angular_midpoint_f32x4);
548
+ float32x4_t sin_sq_f32x4 = vmulq_f32(sin_angular_distance_f32x4, sin_angular_distance_f32x4);
549
+ float32x4_t term1_f32x4 = vfmaq_f32(vdupq_n_f32(-1.0f), two_f32x4, cos_2sm_sq_f32x4);
550
+ term1_f32x4 = vmulq_f32(cos_angular_distance_f32x4, term1_f32x4);
551
+ float32x4_t term2_f32x4 = vfmaq_f32(vdupq_n_f32(-3.0f), four_f32x4, sin_sq_f32x4);
552
+ float32x4_t term3_f32x4 = vfmaq_f32(vdupq_n_f32(-3.0f), four_f32x4, cos_2sm_sq_f32x4);
553
+ term2_f32x4 = vmulq_f32(vmulq_f32(vdivq_f32(series_b_f32x4, six_f32x4), cos_double_angular_midpoint_f32x4),
554
+ vmulq_f32(term2_f32x4, term3_f32x4));
555
+ float32x4_t delta_sigma_f32x4 = vmulq_f32(
556
+ series_b_f32x4,
557
+ vmulq_f32(sin_angular_distance_f32x4,
558
+ vaddq_f32(cos_double_angular_midpoint_f32x4,
559
+ vmulq_f32(vdivq_f32(series_b_f32x4, four_f32x4), vsubq_f32(term1_f32x4, term2_f32x4)))));
560
+
561
+ // s = b * A * (σ - Δσ)
562
+ float32x4_t distances_f32x4 = vmulq_f32(vmulq_f32(polar_radius_f32x4, series_a_f32x4),
563
+ vsubq_f32(angular_distance_f32x4, delta_sigma_f32x4));
564
+
565
+ // Set coincident points to zero
566
+ distances_f32x4 = vbslq_f32(coincident_mask_u32x4, vdupq_n_f32(0.0f), distances_f32x4);
567
+
568
+ return distances_f32x4;
569
+ }
570
+
571
+ NK_PUBLIC void nk_vincenty_f32_neon( //
572
+ nk_f32_t const *a_lats, nk_f32_t const *a_lons, //
573
+ nk_f32_t const *b_lats, nk_f32_t const *b_lons, //
574
+ nk_size_t n, nk_f32_t *results) {
575
+
576
+ while (n >= 4) {
577
+ float32x4_t first_latitudes_f32x4 = vld1q_f32(a_lats);
578
+ float32x4_t first_longitudes_f32x4 = vld1q_f32(a_lons);
579
+ float32x4_t second_latitudes_f32x4 = vld1q_f32(b_lats);
580
+ float32x4_t second_longitudes_f32x4 = vld1q_f32(b_lons);
581
+
582
+ float32x4_t distances_f32x4 = nk_vincenty_f32x4_neon_(first_latitudes_f32x4, first_longitudes_f32x4,
583
+ second_latitudes_f32x4, second_longitudes_f32x4);
584
+ vst1q_f32(results, distances_f32x4);
585
+
586
+ a_lats += 4, a_lons += 4, b_lats += 4, b_lons += 4, results += 4, n -= 4;
587
+ }
588
+
589
+ // Handle remaining elements with partial loads (n can be 1-3 here)
590
+ if (n > 0) {
591
+ nk_b128_vec_t a_lat_vec, a_lon_vec, b_lat_vec, b_lon_vec, result_vec;
592
+ nk_partial_load_b32x4_serial_(a_lats, &a_lat_vec, n);
593
+ nk_partial_load_b32x4_serial_(a_lons, &a_lon_vec, n);
594
+ nk_partial_load_b32x4_serial_(b_lats, &b_lat_vec, n);
595
+ nk_partial_load_b32x4_serial_(b_lons, &b_lon_vec, n);
596
+ float32x4_t distances_f32x4 = nk_vincenty_f32x4_neon_(a_lat_vec.f32x4, a_lon_vec.f32x4, b_lat_vec.f32x4,
597
+ b_lon_vec.f32x4);
598
+ result_vec.f32x4 = distances_f32x4;
599
+ nk_partial_store_b32x4_serial_(&result_vec, results, n);
600
+ }
601
+ }
602
+
603
+ #if defined(__clang__)
604
+ #pragma clang attribute pop
605
+ #elif defined(__GNUC__)
606
+ #pragma GCC pop_options
607
+ #endif
608
+
609
+ #if defined(__cplusplus)
610
+ } // extern "C"
611
+ #endif
612
+
613
+ #endif // NK_TARGET_NEON
614
+ #endif // NK_TARGET_ARM64_
615
+ #endif // NK_GEOSPATIAL_NEON_H