usearch 2.21.4 → 2.25.1

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 (604) hide show
  1. package/README.md +65 -69
  2. package/binding.gyp +15 -37
  3. package/c/CMakeLists.txt +4 -5
  4. package/c/README.md +8 -8
  5. package/c/lib.cpp +32 -14
  6. package/c/test.c +124 -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/README.md +1 -1
  16. package/javascript/dist/cjs/usearch.d.ts +8 -3
  17. package/javascript/dist/cjs/usearch.js +12 -4
  18. package/javascript/dist/esm/usearch.d.ts +8 -3
  19. package/javascript/dist/esm/usearch.js +12 -4
  20. package/javascript/lib.cpp +17 -0
  21. package/javascript/usearch.test.js +117 -94
  22. package/javascript/usearch.ts +14 -6
  23. package/numkong/.clang-tidy +30 -0
  24. package/{simsimd → numkong}/.cmake-format.py +18 -0
  25. package/numkong/.editorconfig +12 -0
  26. package/numkong/.git-blame-ignore-revs +123 -0
  27. package/numkong/.mailmap +15 -0
  28. package/numkong/.spi.yml +11 -0
  29. package/numkong/.swift-format +14 -0
  30. package/numkong/CITATION.cff +81 -0
  31. package/numkong/CONTRIBUTING.md +500 -0
  32. package/numkong/Cargo.toml +54 -0
  33. package/numkong/MANIFEST.in +8 -0
  34. package/numkong/Package.swift +84 -0
  35. package/numkong/README.md +569 -0
  36. package/numkong/bench/README.md +217 -0
  37. package/numkong/bench/bench-browser.html +323 -0
  38. package/numkong/bench/bench.cpp +344 -0
  39. package/numkong/bench/bench.hpp +816 -0
  40. package/numkong/bench/bench.js +148 -0
  41. package/numkong/bench/bench.mjs +616 -0
  42. package/numkong/bench/bench_attention.cpp +113 -0
  43. package/numkong/bench/bench_cast.cpp +264 -0
  44. package/numkong/bench/bench_cross_amx.cpp +62 -0
  45. package/numkong/bench/bench_cross_arm.cpp +245 -0
  46. package/numkong/bench/bench_cross_blas.cpp +309 -0
  47. package/numkong/bench/bench_cross_loongarch.cpp +106 -0
  48. package/numkong/bench/bench_cross_power.cpp +84 -0
  49. package/numkong/bench/bench_cross_rvv.cpp +121 -0
  50. package/numkong/bench/bench_cross_serial.cpp +151 -0
  51. package/numkong/bench/bench_cross_sme.cpp +163 -0
  52. package/numkong/bench/bench_cross_wasm.cpp +142 -0
  53. package/numkong/bench/bench_cross_x86.cpp +455 -0
  54. package/numkong/bench/bench_curved.cpp +210 -0
  55. package/numkong/bench/bench_dot.cpp +315 -0
  56. package/numkong/bench/bench_each.cpp +503 -0
  57. package/numkong/bench/bench_geospatial.cpp +112 -0
  58. package/numkong/bench/bench_maxsim.cpp +154 -0
  59. package/numkong/bench/bench_mesh.cpp +148 -0
  60. package/numkong/bench/bench_probability.cpp +60 -0
  61. package/numkong/bench/bench_reduce.cpp +371 -0
  62. package/numkong/bench/bench_set.cpp +73 -0
  63. package/numkong/bench/bench_sparse.cpp +198 -0
  64. package/numkong/bench/bench_spatial.cpp +347 -0
  65. package/numkong/bench/bench_trigonometry.cpp +137 -0
  66. package/numkong/bench/swift/Bench.swift +506 -0
  67. package/numkong/binding.gyp +169 -0
  68. package/numkong/build.rs +484 -0
  69. package/numkong/c/dispatch.h +512 -0
  70. package/numkong/c/dispatch_bf16.c +447 -0
  71. package/numkong/c/dispatch_bf16c.c +52 -0
  72. package/numkong/c/dispatch_e2m3.c +296 -0
  73. package/numkong/c/dispatch_e3m2.c +284 -0
  74. package/numkong/c/dispatch_e4m3.c +318 -0
  75. package/numkong/c/dispatch_e5m2.c +316 -0
  76. package/numkong/c/dispatch_f16.c +452 -0
  77. package/numkong/c/dispatch_f16c.c +58 -0
  78. package/numkong/c/dispatch_f32.c +434 -0
  79. package/numkong/c/dispatch_f32c.c +99 -0
  80. package/numkong/c/dispatch_f64.c +348 -0
  81. package/numkong/c/dispatch_f64c.c +98 -0
  82. package/numkong/c/dispatch_i16.c +96 -0
  83. package/numkong/c/dispatch_i32.c +89 -0
  84. package/numkong/c/dispatch_i4.c +153 -0
  85. package/numkong/c/dispatch_i64.c +86 -0
  86. package/numkong/c/dispatch_i8.c +348 -0
  87. package/numkong/c/dispatch_other.c +348 -0
  88. package/numkong/c/dispatch_u1.c +193 -0
  89. package/numkong/c/dispatch_u16.c +124 -0
  90. package/numkong/c/dispatch_u32.c +118 -0
  91. package/numkong/c/dispatch_u4.c +153 -0
  92. package/numkong/c/dispatch_u64.c +102 -0
  93. package/numkong/c/dispatch_u8.c +364 -0
  94. package/numkong/c/numkong.c +940 -0
  95. package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
  96. package/numkong/cmake/nk_isa_probe.cmake +90 -0
  97. package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
  98. package/numkong/cmake/nk_math_lib.cmake +46 -0
  99. package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
  100. package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
  101. package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
  102. package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
  103. package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
  104. package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
  105. package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
  106. package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
  107. package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
  108. package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
  109. package/numkong/cmake/toolchain-wasi.cmake +77 -0
  110. package/numkong/cmake/toolchain-wasm.cmake +70 -0
  111. package/numkong/cmake/toolchain-wasm64.cmake +62 -0
  112. package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
  113. package/numkong/include/README.md +643 -0
  114. package/numkong/include/module.modulemap +129 -0
  115. package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
  116. package/numkong/include/numkong/attention/sme.h +2075 -0
  117. package/numkong/include/numkong/attention.h +49 -0
  118. package/numkong/include/numkong/capabilities.h +930 -0
  119. package/numkong/include/numkong/cast/README.md +262 -0
  120. package/numkong/include/numkong/cast/diamond.h +64 -0
  121. package/numkong/include/numkong/cast/haswell.h +823 -0
  122. package/numkong/include/numkong/cast/icelake.h +475 -0
  123. package/numkong/include/numkong/cast/loongsonasx.h +252 -0
  124. package/numkong/include/numkong/cast/neon.h +1174 -0
  125. package/numkong/include/numkong/cast/powervsx.h +449 -0
  126. package/numkong/include/numkong/cast/rvv.h +970 -0
  127. package/numkong/include/numkong/cast/sapphire.h +262 -0
  128. package/numkong/include/numkong/cast/serial.h +2336 -0
  129. package/numkong/include/numkong/cast/skylake.h +915 -0
  130. package/numkong/include/numkong/cast/v128relaxed.h +537 -0
  131. package/numkong/include/numkong/cast.h +254 -0
  132. package/numkong/include/numkong/cast.hpp +44 -0
  133. package/numkong/include/numkong/curved/README.md +223 -0
  134. package/numkong/include/numkong/curved/genoa.h +182 -0
  135. package/numkong/include/numkong/curved/haswell.h +276 -0
  136. package/numkong/include/numkong/curved/neon.h +329 -0
  137. package/numkong/include/numkong/curved/neonbfdot.h +211 -0
  138. package/numkong/include/numkong/curved/rvv.h +305 -0
  139. package/numkong/include/numkong/curved/serial.h +207 -0
  140. package/numkong/include/numkong/curved/skylake.h +457 -0
  141. package/numkong/include/numkong/curved/smef64.h +530 -0
  142. package/numkong/include/numkong/curved.h +513 -0
  143. package/numkong/include/numkong/curved.hpp +144 -0
  144. package/numkong/include/numkong/dot/README.md +442 -0
  145. package/numkong/include/numkong/dot/alder.h +563 -0
  146. package/numkong/include/numkong/dot/diamond.h +158 -0
  147. package/numkong/include/numkong/dot/genoa.h +289 -0
  148. package/numkong/include/numkong/dot/haswell.h +1723 -0
  149. package/numkong/include/numkong/dot/icelake.h +997 -0
  150. package/numkong/include/numkong/dot/loongsonasx.h +671 -0
  151. package/numkong/include/numkong/dot/neon.h +869 -0
  152. package/numkong/include/numkong/dot/neonbfdot.h +243 -0
  153. package/numkong/include/numkong/dot/neonfhm.h +358 -0
  154. package/numkong/include/numkong/dot/neonfp8.h +323 -0
  155. package/numkong/include/numkong/dot/neonsdot.h +622 -0
  156. package/numkong/include/numkong/dot/powervsx.h +752 -0
  157. package/numkong/include/numkong/dot/rvv.h +722 -0
  158. package/numkong/include/numkong/dot/rvvbb.h +72 -0
  159. package/numkong/include/numkong/dot/rvvbf16.h +123 -0
  160. package/numkong/include/numkong/dot/rvvhalf.h +129 -0
  161. package/numkong/include/numkong/dot/sapphire.h +141 -0
  162. package/numkong/include/numkong/dot/serial.h +874 -0
  163. package/numkong/include/numkong/dot/sierra.h +405 -0
  164. package/numkong/include/numkong/dot/skylake.h +1116 -0
  165. package/numkong/include/numkong/dot/sve.h +420 -0
  166. package/numkong/include/numkong/dot/svebfdot.h +75 -0
  167. package/numkong/include/numkong/dot/svehalf.h +168 -0
  168. package/numkong/include/numkong/dot/svesdot.h +90 -0
  169. package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
  170. package/numkong/include/numkong/dot.h +1136 -0
  171. package/numkong/include/numkong/dot.hpp +94 -0
  172. package/numkong/include/numkong/dots/README.md +496 -0
  173. package/numkong/include/numkong/dots/alder.h +117 -0
  174. package/numkong/include/numkong/dots/diamond.h +86 -0
  175. package/numkong/include/numkong/dots/genoa.h +100 -0
  176. package/numkong/include/numkong/dots/graniteamx.h +733 -0
  177. package/numkong/include/numkong/dots/haswell.h +310 -0
  178. package/numkong/include/numkong/dots/icelake.h +180 -0
  179. package/numkong/include/numkong/dots/loongsonasx.h +176 -0
  180. package/numkong/include/numkong/dots/neon.h +123 -0
  181. package/numkong/include/numkong/dots/neonbfdot.h +59 -0
  182. package/numkong/include/numkong/dots/neonfhm.h +96 -0
  183. package/numkong/include/numkong/dots/neonfp8.h +99 -0
  184. package/numkong/include/numkong/dots/neonsdot.h +144 -0
  185. package/numkong/include/numkong/dots/powervsx.h +194 -0
  186. package/numkong/include/numkong/dots/rvv.h +2593 -0
  187. package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
  188. package/numkong/include/numkong/dots/serial.h +2867 -0
  189. package/numkong/include/numkong/dots/sierra.h +100 -0
  190. package/numkong/include/numkong/dots/skylake.h +207 -0
  191. package/numkong/include/numkong/dots/sme.h +5041 -0
  192. package/numkong/include/numkong/dots/smebi32.h +477 -0
  193. package/numkong/include/numkong/dots/smef64.h +1328 -0
  194. package/numkong/include/numkong/dots/smehalf.h +47 -0
  195. package/numkong/include/numkong/dots/v128relaxed.h +317 -0
  196. package/numkong/include/numkong/dots.h +3110 -0
  197. package/numkong/include/numkong/dots.hpp +641 -0
  198. package/numkong/include/numkong/each/README.md +465 -0
  199. package/numkong/include/numkong/each/haswell.h +1658 -0
  200. package/numkong/include/numkong/each/icelake.h +272 -0
  201. package/numkong/include/numkong/each/neon.h +1138 -0
  202. package/numkong/include/numkong/each/neonbfdot.h +211 -0
  203. package/numkong/include/numkong/each/neonhalf.h +318 -0
  204. package/numkong/include/numkong/each/rvv.h +1121 -0
  205. package/numkong/include/numkong/each/sapphire.h +343 -0
  206. package/numkong/include/numkong/each/serial.h +282 -0
  207. package/numkong/include/numkong/each/skylake.h +1562 -0
  208. package/numkong/include/numkong/each/v128relaxed.h +562 -0
  209. package/numkong/include/numkong/each.h +2232 -0
  210. package/numkong/include/numkong/each.hpp +434 -0
  211. package/numkong/include/numkong/geospatial/README.md +147 -0
  212. package/numkong/include/numkong/geospatial/haswell.h +633 -0
  213. package/numkong/include/numkong/geospatial/neon.h +615 -0
  214. package/numkong/include/numkong/geospatial/rvv.h +701 -0
  215. package/numkong/include/numkong/geospatial/serial.h +309 -0
  216. package/numkong/include/numkong/geospatial/skylake.h +613 -0
  217. package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
  218. package/numkong/include/numkong/geospatial.h +453 -0
  219. package/numkong/include/numkong/geospatial.hpp +235 -0
  220. package/numkong/include/numkong/matrix.hpp +336 -0
  221. package/numkong/include/numkong/maxsim/README.md +187 -0
  222. package/numkong/include/numkong/maxsim/alder.h +511 -0
  223. package/numkong/include/numkong/maxsim/genoa.h +115 -0
  224. package/numkong/include/numkong/maxsim/haswell.h +553 -0
  225. package/numkong/include/numkong/maxsim/icelake.h +480 -0
  226. package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
  227. package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
  228. package/numkong/include/numkong/maxsim/serial.h +490 -0
  229. package/numkong/include/numkong/maxsim/sme.h +993 -0
  230. package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
  231. package/numkong/include/numkong/maxsim.h +571 -0
  232. package/numkong/include/numkong/maxsim.hpp +133 -0
  233. package/numkong/include/numkong/mesh/README.md +219 -0
  234. package/numkong/include/numkong/mesh/haswell.h +2013 -0
  235. package/numkong/include/numkong/mesh/neon.h +1821 -0
  236. package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
  237. package/numkong/include/numkong/mesh/rvv.h +936 -0
  238. package/numkong/include/numkong/mesh/serial.h +721 -0
  239. package/numkong/include/numkong/mesh/skylake.h +2344 -0
  240. package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
  241. package/numkong/include/numkong/mesh.h +666 -0
  242. package/numkong/include/numkong/mesh.hpp +701 -0
  243. package/numkong/include/numkong/numkong.h +78 -0
  244. package/numkong/include/numkong/numkong.hpp +58 -0
  245. package/numkong/include/numkong/probability/README.md +177 -0
  246. package/numkong/include/numkong/probability/haswell.h +267 -0
  247. package/numkong/include/numkong/probability/neon.h +255 -0
  248. package/numkong/include/numkong/probability/rvv.h +414 -0
  249. package/numkong/include/numkong/probability/serial.h +181 -0
  250. package/numkong/include/numkong/probability/skylake.h +324 -0
  251. package/numkong/include/numkong/probability.h +380 -0
  252. package/numkong/include/numkong/probability.hpp +120 -0
  253. package/numkong/include/numkong/random.h +50 -0
  254. package/numkong/include/numkong/random.hpp +285 -0
  255. package/numkong/include/numkong/reduce/README.md +552 -0
  256. package/numkong/include/numkong/reduce/alder.h +636 -0
  257. package/numkong/include/numkong/reduce/genoa.h +201 -0
  258. package/numkong/include/numkong/reduce/haswell.h +3785 -0
  259. package/numkong/include/numkong/reduce/icelake.h +549 -0
  260. package/numkong/include/numkong/reduce/neon.h +3980 -0
  261. package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
  262. package/numkong/include/numkong/reduce/neonfhm.h +293 -0
  263. package/numkong/include/numkong/reduce/neonsdot.h +357 -0
  264. package/numkong/include/numkong/reduce/rvv.h +3533 -0
  265. package/numkong/include/numkong/reduce/serial.h +753 -0
  266. package/numkong/include/numkong/reduce/sierra.h +338 -0
  267. package/numkong/include/numkong/reduce/skylake.h +3794 -0
  268. package/numkong/include/numkong/reduce/sve.h +52 -0
  269. package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
  270. package/numkong/include/numkong/reduce.h +1582 -0
  271. package/numkong/include/numkong/reduce.hpp +778 -0
  272. package/numkong/include/numkong/scalar/README.md +89 -0
  273. package/numkong/include/numkong/scalar/haswell.h +122 -0
  274. package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
  275. package/numkong/include/numkong/scalar/neon.h +122 -0
  276. package/numkong/include/numkong/scalar/neonhalf.h +70 -0
  277. package/numkong/include/numkong/scalar/powervsx.h +96 -0
  278. package/numkong/include/numkong/scalar/rvv.h +211 -0
  279. package/numkong/include/numkong/scalar/sapphire.h +74 -0
  280. package/numkong/include/numkong/scalar/serial.h +332 -0
  281. package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
  282. package/numkong/include/numkong/scalar.h +696 -0
  283. package/numkong/include/numkong/set/README.md +179 -0
  284. package/numkong/include/numkong/set/haswell.h +334 -0
  285. package/numkong/include/numkong/set/icelake.h +485 -0
  286. package/numkong/include/numkong/set/loongsonasx.h +181 -0
  287. package/numkong/include/numkong/set/neon.h +363 -0
  288. package/numkong/include/numkong/set/powervsx.h +326 -0
  289. package/numkong/include/numkong/set/rvv.h +226 -0
  290. package/numkong/include/numkong/set/rvvbb.h +117 -0
  291. package/numkong/include/numkong/set/serial.h +174 -0
  292. package/numkong/include/numkong/set/sve.h +187 -0
  293. package/numkong/include/numkong/set/v128relaxed.h +240 -0
  294. package/numkong/include/numkong/set.h +471 -0
  295. package/numkong/include/numkong/set.hpp +114 -0
  296. package/numkong/include/numkong/sets/README.md +149 -0
  297. package/numkong/include/numkong/sets/haswell.h +63 -0
  298. package/numkong/include/numkong/sets/icelake.h +66 -0
  299. package/numkong/include/numkong/sets/loongsonasx.h +52 -0
  300. package/numkong/include/numkong/sets/neon.h +61 -0
  301. package/numkong/include/numkong/sets/powervsx.h +65 -0
  302. package/numkong/include/numkong/sets/serial.h +43 -0
  303. package/numkong/include/numkong/sets/smebi32.h +1135 -0
  304. package/numkong/include/numkong/sets/v128relaxed.h +58 -0
  305. package/numkong/include/numkong/sets.h +382 -0
  306. package/numkong/include/numkong/sparse/README.md +156 -0
  307. package/numkong/include/numkong/sparse/icelake.h +463 -0
  308. package/numkong/include/numkong/sparse/neon.h +288 -0
  309. package/numkong/include/numkong/sparse/serial.h +117 -0
  310. package/numkong/include/numkong/sparse/sve2.h +504 -0
  311. package/numkong/include/numkong/sparse/turin.h +322 -0
  312. package/numkong/include/numkong/sparse.h +363 -0
  313. package/numkong/include/numkong/sparse.hpp +113 -0
  314. package/numkong/include/numkong/spatial/README.md +439 -0
  315. package/numkong/include/numkong/spatial/alder.h +604 -0
  316. package/numkong/include/numkong/spatial/diamond.h +240 -0
  317. package/numkong/include/numkong/spatial/genoa.h +222 -0
  318. package/numkong/include/numkong/spatial/haswell.h +979 -0
  319. package/numkong/include/numkong/spatial/icelake.h +1067 -0
  320. package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
  321. package/numkong/include/numkong/spatial/neon.h +846 -0
  322. package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
  323. package/numkong/include/numkong/spatial/neonfp8.h +258 -0
  324. package/numkong/include/numkong/spatial/neonsdot.h +429 -0
  325. package/numkong/include/numkong/spatial/powervsx.h +738 -0
  326. package/numkong/include/numkong/spatial/rvv.h +991 -0
  327. package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
  328. package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
  329. package/numkong/include/numkong/spatial/serial.h +347 -0
  330. package/numkong/include/numkong/spatial/sierra.h +516 -0
  331. package/numkong/include/numkong/spatial/skylake.h +605 -0
  332. package/numkong/include/numkong/spatial/sve.h +246 -0
  333. package/numkong/include/numkong/spatial/svebfdot.h +125 -0
  334. package/numkong/include/numkong/spatial/svehalf.h +136 -0
  335. package/numkong/include/numkong/spatial/svesdot.h +143 -0
  336. package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
  337. package/numkong/include/numkong/spatial.h +1681 -0
  338. package/numkong/include/numkong/spatial.hpp +183 -0
  339. package/numkong/include/numkong/spatials/README.md +580 -0
  340. package/numkong/include/numkong/spatials/alder.h +94 -0
  341. package/numkong/include/numkong/spatials/diamond.h +82 -0
  342. package/numkong/include/numkong/spatials/genoa.h +94 -0
  343. package/numkong/include/numkong/spatials/graniteamx.h +173 -0
  344. package/numkong/include/numkong/spatials/haswell.h +219 -0
  345. package/numkong/include/numkong/spatials/icelake.h +113 -0
  346. package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
  347. package/numkong/include/numkong/spatials/neon.h +109 -0
  348. package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
  349. package/numkong/include/numkong/spatials/neonfhm.h +92 -0
  350. package/numkong/include/numkong/spatials/neonfp8.h +111 -0
  351. package/numkong/include/numkong/spatials/neonsdot.h +143 -0
  352. package/numkong/include/numkong/spatials/powervsx.h +153 -0
  353. package/numkong/include/numkong/spatials/rvv.h +1976 -0
  354. package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
  355. package/numkong/include/numkong/spatials/serial.h +248 -0
  356. package/numkong/include/numkong/spatials/sierra.h +96 -0
  357. package/numkong/include/numkong/spatials/skylake.h +184 -0
  358. package/numkong/include/numkong/spatials/sme.h +1927 -0
  359. package/numkong/include/numkong/spatials/smef64.h +483 -0
  360. package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
  361. package/numkong/include/numkong/spatials.h +3374 -0
  362. package/numkong/include/numkong/spatials.hpp +509 -0
  363. package/numkong/include/numkong/tensor.hpp +1981 -0
  364. package/numkong/include/numkong/trigonometry/README.md +184 -0
  365. package/numkong/include/numkong/trigonometry/haswell.h +653 -0
  366. package/numkong/include/numkong/trigonometry/neon.h +638 -0
  367. package/numkong/include/numkong/trigonometry/rvv.h +700 -0
  368. package/numkong/include/numkong/trigonometry/serial.h +703 -0
  369. package/numkong/include/numkong/trigonometry/skylake.h +725 -0
  370. package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
  371. package/numkong/include/numkong/trigonometry.h +467 -0
  372. package/numkong/include/numkong/trigonometry.hpp +166 -0
  373. package/numkong/include/numkong/types.h +1695 -0
  374. package/numkong/include/numkong/types.hpp +6027 -0
  375. package/numkong/include/numkong/vector.hpp +766 -0
  376. package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
  377. package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
  378. package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
  379. package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
  380. package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
  381. package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
  382. package/numkong/javascript/README.md +254 -0
  383. package/numkong/javascript/node-gyp-build.d.ts +4 -0
  384. package/numkong/javascript/numkong-browser.ts +40 -0
  385. package/numkong/javascript/numkong-wasm.ts +750 -0
  386. package/numkong/javascript/numkong.c +712 -0
  387. package/numkong/javascript/numkong.ts +613 -0
  388. package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
  389. package/numkong/javascript/tsconfig-cjs.json +13 -0
  390. package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
  391. package/numkong/javascript/types.ts +784 -0
  392. package/numkong/numkong.gypi +104 -0
  393. package/numkong/package.json +108 -0
  394. package/numkong/probes/arm_neon.c +8 -0
  395. package/numkong/probes/arm_neon_bfdot.c +9 -0
  396. package/numkong/probes/arm_neon_fhm.c +9 -0
  397. package/numkong/probes/arm_neon_half.c +8 -0
  398. package/numkong/probes/arm_neon_sdot.c +9 -0
  399. package/numkong/probes/arm_neonfp8.c +9 -0
  400. package/numkong/probes/arm_sme.c +16 -0
  401. package/numkong/probes/arm_sme2.c +16 -0
  402. package/numkong/probes/arm_sme2p1.c +16 -0
  403. package/numkong/probes/arm_sme_bf16.c +16 -0
  404. package/numkong/probes/arm_sme_bi32.c +16 -0
  405. package/numkong/probes/arm_sme_f64.c +16 -0
  406. package/numkong/probes/arm_sme_fa64.c +14 -0
  407. package/numkong/probes/arm_sme_half.c +16 -0
  408. package/numkong/probes/arm_sme_lut2.c +15 -0
  409. package/numkong/probes/arm_sve.c +18 -0
  410. package/numkong/probes/arm_sve2.c +20 -0
  411. package/numkong/probes/arm_sve2p1.c +18 -0
  412. package/numkong/probes/arm_sve_bfdot.c +20 -0
  413. package/numkong/probes/arm_sve_half.c +18 -0
  414. package/numkong/probes/arm_sve_sdot.c +21 -0
  415. package/numkong/probes/loongarch_lasx.c +12 -0
  416. package/numkong/probes/power_vsx.c +12 -0
  417. package/numkong/probes/probe.js +127 -0
  418. package/numkong/probes/riscv_rvv.c +14 -0
  419. package/numkong/probes/riscv_rvv_bb.c +15 -0
  420. package/numkong/probes/riscv_rvv_bf16.c +17 -0
  421. package/numkong/probes/riscv_rvv_half.c +14 -0
  422. package/numkong/probes/wasm_v128relaxed.c +11 -0
  423. package/numkong/probes/x86_alder.c +17 -0
  424. package/numkong/probes/x86_diamond.c +17 -0
  425. package/numkong/probes/x86_genoa.c +17 -0
  426. package/numkong/probes/x86_graniteamx.c +19 -0
  427. package/numkong/probes/x86_haswell.c +11 -0
  428. package/numkong/probes/x86_icelake.c +17 -0
  429. package/numkong/probes/x86_sapphire.c +16 -0
  430. package/numkong/probes/x86_sapphireamx.c +18 -0
  431. package/numkong/probes/x86_sierra.c +17 -0
  432. package/numkong/probes/x86_skylake.c +15 -0
  433. package/numkong/probes/x86_turin.c +17 -0
  434. package/numkong/rust/README.md +698 -0
  435. package/numkong/rust/capabilities.rs +96 -0
  436. package/numkong/rust/cast.rs +314 -0
  437. package/numkong/rust/curved.rs +391 -0
  438. package/numkong/rust/each.rs +2721 -0
  439. package/numkong/rust/geospatial.rs +343 -0
  440. package/numkong/rust/matrix.rs +5424 -0
  441. package/numkong/rust/maxsim.rs +540 -0
  442. package/numkong/rust/mesh.rs +837 -0
  443. package/numkong/rust/numkong.rs +409 -0
  444. package/numkong/rust/probability.rs +272 -0
  445. package/numkong/rust/reduce.rs +1249 -0
  446. package/numkong/rust/set.rs +194 -0
  447. package/numkong/rust/sparse.rs +513 -0
  448. package/numkong/rust/spatial.rs +1815 -0
  449. package/numkong/rust/tensor.rs +7565 -0
  450. package/numkong/rust/types.rs +3666 -0
  451. package/numkong/rust/vector.rs +1899 -0
  452. package/numkong/rustfmt.toml +2 -0
  453. package/numkong/swift/Matrix.swift +1388 -0
  454. package/numkong/swift/MaxSim.swift +151 -0
  455. package/numkong/swift/NumKong.swift +283 -0
  456. package/numkong/swift/Numerics.swift +329 -0
  457. package/numkong/swift/README.md +433 -0
  458. package/numkong/swift/Scalar.swift +741 -0
  459. package/numkong/swift/Tensor.swift +209 -0
  460. package/numkong/test/README.md +315 -0
  461. package/numkong/test/playwright.config.ts +16 -0
  462. package/numkong/test/swift/Test.swift +534 -0
  463. package/numkong/test/test-browser-bundle-runner.mjs +36 -0
  464. package/numkong/test/test-browser-bundle.html +188 -0
  465. package/numkong/test/test-browser-runner.mjs +43 -0
  466. package/numkong/test/test-browser.html +305 -0
  467. package/numkong/test/test-dtypes.mjs +453 -0
  468. package/numkong/test/test-wasm.mjs +267 -0
  469. package/numkong/test/test.cpp +442 -0
  470. package/numkong/test/test.hpp +580 -0
  471. package/numkong/test/test.mjs +363 -0
  472. package/numkong/test/test_base.py +1041 -0
  473. package/numkong/test/test_cast.cpp +242 -0
  474. package/numkong/test/test_cross.hpp +551 -0
  475. package/numkong/test/test_cross.py +467 -0
  476. package/numkong/test/test_cross_amx.cpp +50 -0
  477. package/numkong/test/test_cross_arm.cpp +225 -0
  478. package/numkong/test/test_cross_blas.cpp +354 -0
  479. package/numkong/test/test_cross_loongarch.cpp +90 -0
  480. package/numkong/test/test_cross_power.cpp +73 -0
  481. package/numkong/test/test_cross_rvv.cpp +109 -0
  482. package/numkong/test/test_cross_serial.cpp +181 -0
  483. package/numkong/test/test_cross_sme.cpp +148 -0
  484. package/numkong/test/test_cross_wasm.cpp +141 -0
  485. package/numkong/test/test_cross_x86.cpp +442 -0
  486. package/numkong/test/test_curved.cpp +182 -0
  487. package/numkong/test/test_curved.py +207 -0
  488. package/numkong/test/test_dot.cpp +353 -0
  489. package/numkong/test/test_dot.py +296 -0
  490. package/numkong/test/test_each.cpp +437 -0
  491. package/numkong/test/test_each.py +756 -0
  492. package/numkong/test/test_geospatial.cpp +155 -0
  493. package/numkong/test/test_geospatial.py +241 -0
  494. package/numkong/test/test_maxsim.cpp +138 -0
  495. package/numkong/test/test_maxsim.py +209 -0
  496. package/numkong/test/test_mesh.cpp +203 -0
  497. package/numkong/test/test_mesh.py +394 -0
  498. package/numkong/test/test_probability.cpp +136 -0
  499. package/numkong/test/test_probability.py +166 -0
  500. package/numkong/test/test_reduce.cpp +563 -0
  501. package/numkong/test/test_reduce.py +781 -0
  502. package/numkong/test/test_set.cpp +151 -0
  503. package/numkong/test/test_set.py +120 -0
  504. package/numkong/test/test_similarities.py +579 -0
  505. package/numkong/test/test_sparse.cpp +121 -0
  506. package/numkong/test/test_sparse.py +118 -0
  507. package/numkong/test/test_spatial.cpp +518 -0
  508. package/numkong/test/test_spatial.py +269 -0
  509. package/numkong/test/test_tensor.cpp +884 -0
  510. package/numkong/test/test_tensor.py +1417 -0
  511. package/numkong/test/test_trigonometry.cpp +156 -0
  512. package/numkong/test/test_trigonometry.py +180 -0
  513. package/package.json +7 -8
  514. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  515. package/prebuilds/linux-arm64/usearch.node +0 -0
  516. package/prebuilds/linux-x64/usearch.node +0 -0
  517. package/fp16/CMakeLists.txt +0 -176
  518. package/fp16/LICENSE +0 -11
  519. package/fp16/README.md +0 -20
  520. package/fp16/bench/alt-element.cc +0 -89
  521. package/fp16/bench/from-alt-array.cc +0 -175
  522. package/fp16/bench/from-ieee-array.cc +0 -399
  523. package/fp16/bench/ieee-element.cc +0 -272
  524. package/fp16/bench/to-alt-array.cc +0 -91
  525. package/fp16/bench/to-ieee-array.cc +0 -303
  526. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  527. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  528. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  529. package/fp16/configure.py +0 -65
  530. package/fp16/confu.yaml +0 -8
  531. package/fp16/include/fp16/__init__.py +0 -0
  532. package/fp16/include/fp16/avx.py +0 -50
  533. package/fp16/include/fp16/avx2.py +0 -53
  534. package/fp16/include/fp16/bitcasts.h +0 -92
  535. package/fp16/include/fp16/fp16.h +0 -451
  536. package/fp16/include/fp16/psimd.h +0 -131
  537. package/fp16/include/fp16.h +0 -11
  538. package/fp16/jni/Android.mk +0 -6
  539. package/fp16/jni/Application.mk +0 -4
  540. package/fp16/test/alt-from-fp32-value.cc +0 -498
  541. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  542. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  543. package/fp16/test/alt-to-fp32-value.cc +0 -348
  544. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  545. package/fp16/test/bitcasts.cc +0 -56
  546. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  547. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  548. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  549. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  550. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  551. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  552. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  553. package/fp16/test/peachpy/stubs.py +0 -38
  554. package/fp16/test/tables.cc +0 -530
  555. package/fp16/test/tables.h +0 -13
  556. package/fp16/third-party/THHalf.h +0 -92
  557. package/fp16/third-party/eigen-half.h +0 -170
  558. package/fp16/third-party/float16-compressor.h +0 -80
  559. package/fp16/third-party/half.hpp +0 -2916
  560. package/fp16/third-party/npy-halffloat.h +0 -328
  561. package/simsimd/.git-blame-ignore-revs +0 -2
  562. package/simsimd/CONTRIBUTING.md +0 -321
  563. package/simsimd/Cargo.toml +0 -49
  564. package/simsimd/MANIFEST.in +0 -4
  565. package/simsimd/Package.swift +0 -28
  566. package/simsimd/README.md +0 -1493
  567. package/simsimd/binding.gyp +0 -35
  568. package/simsimd/build.rs +0 -70
  569. package/simsimd/c/lib.c +0 -374
  570. package/simsimd/include/module.modulemap +0 -4
  571. package/simsimd/include/simsimd/binary.h +0 -486
  572. package/simsimd/include/simsimd/curved.h +0 -1541
  573. package/simsimd/include/simsimd/dot.h +0 -1853
  574. package/simsimd/include/simsimd/elementwise.h +0 -2465
  575. package/simsimd/include/simsimd/geospatial.h +0 -43
  576. package/simsimd/include/simsimd/mesh.h +0 -69
  577. package/simsimd/include/simsimd/probability.h +0 -606
  578. package/simsimd/include/simsimd/simsimd.h +0 -2567
  579. package/simsimd/include/simsimd/sparse.h +0 -1384
  580. package/simsimd/include/simsimd/spatial.h +0 -2335
  581. package/simsimd/include/simsimd/types.h +0 -666
  582. package/simsimd/javascript/fallback.ts +0 -233
  583. package/simsimd/javascript/lib.c +0 -125
  584. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  585. package/simsimd/javascript/simsimd.ts +0 -179
  586. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  587. package/simsimd/package-lock.json +0 -1761
  588. package/simsimd/package.json +0 -61
  589. package/simsimd/rust/lib.rs +0 -1821
  590. package/simsimd/scripts/bench.cxx +0 -1170
  591. package/simsimd/scripts/bench.ipynb +0 -187
  592. package/simsimd/scripts/bench.js +0 -127
  593. package/simsimd/scripts/bench.rs +0 -876
  594. package/simsimd/scripts/bench_vectors.py +0 -734
  595. package/simsimd/scripts/bench_vectors_live.py +0 -213
  596. package/simsimd/scripts/test.c +0 -187
  597. package/simsimd/scripts/test.mjs +0 -220
  598. package/simsimd/scripts/test.py +0 -1505
  599. package/simsimd/swift/SimSIMD.swift +0 -149
  600. package/simsimd/swift/Test.swift +0 -128
  601. /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
  602. /package/{simsimd → numkong}/LICENSE +0 -0
  603. /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
  604. /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