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,1899 @@
1
+ //! Owning and non-owning vector types with signed indexing and sub-byte support.
2
+ //!
3
+ //! This module provides:
4
+ //!
5
+ //! - [`Vector`]: Owning, non-resizable, SIMD-aligned vector
6
+ //! - [`VectorView`]: Immutable, strided, non-owning view
7
+ //! - [`VectorSpan`]: Mutable, strided, non-owning view
8
+ //! - [`VectorIndex`]: Signed indexing trait (negative indices wrap from end)
9
+ //!
10
+ //! All types use [`StorageElement`] as their element bound, with sub-byte types
11
+ //! (i4x2, u4x2, u1x8) supported via `try_get`/`try_set` and iterators.
12
+ //!
13
+ //! # Signed (Python-style) indexing
14
+ //!
15
+ //! Negative indices count from the end, mirroring Python's `list[-1]` idiom:
16
+ //!
17
+ //! ```ignore
18
+ //! use numkong::vector::Vector;
19
+ //! let v = Vector::<f32>::try_from_dims(&[10.0, 20.0, 30.0, 40.0, 50.0]).unwrap();
20
+ //! // Last element via negative index.
21
+ //! assert_eq!(v.try_get(-1_i32).unwrap(), 50.0);
22
+ //! // Second-to-last.
23
+ //! assert_eq!(v.try_get(-2_i32).unwrap(), 40.0);
24
+ //! // Out-of-range negatives return an error rather than panic.
25
+ //! assert!(v.try_get(-6_i32).is_err());
26
+ //! ```
27
+ //!
28
+ //! # Sub-byte element iteration
29
+ //!
30
+ //! For packed types such as `i4x2`, `u4x2`, and `u1x8`, iteration yields one
31
+ //! logical dimension per step (not one packed storage value):
32
+ //!
33
+ //! ```ignore
34
+ //! use numkong::vector::Vector;
35
+ //! use numkong::types::u1x8;
36
+ //! let mut v = Vector::<u1x8>::try_zeros(8).unwrap();
37
+ //! for (i, mut bit) in v.iter_mut().enumerate() {
38
+ //! *bit = if i % 2 == 0 { 1 } else { 0 };
39
+ //! }
40
+ //! assert_eq!(v.try_get(0_usize).unwrap(), 1);
41
+ //! assert_eq!(v.try_get(1_usize).unwrap(), 0);
42
+ //! ```
43
+
44
+ extern crate alloc;
45
+
46
+ use core::marker::PhantomData;
47
+ use core::ptr::NonNull;
48
+
49
+ use crate::tensor::{Allocator, Global, Tensor, TensorError, SIMD_ALIGNMENT};
50
+ use crate::types::{DimMut, DimRef, FloatConvertible, NumberLike, StorageElement};
51
+
52
+ // region: VectorIndex — Signed Indexing
53
+
54
+ mod private {
55
+ pub trait Sealed {}
56
+ }
57
+
58
+ /// Trait for vector index types. Supports signed integers (negative = from end).
59
+ pub trait VectorIndex: private::Sealed + Copy {
60
+ /// Resolve this index to a `usize` offset, or `None` if out of bounds.
61
+ fn resolve(self, len: usize) -> Option<usize>;
62
+ }
63
+
64
+ macro_rules! impl_vec_index_unsigned {
65
+ ($($t:ty),*) => {$(
66
+ impl private::Sealed for $t {}
67
+ impl VectorIndex for $t {
68
+ #[inline]
69
+ fn resolve(self, len: usize) -> Option<usize> {
70
+ let index = self as usize;
71
+ if index < len { Some(index) } else { None }
72
+ }
73
+ }
74
+ )*};
75
+ }
76
+
77
+ macro_rules! impl_vec_index_signed {
78
+ ($($t:ty),*) => {$(
79
+ impl private::Sealed for $t {}
80
+ impl VectorIndex for $t {
81
+ #[inline]
82
+ fn resolve(self, len: usize) -> Option<usize> {
83
+ let index = if self >= 0 {
84
+ self as usize
85
+ } else {
86
+ let neg = (-(self as isize)) as usize;
87
+ if neg > len { return None; }
88
+ len - neg
89
+ };
90
+ if index < len { Some(index) } else { None }
91
+ }
92
+ }
93
+ )*};
94
+ }
95
+
96
+ impl_vec_index_unsigned!(usize, u8, u16, u32, u64);
97
+ impl_vec_index_signed!(isize, i8, i16, i32, i64);
98
+
99
+ // endregion: VectorIndex
100
+
101
+ // region: Sub-byte Proxy Types
102
+
103
+ /// Immutable reference to a nibble (4-bit value) within a packed byte.
104
+ ///
105
+ /// Obtained by iterating over or indexing into a `Vector<i4x2>` / `Vector<u4x2>`;
106
+ /// the proxy remembers whether it refers to the low or high nibble of the shared
107
+ /// byte and decodes accordingly. Prefer [`NibbleRef::get_unsigned`] for `u4x2`
108
+ /// storage and [`NibbleRef::get_signed`] for `i4x2` storage (the latter
109
+ /// sign-extends bit 3 so the return range is `-8..=7`).
110
+ ///
111
+ /// # Example
112
+ ///
113
+ /// ```ignore
114
+ /// use numkong::vector::Vector;
115
+ /// use numkong::types::i4x2;
116
+ /// let mut v = Vector::<i4x2>::try_zeros(2).unwrap();
117
+ /// v.try_set(0_usize, -3).unwrap();
118
+ /// // Reading via `try_get` returns the same signed value NibbleRef::get_signed yields.
119
+ /// assert_eq!(v.try_get(0_usize).unwrap(), -3);
120
+ /// ```
121
+ pub struct NibbleRef<'a> {
122
+ byte: *const u8,
123
+ high: bool,
124
+ _marker: PhantomData<&'a u8>,
125
+ }
126
+
127
+ impl<'a> NibbleRef<'a> {
128
+ /// Read the nibble value as u8 (0..16).
129
+ #[inline]
130
+ pub fn get_unsigned(&self) -> u8 {
131
+ // SAFETY: byte pointer is valid for the lifetime 'a
132
+ let byte_value = unsafe { *self.byte };
133
+ if self.high {
134
+ byte_value >> 4
135
+ } else {
136
+ byte_value & 0x0F
137
+ }
138
+ }
139
+
140
+ /// Read the nibble value as i8 (-8..7), sign-extending bit 3.
141
+ #[inline]
142
+ pub fn get_signed(&self) -> i8 {
143
+ let nibble = self.get_unsigned();
144
+ if nibble & 0x08 != 0 {
145
+ nibble as i8 | !0x0Fi8
146
+ } else {
147
+ nibble as i8
148
+ }
149
+ }
150
+ }
151
+
152
+ /// Mutable reference to a nibble (4-bit value) within a packed byte.
153
+ ///
154
+ /// Obtained via mutable iteration / indexing of a `Vector<i4x2>` or
155
+ /// `Vector<u4x2>`. Writes perform a read-modify-write on the shared byte so the
156
+ /// sibling nibble is preserved. Pair `get_unsigned` / `set_unsigned` for
157
+ /// unsigned storage and `get_signed` / `set_signed` for signed storage.
158
+ ///
159
+ /// # Example
160
+ ///
161
+ /// ```ignore
162
+ /// use numkong::vector::Vector;
163
+ /// use numkong::types::u4x2;
164
+ /// let mut v = Vector::<u4x2>::try_zeros(4).unwrap();
165
+ /// for (i, mut nibble) in v.iter_mut().enumerate() {
166
+ /// *nibble = i as u8;
167
+ /// }
168
+ /// assert_eq!(v.try_get(3_usize).unwrap(), 3);
169
+ /// ```
170
+ pub struct NibbleRefMut<'a> {
171
+ byte: *mut u8,
172
+ high: bool,
173
+ _marker: PhantomData<&'a mut u8>,
174
+ }
175
+
176
+ impl<'a> NibbleRefMut<'a> {
177
+ /// Read the nibble value as u8.
178
+ #[inline]
179
+ pub fn get_unsigned(&self) -> u8 {
180
+ let byte_value = unsafe { *self.byte };
181
+ if self.high {
182
+ byte_value >> 4
183
+ } else {
184
+ byte_value & 0x0F
185
+ }
186
+ }
187
+
188
+ /// Read the nibble value as i8, sign-extending bit 3.
189
+ #[inline]
190
+ pub fn get_signed(&self) -> i8 {
191
+ let nibble = self.get_unsigned();
192
+ if nibble & 0x08 != 0 {
193
+ nibble as i8 | !0x0Fi8
194
+ } else {
195
+ nibble as i8
196
+ }
197
+ }
198
+
199
+ /// Set the nibble to an unsigned value (low 4 bits used).
200
+ #[inline]
201
+ pub fn set_unsigned(&self, value: u8) {
202
+ // SAFETY: byte pointer is valid and mutable for the lifetime 'a
203
+ unsafe {
204
+ let byte_value = *self.byte;
205
+ if self.high {
206
+ *self.byte = (byte_value & 0x0F) | ((value & 0x0F) << 4);
207
+ } else {
208
+ *self.byte = (byte_value & 0xF0) | (value & 0x0F);
209
+ }
210
+ }
211
+ }
212
+
213
+ /// Set the nibble to a signed value (low 4 bits used).
214
+ #[inline]
215
+ pub fn set_signed(&self, value: i8) {
216
+ self.set_unsigned(value as u8);
217
+ }
218
+ }
219
+
220
+ /// Immutable reference to a single bit within a packed byte.
221
+ ///
222
+ /// Obtained by iterating over or indexing into a `Vector<u1x8>`. The `mask`
223
+ /// selects which of the eight bits the proxy represents; the byte itself is
224
+ /// shared with the seven sibling proxies for the same byte.
225
+ ///
226
+ /// # Example
227
+ ///
228
+ /// ```ignore
229
+ /// use numkong::vector::Vector;
230
+ /// use numkong::types::u1x8;
231
+ /// let mut v = Vector::<u1x8>::try_zeros(8).unwrap();
232
+ /// v.try_set(3_usize, 1).unwrap();
233
+ /// // Immutable iteration yields BitRef-like proxies that deref to the bit value.
234
+ /// let bits: Vec<u8> = v.iter().map(|b| *b).collect();
235
+ /// assert_eq!(bits, vec![0, 0, 0, 1, 0, 0, 0, 0]);
236
+ /// ```
237
+ pub struct BitRef<'a> {
238
+ byte: *const u8,
239
+ mask: u8,
240
+ _marker: PhantomData<&'a u8>,
241
+ }
242
+
243
+ impl<'a> BitRef<'a> {
244
+ /// Read the bit as bool.
245
+ #[inline]
246
+ pub fn get(&self) -> bool {
247
+ // SAFETY: byte pointer is valid for the lifetime 'a
248
+ (unsafe { *self.byte } & self.mask) != 0
249
+ }
250
+ }
251
+
252
+ /// Mutable reference to a single bit within a packed byte.
253
+ ///
254
+ /// Obtained via mutable iteration / indexing of a `Vector<u1x8>`. Writes OR or
255
+ /// AND the stored byte so sibling bits are preserved; the `mask` selects which
256
+ /// bit this proxy represents.
257
+ ///
258
+ /// # Example
259
+ ///
260
+ /// ```ignore
261
+ /// use numkong::vector::Vector;
262
+ /// use numkong::types::u1x8;
263
+ /// let mut v = Vector::<u1x8>::try_zeros(8).unwrap();
264
+ /// for (i, mut bit) in v.iter_mut().enumerate() {
265
+ /// *bit = (i % 2 == 0) as u8;
266
+ /// }
267
+ /// assert_eq!(v.try_get(0_usize).unwrap(), 1);
268
+ /// assert_eq!(v.try_get(1_usize).unwrap(), 0);
269
+ /// ```
270
+ pub struct BitRefMut<'a> {
271
+ byte: *mut u8,
272
+ mask: u8,
273
+ _marker: PhantomData<&'a mut u8>,
274
+ }
275
+
276
+ impl<'a> BitRefMut<'a> {
277
+ /// Read the bit as bool.
278
+ #[inline]
279
+ pub fn get(&self) -> bool {
280
+ // SAFETY: byte pointer is valid for the lifetime 'a
281
+ (unsafe { *self.byte } & self.mask) != 0
282
+ }
283
+
284
+ /// Set the bit.
285
+ #[inline]
286
+ pub fn set(&self, value: bool) {
287
+ // SAFETY: byte pointer is valid and mutable for the lifetime 'a
288
+ unsafe {
289
+ if value {
290
+ *self.byte |= self.mask;
291
+ } else {
292
+ *self.byte &= !self.mask;
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ // endregion: Sub-byte Proxy Types
299
+
300
+ // region: Vector
301
+
302
+ /// Owning, non-resizable, SIMD-aligned vector.
303
+ ///
304
+ /// Size is fixed at construction. Uses [`StorageElement`] for element types,
305
+ /// including sub-byte packed types via `Scalar::dimensions_per_value()`.
306
+ ///
307
+ /// For normal types (`dimensions_per_value() == 1`), supports `Index`/`IndexMut`.
308
+ /// For sub-byte types, use `try_get`/`try_set` or iterators.
309
+ pub struct Vector<Scalar: StorageElement, Alloc: Allocator = Global> {
310
+ /// Pointer to the allocated buffer (typed as `Scalar` for alignment).
311
+ data: NonNull<Scalar>,
312
+ /// Number of logical dimensions.
313
+ dims: usize,
314
+ /// Number of storage values (dims / dimensions_per_value, rounded up).
315
+ values: usize,
316
+ /// Allocator instance.
317
+ alloc: Alloc,
318
+ }
319
+
320
+ unsafe impl<Scalar: StorageElement + Send, Alloc: Allocator + Send> Send for Vector<Scalar, Alloc> {}
321
+ unsafe impl<Scalar: StorageElement + Sync, Alloc: Allocator + Sync> Sync for Vector<Scalar, Alloc> {}
322
+
323
+ impl<Scalar: StorageElement, Alloc: Allocator> Drop for Vector<Scalar, Alloc> {
324
+ fn drop(&mut self) {
325
+ if self.values > 0 {
326
+ let layout = alloc::alloc::Layout::from_size_align(
327
+ self.values * core::mem::size_of::<Scalar>(),
328
+ SIMD_ALIGNMENT,
329
+ )
330
+ .unwrap();
331
+ // SAFETY: data was allocated with this layout in try_zeros_in,
332
+ // and values > 0 guarantees the pointer is non-dangling.
333
+ unsafe {
334
+ self.alloc.deallocate(
335
+ NonNull::new_unchecked(self.data.as_ptr() as *mut u8),
336
+ layout,
337
+ );
338
+ }
339
+ }
340
+ }
341
+ }
342
+
343
+ /// Convert dimension count to value count for `Scalar`.
344
+ ///
345
+ /// For sub-byte types where `dimensions_per_value() > 1`, this is a ceiling
346
+ /// division via [`usize::div_ceil`].
347
+ #[inline]
348
+ fn dims_to_values<Scalar: StorageElement>(dims: usize) -> usize {
349
+ dims.div_ceil(Scalar::dimensions_per_value())
350
+ }
351
+
352
+ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
353
+ /// Construct a vector from raw parts, taking ownership of the allocation.
354
+ ///
355
+ /// # Safety
356
+ /// - `data` must point to a valid allocation of `values * size_of::<Scalar>()` bytes
357
+ /// obtained from `alloc`, aligned to [`SIMD_ALIGNMENT`].
358
+ /// - `dims` and `values` must be consistent (`values == ceil(dims / dimensions_per_value())`).
359
+ /// - The caller must not free the memory (this vector takes ownership).
360
+ pub unsafe fn from_raw_parts(
361
+ data: NonNull<Scalar>,
362
+ dims: usize,
363
+ values: usize,
364
+ alloc: Alloc,
365
+ ) -> Self {
366
+ Self {
367
+ data,
368
+ dims,
369
+ values,
370
+ alloc,
371
+ }
372
+ }
373
+
374
+ /// Try to create a zero-initialized vector with the given number of dimensions.
375
+ pub fn try_zeros_in(dims: usize, alloc: Alloc) -> Result<Self, TensorError> {
376
+ let values = dims_to_values::<Scalar>(dims);
377
+ if values == 0 {
378
+ return Ok(Self {
379
+ data: NonNull::dangling(),
380
+ dims: 0,
381
+ values: 0,
382
+ alloc,
383
+ });
384
+ }
385
+ let size = values * core::mem::size_of::<Scalar>();
386
+ let layout = alloc::alloc::Layout::from_size_align(size, SIMD_ALIGNMENT)
387
+ .map_err(|_| TensorError::AllocationFailed)?;
388
+ let ptr = alloc
389
+ .allocate(layout)
390
+ .ok_or(TensorError::AllocationFailed)?;
391
+ unsafe { core::ptr::write_bytes(ptr.as_ptr(), 0, size) };
392
+ Ok(Self {
393
+ data: unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut Scalar) },
394
+ dims,
395
+ values,
396
+ alloc,
397
+ })
398
+ }
399
+
400
+ /// Try to create a vector filled with `value`.
401
+ pub fn try_full_in(dims: usize, value: Scalar, alloc: Alloc) -> Result<Self, TensorError> {
402
+ let v = Self::try_zeros_in(dims, alloc)?;
403
+ if v.values > 0 {
404
+ let ptr = v.data.as_ptr();
405
+ for i in 0..v.values {
406
+ unsafe { ptr.add(i).write(value) };
407
+ }
408
+ }
409
+ Ok(v)
410
+ }
411
+
412
+ /// Try to create a vector filled with ones.
413
+ pub fn try_ones_in(dims: usize, alloc: Alloc) -> Result<Self, TensorError>
414
+ where
415
+ Scalar: NumberLike,
416
+ {
417
+ Self::try_full_in(dims, Scalar::one(), alloc)
418
+ }
419
+
420
+ /// Try to create an uninitialized vector.
421
+ ///
422
+ /// # Safety
423
+ /// The returned vector's contents are uninitialized. Reading from it before
424
+ /// writing is undefined behavior.
425
+ pub unsafe fn try_empty_in(dims: usize, alloc: Alloc) -> Result<Self, TensorError> {
426
+ let values = dims_to_values::<Scalar>(dims);
427
+ if values == 0 {
428
+ return Ok(Self {
429
+ data: NonNull::dangling(),
430
+ dims: 0,
431
+ values: 0,
432
+ alloc,
433
+ });
434
+ }
435
+ let size = values * core::mem::size_of::<Scalar>();
436
+ let layout = alloc::alloc::Layout::from_size_align(size, SIMD_ALIGNMENT)
437
+ .map_err(|_| TensorError::AllocationFailed)?;
438
+ let ptr = alloc
439
+ .allocate(layout)
440
+ .ok_or(TensorError::AllocationFailed)?;
441
+ Ok(Self {
442
+ data: unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut Scalar) },
443
+ dims,
444
+ values,
445
+ alloc,
446
+ })
447
+ }
448
+
449
+ /// Try to create a vector from a slice of scalars (f32 values).
450
+ ///
451
+ /// Each f32 value is converted through `DimScalar::from_f32()` before storage.
452
+ pub fn try_from_scalars_in(scalars: &[f32], alloc: Alloc) -> Result<Self, TensorError>
453
+ where
454
+ Scalar: FloatConvertible,
455
+ {
456
+ let element_count = scalars.len();
457
+ let mut v = Self::try_zeros_in(element_count, alloc)?;
458
+ for (i, &s) in scalars.iter().enumerate() {
459
+ v.try_set(i, Scalar::DimScalar::from_f32(s))?;
460
+ }
461
+ Ok(v)
462
+ }
463
+
464
+ /// Try to create a vector from a slice of per-dimension scalars.
465
+ ///
466
+ /// Each element in `dim_values` corresponds to one logical dimension.
467
+ pub fn try_from_dims_in(
468
+ dim_values: &[Scalar::DimScalar],
469
+ alloc: Alloc,
470
+ ) -> Result<Self, TensorError>
471
+ where
472
+ Scalar: FloatConvertible,
473
+ {
474
+ let element_count = dim_values.len();
475
+ let mut v = Self::try_zeros_in(element_count, alloc)?;
476
+ for (i, &d) in dim_values.iter().enumerate() {
477
+ v.try_set(i, d)?;
478
+ }
479
+ Ok(v)
480
+ }
481
+
482
+ /// Number of logical dimensions.
483
+ #[inline]
484
+ pub fn dims(&self) -> usize {
485
+ self.dims
486
+ }
487
+
488
+ /// Number of logical dimensions (same as `dims()`).
489
+ #[inline]
490
+ pub fn size(&self) -> usize {
491
+ self.dims
492
+ }
493
+
494
+ /// Number of underlying storage values (`Scalar` instances).
495
+ #[inline]
496
+ pub fn size_values(&self) -> usize {
497
+ self.values
498
+ }
499
+
500
+ /// Returns true if the vector has zero dimensions.
501
+ #[inline]
502
+ pub fn is_empty(&self) -> bool {
503
+ self.dims == 0
504
+ }
505
+
506
+ /// Raw pointer to the underlying data.
507
+ #[inline]
508
+ pub fn as_ptr(&self) -> *const Scalar {
509
+ self.data.as_ptr()
510
+ }
511
+
512
+ /// Mutable raw pointer to the underlying data.
513
+ #[inline]
514
+ pub fn as_mut_ptr(&mut self) -> *mut Scalar {
515
+ self.data.as_ptr()
516
+ }
517
+
518
+ /// Size in bytes.
519
+ #[inline]
520
+ pub fn size_bytes(&self) -> usize {
521
+ self.values * core::mem::size_of::<Scalar>()
522
+ }
523
+
524
+ /// Create an immutable view of this vector.
525
+ #[inline]
526
+ pub fn view(&self) -> VectorView<'_, Scalar> {
527
+ VectorView {
528
+ data: self.data.as_ptr() as *const Scalar,
529
+ dims: self.dims,
530
+ stride_bytes: core::mem::size_of::<Scalar>() as isize,
531
+ _marker: PhantomData,
532
+ }
533
+ }
534
+
535
+ /// Create a mutable span of this vector.
536
+ #[inline]
537
+ pub fn span(&mut self) -> VectorSpan<'_, Scalar> {
538
+ VectorSpan {
539
+ data: self.data.as_ptr(),
540
+ dims: self.dims,
541
+ stride_bytes: core::mem::size_of::<Scalar>() as isize,
542
+ _marker: PhantomData,
543
+ }
544
+ }
545
+
546
+ /// Try to get the logical dimension at `index` (supports signed indexing).
547
+ ///
548
+ /// Returns the native `DimScalar` type (e.g., `f64` for `Vector<f64>`, `i8` for `Vector<i4x2>`).
549
+ /// For sub-byte types, unpacks the appropriate sub-dimension from the packed storage value.
550
+ ///
551
+ /// # Examples
552
+ ///
553
+ /// Positive and Python-style negative indexing:
554
+ ///
555
+ /// ```ignore
556
+ /// use numkong::vector::Vector;
557
+ /// let v = Vector::<f32>::try_from_dims(&[10.0, 20.0, 30.0, 40.0, 50.0]).unwrap();
558
+ /// assert_eq!(v.try_get(0_usize).unwrap(), 10.0);
559
+ /// assert_eq!(v.try_get(-1_i32).unwrap(), 50.0);
560
+ /// assert_eq!(v.try_get(-5_i32).unwrap(), 10.0);
561
+ /// // Out-of-range indices return an error instead of panicking.
562
+ /// assert!(v.try_get(5_usize).is_err());
563
+ /// assert!(v.try_get(-6_i32).is_err());
564
+ /// ```
565
+ #[inline]
566
+ pub fn try_get<AnyIndex: VectorIndex>(
567
+ &self,
568
+ index: AnyIndex,
569
+ ) -> Result<Scalar::DimScalar, TensorError>
570
+ where
571
+ Scalar: FloatConvertible,
572
+ {
573
+ let i = index
574
+ .resolve(self.dims)
575
+ .ok_or(TensorError::IndexOutOfBounds {
576
+ index: 0,
577
+ size: self.dims,
578
+ })?;
579
+ let dims_per_value = Scalar::dimensions_per_value();
580
+ let value_index = i / dims_per_value;
581
+ let sub_index = i % dims_per_value;
582
+ // SAFETY: value_index < self.values, guaranteed by dims/values invariant
583
+ let packed = unsafe { *self.data.as_ptr().add(value_index) };
584
+ Ok(packed.unpack().as_ref()[sub_index])
585
+ }
586
+
587
+ /// Try to set the logical dimension at `index`.
588
+ ///
589
+ /// Accepts the native `DimScalar` type. For sub-byte types, reads the current packed value,
590
+ /// updates the targeted sub-dimension, and writes back the modified packed value.
591
+ ///
592
+ /// # Examples
593
+ ///
594
+ /// ```ignore
595
+ /// use numkong::vector::Vector;
596
+ /// let mut v = Vector::<f32>::try_zeros(4).unwrap();
597
+ /// v.try_set(0_usize, 1.0).unwrap();
598
+ /// // Signed indices let you write to the tail without computing the length.
599
+ /// v.try_set(-1_i32, 4.0).unwrap();
600
+ /// assert_eq!(v.try_get(0_usize).unwrap(), 1.0);
601
+ /// assert_eq!(v.try_get(3_usize).unwrap(), 4.0);
602
+ /// ```
603
+ #[inline]
604
+ pub fn try_set<AnyIndex: VectorIndex>(
605
+ &mut self,
606
+ index: AnyIndex,
607
+ value: Scalar::DimScalar,
608
+ ) -> Result<(), TensorError>
609
+ where
610
+ Scalar: FloatConvertible,
611
+ {
612
+ let i = index
613
+ .resolve(self.dims)
614
+ .ok_or(TensorError::IndexOutOfBounds {
615
+ index: 0,
616
+ size: self.dims,
617
+ })?;
618
+ let dims_per_value = Scalar::dimensions_per_value();
619
+ let value_index = i / dims_per_value;
620
+ let sub_index = i % dims_per_value;
621
+ // SAFETY: value_index < self.values, guaranteed by dims/values invariant
622
+ let ptr = unsafe { self.data.as_ptr().add(value_index) };
623
+ let mut unpacked = unsafe { *ptr }.unpack();
624
+ unpacked.as_mut()[sub_index] = value;
625
+ unsafe { ptr.write(Scalar::pack(unpacked)) };
626
+ Ok(())
627
+ }
628
+
629
+ /// Get a slice of the underlying storage values (only for normal types).
630
+ #[inline]
631
+ pub fn as_slice(&self) -> &[Scalar] {
632
+ if Scalar::dimensions_per_value() == 1 {
633
+ unsafe { core::slice::from_raw_parts(self.data.as_ptr(), self.values) }
634
+ } else {
635
+ // For sub-byte types, return the packed values
636
+ unsafe { core::slice::from_raw_parts(self.data.as_ptr(), self.values) }
637
+ }
638
+ }
639
+
640
+ /// Get a mutable slice of the underlying storage values.
641
+ #[inline]
642
+ pub fn as_mut_slice(&mut self) -> &mut [Scalar] {
643
+ unsafe { core::slice::from_raw_parts_mut(self.data.as_ptr(), self.values) }
644
+ }
645
+
646
+ /// Returns an iterator over the logical dimension values, yielding [`DimRef`] proxies.
647
+ pub fn iter(&self) -> VectorViewIterator<'_, Scalar>
648
+ where
649
+ Scalar: FloatConvertible,
650
+ {
651
+ self.view().iter()
652
+ }
653
+
654
+ /// Returns a mutable iterator over the logical dimension values, yielding [`DimMut`] proxies.
655
+ pub fn iter_mut(&mut self) -> VectorSpanIterator<'_, Scalar>
656
+ where
657
+ Scalar: FloatConvertible,
658
+ {
659
+ VectorSpanIterator {
660
+ data: self.data.as_ptr(),
661
+ stride_bytes: core::mem::size_of::<Scalar>() as isize,
662
+ front: 0,
663
+ back: self.dims,
664
+ _marker: PhantomData,
665
+ }
666
+ }
667
+ }
668
+
669
+ impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
670
+ /// Convert this vector into a 1D tensor, transferring ownership without copying.
671
+ pub fn try_into_tensor<const MAX_RANK: usize>(
672
+ self,
673
+ ) -> Result<Tensor<Scalar, Alloc, MAX_RANK>, TensorError> {
674
+ if MAX_RANK == 0 {
675
+ return Err(TensorError::TooManyRanks { got: 1 });
676
+ }
677
+ let mut shape = [0usize; MAX_RANK];
678
+ shape[0] = self.dims;
679
+ let mut strides = [0isize; MAX_RANK];
680
+ strides[0] = core::mem::size_of::<Scalar>() as isize;
681
+ let alloc_bytes = self.values * core::mem::size_of::<Scalar>();
682
+ let data = self.data;
683
+ // SAFETY: we read the allocator out before forget, transferring ownership
684
+ let alloc = unsafe { core::ptr::read(&self.alloc) };
685
+ core::mem::forget(self);
686
+ // SAFETY: data/alloc_bytes match the original allocation
687
+ let tensor = unsafe { Tensor::from_raw_parts(data, alloc_bytes, shape, strides, 1, alloc) };
688
+ Ok(tensor)
689
+ }
690
+ }
691
+
692
+ impl<Scalar: StorageElement> Vector<Scalar, Global> {
693
+ /// Create a zero-initialized vector with the global allocator.
694
+ pub fn try_zeros(dims: usize) -> Result<Self, TensorError> {
695
+ Self::try_zeros_in(dims, Global)
696
+ }
697
+
698
+ /// Create a vector filled with `value`.
699
+ pub fn try_full(dims: usize, value: Scalar) -> Result<Self, TensorError> {
700
+ Self::try_full_in(dims, value, Global)
701
+ }
702
+
703
+ /// Create a vector filled with ones.
704
+ pub fn try_ones(dims: usize) -> Result<Self, TensorError>
705
+ where
706
+ Scalar: NumberLike,
707
+ {
708
+ Self::try_full(dims, Scalar::one())
709
+ }
710
+
711
+ /// Create an uninitialized vector.
712
+ ///
713
+ /// # Safety
714
+ /// The returned vector's contents are uninitialized. Reading from it before
715
+ /// writing is undefined behavior.
716
+ pub unsafe fn try_empty(dims: usize) -> Result<Self, TensorError> {
717
+ unsafe { Self::try_empty_in(dims, Global) }
718
+ }
719
+
720
+ /// Create a vector from scalar f32 values.
721
+ pub fn try_from_scalars(scalars: &[f32]) -> Result<Self, TensorError>
722
+ where
723
+ Scalar: FloatConvertible,
724
+ {
725
+ Self::try_from_scalars_in(scalars, Global)
726
+ }
727
+
728
+ /// Create a vector from per-dimension scalars.
729
+ pub fn try_from_dims(dims: &[Scalar::DimScalar]) -> Result<Self, TensorError>
730
+ where
731
+ Scalar: FloatConvertible,
732
+ {
733
+ Self::try_from_dims_in(dims, Global)
734
+ }
735
+ }
736
+
737
+ // Index for normal types (dimensions_per_value == 1)
738
+ impl<AnyIndex: VectorIndex, Scalar: StorageElement, Alloc: Allocator> core::ops::Index<AnyIndex>
739
+ for Vector<Scalar, Alloc>
740
+ {
741
+ type Output = Scalar;
742
+
743
+ #[inline]
744
+ fn index(&self, index: AnyIndex) -> &Scalar {
745
+ let i = index
746
+ .resolve(self.dims)
747
+ .expect("vector index out of bounds");
748
+ debug_assert_eq!(
749
+ Scalar::dimensions_per_value(),
750
+ 1,
751
+ "Index trait not supported for sub-byte types"
752
+ );
753
+ unsafe { &*self.data.as_ptr().add(i) }
754
+ }
755
+ }
756
+
757
+ impl<AnyIndex: VectorIndex, Scalar: StorageElement, Alloc: Allocator> core::ops::IndexMut<AnyIndex>
758
+ for Vector<Scalar, Alloc>
759
+ {
760
+ #[inline]
761
+ fn index_mut(&mut self, index: AnyIndex) -> &mut Scalar {
762
+ let i = index
763
+ .resolve(self.dims)
764
+ .expect("vector index out of bounds");
765
+ debug_assert_eq!(
766
+ Scalar::dimensions_per_value(),
767
+ 1,
768
+ "IndexMut trait not supported for sub-byte types"
769
+ );
770
+ unsafe { &mut *self.data.as_ptr().add(i) }
771
+ }
772
+ }
773
+
774
+ impl<Scalar: StorageElement + Clone, Alloc: Allocator + Clone> Vector<Scalar, Alloc> {
775
+ /// Try to clone this vector, returning an error on allocation failure.
776
+ pub fn try_clone(&self) -> Result<Self, TensorError> {
777
+ if self.values == 0 {
778
+ return Ok(Self {
779
+ data: NonNull::dangling(),
780
+ dims: 0,
781
+ values: 0,
782
+ alloc: self.alloc.clone(),
783
+ });
784
+ }
785
+ let size = self.values * core::mem::size_of::<Scalar>();
786
+ let layout = alloc::alloc::Layout::from_size_align(size, SIMD_ALIGNMENT)
787
+ .map_err(|_| TensorError::AllocationFailed)?;
788
+ let ptr = self
789
+ .alloc
790
+ .allocate(layout)
791
+ .ok_or(TensorError::AllocationFailed)?;
792
+ unsafe {
793
+ core::ptr::copy_nonoverlapping(self.data.as_ptr() as *const u8, ptr.as_ptr(), size);
794
+ }
795
+ Ok(Self {
796
+ data: unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut Scalar) },
797
+ dims: self.dims,
798
+ values: self.values,
799
+ alloc: self.alloc.clone(),
800
+ })
801
+ }
802
+ }
803
+
804
+ impl<Scalar: StorageElement + Clone, Alloc: Allocator + Clone> Clone for Vector<Scalar, Alloc> {
805
+ fn clone(&self) -> Self {
806
+ self.try_clone().expect("vector clone allocation failed")
807
+ }
808
+ }
809
+
810
+ impl<Scalar: StorageElement> Default for Vector<Scalar, Global> {
811
+ fn default() -> Self {
812
+ Self {
813
+ data: NonNull::dangling(),
814
+ dims: 0,
815
+ values: 0,
816
+ alloc: Global,
817
+ }
818
+ }
819
+ }
820
+
821
+ // endregion: Vector
822
+
823
+ // region: VectorView
824
+
825
+ /// Immutable, possibly strided, non-owning view into a vector.
826
+ ///
827
+ /// `VectorView` is a zero-copy borrow: it holds a raw pointer, a dimension
828
+ /// count, and a byte stride between consecutive logical elements. It never
829
+ /// frees its memory and is tied to the lifetime `'a` of whatever owns the
830
+ /// storage (typically a [`Vector`] obtained via [`Vector::view`], or a row /
831
+ /// column of a tensor).
832
+ ///
833
+ /// Because the stride is stored in bytes and may be negative, views can walk
834
+ /// memory in either direction, skip entries (see [`VectorView::try_strided`]),
835
+ /// or expose a reversed iteration order (see [`VectorView::rev`]).
836
+ ///
837
+ /// Use [`VectorView`] when only read access is needed. Its mutable counterpart
838
+ /// is [`VectorSpan`], which offers the same striding semantics plus element
839
+ /// writes. Both types support the [`VectorIndex`] trait, so `view[0_usize]`,
840
+ /// `view[-1_i32]`, and friends all resolve via the same Python-style rules.
841
+ pub struct VectorView<'a, Scalar: StorageElement> {
842
+ data: *const Scalar,
843
+ dims: usize,
844
+ stride_bytes: isize,
845
+ _marker: PhantomData<&'a Scalar>,
846
+ }
847
+
848
+ unsafe impl<'a, Scalar: StorageElement + Sync> Send for VectorView<'a, Scalar> {}
849
+ unsafe impl<'a, Scalar: StorageElement + Sync> Sync for VectorView<'a, Scalar> {}
850
+
851
+ impl<'a, Scalar: StorageElement> Clone for VectorView<'a, Scalar> {
852
+ fn clone(&self) -> Self {
853
+ *self
854
+ }
855
+ }
856
+ impl<'a, Scalar: StorageElement> Copy for VectorView<'a, Scalar> {}
857
+
858
+ impl<'a, Scalar: StorageElement> VectorView<'a, Scalar> {
859
+ /// Create a view from a raw pointer, dimension count, and byte stride.
860
+ ///
861
+ /// # Safety
862
+ /// - `data` must be valid for reads of `dims` elements at the given stride.
863
+ /// - The pointed-to memory must outlive `'a`.
864
+ /// - `stride_bytes` must be non-zero for non-empty views.
865
+ #[inline]
866
+ pub unsafe fn from_raw_parts(data: *const Scalar, dims: usize, stride_bytes: isize) -> Self {
867
+ Self {
868
+ data,
869
+ dims,
870
+ stride_bytes,
871
+ _marker: PhantomData,
872
+ }
873
+ }
874
+
875
+ /// Number of logical dimensions.
876
+ #[inline]
877
+ pub fn dims(&self) -> usize {
878
+ self.dims
879
+ }
880
+
881
+ /// Number of logical dimensions (alias for dims).
882
+ #[inline]
883
+ pub fn size(&self) -> usize {
884
+ self.dims
885
+ }
886
+
887
+ /// Returns true if empty.
888
+ #[inline]
889
+ pub fn is_empty(&self) -> bool {
890
+ self.dims == 0
891
+ }
892
+
893
+ /// Stride in bytes between consecutive elements.
894
+ #[inline]
895
+ pub fn stride_bytes(&self) -> isize {
896
+ self.stride_bytes
897
+ }
898
+
899
+ /// Returns true if elements are stored contiguously (stride == sizeof(Scalar)).
900
+ #[inline]
901
+ pub fn is_contiguous(&self) -> bool {
902
+ self.stride_bytes == core::mem::size_of::<Scalar>() as isize
903
+ }
904
+
905
+ /// Get the underlying pointer.
906
+ #[inline]
907
+ pub fn as_ptr(&self) -> *const Scalar {
908
+ self.data
909
+ }
910
+
911
+ /// Get a contiguous slice, if this view is contiguous.
912
+ #[inline]
913
+ pub fn as_contiguous_slice(&self) -> Option<&'a [Scalar]> {
914
+ if self.is_contiguous() && Scalar::dimensions_per_value() == 1 {
915
+ Some(unsafe { core::slice::from_raw_parts(self.data, self.dims) })
916
+ } else {
917
+ None
918
+ }
919
+ }
920
+
921
+ /// Try to get element at index (supports signed indexing).
922
+ ///
923
+ /// Returns the native `DimScalar` type. For sub-byte types, uses value_index
924
+ /// for stride-based pointer walks to avoid buffer overread.
925
+ #[inline]
926
+ pub fn try_get<AnyIndex: VectorIndex>(
927
+ &self,
928
+ index: AnyIndex,
929
+ ) -> Result<Scalar::DimScalar, TensorError>
930
+ where
931
+ Scalar: FloatConvertible,
932
+ {
933
+ let i = index
934
+ .resolve(self.dims)
935
+ .ok_or(TensorError::IndexOutOfBounds {
936
+ index: 0,
937
+ size: self.dims,
938
+ })?;
939
+ let dims_per_value = Scalar::dimensions_per_value();
940
+ let value_index = i / dims_per_value;
941
+ let sub_index = i % dims_per_value;
942
+ // SAFETY: stride * value_index stays within allocation
943
+ let ptr = unsafe {
944
+ (self.data as *const u8).offset(self.stride_bytes * value_index as isize)
945
+ as *const Scalar
946
+ };
947
+ Ok(unsafe { *ptr }.unpack().as_ref()[sub_index])
948
+ }
949
+
950
+ /// Create a reversed view by negating the stride and pointing to the last element.
951
+ ///
952
+ /// The returned view has the same number of dimensions but iterates in the
953
+ /// opposite direction. For an empty view, returns a copy unchanged.
954
+ pub fn rev(&self) -> Self {
955
+ if self.dims == 0 {
956
+ return *self;
957
+ }
958
+ let last_offset = self.stride_bytes * (self.dims as isize - 1);
959
+ Self {
960
+ data: unsafe { (self.data as *const u8).offset(last_offset) as *const Scalar },
961
+ dims: self.dims,
962
+ stride_bytes: -self.stride_bytes,
963
+ _marker: PhantomData,
964
+ }
965
+ }
966
+
967
+ /// Create a sub-view with start, end, and step (Python-style slicing).
968
+ ///
969
+ /// Supports negative steps for reverse iteration. `step` must be non-zero.
970
+ /// Returns an error if `start` or `end` exceed `dims()`, or if `step == 0`.
971
+ pub fn try_strided(&self, start: usize, end: usize, step: isize) -> Result<Self, TensorError> {
972
+ if start > self.dims || end > self.dims || step == 0 {
973
+ return Err(TensorError::IndexOutOfBounds {
974
+ index: start.max(end),
975
+ size: self.dims,
976
+ });
977
+ }
978
+ let count = if step > 0 {
979
+ if end > start {
980
+ (end - start + step as usize - 1) / step as usize
981
+ } else {
982
+ 0
983
+ }
984
+ } else if start > end {
985
+ let abs_step = (-step) as usize;
986
+ (start - end + abs_step - 1) / abs_step
987
+ } else {
988
+ 0
989
+ };
990
+ let new_data = unsafe {
991
+ (self.data as *const u8).offset(self.stride_bytes * start as isize) as *const Scalar
992
+ };
993
+ Ok(Self {
994
+ data: new_data,
995
+ dims: count,
996
+ stride_bytes: self.stride_bytes * step,
997
+ _marker: PhantomData,
998
+ })
999
+ }
1000
+
1001
+ /// Returns an iterator over logical dimension values, yielding [`DimRef`] proxies.
1002
+ pub fn iter(&self) -> VectorViewIterator<'a, Scalar>
1003
+ where
1004
+ Scalar: FloatConvertible,
1005
+ {
1006
+ VectorViewIterator {
1007
+ data: self.data,
1008
+ stride_bytes: self.stride_bytes,
1009
+ front: 0,
1010
+ back: self.dims,
1011
+ _marker: PhantomData,
1012
+ }
1013
+ }
1014
+ }
1015
+
1016
+ impl<'a, AnyIndex: VectorIndex, Scalar: StorageElement> core::ops::Index<AnyIndex>
1017
+ for VectorView<'a, Scalar>
1018
+ {
1019
+ type Output = Scalar;
1020
+
1021
+ #[inline]
1022
+ fn index(&self, index: AnyIndex) -> &Scalar {
1023
+ let i = index.resolve(self.dims).expect("view index out of bounds");
1024
+ debug_assert_eq!(
1025
+ Scalar::dimensions_per_value(),
1026
+ 1,
1027
+ "Index trait not supported for sub-byte types"
1028
+ );
1029
+ unsafe {
1030
+ &*((self.data as *const u8).offset(self.stride_bytes * i as isize) as *const Scalar)
1031
+ }
1032
+ }
1033
+ }
1034
+
1035
+ // endregion: VectorView
1036
+
1037
+ // region: VectorSpan
1038
+
1039
+ /// Mutable, possibly strided, non-owning view into a vector.
1040
+ ///
1041
+ /// `VectorSpan` is the read-write counterpart of [`VectorView`]: it still
1042
+ /// performs zero-copy borrowing via a raw pointer, dimension count, and byte
1043
+ /// stride, but additionally allows element writes through `try_set`, `fill`,
1044
+ /// `iter_mut`, and `IndexMut`. Spans are typically obtained via
1045
+ /// [`Vector::span`] or a mutable slice of a tensor row.
1046
+ ///
1047
+ /// Like views, spans support signed ([`VectorIndex`]) indexing, negative
1048
+ /// strides via [`VectorSpan::as_view`] + [`VectorView::rev`], and Python-style
1049
+ /// slicing through their view projection. The `'a` lifetime mirrors the owner
1050
+ /// of the underlying memory, and Rust's borrow checker prevents aliasing a
1051
+ /// single span with any other reference for the duration of `'a`.
1052
+ pub struct VectorSpan<'a, Scalar: StorageElement> {
1053
+ data: *mut Scalar,
1054
+ dims: usize,
1055
+ stride_bytes: isize,
1056
+ _marker: PhantomData<&'a mut Scalar>,
1057
+ }
1058
+
1059
+ unsafe impl<'a, Scalar: StorageElement + Send> Send for VectorSpan<'a, Scalar> {}
1060
+ unsafe impl<'a, Scalar: StorageElement + Sync> Sync for VectorSpan<'a, Scalar> {}
1061
+
1062
+ impl<'a, Scalar: StorageElement> VectorSpan<'a, Scalar> {
1063
+ /// Create a mutable view from a raw pointer, dimension count, and byte stride.
1064
+ ///
1065
+ /// # Safety
1066
+ /// - `data` must be valid for reads and writes of `dims` elements at the given stride.
1067
+ /// - The pointed-to memory must outlive `'a`.
1068
+ /// - `stride_bytes` must be non-zero for non-empty views.
1069
+ /// - No other references to the memory may exist for the duration of `'a`.
1070
+ #[inline]
1071
+ pub unsafe fn from_raw_parts(data: *mut Scalar, dims: usize, stride_bytes: isize) -> Self {
1072
+ Self {
1073
+ data,
1074
+ dims,
1075
+ stride_bytes,
1076
+ _marker: PhantomData,
1077
+ }
1078
+ }
1079
+
1080
+ /// Number of logical dimensions.
1081
+ #[inline]
1082
+ pub fn dims(&self) -> usize {
1083
+ self.dims
1084
+ }
1085
+
1086
+ /// Number of logical dimensions (alias for dims).
1087
+ #[inline]
1088
+ pub fn size(&self) -> usize {
1089
+ self.dims
1090
+ }
1091
+
1092
+ /// Returns true if empty.
1093
+ #[inline]
1094
+ pub fn is_empty(&self) -> bool {
1095
+ self.dims == 0
1096
+ }
1097
+
1098
+ /// Stride in bytes.
1099
+ #[inline]
1100
+ pub fn stride_bytes(&self) -> isize {
1101
+ self.stride_bytes
1102
+ }
1103
+
1104
+ /// Returns true if contiguous.
1105
+ #[inline]
1106
+ pub fn is_contiguous(&self) -> bool {
1107
+ self.stride_bytes == core::mem::size_of::<Scalar>() as isize
1108
+ }
1109
+
1110
+ /// Get the underlying pointer.
1111
+ #[inline]
1112
+ pub fn as_ptr(&self) -> *const Scalar {
1113
+ self.data
1114
+ }
1115
+
1116
+ /// Get the mutable underlying pointer.
1117
+ #[inline]
1118
+ pub fn as_mut_ptr(&mut self) -> *mut Scalar {
1119
+ self.data
1120
+ }
1121
+
1122
+ /// Reborrow as an immutable view, sharing the same data pointer and stride.
1123
+ pub fn as_view(&self) -> VectorView<'_, Scalar> {
1124
+ VectorView {
1125
+ data: self.data,
1126
+ dims: self.dims,
1127
+ stride_bytes: self.stride_bytes,
1128
+ _marker: PhantomData,
1129
+ }
1130
+ }
1131
+
1132
+ /// Try to get element at index.
1133
+ #[inline]
1134
+ pub fn try_get<AnyIndex: VectorIndex>(
1135
+ &self,
1136
+ index: AnyIndex,
1137
+ ) -> Result<Scalar::DimScalar, TensorError>
1138
+ where
1139
+ Scalar: FloatConvertible,
1140
+ {
1141
+ self.as_view().try_get(index)
1142
+ }
1143
+
1144
+ /// Try to set the element at `index`.
1145
+ ///
1146
+ /// Accepts the native `DimScalar` type. For sub-byte types, uses value_index
1147
+ /// for stride-based pointer walks to avoid buffer overwrite.
1148
+ #[inline]
1149
+ pub fn try_set<AnyIndex: VectorIndex>(
1150
+ &mut self,
1151
+ index: AnyIndex,
1152
+ value: Scalar::DimScalar,
1153
+ ) -> Result<(), TensorError>
1154
+ where
1155
+ Scalar: FloatConvertible,
1156
+ {
1157
+ let i = index
1158
+ .resolve(self.dims)
1159
+ .ok_or(TensorError::IndexOutOfBounds {
1160
+ index: 0,
1161
+ size: self.dims,
1162
+ })?;
1163
+ let dims_per_value = Scalar::dimensions_per_value();
1164
+ let value_index = i / dims_per_value;
1165
+ let sub_index = i % dims_per_value;
1166
+ // SAFETY: stride * value_index stays within allocation
1167
+ let ptr = unsafe {
1168
+ (self.data as *mut u8).offset(self.stride_bytes * value_index as isize) as *mut Scalar
1169
+ };
1170
+ let mut unpacked = unsafe { *ptr }.unpack();
1171
+ unpacked.as_mut()[sub_index] = value;
1172
+ unsafe { ptr.write(Scalar::pack(unpacked)) };
1173
+ Ok(())
1174
+ }
1175
+
1176
+ /// Fill all elements with a value, respecting the stride between elements.
1177
+ ///
1178
+ /// Iterates over storage values (not logical dimensions) to avoid buffer
1179
+ /// overwrite for sub-byte types.
1180
+ pub fn fill(&mut self, value: Scalar) {
1181
+ let values = self.dims.div_ceil(Scalar::dimensions_per_value());
1182
+ for i in 0..values {
1183
+ // SAFETY: i < values; stride * i stays within the allocation
1184
+ let ptr = unsafe {
1185
+ (self.data as *mut u8).offset(self.stride_bytes * i as isize) as *mut Scalar
1186
+ };
1187
+ unsafe { ptr.write(value) };
1188
+ }
1189
+ }
1190
+
1191
+ /// Get a contiguous slice, if this span is contiguous.
1192
+ #[inline]
1193
+ pub fn as_contiguous_slice(&self) -> Option<&[Scalar]> {
1194
+ if self.is_contiguous() && Scalar::dimensions_per_value() == 1 {
1195
+ Some(unsafe { core::slice::from_raw_parts(self.data, self.dims) })
1196
+ } else {
1197
+ None
1198
+ }
1199
+ }
1200
+
1201
+ /// Get a mutable contiguous slice, if this span is contiguous.
1202
+ #[inline]
1203
+ pub fn as_contiguous_slice_mut(&mut self) -> Option<&mut [Scalar]> {
1204
+ if self.is_contiguous() && Scalar::dimensions_per_value() == 1 {
1205
+ Some(unsafe { core::slice::from_raw_parts_mut(self.data, self.dims) })
1206
+ } else {
1207
+ None
1208
+ }
1209
+ }
1210
+
1211
+ /// Returns an iterator over logical dimension values, yielding [`DimRef`] proxies.
1212
+ pub fn iter(&self) -> VectorViewIterator<'_, Scalar>
1213
+ where
1214
+ Scalar: FloatConvertible,
1215
+ {
1216
+ VectorViewIterator {
1217
+ data: self.data,
1218
+ stride_bytes: self.stride_bytes,
1219
+ front: 0,
1220
+ back: self.dims,
1221
+ _marker: PhantomData,
1222
+ }
1223
+ }
1224
+
1225
+ /// Returns a mutable iterator over logical dimension values, yielding [`DimMut`] proxies.
1226
+ pub fn iter_mut(&mut self) -> VectorSpanIterator<'_, Scalar>
1227
+ where
1228
+ Scalar: FloatConvertible,
1229
+ {
1230
+ VectorSpanIterator {
1231
+ data: self.data,
1232
+ stride_bytes: self.stride_bytes,
1233
+ front: 0,
1234
+ back: self.dims,
1235
+ _marker: PhantomData,
1236
+ }
1237
+ }
1238
+ }
1239
+
1240
+ impl<'a, AnyIndex: VectorIndex, Scalar: StorageElement> core::ops::Index<AnyIndex>
1241
+ for VectorSpan<'a, Scalar>
1242
+ {
1243
+ type Output = Scalar;
1244
+
1245
+ #[inline]
1246
+ fn index(&self, index: AnyIndex) -> &Scalar {
1247
+ let i = index.resolve(self.dims).expect("span index out of bounds");
1248
+ debug_assert_eq!(
1249
+ Scalar::dimensions_per_value(),
1250
+ 1,
1251
+ "Index trait not supported for sub-byte types"
1252
+ );
1253
+ unsafe {
1254
+ &*((self.data as *const u8).offset(self.stride_bytes * i as isize) as *const Scalar)
1255
+ }
1256
+ }
1257
+ }
1258
+
1259
+ impl<'a, AnyIndex: VectorIndex, Scalar: StorageElement> core::ops::IndexMut<AnyIndex>
1260
+ for VectorSpan<'a, Scalar>
1261
+ {
1262
+ #[inline]
1263
+ fn index_mut(&mut self, index: AnyIndex) -> &mut Scalar {
1264
+ let i = index.resolve(self.dims).expect("span index out of bounds");
1265
+ debug_assert_eq!(
1266
+ Scalar::dimensions_per_value(),
1267
+ 1,
1268
+ "IndexMut trait not supported for sub-byte types"
1269
+ );
1270
+ unsafe {
1271
+ &mut *((self.data as *mut u8).offset(self.stride_bytes * i as isize) as *mut Scalar)
1272
+ }
1273
+ }
1274
+ }
1275
+
1276
+ // endregion: VectorSpan
1277
+
1278
+ // region: Iterators
1279
+
1280
+ /// Stride-aware immutable iterator over logical dimensions, yielding [`DimRef`] proxies.
1281
+ ///
1282
+ /// For normal types (`dimensions_per_value=1`), yields one proxy per storage value.
1283
+ /// For sub-byte types, unpacks each storage value and yields sub-dimensions individually.
1284
+ /// Implements `ExactSizeIterator`, `FusedIterator`, and `DoubleEndedIterator`.
1285
+ pub struct VectorViewIterator<'a, Scalar: FloatConvertible> {
1286
+ data: *const Scalar,
1287
+ stride_bytes: isize,
1288
+ front: usize,
1289
+ back: usize,
1290
+ _marker: PhantomData<&'a Scalar>,
1291
+ }
1292
+
1293
+ /// Backward-compatible alias for [`VectorViewIterator`].
1294
+ pub type VectorIterator<'a, Scalar> = VectorViewIterator<'a, Scalar>;
1295
+
1296
+ impl<'a, Scalar: FloatConvertible> Iterator for VectorViewIterator<'a, Scalar> {
1297
+ type Item = DimRef<'a, Scalar>;
1298
+
1299
+ #[inline]
1300
+ fn next(&mut self) -> Option<DimRef<'a, Scalar>> {
1301
+ if self.front >= self.back {
1302
+ return None;
1303
+ }
1304
+ let dims_per_value = Scalar::dimensions_per_value();
1305
+ let value_index = self.front / dims_per_value;
1306
+ let sub_index = self.front % dims_per_value;
1307
+ // SAFETY: value_index < values, stride * value_index within allocation
1308
+ let ptr = unsafe {
1309
+ (self.data as *const u8).offset(self.stride_bytes * value_index as isize)
1310
+ as *const Scalar
1311
+ };
1312
+ let scalar = unsafe { *ptr }.unpack().as_ref()[sub_index];
1313
+ self.front += 1;
1314
+ Some(DimRef::new(scalar))
1315
+ }
1316
+
1317
+ #[inline]
1318
+ fn size_hint(&self) -> (usize, Option<usize>) {
1319
+ let element_count = self.back - self.front;
1320
+ (element_count, Some(element_count))
1321
+ }
1322
+ }
1323
+
1324
+ impl<'a, Scalar: FloatConvertible> ExactSizeIterator for VectorViewIterator<'a, Scalar> {}
1325
+ impl<'a, Scalar: FloatConvertible> core::iter::FusedIterator for VectorViewIterator<'a, Scalar> {}
1326
+
1327
+ impl<'a, Scalar: FloatConvertible> DoubleEndedIterator for VectorViewIterator<'a, Scalar> {
1328
+ #[inline]
1329
+ fn next_back(&mut self) -> Option<DimRef<'a, Scalar>> {
1330
+ if self.front >= self.back {
1331
+ return None;
1332
+ }
1333
+ self.back -= 1;
1334
+ let dims_per_value = Scalar::dimensions_per_value();
1335
+ let value_index = self.back / dims_per_value;
1336
+ let sub_index = self.back % dims_per_value;
1337
+ let ptr = unsafe {
1338
+ (self.data as *const u8).offset(self.stride_bytes * value_index as isize)
1339
+ as *const Scalar
1340
+ };
1341
+ Some(DimRef::new(unsafe { *ptr }.unpack().as_ref()[sub_index]))
1342
+ }
1343
+ }
1344
+
1345
+ /// Stride-aware mutable iterator over logical dimensions, yielding [`DimMut`] proxies.
1346
+ ///
1347
+ /// For normal types (`dimensions_per_value=1`), yields one proxy per storage value.
1348
+ /// For sub-byte types, each proxy performs a read-modify-write on drop.
1349
+ /// Implements `ExactSizeIterator`, `FusedIterator`, and `DoubleEndedIterator`.
1350
+ pub struct VectorSpanIterator<'a, Scalar: FloatConvertible> {
1351
+ data: *mut Scalar,
1352
+ stride_bytes: isize,
1353
+ front: usize,
1354
+ back: usize,
1355
+ _marker: PhantomData<&'a mut Scalar>,
1356
+ }
1357
+
1358
+ impl<'a, Scalar: FloatConvertible> Iterator for VectorSpanIterator<'a, Scalar> {
1359
+ type Item = DimMut<'a, Scalar>;
1360
+
1361
+ #[inline]
1362
+ fn next(&mut self) -> Option<DimMut<'a, Scalar>> {
1363
+ if self.front >= self.back {
1364
+ return None;
1365
+ }
1366
+ let dims_per_value = Scalar::dimensions_per_value();
1367
+ let value_index = self.front / dims_per_value;
1368
+ let sub_index = self.front % dims_per_value;
1369
+ let ptr = unsafe {
1370
+ (self.data as *mut u8).offset(self.stride_bytes * value_index as isize) as *mut Scalar
1371
+ };
1372
+ let scalar = unsafe { *ptr }.unpack().as_ref()[sub_index];
1373
+ self.front += 1;
1374
+ Some(unsafe { DimMut::new(ptr, sub_index, scalar) })
1375
+ }
1376
+
1377
+ #[inline]
1378
+ fn size_hint(&self) -> (usize, Option<usize>) {
1379
+ let element_count = self.back - self.front;
1380
+ (element_count, Some(element_count))
1381
+ }
1382
+ }
1383
+
1384
+ impl<'a, Scalar: FloatConvertible> ExactSizeIterator for VectorSpanIterator<'a, Scalar> {}
1385
+ impl<'a, Scalar: FloatConvertible> core::iter::FusedIterator for VectorSpanIterator<'a, Scalar> {}
1386
+
1387
+ impl<'a, Scalar: FloatConvertible> DoubleEndedIterator for VectorSpanIterator<'a, Scalar> {
1388
+ #[inline]
1389
+ fn next_back(&mut self) -> Option<DimMut<'a, Scalar>> {
1390
+ if self.front >= self.back {
1391
+ return None;
1392
+ }
1393
+ self.back -= 1;
1394
+ let dims_per_value = Scalar::dimensions_per_value();
1395
+ let value_index = self.back / dims_per_value;
1396
+ let sub_index = self.back % dims_per_value;
1397
+ let ptr = unsafe {
1398
+ (self.data as *mut u8).offset(self.stride_bytes * value_index as isize) as *mut Scalar
1399
+ };
1400
+ let scalar = unsafe { *ptr }.unpack().as_ref()[sub_index];
1401
+ Some(unsafe { DimMut::new(ptr, sub_index, scalar) })
1402
+ }
1403
+ }
1404
+
1405
+ // endregion: Iterators
1406
+
1407
+ // region: IntoIterator (immutable)
1408
+
1409
+ impl<'a, Scalar: FloatConvertible, Alloc: Allocator> IntoIterator for &'a Vector<Scalar, Alloc> {
1410
+ type Item = DimRef<'a, Scalar>;
1411
+ type IntoIter = VectorViewIterator<'a, Scalar>;
1412
+ fn into_iter(self) -> Self::IntoIter {
1413
+ self.iter()
1414
+ }
1415
+ }
1416
+
1417
+ impl<'a, Scalar: FloatConvertible> IntoIterator for &'a VectorView<'a, Scalar> {
1418
+ type Item = DimRef<'a, Scalar>;
1419
+ type IntoIter = VectorViewIterator<'a, Scalar>;
1420
+ fn into_iter(self) -> Self::IntoIter {
1421
+ self.iter()
1422
+ }
1423
+ }
1424
+
1425
+ impl<'a, Scalar: FloatConvertible> IntoIterator for &'a VectorSpan<'a, Scalar> {
1426
+ type Item = DimRef<'a, Scalar>;
1427
+ type IntoIter = VectorViewIterator<'a, Scalar>;
1428
+ fn into_iter(self) -> Self::IntoIter {
1429
+ self.iter()
1430
+ }
1431
+ }
1432
+
1433
+ // endregion: IntoIterator (immutable)
1434
+
1435
+ // region: IntoIterator (mutable)
1436
+
1437
+ impl<'a, Scalar: FloatConvertible, Alloc: Allocator> IntoIterator
1438
+ for &'a mut Vector<Scalar, Alloc>
1439
+ {
1440
+ type Item = DimMut<'a, Scalar>;
1441
+ type IntoIter = VectorSpanIterator<'a, Scalar>;
1442
+ fn into_iter(self) -> Self::IntoIter {
1443
+ self.iter_mut()
1444
+ }
1445
+ }
1446
+
1447
+ impl<'a, Scalar: FloatConvertible> IntoIterator for &'a mut VectorSpan<'a, Scalar> {
1448
+ type Item = DimMut<'a, Scalar>;
1449
+ type IntoIter = VectorSpanIterator<'a, Scalar>;
1450
+ fn into_iter(self) -> Self::IntoIter {
1451
+ self.iter_mut()
1452
+ }
1453
+ }
1454
+
1455
+ // endregion: IntoIterator (mutable)
1456
+
1457
+ // region: AsRef
1458
+
1459
+ impl<Scalar: StorageElement, Alloc: Allocator> AsRef<[Scalar]> for Vector<Scalar, Alloc> {
1460
+ fn as_ref(&self) -> &[Scalar] {
1461
+ self.as_slice()
1462
+ }
1463
+ }
1464
+
1465
+ // endregion: AsRef
1466
+
1467
+ // region: PartialEq
1468
+
1469
+ impl<Scalar: FloatConvertible, Alloc: Allocator> PartialEq for Vector<Scalar, Alloc>
1470
+ where
1471
+ Scalar::DimScalar: PartialEq,
1472
+ {
1473
+ fn eq(&self, other: &Self) -> bool {
1474
+ self.dims == other.dims && self.iter().zip(other.iter()).all(|(a, b)| a == b)
1475
+ }
1476
+ }
1477
+
1478
+ impl<Scalar: FloatConvertible, Alloc: Allocator> PartialEq<[Scalar::DimScalar]>
1479
+ for Vector<Scalar, Alloc>
1480
+ where
1481
+ Scalar::DimScalar: PartialEq,
1482
+ {
1483
+ fn eq(&self, other: &[Scalar::DimScalar]) -> bool {
1484
+ self.dims == other.len() && self.iter().zip(other.iter()).all(|(a, b)| *a == *b)
1485
+ }
1486
+ }
1487
+
1488
+ impl<'a, Scalar: FloatConvertible> PartialEq for VectorView<'a, Scalar>
1489
+ where
1490
+ Scalar::DimScalar: PartialEq,
1491
+ {
1492
+ fn eq(&self, other: &Self) -> bool {
1493
+ self.dims == other.dims && self.iter().zip(other.iter()).all(|(a, b)| a == b)
1494
+ }
1495
+ }
1496
+
1497
+ impl<'a, Scalar: FloatConvertible> PartialEq for VectorSpan<'a, Scalar>
1498
+ where
1499
+ Scalar::DimScalar: PartialEq,
1500
+ {
1501
+ fn eq(&self, other: &Self) -> bool {
1502
+ self.dims == other.dims && self.iter().zip(other.iter()).all(|(a, b)| a == b)
1503
+ }
1504
+ }
1505
+
1506
+ // endregion: PartialEq
1507
+
1508
+ // region: Tolerance Equality
1509
+
1510
+ impl<Scalar: FloatConvertible, Alloc: Allocator> Vector<Scalar, Alloc>
1511
+ where
1512
+ Scalar::DimScalar: NumberLike,
1513
+ {
1514
+ /// Check if all elements are within tolerance of `other`.
1515
+ ///
1516
+ /// Uses the formula `|a - b| <= atol + rtol * |b|` per element.
1517
+ /// Returns `false` if dimensions differ.
1518
+ pub fn allclose<OtherAlloc: Allocator>(
1519
+ &self,
1520
+ other: &Vector<Scalar, OtherAlloc>,
1521
+ atol: f64,
1522
+ rtol: f64,
1523
+ ) -> bool {
1524
+ self.dims == other.dims
1525
+ && self
1526
+ .iter()
1527
+ .zip(other.iter())
1528
+ .all(|(a, b)| crate::types::is_close(a.to_f64(), b.to_f64(), atol, rtol))
1529
+ }
1530
+ }
1531
+
1532
+ impl<'a, Scalar: FloatConvertible> VectorView<'a, Scalar>
1533
+ where
1534
+ Scalar::DimScalar: NumberLike,
1535
+ {
1536
+ /// Check if all elements are within tolerance of `other`.
1537
+ ///
1538
+ /// Uses the formula `|a - b| <= atol + rtol * |b|` per element.
1539
+ /// Returns `false` if dimensions differ.
1540
+ pub fn allclose(&self, other: &Self, atol: f64, rtol: f64) -> bool {
1541
+ self.dims == other.dims
1542
+ && self
1543
+ .iter()
1544
+ .zip(other.iter())
1545
+ .all(|(a, b)| crate::types::is_close(a.to_f64(), b.to_f64(), atol, rtol))
1546
+ }
1547
+ }
1548
+
1549
+ impl<'a, Scalar: FloatConvertible> VectorSpan<'a, Scalar>
1550
+ where
1551
+ Scalar::DimScalar: NumberLike,
1552
+ {
1553
+ /// Check if all elements are within tolerance of `other`.
1554
+ ///
1555
+ /// Uses the formula `|a - b| <= atol + rtol * |b|` per element.
1556
+ /// Returns `false` if dimensions differ.
1557
+ pub fn allclose(&self, other: &Self, atol: f64, rtol: f64) -> bool {
1558
+ self.dims == other.dims
1559
+ && self
1560
+ .iter()
1561
+ .zip(other.iter())
1562
+ .all(|(a, b)| crate::types::is_close(a.to_f64(), b.to_f64(), atol, rtol))
1563
+ }
1564
+ }
1565
+
1566
+ // endregion: Tolerance Equality
1567
+
1568
+ // region: Debug and Display
1569
+
1570
+ /// Write a truncated, debug-formatted list from an iterator.
1571
+ fn fmt_debug_list<I: Iterator>(
1572
+ f: &mut core::fmt::Formatter<'_>,
1573
+ name: &str,
1574
+ dims: usize,
1575
+ iter: I,
1576
+ limit: usize,
1577
+ ) -> core::fmt::Result
1578
+ where
1579
+ I::Item: core::fmt::Debug,
1580
+ {
1581
+ write!(f, "{}(dims={}, [", name, dims)?;
1582
+ for (i, value) in iter.enumerate() {
1583
+ if i >= limit {
1584
+ write!(f, ", ...")?;
1585
+ break;
1586
+ }
1587
+ if i > 0 {
1588
+ write!(f, ", ")?;
1589
+ }
1590
+ write!(f, "{:?}", value)?;
1591
+ }
1592
+ write!(f, "])")
1593
+ }
1594
+
1595
+ /// Write a truncated, display-formatted list from an iterator.
1596
+ fn fmt_display_list<I: Iterator>(
1597
+ f: &mut core::fmt::Formatter<'_>,
1598
+ iter: I,
1599
+ limit: usize,
1600
+ ) -> core::fmt::Result
1601
+ where
1602
+ I::Item: core::fmt::Display,
1603
+ {
1604
+ let prec = f.precision();
1605
+ write!(f, "[")?;
1606
+ for (i, value) in iter.enumerate() {
1607
+ if i >= limit {
1608
+ write!(f, ", ...")?;
1609
+ break;
1610
+ }
1611
+ if i > 0 {
1612
+ write!(f, ", ")?;
1613
+ }
1614
+ if let Some(p) = prec {
1615
+ write!(f, "{:.p$}", value)?;
1616
+ } else {
1617
+ write!(f, "{}", value)?;
1618
+ }
1619
+ }
1620
+ write!(f, "]")
1621
+ }
1622
+
1623
+ impl<Scalar: FloatConvertible, Alloc: Allocator> core::fmt::Debug for Vector<Scalar, Alloc>
1624
+ where
1625
+ Scalar::DimScalar: core::fmt::Debug,
1626
+ {
1627
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1628
+ fmt_debug_list(f, "Vector", self.dims, self.iter(), 8)
1629
+ }
1630
+ }
1631
+
1632
+ impl<'a, Scalar: FloatConvertible> core::fmt::Debug for VectorView<'a, Scalar>
1633
+ where
1634
+ Scalar::DimScalar: core::fmt::Debug,
1635
+ {
1636
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1637
+ fmt_debug_list(f, "VectorView", self.dims, self.iter(), 8)
1638
+ }
1639
+ }
1640
+
1641
+ impl<'a, Scalar: FloatConvertible> core::fmt::Debug for VectorSpan<'a, Scalar>
1642
+ where
1643
+ Scalar::DimScalar: core::fmt::Debug,
1644
+ {
1645
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1646
+ fmt_debug_list(f, "VectorSpan", self.dims, self.iter(), 8)
1647
+ }
1648
+ }
1649
+
1650
+ impl<Scalar: FloatConvertible, Alloc: Allocator> core::fmt::Display for Vector<Scalar, Alloc>
1651
+ where
1652
+ Scalar::DimScalar: core::fmt::Display,
1653
+ {
1654
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1655
+ fmt_display_list(f, self.iter(), 20)
1656
+ }
1657
+ }
1658
+
1659
+ // endregion: Debug and Display
1660
+
1661
+ // region: Tests
1662
+
1663
+ #[cfg(test)]
1664
+ mod tests {
1665
+ use super::*;
1666
+ use crate::types::{bf16, f16, i4x2, u1x8, u4x2};
1667
+
1668
+ fn check_vector_roundtrip<Scalar: FloatConvertible>() {
1669
+ let dims_per_value = Scalar::dimensions_per_value();
1670
+ let test_dims = 16 * dims_per_value;
1671
+ let v = Vector::<Scalar>::try_zeros(test_dims).unwrap();
1672
+ assert_eq!(v.dims(), test_dims);
1673
+ assert_eq!(v.size_values(), test_dims / dims_per_value);
1674
+ let count = v.iter().count();
1675
+ assert_eq!(count, test_dims);
1676
+ }
1677
+
1678
+ fn check_vector_try_get_set<Scalar: FloatConvertible>()
1679
+ where
1680
+ Scalar::DimScalar: core::fmt::Debug,
1681
+ {
1682
+ let dims_per_value = Scalar::dimensions_per_value();
1683
+ let test_dims = 4 * dims_per_value;
1684
+ let mut v = Vector::<Scalar>::try_zeros(test_dims).unwrap();
1685
+ let one = Scalar::DimScalar::from_f32(1.0);
1686
+ v.try_set(0_usize, one).unwrap();
1687
+ v.try_set((test_dims - 1) as i32, one).unwrap();
1688
+ let first = v.try_get(0_usize).unwrap();
1689
+ let last = v.try_get(-1_i32).unwrap();
1690
+ assert!(
1691
+ first.to_f32() >= 0.5,
1692
+ "first dim should be ~1.0, got {:?}",
1693
+ first
1694
+ );
1695
+ assert!(
1696
+ last.to_f32() >= 0.5,
1697
+ "last dim should be ~1.0, got {:?}",
1698
+ last
1699
+ );
1700
+ }
1701
+
1702
+ #[test]
1703
+ fn vector_roundtrip_all_types() {
1704
+ check_vector_roundtrip::<f32>();
1705
+ check_vector_roundtrip::<f64>();
1706
+ check_vector_roundtrip::<f16>();
1707
+ check_vector_roundtrip::<bf16>();
1708
+ check_vector_roundtrip::<i4x2>();
1709
+ check_vector_roundtrip::<u4x2>();
1710
+ check_vector_roundtrip::<u1x8>();
1711
+ }
1712
+
1713
+ #[test]
1714
+ fn vector_try_get_set_all_types() {
1715
+ check_vector_try_get_set::<f32>();
1716
+ check_vector_try_get_set::<f64>();
1717
+ check_vector_try_get_set::<i4x2>();
1718
+ check_vector_try_get_set::<u4x2>();
1719
+ check_vector_try_get_set::<u1x8>();
1720
+ }
1721
+
1722
+ #[test]
1723
+ fn vec_index_signed() {
1724
+ let v = Vector::<f32>::try_from_dims(&[10.0, 20.0, 30.0, 40.0, 50.0]).unwrap();
1725
+ // Positive indexing
1726
+ assert_eq!(v[0], 10.0);
1727
+ assert_eq!(v[4], 50.0);
1728
+ // Negative indexing (i32 default for integer literals)
1729
+ assert_eq!(v[-1_i32], 50.0);
1730
+ assert_eq!(v[-2_i32], 40.0);
1731
+ assert_eq!(v[-5_i32], 10.0);
1732
+ }
1733
+
1734
+ #[test]
1735
+ fn vector_view_stride() {
1736
+ let v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0, 4.0, 5.0]).unwrap();
1737
+ let view = v.view();
1738
+ assert!(view.is_contiguous());
1739
+ assert_eq!(view.size(), 5);
1740
+
1741
+ // Reversed view
1742
+ let rev = view.rev();
1743
+ assert_eq!(rev.try_get(0_usize).unwrap(), 5.0);
1744
+ assert_eq!(rev.try_get(4_usize).unwrap(), 1.0);
1745
+ }
1746
+
1747
+ #[test]
1748
+ fn vector_span_fill() {
1749
+ let mut v = Vector::<f32>::try_zeros(4).unwrap();
1750
+ {
1751
+ let mut span = v.span();
1752
+ span.fill(42.0);
1753
+ }
1754
+ assert_eq!(v[0], 42.0);
1755
+ assert_eq!(v[3], 42.0);
1756
+ }
1757
+
1758
+ #[test]
1759
+ fn vector_iter() {
1760
+ let v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0]).unwrap();
1761
+ let values: Vec<f32> = v.iter().map(|x| *x).collect();
1762
+ assert_eq!(values, vec![1.0, 2.0, 3.0]);
1763
+
1764
+ // Double-ended
1765
+ let reversed_values: Vec<f32> = v.iter().rev().map(|x| *x).collect();
1766
+ assert_eq!(reversed_values, vec![3.0, 2.0, 1.0]);
1767
+ }
1768
+
1769
+ #[test]
1770
+ fn view_strided_iteration() {
1771
+ let v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0, 4.0, 5.0]).unwrap();
1772
+ let view = v.view();
1773
+
1774
+ // Every other element
1775
+ let strided = view.try_strided(0, 5, 2).unwrap();
1776
+ assert_eq!(strided.size(), 3);
1777
+ let values: Vec<f32> = strided.iter().map(|x| *x).collect();
1778
+ assert_eq!(values, vec![1.0, 3.0, 5.0]);
1779
+ }
1780
+
1781
+ #[test]
1782
+ fn vector_filled() {
1783
+ let v = Vector::<f32>::try_full(3, 7.5).unwrap();
1784
+ assert_eq!(v[0], 7.5);
1785
+ assert_eq!(v[1], 7.5);
1786
+ assert_eq!(v[2], 7.5);
1787
+ }
1788
+
1789
+ #[test]
1790
+ fn empty_vector() {
1791
+ let v = Vector::<f32>::try_zeros(0).unwrap();
1792
+ assert!(v.is_empty());
1793
+ assert_eq!(v.size(), 0);
1794
+ }
1795
+
1796
+ #[test]
1797
+ #[should_panic]
1798
+ fn index_out_of_bounds() {
1799
+ let v = Vector::<f32>::try_zeros(3).unwrap();
1800
+ let _ = v[3_usize];
1801
+ }
1802
+
1803
+ #[test]
1804
+ fn vector_allclose_matching() {
1805
+ let a = Vector::<f32>::try_full(4, 1.0).unwrap();
1806
+ let b = Vector::<f32>::try_full(4, 1.0 + 1e-7).unwrap();
1807
+ assert!(a.allclose(&b, 1e-6, 0.0));
1808
+ }
1809
+
1810
+ #[test]
1811
+ fn vector_allclose_mismatching() {
1812
+ let a = Vector::<f32>::try_full(4, 1.0).unwrap();
1813
+ let b = Vector::<f32>::try_full(4, 2.0).unwrap();
1814
+ assert!(!a.allclose(&b, 1e-6, 0.0));
1815
+ }
1816
+
1817
+ #[test]
1818
+ fn vector_allclose_different_dims() {
1819
+ let a = Vector::<f32>::try_full(3, 1.0).unwrap();
1820
+ let b = Vector::<f32>::try_full(4, 1.0).unwrap();
1821
+ assert!(!a.allclose(&b, 1e-6, 1e-6));
1822
+ }
1823
+
1824
+ #[test]
1825
+ fn display_precision_forwarding() {
1826
+ let v = Vector::<f32>::try_full(3, 1.0).unwrap();
1827
+ let s = format!("{:.2}", v);
1828
+ assert_eq!(s, "[1.00, 1.00, 1.00]");
1829
+ }
1830
+
1831
+ #[test]
1832
+ fn vector_span_iter_mut_f32() {
1833
+ let mut v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0]).unwrap();
1834
+ for mut value in &mut v {
1835
+ *value += 10.0;
1836
+ }
1837
+ let values: Vec<f32> = v.iter().map(|x| *x).collect();
1838
+ assert_eq!(values, vec![11.0, 12.0, 13.0]);
1839
+ }
1840
+
1841
+ #[test]
1842
+ fn vector_span_iter_mut_i4x2() {
1843
+ let mut v = Vector::<i4x2>::try_zeros(4).unwrap();
1844
+ {
1845
+ let mut span = v.span();
1846
+ for (i, mut value) in span.iter_mut().enumerate() {
1847
+ *value = (i + 1) as i8;
1848
+ }
1849
+ }
1850
+ assert_eq!(v.try_get(0_usize).unwrap(), 1);
1851
+ assert_eq!(v.try_get(1_usize).unwrap(), 2);
1852
+ assert_eq!(v.try_get(2_usize).unwrap(), 3);
1853
+ assert_eq!(v.try_get(3_usize).unwrap(), 4);
1854
+ }
1855
+
1856
+ #[test]
1857
+ fn vector_span_iter_mut_u1x8() {
1858
+ let mut v = Vector::<u1x8>::try_zeros(8).unwrap();
1859
+ for (i, mut value) in v.iter_mut().enumerate() {
1860
+ if i % 2 == 0 {
1861
+ *value = 1;
1862
+ }
1863
+ }
1864
+ // Even indices should be 1, odd should be 0
1865
+ assert_eq!(v.try_get(0_usize).unwrap(), 1);
1866
+ assert_eq!(v.try_get(1_usize).unwrap(), 0);
1867
+ assert_eq!(v.try_get(2_usize).unwrap(), 1);
1868
+ assert_eq!(v.try_get(3_usize).unwrap(), 0);
1869
+ }
1870
+
1871
+ #[test]
1872
+ fn vector_span_iter_double_ended() {
1873
+ let mut v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0]).unwrap();
1874
+ let mut span = v.span();
1875
+ let mut it = span.iter_mut();
1876
+ // Take from front
1877
+ let mut first = it.next().unwrap();
1878
+ *first = 10.0;
1879
+ drop(first);
1880
+ // Take from back
1881
+ let mut last = it.next_back().unwrap();
1882
+ *last = 30.0;
1883
+ drop(last);
1884
+ drop(it);
1885
+ drop(span);
1886
+ assert_eq!(v.try_get(0_usize).unwrap(), 10.0);
1887
+ assert_eq!(v.try_get(1_usize).unwrap(), 2.0);
1888
+ assert_eq!(v.try_get(2_usize).unwrap(), 30.0);
1889
+ }
1890
+
1891
+ #[test]
1892
+ fn vector_iterator_alias_compat() {
1893
+ // VectorIterator type alias should still work
1894
+ let v = Vector::<f32>::try_from_scalars(&[1.0]).unwrap();
1895
+ let _it: VectorIterator<'_, f32> = v.iter();
1896
+ }
1897
+ }
1898
+
1899
+ // endregion: Tests