usearch 2.21.3 → 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 +235 -52
  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,701 @@
1
+ /**
2
+ * @brief C++ bindings for mesh-distance kernels.
3
+ * @file include/numkong/mesh.hpp
4
+ * @author Ash Vardanian
5
+ * @date February 5, 2026
6
+ */
7
+ #ifndef NK_MESH_HPP
8
+ #define NK_MESH_HPP
9
+
10
+ #include <cstdint>
11
+ #include <type_traits>
12
+ #include <utility>
13
+
14
+ #include "numkong/mesh.h"
15
+
16
+ #include "numkong/types.hpp"
17
+
18
+ namespace ashvardanian::numkong {
19
+
20
+ #pragma region SVD Helpers for Scalar Fallbacks
21
+
22
+ /** @brief 3x3 matrix determinant. */
23
+ template <typename scalar_type_>
24
+ scalar_type_ det3x3_(scalar_type_ const *m) {
25
+ return m[0] * (m[4] * m[8] - m[5] * m[7]) - m[1] * (m[3] * m[8] - m[5] * m[6]) + m[2] * (m[3] * m[7] - m[4] * m[6]);
26
+ }
27
+
28
+ /** @brief Conditional swap helper. */
29
+ template <typename scalar_type_>
30
+ void conditional_swap_(bool c, scalar_type_ *x, scalar_type_ *y) {
31
+ scalar_type_ temp = *x;
32
+ *x = c ? *y : *x;
33
+ *y = c ? temp : *y;
34
+ }
35
+
36
+ /** @brief Conditional negating swap helper. */
37
+ template <typename scalar_type_>
38
+ void conditional_negating_swap_(bool c, scalar_type_ *x, scalar_type_ *y) {
39
+ scalar_type_ neg_x = scalar_type_(0.0) - *x;
40
+ *x = c ? *y : *x;
41
+ *y = c ? neg_x : *y;
42
+ }
43
+
44
+ /** @brief Approximate Givens quaternion for Jacobi eigenanalysis. */
45
+ template <typename scalar_type_>
46
+ void approximate_givens_quaternion_(scalar_type_ a11, scalar_type_ a12, scalar_type_ a22, scalar_type_ *cos_half,
47
+ scalar_type_ *sin_half) {
48
+ constexpr scalar_type_ gamma_k = scalar_type_(5.828427124746190); // gamma = (sqrt8 + 3)^2 / 4
49
+ constexpr scalar_type_ cstar_k = scalar_type_(0.9238795325112867); // cos(pi/8)
50
+ constexpr scalar_type_ sstar_k = scalar_type_(0.3826834323650898); // sin(pi/8)
51
+
52
+ *cos_half = scalar_type_(2.0) * (a11 - a22);
53
+ *sin_half = a12;
54
+ bool use_givens = gamma_k * (*sin_half) * (*sin_half) < (*cos_half) * (*cos_half);
55
+ scalar_type_ w = ((*cos_half) * (*cos_half) + (*sin_half) * (*sin_half)).rsqrt();
56
+ *cos_half = use_givens ? w * (*cos_half) : cstar_k;
57
+ *sin_half = use_givens ? w * (*sin_half) : sstar_k;
58
+ }
59
+
60
+ /** @brief Jacobi conjugation step for eigenanalysis. */
61
+ template <typename scalar_type_>
62
+ void jacobi_conjugation_(int idx_x, int idx_y, int idx_z, scalar_type_ *s11, scalar_type_ *s21, scalar_type_ *s22,
63
+ scalar_type_ *s31, scalar_type_ *s32, scalar_type_ *s33, scalar_type_ *quat) {
64
+
65
+ scalar_type_ cos_half, sin_half;
66
+ approximate_givens_quaternion_(*s11, *s21, *s22, &cos_half, &sin_half);
67
+ scalar_type_ scale = cos_half * cos_half + sin_half * sin_half;
68
+ scalar_type_ cos_theta = (cos_half * cos_half - sin_half * sin_half) / scale;
69
+ scalar_type_ sin_theta = (scalar_type_(2.0) * sin_half * cos_half) / scale;
70
+ scalar_type_ s11_old = *s11, s21_old = *s21, s22_old = *s22;
71
+ scalar_type_ s31_old = *s31, s32_old = *s32, s33_old = *s33;
72
+
73
+ *s11 = cos_theta * (cos_theta * s11_old + sin_theta * s21_old) +
74
+ sin_theta * (cos_theta * s21_old + sin_theta * s22_old);
75
+ *s21 = cos_theta * ((scalar_type_(0.0) - sin_theta) * s11_old + cos_theta * s21_old) +
76
+ sin_theta * ((scalar_type_(0.0) - sin_theta) * s21_old + cos_theta * s22_old);
77
+ *s22 = (scalar_type_(0.0) - sin_theta) * ((scalar_type_(0.0) - sin_theta) * s11_old + cos_theta * s21_old) +
78
+ cos_theta * ((scalar_type_(0.0) - sin_theta) * s21_old + cos_theta * s22_old);
79
+ *s31 = cos_theta * s31_old + sin_theta * s32_old;
80
+ *s32 = (scalar_type_(0.0) - sin_theta) * s31_old + cos_theta * s32_old;
81
+ *s33 = s33_old;
82
+
83
+ // Update quaternion accumulator
84
+ scalar_type_ quat_temp[3];
85
+ quat_temp[0] = quat[0] * sin_half;
86
+ quat_temp[1] = quat[1] * sin_half;
87
+ quat_temp[2] = quat[2] * sin_half;
88
+ sin_half = sin_half * quat[3];
89
+ quat[0] = quat[0] * cos_half;
90
+ quat[1] = quat[1] * cos_half;
91
+ quat[2] = quat[2] * cos_half;
92
+ quat[3] = quat[3] * cos_half;
93
+ quat[idx_z] = quat[idx_z] + sin_half;
94
+ quat[3] = quat[3] - quat_temp[idx_z];
95
+ quat[idx_x] = quat[idx_x] + quat_temp[idx_y];
96
+ quat[idx_y] = quat[idx_y] - quat_temp[idx_x];
97
+ // Cyclic permutation of matrix elements
98
+ s11_old = *s22, s21_old = *s32, s22_old = *s33, s31_old = *s21, s32_old = *s31, s33_old = *s11;
99
+ *s11 = s11_old, *s21 = s21_old, *s22 = s22_old, *s31 = s31_old, *s32 = s32_old, *s33 = s33_old;
100
+ }
101
+
102
+ /** @brief Convert quaternion to 3x3 rotation matrix. */
103
+ template <typename scalar_type_>
104
+ void quaternion_to_mat3x3_(scalar_type_ const *quat, scalar_type_ *matrix) {
105
+ scalar_type_ w = quat[3], x = quat[0], y = quat[1], z = quat[2];
106
+ scalar_type_ q_xx = x * x, q_yy = y * y, q_zz = z * z;
107
+ scalar_type_ q_xz = x * z, q_xy = x * y, q_yz = y * z;
108
+ scalar_type_ q_wx = w * x, q_wy = w * y, q_wz = w * z;
109
+ matrix[0] = scalar_type_(1.0) - scalar_type_(2.0) * (q_yy + q_zz);
110
+ matrix[1] = scalar_type_(2.0) * (q_xy - q_wz);
111
+ matrix[2] = scalar_type_(2.0) * (q_xz + q_wy);
112
+ matrix[3] = scalar_type_(2.0) * (q_xy + q_wz);
113
+ matrix[4] = scalar_type_(1.0) - scalar_type_(2.0) * (q_xx + q_zz);
114
+ matrix[5] = scalar_type_(2.0) * (q_yz - q_wx);
115
+ matrix[6] = scalar_type_(2.0) * (q_xz - q_wy);
116
+ matrix[7] = scalar_type_(2.0) * (q_yz + q_wx);
117
+ matrix[8] = scalar_type_(1.0) - scalar_type_(2.0) * (q_xx + q_yy);
118
+ }
119
+
120
+ /** @brief Jacobi eigenanalysis for symmetric 3x3 matrix. */
121
+ template <typename scalar_type_>
122
+ void jacobi_eigenanalysis_(scalar_type_ *s11, scalar_type_ *s21, scalar_type_ *s22, scalar_type_ *s31,
123
+ scalar_type_ *s32, scalar_type_ *s33, scalar_type_ *quat) {
124
+ quat[0] = scalar_type_(0.0);
125
+ quat[1] = scalar_type_(0.0);
126
+ quat[2] = scalar_type_(0.0);
127
+ quat[3] = scalar_type_(1.0);
128
+ // 16 iterations for better convergence
129
+ for (unsigned int iter = 0; iter < 16; iter++) {
130
+ jacobi_conjugation_(0, 1, 2, s11, s21, s22, s31, s32, s33, quat);
131
+ jacobi_conjugation_(1, 2, 0, s11, s21, s22, s31, s32, s33, quat);
132
+ jacobi_conjugation_(2, 0, 1, s11, s21, s22, s31, s32, s33, quat);
133
+ }
134
+ scalar_type_ norm = (quat[0] * quat[0] + quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3]).rsqrt();
135
+ quat[0] = quat[0] * norm;
136
+ quat[1] = quat[1] * norm;
137
+ quat[2] = quat[2] * norm;
138
+ quat[3] = quat[3] * norm;
139
+ }
140
+
141
+ /** @brief QR Givens quaternion for QR decomposition. */
142
+ template <typename scalar_type_>
143
+ void qr_givens_quaternion_(scalar_type_ a1, scalar_type_ a2, scalar_type_ *cos_half, scalar_type_ *sin_half) {
144
+ constexpr scalar_type_ epsilon_k = scalar_type_(1e-12);
145
+
146
+ scalar_type_ a1_sq_plus_a2_sq = a1 * a1 + a2 * a2;
147
+ scalar_type_ rho = a1_sq_plus_a2_sq * a1_sq_plus_a2_sq.rsqrt();
148
+ rho = a1_sq_plus_a2_sq > epsilon_k ? rho : scalar_type_(0.0);
149
+ *sin_half = rho > epsilon_k ? a2 : scalar_type_(0.0);
150
+ scalar_type_ abs_a1 = a1 < scalar_type_(0.0) ? (scalar_type_(0.0) - a1) : a1;
151
+ scalar_type_ max_rho = rho > epsilon_k ? rho : epsilon_k;
152
+ *cos_half = abs_a1 + max_rho;
153
+ bool should_swap = a1 < scalar_type_(0.0);
154
+ conditional_swap_(should_swap, sin_half, cos_half);
155
+ scalar_type_ w = ((*cos_half) * (*cos_half) + (*sin_half) * (*sin_half)).rsqrt();
156
+ *cos_half = (*cos_half) * w;
157
+ *sin_half = (*sin_half) * w;
158
+ }
159
+
160
+ /** @brief Sort singular values in descending order. */
161
+ template <typename scalar_type_>
162
+ void sort_singular_values_(scalar_type_ *b, scalar_type_ *v) {
163
+ scalar_type_ rho1 = b[0] * b[0] + b[3] * b[3] + b[6] * b[6];
164
+ scalar_type_ rho2 = b[1] * b[1] + b[4] * b[4] + b[7] * b[7];
165
+ scalar_type_ rho3 = b[2] * b[2] + b[5] * b[5] + b[8] * b[8];
166
+ bool should_swap;
167
+ // Sort columns by descending singular value magnitude
168
+ should_swap = rho1 < rho2;
169
+ conditional_negating_swap_(should_swap, &b[0], &b[1]);
170
+ conditional_negating_swap_(should_swap, &v[0], &v[1]);
171
+ conditional_negating_swap_(should_swap, &b[3], &b[4]);
172
+ conditional_negating_swap_(should_swap, &v[3], &v[4]);
173
+ conditional_negating_swap_(should_swap, &b[6], &b[7]);
174
+ conditional_negating_swap_(should_swap, &v[6], &v[7]);
175
+ conditional_swap_(should_swap, &rho1, &rho2);
176
+ should_swap = rho1 < rho3;
177
+ conditional_negating_swap_(should_swap, &b[0], &b[2]);
178
+ conditional_negating_swap_(should_swap, &v[0], &v[2]);
179
+ conditional_negating_swap_(should_swap, &b[3], &b[5]);
180
+ conditional_negating_swap_(should_swap, &v[3], &v[5]);
181
+ conditional_negating_swap_(should_swap, &b[6], &b[8]);
182
+ conditional_negating_swap_(should_swap, &v[6], &v[8]);
183
+ conditional_swap_(should_swap, &rho1, &rho3);
184
+ should_swap = rho2 < rho3;
185
+ conditional_negating_swap_(should_swap, &b[1], &b[2]);
186
+ conditional_negating_swap_(should_swap, &v[1], &v[2]);
187
+ conditional_negating_swap_(should_swap, &b[4], &b[5]);
188
+ conditional_negating_swap_(should_swap, &v[4], &v[5]);
189
+ conditional_negating_swap_(should_swap, &b[7], &b[8]);
190
+ conditional_negating_swap_(should_swap, &v[7], &v[8]);
191
+ }
192
+
193
+ /** @brief QR decomposition of 3x3 matrix. */
194
+ template <typename scalar_type_>
195
+ void qr_decomposition_(scalar_type_ const *input, scalar_type_ *q, scalar_type_ *r) {
196
+ scalar_type_ cos_half_1, sin_half_1;
197
+ scalar_type_ cos_half_2, sin_half_2;
198
+ scalar_type_ cos_half_3, sin_half_3;
199
+ scalar_type_ cos_theta, sin_theta;
200
+ scalar_type_ rotation_temp[9], matrix_temp[9];
201
+ // First Givens rotation (zero input[3])
202
+ qr_givens_quaternion_(input[0], input[3], &cos_half_1, &sin_half_1);
203
+ cos_theta = scalar_type_(1.0) - scalar_type_(2.0) * sin_half_1 * sin_half_1;
204
+ sin_theta = scalar_type_(2.0) * cos_half_1 * sin_half_1;
205
+ rotation_temp[0] = cos_theta * input[0] + sin_theta * input[3];
206
+ rotation_temp[1] = cos_theta * input[1] + sin_theta * input[4];
207
+ rotation_temp[2] = cos_theta * input[2] + sin_theta * input[5];
208
+ rotation_temp[3] = (scalar_type_(0.0) - sin_theta) * input[0] + cos_theta * input[3];
209
+ rotation_temp[4] = (scalar_type_(0.0) - sin_theta) * input[1] + cos_theta * input[4];
210
+ rotation_temp[5] = (scalar_type_(0.0) - sin_theta) * input[2] + cos_theta * input[5];
211
+ rotation_temp[6] = input[6];
212
+ rotation_temp[7] = input[7];
213
+ rotation_temp[8] = input[8];
214
+ // Second Givens rotation (zero rotation_temp[6])
215
+ qr_givens_quaternion_(rotation_temp[0], rotation_temp[6], &cos_half_2, &sin_half_2);
216
+ cos_theta = scalar_type_(1.0) - scalar_type_(2.0) * sin_half_2 * sin_half_2;
217
+ sin_theta = scalar_type_(2.0) * cos_half_2 * sin_half_2;
218
+ matrix_temp[0] = cos_theta * rotation_temp[0] + sin_theta * rotation_temp[6];
219
+ matrix_temp[1] = cos_theta * rotation_temp[1] + sin_theta * rotation_temp[7];
220
+ matrix_temp[2] = cos_theta * rotation_temp[2] + sin_theta * rotation_temp[8];
221
+ matrix_temp[3] = rotation_temp[3];
222
+ matrix_temp[4] = rotation_temp[4];
223
+ matrix_temp[5] = rotation_temp[5];
224
+ matrix_temp[6] = (scalar_type_(0.0) - sin_theta) * rotation_temp[0] + cos_theta * rotation_temp[6];
225
+ matrix_temp[7] = (scalar_type_(0.0) - sin_theta) * rotation_temp[1] + cos_theta * rotation_temp[7];
226
+ matrix_temp[8] = (scalar_type_(0.0) - sin_theta) * rotation_temp[2] + cos_theta * rotation_temp[8];
227
+ // Third Givens rotation (zero matrix_temp[7])
228
+ qr_givens_quaternion_(matrix_temp[4], matrix_temp[7], &cos_half_3, &sin_half_3);
229
+ cos_theta = scalar_type_(1.0) - scalar_type_(2.0) * sin_half_3 * sin_half_3;
230
+ sin_theta = scalar_type_(2.0) * cos_half_3 * sin_half_3;
231
+ r[0] = matrix_temp[0];
232
+ r[1] = matrix_temp[1];
233
+ r[2] = matrix_temp[2];
234
+ r[3] = cos_theta * matrix_temp[3] + sin_theta * matrix_temp[6];
235
+ r[4] = cos_theta * matrix_temp[4] + sin_theta * matrix_temp[7];
236
+ r[5] = cos_theta * matrix_temp[5] + sin_theta * matrix_temp[8];
237
+ r[6] = (scalar_type_(0.0) - sin_theta) * matrix_temp[3] + cos_theta * matrix_temp[6];
238
+ r[7] = (scalar_type_(0.0) - sin_theta) * matrix_temp[4] + cos_theta * matrix_temp[7];
239
+ r[8] = (scalar_type_(0.0) - sin_theta) * matrix_temp[5] + cos_theta * matrix_temp[8];
240
+ // Construct Q = Q1 * Q2 * Q3 (closed-form expressions)
241
+ scalar_type_ sin_half_1_sq = sin_half_1 * sin_half_1;
242
+ scalar_type_ sin_half_2_sq = sin_half_2 * sin_half_2;
243
+ scalar_type_ sin_half_3_sq = sin_half_3 * sin_half_3;
244
+ q[0] = (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_1_sq) *
245
+ (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_2_sq);
246
+ q[1] = scalar_type_(4.0) * cos_half_2 * cos_half_3 * (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_1_sq) *
247
+ sin_half_2 * sin_half_3 +
248
+ scalar_type_(2.0) * cos_half_1 * sin_half_1 * (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_3_sq);
249
+ q[2] = scalar_type_(4.0) * cos_half_1 * cos_half_3 * sin_half_1 * sin_half_3 -
250
+ scalar_type_(2.0) * cos_half_2 * (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_1_sq) * sin_half_2 *
251
+ (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_3_sq);
252
+ q[3] = scalar_type_(2.0) * cos_half_1 * sin_half_1 * (scalar_type_(1.0) - scalar_type_(2.0) * sin_half_2_sq);
253
+ q[4] = scalar_type_(-8.0) * cos_half_1 * cos_half_2 * cos_half_3 * sin_half_1 * sin_half_2 * sin_half_3 +
254
+ (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_1_sq) *
255
+ (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_3_sq);
256
+ q[5] = scalar_type_(-2.0) * cos_half_3 * sin_half_3 +
257
+ scalar_type_(4.0) * sin_half_1 *
258
+ (cos_half_3 * sin_half_1 * sin_half_3 +
259
+ cos_half_1 * cos_half_2 * sin_half_2 * (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_3_sq));
260
+ q[6] = scalar_type_(2.0) * cos_half_2 * sin_half_2;
261
+ q[7] = scalar_type_(2.0) * cos_half_3 * (scalar_type_(1.0) - scalar_type_(2.0) * sin_half_2_sq) * sin_half_3;
262
+ q[8] = (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_2_sq) *
263
+ (scalar_type_(-1.0) + scalar_type_(2.0) * sin_half_3_sq);
264
+ }
265
+
266
+ /** @brief 3x3 SVD: A = U * S * Vt using McAdams algorithm. */
267
+ template <typename scalar_type_>
268
+ void svd3x3_(scalar_type_ const *a, scalar_type_ *svd_u, scalar_type_ *svd_s, scalar_type_ *svd_v) {
269
+ // Compute At * A (symmetric)
270
+ scalar_type_ ata[9];
271
+ ata[0] = a[0] * a[0] + a[3] * a[3] + a[6] * a[6];
272
+ ata[1] = a[0] * a[1] + a[3] * a[4] + a[6] * a[7];
273
+ ata[2] = a[0] * a[2] + a[3] * a[5] + a[6] * a[8];
274
+ ata[3] = ata[1];
275
+ ata[4] = a[1] * a[1] + a[4] * a[4] + a[7] * a[7];
276
+ ata[5] = a[1] * a[2] + a[4] * a[5] + a[7] * a[8];
277
+ ata[6] = ata[2];
278
+ ata[7] = ata[5];
279
+ ata[8] = a[2] * a[2] + a[5] * a[5] + a[8] * a[8];
280
+ // Jacobi eigenanalysis of At * A
281
+ scalar_type_ quat[4];
282
+ jacobi_eigenanalysis_(&ata[0], &ata[1], &ata[4], &ata[2], &ata[5], &ata[8], quat);
283
+ quaternion_to_mat3x3_(quat, svd_v);
284
+ // B = A * V
285
+ scalar_type_ product[9];
286
+ product[0] = a[0] * svd_v[0] + a[1] * svd_v[3] + a[2] * svd_v[6];
287
+ product[1] = a[0] * svd_v[1] + a[1] * svd_v[4] + a[2] * svd_v[7];
288
+ product[2] = a[0] * svd_v[2] + a[1] * svd_v[5] + a[2] * svd_v[8];
289
+ product[3] = a[3] * svd_v[0] + a[4] * svd_v[3] + a[5] * svd_v[6];
290
+ product[4] = a[3] * svd_v[1] + a[4] * svd_v[4] + a[5] * svd_v[7];
291
+ product[5] = a[3] * svd_v[2] + a[4] * svd_v[5] + a[5] * svd_v[8];
292
+ product[6] = a[6] * svd_v[0] + a[7] * svd_v[3] + a[8] * svd_v[6];
293
+ product[7] = a[6] * svd_v[1] + a[7] * svd_v[4] + a[8] * svd_v[7];
294
+ product[8] = a[6] * svd_v[2] + a[7] * svd_v[5] + a[8] * svd_v[8];
295
+ // Sort singular values and update V
296
+ sort_singular_values_(product, svd_v);
297
+ // Compute singular values from column norms of sorted B
298
+ scalar_type_ s1_sq = product[0] * product[0] + product[3] * product[3] + product[6] * product[6];
299
+ scalar_type_ s2_sq = product[1] * product[1] + product[4] * product[4] + product[7] * product[7];
300
+ scalar_type_ s3_sq = product[2] * product[2] + product[5] * product[5] + product[8] * product[8];
301
+ // QR decomposition: B = U * R
302
+ scalar_type_ qr_r[9];
303
+ qr_decomposition_(product, svd_u, qr_r);
304
+ // Store singular values in diagonal of svd_s
305
+ svd_s[0] = s1_sq.sqrt();
306
+ svd_s[1] = scalar_type_(0.0);
307
+ svd_s[2] = scalar_type_(0.0);
308
+ svd_s[3] = scalar_type_(0.0);
309
+ svd_s[4] = s2_sq.sqrt();
310
+ svd_s[5] = scalar_type_(0.0);
311
+ svd_s[6] = scalar_type_(0.0);
312
+ svd_s[7] = scalar_type_(0.0);
313
+ svd_s[8] = s3_sq.sqrt();
314
+ }
315
+
316
+ #pragma endregion SVD Helpers for Scalar Fallbacks
317
+
318
+ #pragma region Mesh Alignment Kernels
319
+
320
+ /**
321
+ * @brief Root Mean Square Deviation between two 3D point clouds (no alignment)
322
+ * @param[in] a,b Point clouds [d x 3] interleaved (x0,y0,z0, x1,y1,z1, ...)
323
+ * @param[in] d Number of 3D points
324
+ * @param[out] a_centroid,b_centroid Centroids (3 values each), can be nullptr
325
+ * @param[out] rotation 3x3 rotation matrix (9 values), always identity, can be nullptr
326
+ * @param[out] scale Scale factor, always 1.0, can be nullptr
327
+ * @param[out] metric Output RMSD value
328
+ *
329
+ * @tparam in_type_ Input point type (f32_t, f64_t, f16_t, bf16_t)
330
+ * @tparam transform_type_ Type of centroids, rotation, and scale outputs
331
+ * @tparam metric_type_ Type of the scalar fit metric output
332
+ * @tparam allow_simd_ Enable SIMD kernel dispatch when `prefer_simd_k`
333
+ */
334
+ template <typename in_type_, typename transform_type_ = typename in_type_::mesh_transform_t,
335
+ typename metric_type_ = typename in_type_::mesh_metric_t,
336
+ allow_simd_t allow_simd_ = prefer_simd_k>
337
+ void rmsd( //
338
+ in_type_ const *a, in_type_ const *b, std::size_t n, //
339
+ transform_type_ *a_centroid, transform_type_ *b_centroid, transform_type_ *rotation, transform_type_ *scale,
340
+ metric_type_ *metric) noexcept {
341
+ constexpr bool simd = allow_simd_ == prefer_simd_k &&
342
+ std::is_same_v<transform_type_, typename in_type_::mesh_transform_t> &&
343
+ std::is_same_v<metric_type_, typename in_type_::mesh_metric_t>;
344
+
345
+ if constexpr (std::is_same_v<in_type_, f64_t> && simd)
346
+ nk_rmsd_f64(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
347
+ &metric->raw_);
348
+ else if constexpr (std::is_same_v<in_type_, f32_t> && simd)
349
+ nk_rmsd_f32(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
350
+ &metric->raw_);
351
+ else if constexpr (std::is_same_v<in_type_, f16_t> && simd)
352
+ nk_rmsd_f16(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
353
+ &metric->raw_);
354
+ else if constexpr (std::is_same_v<in_type_, bf16_t> && simd)
355
+ nk_rmsd_bf16(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_,
356
+ scale ? &scale->raw_ : nullptr, &metric->raw_);
357
+ // Scalar fallback: raw √(Σ‖aᵢ − bᵢ‖² / n), no centering
358
+ else {
359
+ if (a_centroid)
360
+ a_centroid[0] = transform_type_(0.0), a_centroid[1] = transform_type_(0.0),
361
+ a_centroid[2] = transform_type_(0.0);
362
+ if (b_centroid)
363
+ b_centroid[0] = transform_type_(0.0), b_centroid[1] = transform_type_(0.0),
364
+ b_centroid[2] = transform_type_(0.0);
365
+ if (rotation) {
366
+ rotation[0] = transform_type_(1.0), rotation[1] = transform_type_(0.0), rotation[2] = transform_type_(0.0);
367
+ rotation[3] = transform_type_(0.0), rotation[4] = transform_type_(1.0), rotation[5] = transform_type_(0.0);
368
+ rotation[6] = transform_type_(0.0), rotation[7] = transform_type_(0.0), rotation[8] = transform_type_(1.0);
369
+ }
370
+ if (scale) *scale = transform_type_(1.0);
371
+
372
+ metric_type_ sum_squared {};
373
+ for (std::size_t i = 0; i < n; i++) {
374
+ metric_type_ dx = metric_type_(a[i * 3 + 0]) - metric_type_(b[i * 3 + 0]);
375
+ metric_type_ dy = metric_type_(a[i * 3 + 1]) - metric_type_(b[i * 3 + 1]);
376
+ metric_type_ dz = metric_type_(a[i * 3 + 2]) - metric_type_(b[i * 3 + 2]);
377
+ sum_squared = sum_squared + dx * dx + dy * dy + dz * dz;
378
+ }
379
+
380
+ *metric = (sum_squared / metric_type_(static_cast<double>(n))).sqrt();
381
+ }
382
+ }
383
+
384
+ /**
385
+ * @brief Kabsch algorithm: min ‖P − R × Q‖² over rotation R ∈ SO(3)
386
+ * @param[in] a,b Point clouds [n x 3] interleaved (source and target)
387
+ * @param[in] n Number of 3D points
388
+ * @param[out] a_centroid,b_centroid Centroids (3 values each), can be nullptr
389
+ * @param[out] rotation 3x3 rotation matrix (9 values, row-major), can be nullptr
390
+ * @param[out] scale Scale factor, always 1.0 for Kabsch, can be nullptr
391
+ * @param[out] metric Output RMSD after optimal rotation
392
+ *
393
+ * @tparam in_type_ Input point type (f32_t, f64_t, f16_t, bf16_t)
394
+ * @tparam transform_type_ Type of centroids, rotation, and scale outputs
395
+ * @tparam metric_type_ Type of the scalar fit metric output
396
+ * @tparam allow_simd_ Enable SIMD kernel dispatch when `prefer_simd_k`
397
+ */
398
+ template <typename in_type_, typename transform_type_ = typename in_type_::mesh_transform_t,
399
+ typename metric_type_ = typename in_type_::mesh_metric_t,
400
+ allow_simd_t allow_simd_ = prefer_simd_k>
401
+ void kabsch( //
402
+ in_type_ const *a, in_type_ const *b, std::size_t n, //
403
+ transform_type_ *a_centroid, transform_type_ *b_centroid, transform_type_ *rotation, transform_type_ *scale,
404
+ metric_type_ *metric) noexcept {
405
+ constexpr bool simd = allow_simd_ == prefer_simd_k &&
406
+ std::is_same_v<transform_type_, typename in_type_::mesh_transform_t> &&
407
+ std::is_same_v<metric_type_, typename in_type_::mesh_metric_t>;
408
+
409
+ if constexpr (std::is_same_v<in_type_, f64_t> && simd)
410
+ nk_kabsch_f64(&a->raw_, &b->raw_, n, a_centroid ? &a_centroid->raw_ : nullptr, &b_centroid->raw_,
411
+ &rotation->raw_, &scale->raw_, &metric->raw_);
412
+ else if constexpr (std::is_same_v<in_type_, f32_t> && simd)
413
+ nk_kabsch_f32(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
414
+ &metric->raw_);
415
+ else if constexpr (std::is_same_v<in_type_, f16_t> && simd)
416
+ nk_kabsch_f16(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
417
+ &metric->raw_);
418
+ else if constexpr (std::is_same_v<in_type_, bf16_t> && simd)
419
+ nk_kabsch_bf16(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
420
+ &metric->raw_);
421
+ // Scalar fallback
422
+ else {
423
+ // Step 1: Compute centroids
424
+ metric_type_ sum_a_x {}, sum_a_y {}, sum_a_z {};
425
+ metric_type_ sum_b_x {}, sum_b_y {}, sum_b_z {};
426
+ metric_type_ val_a_x, val_a_y, val_a_z, val_b_x, val_b_y, val_b_z;
427
+
428
+ for (std::size_t i = 0; i < n; i++) {
429
+ val_a_x = metric_type_(a[i * 3 + 0]), val_a_y = metric_type_(a[i * 3 + 1]),
430
+ val_a_z = metric_type_(a[i * 3 + 2]);
431
+ val_b_x = metric_type_(b[i * 3 + 0]), val_b_y = metric_type_(b[i * 3 + 1]),
432
+ val_b_z = metric_type_(b[i * 3 + 2]);
433
+ sum_a_x = sum_a_x + val_a_x, sum_a_y = sum_a_y + val_a_y, sum_a_z = sum_a_z + val_a_z;
434
+ sum_b_x = sum_b_x + val_b_x, sum_b_y = sum_b_y + val_b_y, sum_b_z = sum_b_z + val_b_z;
435
+ }
436
+
437
+ metric_type_ inv_n = metric_type_(1.0) / metric_type_(static_cast<double>(n));
438
+ metric_type_ centroid_a_x = sum_a_x * inv_n;
439
+ metric_type_ centroid_a_y = sum_a_y * inv_n;
440
+ metric_type_ centroid_a_z = sum_a_z * inv_n;
441
+ metric_type_ centroid_b_x = sum_b_x * inv_n;
442
+ metric_type_ centroid_b_y = sum_b_y * inv_n;
443
+ metric_type_ centroid_b_z = sum_b_z * inv_n;
444
+
445
+ if (a_centroid)
446
+ a_centroid[0] = transform_type_(centroid_a_x), a_centroid[1] = transform_type_(centroid_a_y),
447
+ a_centroid[2] = transform_type_(centroid_a_z);
448
+
449
+ if (b_centroid)
450
+ b_centroid[0] = transform_type_(centroid_b_x), b_centroid[1] = transform_type_(centroid_b_y),
451
+ b_centroid[2] = transform_type_(centroid_b_z);
452
+
453
+ // Step 2: Build 3x3 covariance matrix H = (A - A_bar)^T x (B - B_bar)
454
+ metric_type_ cross_covariance[9] = {};
455
+ for (std::size_t i = 0; i < n; i++) {
456
+ val_a_x = metric_type_(a[i * 3 + 0]) - centroid_a_x, val_a_y = metric_type_(a[i * 3 + 1]) - centroid_a_y,
457
+ val_a_z = metric_type_(a[i * 3 + 2]) - centroid_a_z;
458
+ val_b_x = metric_type_(b[i * 3 + 0]) - centroid_b_x, val_b_y = metric_type_(b[i * 3 + 1]) - centroid_b_y,
459
+ val_b_z = metric_type_(b[i * 3 + 2]) - centroid_b_z;
460
+ cross_covariance[0] = cross_covariance[0] + val_a_x * val_b_x;
461
+ cross_covariance[1] = cross_covariance[1] + val_a_x * val_b_y;
462
+ cross_covariance[2] = cross_covariance[2] + val_a_x * val_b_z;
463
+ cross_covariance[3] = cross_covariance[3] + val_a_y * val_b_x;
464
+ cross_covariance[4] = cross_covariance[4] + val_a_y * val_b_y;
465
+ cross_covariance[5] = cross_covariance[5] + val_a_y * val_b_z;
466
+ cross_covariance[6] = cross_covariance[6] + val_a_z * val_b_x;
467
+ cross_covariance[7] = cross_covariance[7] + val_a_z * val_b_y;
468
+ cross_covariance[8] = cross_covariance[8] + val_a_z * val_b_z;
469
+ }
470
+
471
+ // Step 3: SVD of H = U * S * Vt
472
+ metric_type_ svd_u[9], svd_s[9], svd_v[9];
473
+ svd3x3_(cross_covariance, svd_u, svd_s, svd_v);
474
+
475
+ // Step 4: R = V * Ut
476
+ metric_type_ rotation_matrix[9];
477
+ rotation_matrix[0] = svd_v[0] * svd_u[0] + svd_v[1] * svd_u[1] + svd_v[2] * svd_u[2];
478
+ rotation_matrix[1] = svd_v[0] * svd_u[3] + svd_v[1] * svd_u[4] + svd_v[2] * svd_u[5];
479
+ rotation_matrix[2] = svd_v[0] * svd_u[6] + svd_v[1] * svd_u[7] + svd_v[2] * svd_u[8];
480
+ rotation_matrix[3] = svd_v[3] * svd_u[0] + svd_v[4] * svd_u[1] + svd_v[5] * svd_u[2];
481
+ rotation_matrix[4] = svd_v[3] * svd_u[3] + svd_v[4] * svd_u[4] + svd_v[5] * svd_u[5];
482
+ rotation_matrix[5] = svd_v[3] * svd_u[6] + svd_v[4] * svd_u[7] + svd_v[5] * svd_u[8];
483
+ rotation_matrix[6] = svd_v[6] * svd_u[0] + svd_v[7] * svd_u[1] + svd_v[8] * svd_u[2];
484
+ rotation_matrix[7] = svd_v[6] * svd_u[3] + svd_v[7] * svd_u[4] + svd_v[8] * svd_u[5];
485
+ rotation_matrix[8] = svd_v[6] * svd_u[6] + svd_v[7] * svd_u[7] + svd_v[8] * svd_u[8];
486
+
487
+ // Handle reflection: if det(R) < 0, negate third column of V and recompute R
488
+ metric_type_ rotation_det = det3x3_(rotation_matrix);
489
+ if (rotation_det < metric_type_(0.0)) {
490
+ svd_v[2] = metric_type_(0.0) - svd_v[2];
491
+ svd_v[5] = metric_type_(0.0) - svd_v[5];
492
+ svd_v[8] = metric_type_(0.0) - svd_v[8];
493
+ rotation_matrix[0] = svd_v[0] * svd_u[0] + svd_v[1] * svd_u[1] + svd_v[2] * svd_u[2];
494
+ rotation_matrix[1] = svd_v[0] * svd_u[3] + svd_v[1] * svd_u[4] + svd_v[2] * svd_u[5];
495
+ rotation_matrix[2] = svd_v[0] * svd_u[6] + svd_v[1] * svd_u[7] + svd_v[2] * svd_u[8];
496
+ rotation_matrix[3] = svd_v[3] * svd_u[0] + svd_v[4] * svd_u[1] + svd_v[5] * svd_u[2];
497
+ rotation_matrix[4] = svd_v[3] * svd_u[3] + svd_v[4] * svd_u[4] + svd_v[5] * svd_u[5];
498
+ rotation_matrix[5] = svd_v[3] * svd_u[6] + svd_v[4] * svd_u[7] + svd_v[5] * svd_u[8];
499
+ rotation_matrix[6] = svd_v[6] * svd_u[0] + svd_v[7] * svd_u[1] + svd_v[8] * svd_u[2];
500
+ rotation_matrix[7] = svd_v[6] * svd_u[3] + svd_v[7] * svd_u[4] + svd_v[8] * svd_u[5];
501
+ rotation_matrix[8] = svd_v[6] * svd_u[6] + svd_v[7] * svd_u[7] + svd_v[8] * svd_u[8];
502
+ }
503
+
504
+ // Output rotation matrix and scale=1.0
505
+ if (rotation) {
506
+ for (unsigned int j = 0; j < 9; j++) rotation[j] = transform_type_(rotation_matrix[j]);
507
+ }
508
+ if (scale) *scale = transform_type_(1.0);
509
+
510
+ // Step 5: Compute RMSD after rotation
511
+ metric_type_ sum_squared {};
512
+ for (std::size_t i = 0; i < n; i++) {
513
+ metric_type_ point_a[3], point_b[3], rotated_point_a[3];
514
+ point_a[0] = metric_type_(a[i * 3 + 0]) - centroid_a_x,
515
+ point_a[1] = metric_type_(a[i * 3 + 1]) - centroid_a_y,
516
+ point_a[2] = metric_type_(a[i * 3 + 2]) - centroid_a_z;
517
+ point_b[0] = metric_type_(b[i * 3 + 0]) - centroid_b_x,
518
+ point_b[1] = metric_type_(b[i * 3 + 1]) - centroid_b_y,
519
+ point_b[2] = metric_type_(b[i * 3 + 2]) - centroid_b_z;
520
+ rotated_point_a[0] = rotation_matrix[0] * point_a[0] + rotation_matrix[1] * point_a[1] +
521
+ rotation_matrix[2] * point_a[2];
522
+ rotated_point_a[1] = rotation_matrix[3] * point_a[0] + rotation_matrix[4] * point_a[1] +
523
+ rotation_matrix[5] * point_a[2];
524
+ rotated_point_a[2] = rotation_matrix[6] * point_a[0] + rotation_matrix[7] * point_a[1] +
525
+ rotation_matrix[8] * point_a[2];
526
+ metric_type_ dx = rotated_point_a[0] - point_b[0];
527
+ metric_type_ dy = rotated_point_a[1] - point_b[1];
528
+ metric_type_ dz = rotated_point_a[2] - point_b[2];
529
+ sum_squared = sum_squared + dx * dx + dy * dy + dz * dz;
530
+ }
531
+
532
+ *metric = (sum_squared * inv_n).sqrt();
533
+ }
534
+ }
535
+
536
+ /**
537
+ * @brief Umeyama algorithm: min ‖P − s × R × Q‖² over R ∈ SO(3), s ∈ ℝ⁺
538
+ * @param[in] a,b Point clouds [n x 3] interleaved (source and target)
539
+ * @param[in] n Number of 3D points
540
+ * @param[out] a_centroid,b_centroid Centroids (3 values each), can be nullptr
541
+ * @param[out] rotation 3x3 rotation matrix (9 values, row-major), can be nullptr
542
+ * @param[out] scale Uniform scale factor, can be nullptr
543
+ * @param[out] metric Output RMSD after optimal transformation
544
+ *
545
+ * @tparam in_type_ Input point type (f32_t, f64_t, f16_t, bf16_t)
546
+ * @tparam transform_type_ Type of centroids, rotation, and scale outputs
547
+ * @tparam metric_type_ Type of the scalar fit metric output
548
+ * @tparam allow_simd_ Enable SIMD kernel dispatch when `prefer_simd_k`
549
+ */
550
+ template <typename in_type_, typename transform_type_ = typename in_type_::mesh_transform_t,
551
+ typename metric_type_ = typename in_type_::mesh_metric_t, allow_simd_t allow_simd_ = prefer_simd_k>
552
+ void umeyama(in_type_ const *a, in_type_ const *b, std::size_t n, transform_type_ *a_centroid,
553
+ transform_type_ *b_centroid, transform_type_ *rotation, transform_type_ *scale,
554
+ metric_type_ *metric) noexcept {
555
+ constexpr bool simd = allow_simd_ == prefer_simd_k &&
556
+ std::is_same_v<transform_type_, typename in_type_::mesh_transform_t> &&
557
+ std::is_same_v<metric_type_, typename in_type_::mesh_metric_t>;
558
+
559
+ if constexpr (std::is_same_v<in_type_, f64_t> && simd)
560
+ nk_umeyama_f64(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
561
+ &metric->raw_);
562
+ else if constexpr (std::is_same_v<in_type_, f32_t> && simd)
563
+ nk_umeyama_f32(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
564
+ &metric->raw_);
565
+ else if constexpr (std::is_same_v<in_type_, f16_t> && simd)
566
+ nk_umeyama_f16(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
567
+ &metric->raw_);
568
+ else if constexpr (std::is_same_v<in_type_, bf16_t> && simd)
569
+ nk_umeyama_bf16(&a->raw_, &b->raw_, n, &a_centroid->raw_, &b_centroid->raw_, &rotation->raw_, &scale->raw_,
570
+ &metric->raw_);
571
+ // Scalar fallback
572
+ else {
573
+ // Step 1: Compute centroids
574
+ metric_type_ sum_a_x {}, sum_a_y {}, sum_a_z {};
575
+ metric_type_ sum_b_x {}, sum_b_y {}, sum_b_z {};
576
+ metric_type_ val_a_x, val_a_y, val_a_z, val_b_x, val_b_y, val_b_z;
577
+
578
+ for (std::size_t i = 0; i < n; i++) {
579
+ val_a_x = metric_type_(a[i * 3 + 0]), val_a_y = metric_type_(a[i * 3 + 1]),
580
+ val_a_z = metric_type_(a[i * 3 + 2]);
581
+ val_b_x = metric_type_(b[i * 3 + 0]), val_b_y = metric_type_(b[i * 3 + 1]),
582
+ val_b_z = metric_type_(b[i * 3 + 2]);
583
+ sum_a_x = sum_a_x + val_a_x, sum_a_y = sum_a_y + val_a_y, sum_a_z = sum_a_z + val_a_z;
584
+ sum_b_x = sum_b_x + val_b_x, sum_b_y = sum_b_y + val_b_y, sum_b_z = sum_b_z + val_b_z;
585
+ }
586
+
587
+ metric_type_ inv_n = metric_type_(1.0) / metric_type_(static_cast<double>(n));
588
+ metric_type_ centroid_a_x = sum_a_x * inv_n;
589
+ metric_type_ centroid_a_y = sum_a_y * inv_n;
590
+ metric_type_ centroid_a_z = sum_a_z * inv_n;
591
+ metric_type_ centroid_b_x = sum_b_x * inv_n;
592
+ metric_type_ centroid_b_y = sum_b_y * inv_n;
593
+ metric_type_ centroid_b_z = sum_b_z * inv_n;
594
+
595
+ if (a_centroid)
596
+ a_centroid[0] = transform_type_(centroid_a_x), a_centroid[1] = transform_type_(centroid_a_y),
597
+ a_centroid[2] = transform_type_(centroid_a_z);
598
+
599
+ if (b_centroid)
600
+ b_centroid[0] = transform_type_(centroid_b_x), b_centroid[1] = transform_type_(centroid_b_y),
601
+ b_centroid[2] = transform_type_(centroid_b_z);
602
+
603
+ // Step 2: Build covariance matrix H and compute variance of A
604
+ metric_type_ cross_covariance[9] = {};
605
+ metric_type_ variance_a {};
606
+ for (std::size_t i = 0; i < n; i++) {
607
+ val_a_x = metric_type_(a[i * 3 + 0]) - centroid_a_x;
608
+ val_a_y = metric_type_(a[i * 3 + 1]) - centroid_a_y;
609
+ val_a_z = metric_type_(a[i * 3 + 2]) - centroid_a_z;
610
+ val_b_x = metric_type_(b[i * 3 + 0]) - centroid_b_x;
611
+ val_b_y = metric_type_(b[i * 3 + 1]) - centroid_b_y;
612
+ val_b_z = metric_type_(b[i * 3 + 2]) - centroid_b_z;
613
+ variance_a = variance_a + val_a_x * val_a_x + val_a_y * val_a_y + val_a_z * val_a_z;
614
+ cross_covariance[0] = cross_covariance[0] + val_a_x * val_b_x;
615
+ cross_covariance[1] = cross_covariance[1] + val_a_x * val_b_y;
616
+ cross_covariance[2] = cross_covariance[2] + val_a_x * val_b_z;
617
+ cross_covariance[3] = cross_covariance[3] + val_a_y * val_b_x;
618
+ cross_covariance[4] = cross_covariance[4] + val_a_y * val_b_y;
619
+ cross_covariance[5] = cross_covariance[5] + val_a_y * val_b_z;
620
+ cross_covariance[6] = cross_covariance[6] + val_a_z * val_b_x;
621
+ cross_covariance[7] = cross_covariance[7] + val_a_z * val_b_y;
622
+ cross_covariance[8] = cross_covariance[8] + val_a_z * val_b_z;
623
+ }
624
+ variance_a = variance_a * inv_n;
625
+
626
+ // Step 3: SVD of H = U * S * Vt
627
+ metric_type_ svd_u[9], svd_s[9], svd_v[9];
628
+ svd3x3_(cross_covariance, svd_u, svd_s, svd_v);
629
+
630
+ // Step 4: R = V * Ut
631
+ metric_type_ rotation_matrix[9];
632
+ rotation_matrix[0] = svd_v[0] * svd_u[0] + svd_v[1] * svd_u[1] + svd_v[2] * svd_u[2];
633
+ rotation_matrix[1] = svd_v[0] * svd_u[3] + svd_v[1] * svd_u[4] + svd_v[2] * svd_u[5];
634
+ rotation_matrix[2] = svd_v[0] * svd_u[6] + svd_v[1] * svd_u[7] + svd_v[2] * svd_u[8];
635
+ rotation_matrix[3] = svd_v[3] * svd_u[0] + svd_v[4] * svd_u[1] + svd_v[5] * svd_u[2];
636
+ rotation_matrix[4] = svd_v[3] * svd_u[3] + svd_v[4] * svd_u[4] + svd_v[5] * svd_u[5];
637
+ rotation_matrix[5] = svd_v[3] * svd_u[6] + svd_v[4] * svd_u[7] + svd_v[5] * svd_u[8];
638
+ rotation_matrix[6] = svd_v[6] * svd_u[0] + svd_v[7] * svd_u[1] + svd_v[8] * svd_u[2];
639
+ rotation_matrix[7] = svd_v[6] * svd_u[3] + svd_v[7] * svd_u[4] + svd_v[8] * svd_u[5];
640
+ rotation_matrix[8] = svd_v[6] * svd_u[6] + svd_v[7] * svd_u[7] + svd_v[8] * svd_u[8];
641
+
642
+ // Handle reflection and compute scale: c = trace(D*S) / variance_a
643
+ // D = diag(1, 1, det(R)), svd_s contains singular values on diagonal
644
+ metric_type_ rotation_det = det3x3_(rotation_matrix);
645
+ metric_type_ sign_det = rotation_det < metric_type_(0.0) ? metric_type_(-1.0) : metric_type_(1.0);
646
+ metric_type_ trace_scaled_s = svd_s[0] + svd_s[4] + sign_det * svd_s[8];
647
+ metric_type_ scale_factor = trace_scaled_s / (metric_type_(static_cast<double>(n)) * variance_a);
648
+
649
+ if (scale) *scale = transform_type_(scale_factor);
650
+
651
+ if (rotation_det < metric_type_(0.0)) {
652
+ svd_v[2] = metric_type_(0.0) - svd_v[2];
653
+ svd_v[5] = metric_type_(0.0) - svd_v[5];
654
+ svd_v[8] = metric_type_(0.0) - svd_v[8];
655
+ rotation_matrix[0] = svd_v[0] * svd_u[0] + svd_v[1] * svd_u[1] + svd_v[2] * svd_u[2];
656
+ rotation_matrix[1] = svd_v[0] * svd_u[3] + svd_v[1] * svd_u[4] + svd_v[2] * svd_u[5];
657
+ rotation_matrix[2] = svd_v[0] * svd_u[6] + svd_v[1] * svd_u[7] + svd_v[2] * svd_u[8];
658
+ rotation_matrix[3] = svd_v[3] * svd_u[0] + svd_v[4] * svd_u[1] + svd_v[5] * svd_u[2];
659
+ rotation_matrix[4] = svd_v[3] * svd_u[3] + svd_v[4] * svd_u[4] + svd_v[5] * svd_u[5];
660
+ rotation_matrix[5] = svd_v[3] * svd_u[6] + svd_v[4] * svd_u[7] + svd_v[5] * svd_u[8];
661
+ rotation_matrix[6] = svd_v[6] * svd_u[0] + svd_v[7] * svd_u[1] + svd_v[8] * svd_u[2];
662
+ rotation_matrix[7] = svd_v[6] * svd_u[3] + svd_v[7] * svd_u[4] + svd_v[8] * svd_u[5];
663
+ rotation_matrix[8] = svd_v[6] * svd_u[6] + svd_v[7] * svd_u[7] + svd_v[8] * svd_u[8];
664
+ }
665
+
666
+ // Output rotation matrix
667
+ if (rotation) {
668
+ for (unsigned int j = 0; j < 9; j++) rotation[j] = transform_type_(rotation_matrix[j]);
669
+ }
670
+
671
+ // Step 5: Compute RMSD after similarity transform: ||c * R * a - b||
672
+ metric_type_ sum_squared {};
673
+ for (std::size_t i = 0; i < n; i++) {
674
+ metric_type_ point_a[3], point_b[3], rotated_point_a[3];
675
+ point_a[0] = metric_type_(a[i * 3 + 0]) - centroid_a_x,
676
+ point_a[1] = metric_type_(a[i * 3 + 1]) - centroid_a_y,
677
+ point_a[2] = metric_type_(a[i * 3 + 2]) - centroid_a_z;
678
+ point_b[0] = metric_type_(b[i * 3 + 0]) - centroid_b_x,
679
+ point_b[1] = metric_type_(b[i * 3 + 1]) - centroid_b_y,
680
+ point_b[2] = metric_type_(b[i * 3 + 2]) - centroid_b_z;
681
+ rotated_point_a[0] = scale_factor * (rotation_matrix[0] * point_a[0] + rotation_matrix[1] * point_a[1] +
682
+ rotation_matrix[2] * point_a[2]);
683
+ rotated_point_a[1] = scale_factor * (rotation_matrix[3] * point_a[0] + rotation_matrix[4] * point_a[1] +
684
+ rotation_matrix[5] * point_a[2]);
685
+ rotated_point_a[2] = scale_factor * (rotation_matrix[6] * point_a[0] + rotation_matrix[7] * point_a[1] +
686
+ rotation_matrix[8] * point_a[2]);
687
+ metric_type_ dx = rotated_point_a[0] - point_b[0];
688
+ metric_type_ dy = rotated_point_a[1] - point_b[1];
689
+ metric_type_ dz = rotated_point_a[2] - point_b[2];
690
+ sum_squared = sum_squared + dx * dx + dy * dy + dz * dz;
691
+ }
692
+
693
+ *metric = (sum_squared * inv_n).sqrt();
694
+ }
695
+ }
696
+
697
+ #pragma endregion Mesh Alignment Kernels
698
+
699
+ } // namespace ashvardanian::numkong
700
+
701
+ #endif // NK_MESH_HPP