siderust-js 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/.github/workflows/ci.yml +166 -0
  2. package/.gitmodules +9 -0
  3. package/CHANGELOG.md +26 -0
  4. package/LICENSE +661 -0
  5. package/README.md +138 -0
  6. package/package.json +12 -0
  7. package/qtty-js/.github/workflows/ci.yml +151 -0
  8. package/qtty-js/.gitmodules +3 -0
  9. package/qtty-js/CHANGELOG.md +31 -0
  10. package/qtty-js/LICENSE +661 -0
  11. package/qtty-js/README.md +132 -0
  12. package/qtty-js/package.json +20 -0
  13. package/qtty-js/qtty/.github/workflows/ci.yml +155 -0
  14. package/qtty-js/qtty/CHANGELOG.md +120 -0
  15. package/qtty-js/qtty/Cargo.lock +1462 -0
  16. package/qtty-js/qtty/Cargo.toml +12 -0
  17. package/qtty-js/qtty/LICENSE +661 -0
  18. package/qtty-js/qtty/README.md +9 -0
  19. package/qtty-js/qtty/qtty/Cargo.toml +41 -0
  20. package/qtty-js/qtty/qtty/README.md +8 -0
  21. package/qtty-js/qtty/qtty/examples/angles.rs +14 -0
  22. package/qtty-js/qtty/qtty/examples/astronomy.rs +17 -0
  23. package/qtty-js/qtty/qtty/examples/dimensional_arithmetic.rs +83 -0
  24. package/qtty-js/qtty/qtty/examples/python_integration.rs +61 -0
  25. package/qtty-js/qtty/qtty/examples/quickstart.rs +15 -0
  26. package/qtty-js/qtty/qtty/examples/ratios.rs +12 -0
  27. package/qtty-js/qtty/qtty/examples/serde_with_unit.rs +234 -0
  28. package/qtty-js/qtty/qtty/examples/serialization.rs +141 -0
  29. package/qtty-js/qtty/qtty/examples/serialization_advanced.rs +155 -0
  30. package/qtty-js/qtty/qtty/src/f32.rs +108 -0
  31. package/qtty-js/qtty/qtty/src/f64.rs +30 -0
  32. package/qtty-js/qtty/qtty/src/i128.rs +111 -0
  33. package/qtty-js/qtty/qtty/src/i16.rs +111 -0
  34. package/qtty-js/qtty/qtty/src/i32.rs +111 -0
  35. package/qtty-js/qtty/qtty/src/i64.rs +111 -0
  36. package/qtty-js/qtty/qtty/src/i8.rs +111 -0
  37. package/qtty-js/qtty/qtty/src/lib.rs +238 -0
  38. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std/Cargo.lock +83 -0
  39. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std/Cargo.toml +10 -0
  40. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std/src/lib.rs +7 -0
  41. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std-alloc/Cargo.lock +83 -0
  42. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std-alloc/Cargo.toml +10 -0
  43. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std-alloc/src/lib.rs +7 -0
  44. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-std/Cargo.lock +83 -0
  45. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-std/Cargo.toml +10 -0
  46. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-std/src/lib.rs +5 -0
  47. package/qtty-js/qtty/qtty/tests/integration_tests.rs +529 -0
  48. package/qtty-js/qtty/qtty/tests/qtty_vec_feature_matrix.rs +58 -0
  49. package/qtty-js/qtty/qtty-core/Cargo.toml +41 -0
  50. package/qtty-js/qtty/qtty-core/README.md +8 -0
  51. package/qtty-js/qtty/qtty-core/examples/diesel_integration.rs +145 -0
  52. package/qtty-js/qtty/qtty-core/examples/quantity_db_serde.rs +215 -0
  53. package/qtty-js/qtty/qtty-core/src/dimension.rs +249 -0
  54. package/qtty-js/qtty/qtty-core/src/feature_diesel.rs +318 -0
  55. package/qtty-js/qtty/qtty-core/src/feature_pyo3.rs +27 -0
  56. package/qtty-js/qtty/qtty-core/src/feature_serde.rs +203 -0
  57. package/qtty-js/qtty/qtty-core/src/feature_tiberius.rs +28 -0
  58. package/qtty-js/qtty/qtty-core/src/lib.rs +744 -0
  59. package/qtty-js/qtty/qtty-core/src/macros.rs +93 -0
  60. package/qtty-js/qtty/qtty-core/src/quantity.rs +810 -0
  61. package/qtty-js/qtty/qtty-core/src/scalar.rs +1742 -0
  62. package/qtty-js/qtty/qtty-core/src/unit.rs +332 -0
  63. package/qtty-js/qtty/qtty-core/src/units/angular.rs +1228 -0
  64. package/qtty-js/qtty/qtty-core/src/units/area.rs +243 -0
  65. package/qtty-js/qtty/qtty-core/src/units/frequency.rs +179 -0
  66. package/qtty-js/qtty/qtty-core/src/units/length.rs +1270 -0
  67. package/qtty-js/qtty/qtty-core/src/units/mass.rs +488 -0
  68. package/qtty-js/qtty/qtty-core/src/units/mod.rs +26 -0
  69. package/qtty-js/qtty/qtty-core/src/units/power.rs +324 -0
  70. package/qtty-js/qtty/qtty-core/src/units/time.rs +667 -0
  71. package/qtty-js/qtty/qtty-core/src/units/unitless.rs +212 -0
  72. package/qtty-js/qtty/qtty-core/src/units/velocity.rs +210 -0
  73. package/qtty-js/qtty/qtty-core/src/units/volume.rs +269 -0
  74. package/qtty-js/qtty/qtty-core/tests/core.rs +628 -0
  75. package/qtty-js/qtty/qtty-core/tests/diesel.rs +461 -0
  76. package/qtty-js/qtty/qtty-core/tests/integers.rs +632 -0
  77. package/qtty-js/qtty/qtty-core/tests/no_cross_unit_ops.rs +35 -0
  78. package/qtty-js/qtty/qtty-core/tests/pyo3.rs +334 -0
  79. package/qtty-js/qtty/qtty-core/tests/quantity_f32.rs +276 -0
  80. package/qtty-js/qtty/qtty-core/tests/scalar_decimal.rs +258 -0
  81. package/qtty-js/qtty/qtty-core/tests/scalar_f32.rs +286 -0
  82. package/qtty-js/qtty/qtty-core/tests/scalar_f64_real.rs +287 -0
  83. package/qtty-js/qtty/qtty-core/tests/scalar_rational.rs +260 -0
  84. package/qtty-js/qtty/qtty-core/tests/serde.rs +256 -0
  85. package/qtty-js/qtty/qtty-core/tests/tiberius.rs +208 -0
  86. package/qtty-js/qtty/qtty-derive/Cargo.toml +23 -0
  87. package/qtty-js/qtty/qtty-derive/README.md +8 -0
  88. package/qtty-js/qtty/qtty-derive/src/lib.rs +340 -0
  89. package/qtty-js/qtty/qtty-ffi/ARCHITECTURE.md +3 -0
  90. package/qtty-js/qtty/qtty-ffi/Cargo.toml +31 -0
  91. package/qtty-js/qtty/qtty-ffi/README.md +9 -0
  92. package/qtty-js/qtty/qtty-ffi/build.rs +326 -0
  93. package/qtty-js/qtty/qtty-ffi/cbindgen.toml +105 -0
  94. package/qtty-js/qtty/qtty-ffi/include/qtty_ffi.h +1126 -0
  95. package/qtty-js/qtty/qtty-ffi/src/ffi.rs +1251 -0
  96. package/qtty-js/qtty/qtty-ffi/src/ffi_serde.rs +294 -0
  97. package/qtty-js/qtty/qtty-ffi/src/helpers.rs +310 -0
  98. package/qtty-js/qtty/qtty-ffi/src/lib.rs +229 -0
  99. package/qtty-js/qtty/qtty-ffi/src/macros.rs +121 -0
  100. package/qtty-js/qtty/qtty-ffi/src/registry.rs +274 -0
  101. package/qtty-js/qtty/qtty-ffi/src/types.rs +620 -0
  102. package/qtty-js/qtty/qtty-ffi/tests/integration_tests.rs +842 -0
  103. package/qtty-js/qtty/qtty-ffi/units.csv +156 -0
  104. package/qtty-js/qtty/qtty-ffi/units.csv.md +3 -0
  105. package/qtty-js/qtty-node/.prettierignore +6 -0
  106. package/qtty-js/qtty-node/.prettierrc.json +6 -0
  107. package/qtty-js/qtty-node/README.md +250 -0
  108. package/qtty-js/qtty-node/c8.config.json +11 -0
  109. package/qtty-js/qtty-node/eslint.config.js +31 -0
  110. package/qtty-js/qtty-node/examples/arithmetic.mjs +64 -0
  111. package/qtty-js/qtty-node/examples/astronomy.mjs +90 -0
  112. package/qtty-js/qtty-node/examples/quickstart.mjs +36 -0
  113. package/qtty-js/qtty-node/examples/serialization.mjs +125 -0
  114. package/qtty-js/qtty-node/examples/unit_factories.mjs +74 -0
  115. package/qtty-js/qtty-node/index.d.ts +219 -0
  116. package/qtty-js/qtty-node/index.js +323 -0
  117. package/qtty-js/qtty-node/lib/DerivedQuantity.js +122 -0
  118. package/qtty-js/qtty-node/lib/Quantity.js +151 -0
  119. package/qtty-js/qtty-node/lib/backend.js +25 -0
  120. package/qtty-js/qtty-node/native.cjs +306 -0
  121. package/qtty-js/qtty-node/package-lock.json +3223 -0
  122. package/qtty-js/qtty-node/package.json +70 -0
  123. package/qtty-js/qtty-node/units.d.ts +299 -0
  124. package/qtty-js/qtty-node/units.js +210 -0
  125. package/qtty-js/qtty-web/Cargo.lock +767 -0
  126. package/qtty-js/qtty-web/Cargo.toml +21 -0
  127. package/qtty-js/qtty-web/index.d.ts +140 -0
  128. package/qtty-js/qtty-web/index.js +20 -0
  129. package/qtty-js/qtty-web/lib/DerivedQuantity.js +58 -0
  130. package/qtty-js/qtty-web/lib/Quantity.js +75 -0
  131. package/qtty-js/qtty-web/lib/backend.js +80 -0
  132. package/qtty-js/qtty-web/package.json +45 -0
  133. package/qtty-js/qtty-web/src/lib.rs +111 -0
  134. package/qtty-js/scripts/ci.sh +73 -0
  135. package/scripts/ci.sh +123 -0
  136. package/siderust-core/Cargo.lock +787 -0
  137. package/siderust-core/Cargo.toml +18 -0
  138. package/siderust-core/DEDUPLICATION.md +124 -0
  139. package/siderust-core/src/body.rs +120 -0
  140. package/siderust-core/src/events.rs +184 -0
  141. package/siderust-core/src/lib.rs +20 -0
  142. package/siderust-core/src/observer.rs +55 -0
  143. package/siderust-core/src/position.rs +213 -0
  144. package/siderust-node/.prettierignore +7 -0
  145. package/siderust-node/.prettierrc.json +6 -0
  146. package/siderust-node/Cargo.lock +906 -0
  147. package/siderust-node/Cargo.toml +29 -0
  148. package/siderust-node/README.md +109 -0
  149. package/siderust-node/__test__/index.test.mjs +248 -0
  150. package/siderust-node/build.rs +5 -0
  151. package/siderust-node/c8.config.json +3 -0
  152. package/siderust-node/eslint.config.js +31 -0
  153. package/siderust-node/examples/01_basic_coordinates.mjs +24 -0
  154. package/siderust-node/examples/02_coordinate_transformations.mjs +25 -0
  155. package/siderust-node/examples/03_all_frames_conversions.mjs +26 -0
  156. package/siderust-node/examples/04_all_center_conversions.mjs +24 -0
  157. package/siderust-node/examples/05_target_tracking.mjs +22 -0
  158. package/siderust-node/examples/06_night_events.mjs +18 -0
  159. package/siderust-node/examples/07_moon_properties.mjs +21 -0
  160. package/siderust-node/examples/08_solar_system.mjs +19 -0
  161. package/siderust-node/examples/09_star_observability.mjs +22 -0
  162. package/siderust-node/examples/10_time_periods.mjs +9 -0
  163. package/siderust-node/examples/11_serialization.mjs +31 -0
  164. package/siderust-node/examples/12_runtime_ephemeris.mjs +27 -0
  165. package/siderust-node/examples/13_coordinate_operations.mjs +20 -0
  166. package/siderust-node/index.d.ts +623 -0
  167. package/siderust-node/index.js +79 -0
  168. package/siderust-node/lib/Observer.js +112 -0
  169. package/siderust-node/lib/Star.js +118 -0
  170. package/siderust-node/lib/backend.js +63 -0
  171. package/siderust-node/lib/wrappers.js +566 -0
  172. package/siderust-node/main.js +20 -0
  173. package/siderust-node/native.cjs +360 -0
  174. package/siderust-node/package-lock.json +3261 -0
  175. package/siderust-node/package.json +71 -0
  176. package/siderust-node/src/body.rs +74 -0
  177. package/siderust-node/src/coordinates.rs +372 -0
  178. package/siderust-node/src/ephemeris.rs +462 -0
  179. package/siderust-node/src/events.rs +577 -0
  180. package/siderust-node/src/lib.rs +43 -0
  181. package/siderust-node/src/observer.rs +132 -0
  182. package/siderust-node/src/phase.rs +218 -0
  183. package/siderust-node/src/position.rs +292 -0
  184. package/siderust-node/src/star.rs +200 -0
  185. package/siderust-web/Cargo.lock +855 -0
  186. package/siderust-web/Cargo.toml +34 -0
  187. package/siderust-web/README.md +100 -0
  188. package/siderust-web/__test__/index.test.mjs +118 -0
  189. package/siderust-web/examples/github-pages/README.md +31 -0
  190. package/siderust-web/examples/github-pages/index.html +135 -0
  191. package/siderust-web/index.d.ts +311 -0
  192. package/siderust-web/index.js +66 -0
  193. package/siderust-web/lib/Observer.js +103 -0
  194. package/siderust-web/lib/Star.js +116 -0
  195. package/siderust-web/lib/backend.js +400 -0
  196. package/siderust-web/lib/wrappers.js +512 -0
  197. package/siderust-web/package.json +55 -0
  198. package/siderust-web/src/body.rs +69 -0
  199. package/siderust-web/src/coordinates.rs +302 -0
  200. package/siderust-web/src/ephemeris.rs +456 -0
  201. package/siderust-web/src/events.rs +520 -0
  202. package/siderust-web/src/lib.rs +51 -0
  203. package/siderust-web/src/observer.rs +117 -0
  204. package/siderust-web/src/phase.rs +190 -0
  205. package/siderust-web/src/position.rs +291 -0
  206. package/siderust-web/src/star.rs +178 -0
  207. package/tempoch-js/.github/workflows/ci.yml +142 -0
  208. package/tempoch-js/.gitmodules +3 -0
  209. package/tempoch-js/CHANGELOG.md +25 -0
  210. package/tempoch-js/LICENSE +661 -0
  211. package/tempoch-js/README.md +126 -0
  212. package/tempoch-js/package.json +20 -0
  213. package/tempoch-js/scripts/ci.sh +73 -0
  214. package/tempoch-js/tempoch/.github/workflows/ci.yml +113 -0
  215. package/tempoch-js/tempoch/CHANGELOG.md +82 -0
  216. package/tempoch-js/tempoch/Cargo.lock +947 -0
  217. package/tempoch-js/tempoch/Cargo.toml +3 -0
  218. package/tempoch-js/tempoch/LICENSE +661 -0
  219. package/tempoch-js/tempoch/README.md +76 -0
  220. package/tempoch-js/tempoch/tempoch/Cargo.toml +27 -0
  221. package/tempoch-js/tempoch/tempoch/examples/periods.rs +45 -0
  222. package/tempoch-js/tempoch/tempoch/examples/quickstart.rs +13 -0
  223. package/tempoch-js/tempoch/tempoch/src/lib.rs +49 -0
  224. package/tempoch-js/tempoch/tempoch/tests/integration.rs +57 -0
  225. package/tempoch-js/tempoch/tempoch-core/Cargo.toml +24 -0
  226. package/tempoch-js/tempoch/tempoch-core/src/delta_t.rs +345 -0
  227. package/tempoch-js/tempoch/tempoch-core/src/instant.rs +811 -0
  228. package/tempoch-js/tempoch/tempoch-core/src/julian_date_ext.rs +142 -0
  229. package/tempoch-js/tempoch/tempoch-core/src/lib.rs +81 -0
  230. package/tempoch-js/tempoch/tempoch-core/src/period.rs +1168 -0
  231. package/tempoch-js/tempoch/tempoch-core/src/scales.rs +779 -0
  232. package/tempoch-js/tempoch/tempoch-ffi/Cargo.lock +889 -0
  233. package/tempoch-js/tempoch/tempoch-ffi/Cargo.toml +26 -0
  234. package/tempoch-js/tempoch/tempoch-ffi/build.rs +24 -0
  235. package/tempoch-js/tempoch/tempoch-ffi/cbindgen.toml +30 -0
  236. package/tempoch-js/tempoch/tempoch-ffi/src/error.rs +19 -0
  237. package/tempoch-js/tempoch/tempoch-ffi/src/lib.rs +82 -0
  238. package/tempoch-js/tempoch/tempoch-ffi/src/period.rs +101 -0
  239. package/tempoch-js/tempoch/tempoch-ffi/src/time.rs +711 -0
  240. package/tempoch-js/tempoch/tempoch-ffi/tests/ffi.rs +265 -0
  241. package/tempoch-js/tempoch-node/.prettierignore +6 -0
  242. package/tempoch-js/tempoch-node/.prettierrc.json +6 -0
  243. package/tempoch-js/tempoch-node/Cargo.lock +496 -0
  244. package/tempoch-js/tempoch-node/Cargo.toml +29 -0
  245. package/tempoch-js/tempoch-node/README.md +265 -0
  246. package/tempoch-js/tempoch-node/__test__/index.test.mjs +598 -0
  247. package/tempoch-js/tempoch-node/build.rs +5 -0
  248. package/tempoch-js/tempoch-node/c8.config.json +3 -0
  249. package/tempoch-js/tempoch-node/eslint.config.js +31 -0
  250. package/tempoch-js/tempoch-node/examples/periods.mjs +79 -0
  251. package/tempoch-js/tempoch-node/examples/quickstart.mjs +71 -0
  252. package/tempoch-js/tempoch-node/examples/timescales.mjs +92 -0
  253. package/tempoch-js/tempoch-node/index.d.ts +280 -0
  254. package/tempoch-js/tempoch-node/index.js +32 -0
  255. package/tempoch-js/tempoch-node/lib/JulianDate.js +176 -0
  256. package/tempoch-js/tempoch-node/lib/ModifiedJulianDate.js +156 -0
  257. package/tempoch-js/tempoch-node/lib/Period.js +133 -0
  258. package/tempoch-js/tempoch-node/lib/backend.js +38 -0
  259. package/tempoch-js/tempoch-node/lib/qttyCompat.js +92 -0
  260. package/tempoch-js/tempoch-node/native.cjs +317 -0
  261. package/tempoch-js/tempoch-node/package-lock.json +3223 -0
  262. package/tempoch-js/tempoch-node/package.json +56 -0
  263. package/tempoch-js/tempoch-node/src/lib.rs +573 -0
  264. package/tempoch-js/tempoch-web/Cargo.toml +23 -0
  265. package/tempoch-js/tempoch-web/index.d.ts +95 -0
  266. package/tempoch-js/tempoch-web/index.js +27 -0
  267. package/tempoch-js/tempoch-web/lib/JulianDate.js +170 -0
  268. package/tempoch-js/tempoch-web/lib/ModifiedJulianDate.js +145 -0
  269. package/tempoch-js/tempoch-web/lib/Period.js +121 -0
  270. package/tempoch-js/tempoch-web/lib/backend.js +118 -0
  271. package/tempoch-js/tempoch-web/package.json +46 -0
  272. package/tempoch-js/tempoch-web/src/lib.rs +184 -0
@@ -0,0 +1,1742 @@
1
+ //! Scalar traits for quantity values.
2
+ //!
3
+ //! This module defines the trait hierarchy for numeric types that can be used as the
4
+ //! underlying storage for [`Quantity`](crate::Quantity). The traits are sealed to prevent
5
+ //! external implementations, ensuring consistent behavior across the crate.
6
+ //!
7
+ //! # Trait Hierarchy
8
+ //!
9
+ //! ```text
10
+ //! Scalar (basic arithmetic, copy, partial ordering)
11
+ //! │
12
+ //! ├── Real (floating-point-like: from_f64, to_f64, constants like PI)
13
+ //! │ │
14
+ //! │ └── Transcendental (sin, cos, sqrt, etc. - requires std or libm)
15
+ //! │
16
+ //! └── Exact (rational/decimal types - no floating-only ops)
17
+ //! ```
18
+ //!
19
+ //! # Supported Scalar Types
20
+ //!
21
+ //! **Always available:**
22
+ //! - `f64` (default) - implements `Scalar`, `Real`, `Transcendental`
23
+ //! - `f32` - implements `Scalar`, `Real`, `Transcendental`
24
+ //!
25
+ //! **Feature-gated:**
26
+ //! - `rust_decimal::Decimal` (`scalar-decimal`) - implements `Scalar`, `Real`
27
+ //! - `num_rational::Rational64` (`scalar-rational`) - implements `Scalar`, `Exact`
28
+ //! - `num_rational::Rational32` (`scalar-rational`) - implements `Scalar`, `Exact`
29
+ //!
30
+ //! Note: `BigRational` is NOT supported because `BigInt` does not implement `Copy`,
31
+ //! which is required by the `Scalar` trait for performance and ergonomics.
32
+ //!
33
+ //! # Example
34
+ //!
35
+ //! ```rust
36
+ //! use qtty_core::scalar::{Scalar, Real};
37
+ //!
38
+ //! fn print_half<S: Real>(value: S) {
39
+ //! let half = S::from_f64(0.5);
40
+ //! println!("Half of {:?} is {:?}", value.to_f64(), (value * half).to_f64());
41
+ //! }
42
+ //! ```
43
+
44
+ use core::fmt::{Debug, Display};
45
+ use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
46
+
47
+ // ─────────────────────────────────────────────────────────────────────────────
48
+ // Sealed trait pattern
49
+ // ─────────────────────────────────────────────────────────────────────────────
50
+
51
+ mod private {
52
+ pub trait Sealed {}
53
+
54
+ impl Sealed for f64 {}
55
+ impl Sealed for f32 {}
56
+ impl Sealed for i8 {}
57
+ impl Sealed for i16 {}
58
+ impl Sealed for i32 {}
59
+ impl Sealed for i64 {}
60
+ impl Sealed for i128 {}
61
+
62
+ #[cfg(feature = "scalar-decimal")]
63
+ impl Sealed for rust_decimal::Decimal {}
64
+
65
+ #[cfg(feature = "scalar-rational")]
66
+ impl Sealed for num_rational::Rational64 {}
67
+
68
+ #[cfg(feature = "scalar-rational")]
69
+ impl Sealed for num_rational::Rational32 {}
70
+ }
71
+
72
+ // ─────────────────────────────────────────────────────────────────────────────
73
+ // Core Scalar trait
74
+ // ─────────────────────────────────────────────────────────────────────────────
75
+
76
+ /// The base trait for all scalar types usable in [`Quantity`](crate::Quantity).
77
+ ///
78
+ /// This trait provides the minimal requirements for a numeric type to be used
79
+ /// as the underlying storage for quantities: basic arithmetic operations,
80
+ /// copy semantics, and partial ordering.
81
+ ///
82
+ /// This trait is sealed and cannot be implemented outside this crate.
83
+ pub trait Scalar:
84
+ private::Sealed
85
+ + Copy
86
+ + Clone
87
+ + Debug
88
+ + PartialEq
89
+ + PartialOrd
90
+ + Add<Output = Self>
91
+ + Sub<Output = Self>
92
+ + Mul<Output = Self>
93
+ + Div<Output = Self>
94
+ + AddAssign
95
+ + SubAssign
96
+ + MulAssign
97
+ + DivAssign
98
+ + Neg<Output = Self>
99
+ + Sized
100
+ + 'static
101
+ {
102
+ /// The zero value for this scalar type.
103
+ const ZERO: Self;
104
+
105
+ /// The one value for this scalar type.
106
+ const ONE: Self;
107
+
108
+ /// Returns the absolute value.
109
+ fn abs(self) -> Self;
110
+
111
+ /// Returns the minimum of two values.
112
+ fn min(self, other: Self) -> Self;
113
+
114
+ /// Returns the maximum of two values.
115
+ fn max(self, other: Self) -> Self;
116
+
117
+ /// Returns the remainder after division.
118
+ fn rem_euclid(self, rhs: Self) -> Self;
119
+ }
120
+
121
+ // ─────────────────────────────────────────────────────────────────────────────
122
+ // Real trait (floating-point-like operations)
123
+ // ─────────────────────────────────────────────────────────────────────────────
124
+
125
+ /// Trait for scalar types that support real-number operations.
126
+ ///
127
+ /// This extends [`Scalar`] with the ability to convert to/from `f64` and access
128
+ /// common mathematical constants. Types implementing this trait can be used in
129
+ /// operations that require floating-point-like behavior.
130
+ ///
131
+ /// # Conversion Guarantees
132
+ ///
133
+ /// - `from_f64` should produce a reasonable approximation when exact representation
134
+ /// is not possible (e.g., for `Decimal` or `f32`).
135
+ /// - `to_f64` should produce the closest `f64` representation.
136
+ ///
137
+ /// This trait is sealed and cannot be implemented outside this crate.
138
+ pub trait Real: Scalar + Display + Rem<Output = Self> {
139
+ /// The mathematical constant π (pi).
140
+ const PI: Self;
141
+
142
+ /// The mathematical constant τ (tau = 2π).
143
+ const TAU: Self;
144
+
145
+ /// The mathematical constant e (Euler's number).
146
+ const E: Self;
147
+
148
+ /// Positive infinity, if supported by the type.
149
+ const INFINITY: Self;
150
+
151
+ /// Negative infinity, if supported by the type.
152
+ const NEG_INFINITY: Self;
153
+
154
+ /// Not-a-Number value, if supported by the type.
155
+ const NAN: Self;
156
+
157
+ /// Convert from `f64`.
158
+ ///
159
+ /// Returns the closest representable value in this type.
160
+ fn from_f64(value: f64) -> Self;
161
+
162
+ /// Convert to `f64`.
163
+ ///
164
+ /// Returns the closest `f64` representation.
165
+ fn to_f64(self) -> f64;
166
+
167
+ /// Returns the sign of the number.
168
+ ///
169
+ /// - `1.0` if positive
170
+ /// - `-1.0` if negative
171
+ /// - `NaN` if NaN
172
+ fn signum(self) -> Self;
173
+
174
+ /// Returns true if this value is NaN.
175
+ fn is_nan(self) -> bool;
176
+
177
+ /// Returns true if this value is infinite.
178
+ fn is_infinite(self) -> bool;
179
+
180
+ /// Returns true if this value is finite (not infinite and not NaN).
181
+ fn is_finite(self) -> bool;
182
+
183
+ /// Fused multiply-add: `self * a + b` with only one rounding error.
184
+ fn mul_add(self, a: Self, b: Self) -> Self;
185
+
186
+ /// Returns the largest integer less than or equal to self.
187
+ fn floor(self) -> Self;
188
+
189
+ /// Returns the smallest integer greater than or equal to self.
190
+ fn ceil(self) -> Self;
191
+
192
+ /// Returns the nearest integer to self.
193
+ fn round(self) -> Self;
194
+
195
+ /// Returns the integer part of self.
196
+ fn trunc(self) -> Self;
197
+
198
+ /// Returns the fractional part of self.
199
+ fn fract(self) -> Self;
200
+
201
+ /// Raises self to the power of exp.
202
+ fn powf(self, exp: Self) -> Self;
203
+
204
+ /// Raises self to an integer power.
205
+ fn powi(self, exp: i32) -> Self;
206
+
207
+ /// Returns the square root.
208
+ fn sqrt(self) -> Self;
209
+
210
+ /// Returns the cube root.
211
+ fn cbrt(self) -> Self;
212
+
213
+ /// Returns the natural logarithm.
214
+ fn ln(self) -> Self;
215
+
216
+ /// Returns the base-10 logarithm.
217
+ fn log10(self) -> Self;
218
+
219
+ /// Returns the base-2 logarithm.
220
+ fn log2(self) -> Self;
221
+
222
+ /// Returns the logarithm with arbitrary base.
223
+ fn log(self, base: Self) -> Self;
224
+
225
+ /// Returns e^self.
226
+ fn exp(self) -> Self;
227
+
228
+ /// Returns 2^self.
229
+ fn exp2(self) -> Self;
230
+
231
+ /// Computes the length of the hypotenuse: sqrt(self² + other²).
232
+ fn hypot(self, other: Self) -> Self;
233
+ }
234
+
235
+ // ─────────────────────────────────────────────────────────────────────────────
236
+ // Transcendental trait (trigonometric functions)
237
+ // ─────────────────────────────────────────────────────────────────────────────
238
+
239
+ /// Trait for scalar types that support transcendental (trigonometric) functions.
240
+ ///
241
+ /// This extends [`Real`] with trigonometric and hyperbolic functions. When `std`
242
+ /// is not available, these functions are provided via `libm`.
243
+ ///
244
+ /// # Note
245
+ ///
246
+ /// Exact numeric types like `Decimal` or `Rational` typically do not implement
247
+ /// this trait because trigonometric functions produce irrational results.
248
+ ///
249
+ /// This trait is sealed and cannot be implemented outside this crate.
250
+ pub trait Transcendental: Real {
251
+ /// Sine function.
252
+ fn sin(self) -> Self;
253
+
254
+ /// Cosine function.
255
+ fn cos(self) -> Self;
256
+
257
+ /// Tangent function.
258
+ fn tan(self) -> Self;
259
+
260
+ /// Sine and cosine computed together for efficiency.
261
+ fn sin_cos(self) -> (Self, Self);
262
+
263
+ /// Arc sine (inverse sine).
264
+ fn asin(self) -> Self;
265
+
266
+ /// Arc cosine (inverse cosine).
267
+ fn acos(self) -> Self;
268
+
269
+ /// Arc tangent (inverse tangent).
270
+ fn atan(self) -> Self;
271
+
272
+ /// Arc tangent of y/x, with correct quadrant.
273
+ fn atan2(self, other: Self) -> Self;
274
+
275
+ /// Hyperbolic sine.
276
+ fn sinh(self) -> Self;
277
+
278
+ /// Hyperbolic cosine.
279
+ fn cosh(self) -> Self;
280
+
281
+ /// Hyperbolic tangent.
282
+ fn tanh(self) -> Self;
283
+
284
+ /// Inverse hyperbolic sine.
285
+ fn asinh(self) -> Self;
286
+
287
+ /// Inverse hyperbolic cosine.
288
+ fn acosh(self) -> Self;
289
+
290
+ /// Inverse hyperbolic tangent.
291
+ fn atanh(self) -> Self;
292
+ }
293
+
294
+ // ─────────────────────────────────────────────────────────────────────────────
295
+ // Exact trait (for rational/decimal types)
296
+ // ─────────────────────────────────────────────────────────────────────────────
297
+
298
+ /// Trait for exact numeric types that avoid floating-point rounding.
299
+ ///
300
+ /// Types implementing this trait (e.g., `Rational64`, `Decimal`, signed integers)
301
+ /// provide exact arithmetic but typically do not support transcendental functions.
302
+ ///
303
+ /// The `to_f64_approx` and `from_f64_approx` methods enable lossy unit conversion
304
+ /// for types that cannot implement [`Real`]. For integers, `from_f64_approx`
305
+ /// truncates toward zero.
306
+ ///
307
+ /// This trait is sealed and cannot be implemented outside this crate.
308
+ pub trait Exact: Scalar {
309
+ /// Convert to `f64`, potentially losing precision.
310
+ ///
311
+ /// For integers larger than 2^53, this will lose least-significant bits.
312
+ fn to_f64_approx(self) -> f64;
313
+
314
+ /// Convert from `f64`, truncating toward zero.
315
+ ///
316
+ /// For integers, this is equivalent to `value as Self` (truncation + saturation).
317
+ fn from_f64_approx(value: f64) -> Self;
318
+ }
319
+
320
+ /// Marker trait for integer scalar types.
321
+ ///
322
+ /// This is implemented only by signed integer types (`i8`, `i16`, `i32`, `i64`, `i128`).
323
+ /// It is used to provide non-overlapping `Display` implementations for integer quantities,
324
+ /// since `Decimal` implements both [`Real`] and [`Exact`].
325
+ ///
326
+ /// This trait is sealed and cannot be implemented outside this crate.
327
+ pub trait IntegerScalar: Exact + Display {}
328
+
329
+ // ─────────────────────────────────────────────────────────────────────────────
330
+ // f64 implementations
331
+ // ─────────────────────────────────────────────────────────────────────────────
332
+
333
+ impl Scalar for f64 {
334
+ const ZERO: Self = 0.0;
335
+ const ONE: Self = 1.0;
336
+
337
+ #[inline]
338
+ fn abs(self) -> Self {
339
+ #[cfg(feature = "std")]
340
+ {
341
+ f64::abs(self)
342
+ }
343
+ #[cfg(not(feature = "std"))]
344
+ {
345
+ libm::fabs(self)
346
+ }
347
+ }
348
+
349
+ #[inline]
350
+ fn min(self, other: Self) -> Self {
351
+ #[cfg(feature = "std")]
352
+ {
353
+ f64::min(self, other)
354
+ }
355
+ #[cfg(not(feature = "std"))]
356
+ {
357
+ libm::fmin(self, other)
358
+ }
359
+ }
360
+
361
+ #[inline]
362
+ fn max(self, other: Self) -> Self {
363
+ #[cfg(feature = "std")]
364
+ {
365
+ f64::max(self, other)
366
+ }
367
+ #[cfg(not(feature = "std"))]
368
+ {
369
+ libm::fmax(self, other)
370
+ }
371
+ }
372
+
373
+ #[inline]
374
+ fn rem_euclid(self, rhs: Self) -> Self {
375
+ #[cfg(feature = "std")]
376
+ {
377
+ f64::rem_euclid(self, rhs)
378
+ }
379
+ #[cfg(not(feature = "std"))]
380
+ {
381
+ let r = libm::fmod(self, rhs);
382
+ if r < 0.0 {
383
+ r + rhs
384
+ } else {
385
+ r
386
+ }
387
+ }
388
+ }
389
+ }
390
+
391
+ impl Real for f64 {
392
+ const PI: Self = core::f64::consts::PI;
393
+ const TAU: Self = core::f64::consts::TAU;
394
+ const E: Self = core::f64::consts::E;
395
+ const INFINITY: Self = f64::INFINITY;
396
+ const NEG_INFINITY: Self = f64::NEG_INFINITY;
397
+ const NAN: Self = f64::NAN;
398
+
399
+ #[inline]
400
+ fn from_f64(value: f64) -> Self {
401
+ value
402
+ }
403
+
404
+ #[inline]
405
+ fn to_f64(self) -> f64 {
406
+ self
407
+ }
408
+
409
+ #[inline]
410
+ fn signum(self) -> Self {
411
+ f64::signum(self)
412
+ }
413
+
414
+ #[inline]
415
+ fn is_nan(self) -> bool {
416
+ f64::is_nan(self)
417
+ }
418
+
419
+ #[inline]
420
+ fn is_infinite(self) -> bool {
421
+ f64::is_infinite(self)
422
+ }
423
+
424
+ #[inline]
425
+ fn is_finite(self) -> bool {
426
+ f64::is_finite(self)
427
+ }
428
+
429
+ #[inline]
430
+ fn mul_add(self, a: Self, b: Self) -> Self {
431
+ #[cfg(feature = "std")]
432
+ {
433
+ f64::mul_add(self, a, b)
434
+ }
435
+ #[cfg(not(feature = "std"))]
436
+ {
437
+ libm::fma(self, a, b)
438
+ }
439
+ }
440
+
441
+ #[inline]
442
+ fn floor(self) -> Self {
443
+ #[cfg(feature = "std")]
444
+ {
445
+ f64::floor(self)
446
+ }
447
+ #[cfg(not(feature = "std"))]
448
+ {
449
+ libm::floor(self)
450
+ }
451
+ }
452
+
453
+ #[inline]
454
+ fn ceil(self) -> Self {
455
+ #[cfg(feature = "std")]
456
+ {
457
+ f64::ceil(self)
458
+ }
459
+ #[cfg(not(feature = "std"))]
460
+ {
461
+ libm::ceil(self)
462
+ }
463
+ }
464
+
465
+ #[inline]
466
+ fn round(self) -> Self {
467
+ #[cfg(feature = "std")]
468
+ {
469
+ f64::round(self)
470
+ }
471
+ #[cfg(not(feature = "std"))]
472
+ {
473
+ libm::round(self)
474
+ }
475
+ }
476
+
477
+ #[inline]
478
+ fn trunc(self) -> Self {
479
+ #[cfg(feature = "std")]
480
+ {
481
+ f64::trunc(self)
482
+ }
483
+ #[cfg(not(feature = "std"))]
484
+ {
485
+ libm::trunc(self)
486
+ }
487
+ }
488
+
489
+ #[inline]
490
+ fn fract(self) -> Self {
491
+ self - self.trunc()
492
+ }
493
+
494
+ #[inline]
495
+ fn powf(self, exp: Self) -> Self {
496
+ #[cfg(feature = "std")]
497
+ {
498
+ f64::powf(self, exp)
499
+ }
500
+ #[cfg(not(feature = "std"))]
501
+ {
502
+ libm::pow(self, exp)
503
+ }
504
+ }
505
+
506
+ #[inline]
507
+ fn powi(self, exp: i32) -> Self {
508
+ #[cfg(feature = "std")]
509
+ {
510
+ f64::powi(self, exp)
511
+ }
512
+ #[cfg(not(feature = "std"))]
513
+ {
514
+ libm::pow(self, exp as f64)
515
+ }
516
+ }
517
+
518
+ #[inline]
519
+ fn sqrt(self) -> Self {
520
+ #[cfg(feature = "std")]
521
+ {
522
+ f64::sqrt(self)
523
+ }
524
+ #[cfg(not(feature = "std"))]
525
+ {
526
+ libm::sqrt(self)
527
+ }
528
+ }
529
+
530
+ #[inline]
531
+ fn cbrt(self) -> Self {
532
+ #[cfg(feature = "std")]
533
+ {
534
+ f64::cbrt(self)
535
+ }
536
+ #[cfg(not(feature = "std"))]
537
+ {
538
+ libm::cbrt(self)
539
+ }
540
+ }
541
+
542
+ #[inline]
543
+ fn ln(self) -> Self {
544
+ #[cfg(feature = "std")]
545
+ {
546
+ f64::ln(self)
547
+ }
548
+ #[cfg(not(feature = "std"))]
549
+ {
550
+ libm::log(self)
551
+ }
552
+ }
553
+
554
+ #[inline]
555
+ fn log10(self) -> Self {
556
+ #[cfg(feature = "std")]
557
+ {
558
+ f64::log10(self)
559
+ }
560
+ #[cfg(not(feature = "std"))]
561
+ {
562
+ libm::log10(self)
563
+ }
564
+ }
565
+
566
+ #[inline]
567
+ fn log2(self) -> Self {
568
+ #[cfg(feature = "std")]
569
+ {
570
+ f64::log2(self)
571
+ }
572
+ #[cfg(not(feature = "std"))]
573
+ {
574
+ libm::log2(self)
575
+ }
576
+ }
577
+
578
+ #[inline]
579
+ fn log(self, base: Self) -> Self {
580
+ #[cfg(feature = "std")]
581
+ {
582
+ f64::log(self, base)
583
+ }
584
+ #[cfg(not(feature = "std"))]
585
+ {
586
+ libm::log(self) / libm::log(base)
587
+ }
588
+ }
589
+
590
+ #[inline]
591
+ fn exp(self) -> Self {
592
+ #[cfg(feature = "std")]
593
+ {
594
+ f64::exp(self)
595
+ }
596
+ #[cfg(not(feature = "std"))]
597
+ {
598
+ libm::exp(self)
599
+ }
600
+ }
601
+
602
+ #[inline]
603
+ fn exp2(self) -> Self {
604
+ #[cfg(feature = "std")]
605
+ {
606
+ f64::exp2(self)
607
+ }
608
+ #[cfg(not(feature = "std"))]
609
+ {
610
+ libm::exp2(self)
611
+ }
612
+ }
613
+
614
+ #[inline]
615
+ fn hypot(self, other: Self) -> Self {
616
+ #[cfg(feature = "std")]
617
+ {
618
+ f64::hypot(self, other)
619
+ }
620
+ #[cfg(not(feature = "std"))]
621
+ {
622
+ libm::hypot(self, other)
623
+ }
624
+ }
625
+ }
626
+
627
+ impl Transcendental for f64 {
628
+ #[inline]
629
+ fn sin(self) -> Self {
630
+ #[cfg(feature = "std")]
631
+ {
632
+ f64::sin(self)
633
+ }
634
+ #[cfg(not(feature = "std"))]
635
+ {
636
+ libm::sin(self)
637
+ }
638
+ }
639
+
640
+ #[inline]
641
+ fn cos(self) -> Self {
642
+ #[cfg(feature = "std")]
643
+ {
644
+ f64::cos(self)
645
+ }
646
+ #[cfg(not(feature = "std"))]
647
+ {
648
+ libm::cos(self)
649
+ }
650
+ }
651
+
652
+ #[inline]
653
+ fn tan(self) -> Self {
654
+ #[cfg(feature = "std")]
655
+ {
656
+ f64::tan(self)
657
+ }
658
+ #[cfg(not(feature = "std"))]
659
+ {
660
+ libm::tan(self)
661
+ }
662
+ }
663
+
664
+ #[inline]
665
+ fn sin_cos(self) -> (Self, Self) {
666
+ #[cfg(feature = "std")]
667
+ {
668
+ f64::sin_cos(self)
669
+ }
670
+ #[cfg(not(feature = "std"))]
671
+ {
672
+ libm::sincos(self)
673
+ }
674
+ }
675
+
676
+ #[inline]
677
+ fn asin(self) -> Self {
678
+ #[cfg(feature = "std")]
679
+ {
680
+ f64::asin(self)
681
+ }
682
+ #[cfg(not(feature = "std"))]
683
+ {
684
+ libm::asin(self)
685
+ }
686
+ }
687
+
688
+ #[inline]
689
+ fn acos(self) -> Self {
690
+ #[cfg(feature = "std")]
691
+ {
692
+ f64::acos(self)
693
+ }
694
+ #[cfg(not(feature = "std"))]
695
+ {
696
+ libm::acos(self)
697
+ }
698
+ }
699
+
700
+ #[inline]
701
+ fn atan(self) -> Self {
702
+ #[cfg(feature = "std")]
703
+ {
704
+ f64::atan(self)
705
+ }
706
+ #[cfg(not(feature = "std"))]
707
+ {
708
+ libm::atan(self)
709
+ }
710
+ }
711
+
712
+ #[inline]
713
+ fn atan2(self, other: Self) -> Self {
714
+ #[cfg(feature = "std")]
715
+ {
716
+ f64::atan2(self, other)
717
+ }
718
+ #[cfg(not(feature = "std"))]
719
+ {
720
+ libm::atan2(self, other)
721
+ }
722
+ }
723
+
724
+ #[inline]
725
+ fn sinh(self) -> Self {
726
+ #[cfg(feature = "std")]
727
+ {
728
+ f64::sinh(self)
729
+ }
730
+ #[cfg(not(feature = "std"))]
731
+ {
732
+ libm::sinh(self)
733
+ }
734
+ }
735
+
736
+ #[inline]
737
+ fn cosh(self) -> Self {
738
+ #[cfg(feature = "std")]
739
+ {
740
+ f64::cosh(self)
741
+ }
742
+ #[cfg(not(feature = "std"))]
743
+ {
744
+ libm::cosh(self)
745
+ }
746
+ }
747
+
748
+ #[inline]
749
+ fn tanh(self) -> Self {
750
+ #[cfg(feature = "std")]
751
+ {
752
+ f64::tanh(self)
753
+ }
754
+ #[cfg(not(feature = "std"))]
755
+ {
756
+ libm::tanh(self)
757
+ }
758
+ }
759
+
760
+ #[inline]
761
+ fn asinh(self) -> Self {
762
+ #[cfg(feature = "std")]
763
+ {
764
+ f64::asinh(self)
765
+ }
766
+ #[cfg(not(feature = "std"))]
767
+ {
768
+ libm::asinh(self)
769
+ }
770
+ }
771
+
772
+ #[inline]
773
+ fn acosh(self) -> Self {
774
+ #[cfg(feature = "std")]
775
+ {
776
+ f64::acosh(self)
777
+ }
778
+ #[cfg(not(feature = "std"))]
779
+ {
780
+ libm::acosh(self)
781
+ }
782
+ }
783
+
784
+ #[inline]
785
+ fn atanh(self) -> Self {
786
+ #[cfg(feature = "std")]
787
+ {
788
+ f64::atanh(self)
789
+ }
790
+ #[cfg(not(feature = "std"))]
791
+ {
792
+ libm::atanh(self)
793
+ }
794
+ }
795
+ }
796
+
797
+ // ─────────────────────────────────────────────────────────────────────────────
798
+ // f32 implementations
799
+ // ─────────────────────────────────────────────────────────────────────────────
800
+
801
+ impl Scalar for f32 {
802
+ const ZERO: Self = 0.0;
803
+ const ONE: Self = 1.0;
804
+
805
+ #[inline]
806
+ fn abs(self) -> Self {
807
+ #[cfg(feature = "std")]
808
+ {
809
+ f32::abs(self)
810
+ }
811
+ #[cfg(not(feature = "std"))]
812
+ {
813
+ libm::fabsf(self)
814
+ }
815
+ }
816
+
817
+ #[inline]
818
+ fn min(self, other: Self) -> Self {
819
+ #[cfg(feature = "std")]
820
+ {
821
+ f32::min(self, other)
822
+ }
823
+ #[cfg(not(feature = "std"))]
824
+ {
825
+ libm::fminf(self, other)
826
+ }
827
+ }
828
+
829
+ #[inline]
830
+ fn max(self, other: Self) -> Self {
831
+ #[cfg(feature = "std")]
832
+ {
833
+ f32::max(self, other)
834
+ }
835
+ #[cfg(not(feature = "std"))]
836
+ {
837
+ libm::fmaxf(self, other)
838
+ }
839
+ }
840
+
841
+ #[inline]
842
+ fn rem_euclid(self, rhs: Self) -> Self {
843
+ #[cfg(feature = "std")]
844
+ {
845
+ f32::rem_euclid(self, rhs)
846
+ }
847
+ #[cfg(not(feature = "std"))]
848
+ {
849
+ let r = libm::fmodf(self, rhs);
850
+ if r < 0.0 {
851
+ r + rhs
852
+ } else {
853
+ r
854
+ }
855
+ }
856
+ }
857
+ }
858
+
859
+ impl Real for f32 {
860
+ const PI: Self = core::f32::consts::PI;
861
+ const TAU: Self = core::f32::consts::TAU;
862
+ const E: Self = core::f32::consts::E;
863
+ const INFINITY: Self = f32::INFINITY;
864
+ const NEG_INFINITY: Self = f32::NEG_INFINITY;
865
+ const NAN: Self = f32::NAN;
866
+
867
+ #[inline]
868
+ fn from_f64(value: f64) -> Self {
869
+ value as f32
870
+ }
871
+
872
+ #[inline]
873
+ fn to_f64(self) -> f64 {
874
+ self as f64
875
+ }
876
+
877
+ #[inline]
878
+ fn signum(self) -> Self {
879
+ f32::signum(self)
880
+ }
881
+
882
+ #[inline]
883
+ fn is_nan(self) -> bool {
884
+ f32::is_nan(self)
885
+ }
886
+
887
+ #[inline]
888
+ fn is_infinite(self) -> bool {
889
+ f32::is_infinite(self)
890
+ }
891
+
892
+ #[inline]
893
+ fn is_finite(self) -> bool {
894
+ f32::is_finite(self)
895
+ }
896
+
897
+ #[inline]
898
+ fn mul_add(self, a: Self, b: Self) -> Self {
899
+ #[cfg(feature = "std")]
900
+ {
901
+ f32::mul_add(self, a, b)
902
+ }
903
+ #[cfg(not(feature = "std"))]
904
+ {
905
+ libm::fmaf(self, a, b)
906
+ }
907
+ }
908
+
909
+ #[inline]
910
+ fn floor(self) -> Self {
911
+ #[cfg(feature = "std")]
912
+ {
913
+ f32::floor(self)
914
+ }
915
+ #[cfg(not(feature = "std"))]
916
+ {
917
+ libm::floorf(self)
918
+ }
919
+ }
920
+
921
+ #[inline]
922
+ fn ceil(self) -> Self {
923
+ #[cfg(feature = "std")]
924
+ {
925
+ f32::ceil(self)
926
+ }
927
+ #[cfg(not(feature = "std"))]
928
+ {
929
+ libm::ceilf(self)
930
+ }
931
+ }
932
+
933
+ #[inline]
934
+ fn round(self) -> Self {
935
+ #[cfg(feature = "std")]
936
+ {
937
+ f32::round(self)
938
+ }
939
+ #[cfg(not(feature = "std"))]
940
+ {
941
+ libm::roundf(self)
942
+ }
943
+ }
944
+
945
+ #[inline]
946
+ fn trunc(self) -> Self {
947
+ #[cfg(feature = "std")]
948
+ {
949
+ f32::trunc(self)
950
+ }
951
+ #[cfg(not(feature = "std"))]
952
+ {
953
+ libm::truncf(self)
954
+ }
955
+ }
956
+
957
+ #[inline]
958
+ fn fract(self) -> Self {
959
+ self - self.trunc()
960
+ }
961
+
962
+ #[inline]
963
+ fn powf(self, exp: Self) -> Self {
964
+ #[cfg(feature = "std")]
965
+ {
966
+ f32::powf(self, exp)
967
+ }
968
+ #[cfg(not(feature = "std"))]
969
+ {
970
+ libm::powf(self, exp)
971
+ }
972
+ }
973
+
974
+ #[inline]
975
+ fn powi(self, exp: i32) -> Self {
976
+ #[cfg(feature = "std")]
977
+ {
978
+ f32::powi(self, exp)
979
+ }
980
+ #[cfg(not(feature = "std"))]
981
+ {
982
+ libm::powf(self, exp as f32)
983
+ }
984
+ }
985
+
986
+ #[inline]
987
+ fn sqrt(self) -> Self {
988
+ #[cfg(feature = "std")]
989
+ {
990
+ f32::sqrt(self)
991
+ }
992
+ #[cfg(not(feature = "std"))]
993
+ {
994
+ libm::sqrtf(self)
995
+ }
996
+ }
997
+
998
+ #[inline]
999
+ fn cbrt(self) -> Self {
1000
+ #[cfg(feature = "std")]
1001
+ {
1002
+ f32::cbrt(self)
1003
+ }
1004
+ #[cfg(not(feature = "std"))]
1005
+ {
1006
+ libm::cbrtf(self)
1007
+ }
1008
+ }
1009
+
1010
+ #[inline]
1011
+ fn ln(self) -> Self {
1012
+ #[cfg(feature = "std")]
1013
+ {
1014
+ f32::ln(self)
1015
+ }
1016
+ #[cfg(not(feature = "std"))]
1017
+ {
1018
+ libm::logf(self)
1019
+ }
1020
+ }
1021
+
1022
+ #[inline]
1023
+ fn log10(self) -> Self {
1024
+ #[cfg(feature = "std")]
1025
+ {
1026
+ f32::log10(self)
1027
+ }
1028
+ #[cfg(not(feature = "std"))]
1029
+ {
1030
+ libm::log10f(self)
1031
+ }
1032
+ }
1033
+
1034
+ #[inline]
1035
+ fn log2(self) -> Self {
1036
+ #[cfg(feature = "std")]
1037
+ {
1038
+ f32::log2(self)
1039
+ }
1040
+ #[cfg(not(feature = "std"))]
1041
+ {
1042
+ libm::log2f(self)
1043
+ }
1044
+ }
1045
+
1046
+ #[inline]
1047
+ fn log(self, base: Self) -> Self {
1048
+ #[cfg(feature = "std")]
1049
+ {
1050
+ f32::log(self, base)
1051
+ }
1052
+ #[cfg(not(feature = "std"))]
1053
+ {
1054
+ libm::logf(self) / libm::logf(base)
1055
+ }
1056
+ }
1057
+
1058
+ #[inline]
1059
+ fn exp(self) -> Self {
1060
+ #[cfg(feature = "std")]
1061
+ {
1062
+ f32::exp(self)
1063
+ }
1064
+ #[cfg(not(feature = "std"))]
1065
+ {
1066
+ libm::expf(self)
1067
+ }
1068
+ }
1069
+
1070
+ #[inline]
1071
+ fn exp2(self) -> Self {
1072
+ #[cfg(feature = "std")]
1073
+ {
1074
+ f32::exp2(self)
1075
+ }
1076
+ #[cfg(not(feature = "std"))]
1077
+ {
1078
+ libm::exp2f(self)
1079
+ }
1080
+ }
1081
+
1082
+ #[inline]
1083
+ fn hypot(self, other: Self) -> Self {
1084
+ #[cfg(feature = "std")]
1085
+ {
1086
+ f32::hypot(self, other)
1087
+ }
1088
+ #[cfg(not(feature = "std"))]
1089
+ {
1090
+ libm::hypotf(self, other)
1091
+ }
1092
+ }
1093
+ }
1094
+
1095
+ impl Transcendental for f32 {
1096
+ #[inline]
1097
+ fn sin(self) -> Self {
1098
+ #[cfg(feature = "std")]
1099
+ {
1100
+ f32::sin(self)
1101
+ }
1102
+ #[cfg(not(feature = "std"))]
1103
+ {
1104
+ libm::sinf(self)
1105
+ }
1106
+ }
1107
+
1108
+ #[inline]
1109
+ fn cos(self) -> Self {
1110
+ #[cfg(feature = "std")]
1111
+ {
1112
+ f32::cos(self)
1113
+ }
1114
+ #[cfg(not(feature = "std"))]
1115
+ {
1116
+ libm::cosf(self)
1117
+ }
1118
+ }
1119
+
1120
+ #[inline]
1121
+ fn tan(self) -> Self {
1122
+ #[cfg(feature = "std")]
1123
+ {
1124
+ f32::tan(self)
1125
+ }
1126
+ #[cfg(not(feature = "std"))]
1127
+ {
1128
+ libm::tanf(self)
1129
+ }
1130
+ }
1131
+
1132
+ #[inline]
1133
+ fn sin_cos(self) -> (Self, Self) {
1134
+ #[cfg(feature = "std")]
1135
+ {
1136
+ f32::sin_cos(self)
1137
+ }
1138
+ #[cfg(not(feature = "std"))]
1139
+ {
1140
+ libm::sincosf(self)
1141
+ }
1142
+ }
1143
+
1144
+ #[inline]
1145
+ fn asin(self) -> Self {
1146
+ #[cfg(feature = "std")]
1147
+ {
1148
+ f32::asin(self)
1149
+ }
1150
+ #[cfg(not(feature = "std"))]
1151
+ {
1152
+ libm::asinf(self)
1153
+ }
1154
+ }
1155
+
1156
+ #[inline]
1157
+ fn acos(self) -> Self {
1158
+ #[cfg(feature = "std")]
1159
+ {
1160
+ f32::acos(self)
1161
+ }
1162
+ #[cfg(not(feature = "std"))]
1163
+ {
1164
+ libm::acosf(self)
1165
+ }
1166
+ }
1167
+
1168
+ #[inline]
1169
+ fn atan(self) -> Self {
1170
+ #[cfg(feature = "std")]
1171
+ {
1172
+ f32::atan(self)
1173
+ }
1174
+ #[cfg(not(feature = "std"))]
1175
+ {
1176
+ libm::atanf(self)
1177
+ }
1178
+ }
1179
+
1180
+ #[inline]
1181
+ fn atan2(self, other: Self) -> Self {
1182
+ #[cfg(feature = "std")]
1183
+ {
1184
+ f32::atan2(self, other)
1185
+ }
1186
+ #[cfg(not(feature = "std"))]
1187
+ {
1188
+ libm::atan2f(self, other)
1189
+ }
1190
+ }
1191
+
1192
+ #[inline]
1193
+ fn sinh(self) -> Self {
1194
+ #[cfg(feature = "std")]
1195
+ {
1196
+ f32::sinh(self)
1197
+ }
1198
+ #[cfg(not(feature = "std"))]
1199
+ {
1200
+ libm::sinhf(self)
1201
+ }
1202
+ }
1203
+
1204
+ #[inline]
1205
+ fn cosh(self) -> Self {
1206
+ #[cfg(feature = "std")]
1207
+ {
1208
+ f32::cosh(self)
1209
+ }
1210
+ #[cfg(not(feature = "std"))]
1211
+ {
1212
+ libm::coshf(self)
1213
+ }
1214
+ }
1215
+
1216
+ #[inline]
1217
+ fn tanh(self) -> Self {
1218
+ #[cfg(feature = "std")]
1219
+ {
1220
+ f32::tanh(self)
1221
+ }
1222
+ #[cfg(not(feature = "std"))]
1223
+ {
1224
+ libm::tanhf(self)
1225
+ }
1226
+ }
1227
+
1228
+ #[inline]
1229
+ fn asinh(self) -> Self {
1230
+ #[cfg(feature = "std")]
1231
+ {
1232
+ f32::asinh(self)
1233
+ }
1234
+ #[cfg(not(feature = "std"))]
1235
+ {
1236
+ libm::asinhf(self)
1237
+ }
1238
+ }
1239
+
1240
+ #[inline]
1241
+ fn acosh(self) -> Self {
1242
+ #[cfg(feature = "std")]
1243
+ {
1244
+ f32::acosh(self)
1245
+ }
1246
+ #[cfg(not(feature = "std"))]
1247
+ {
1248
+ libm::acoshf(self)
1249
+ }
1250
+ }
1251
+
1252
+ #[inline]
1253
+ fn atanh(self) -> Self {
1254
+ #[cfg(feature = "std")]
1255
+ {
1256
+ f32::atanh(self)
1257
+ }
1258
+ #[cfg(not(feature = "std"))]
1259
+ {
1260
+ libm::atanhf(self)
1261
+ }
1262
+ }
1263
+ }
1264
+
1265
+ // ─────────────────────────────────────────────────────────────────────────────
1266
+ // Decimal implementation (feature-gated)
1267
+ // ─────────────────────────────────────────────────────────────────────────────
1268
+
1269
+ #[cfg(feature = "scalar-decimal")]
1270
+ mod decimal_impl {
1271
+ use super::*;
1272
+ use rust_decimal::Decimal;
1273
+
1274
+ impl Scalar for Decimal {
1275
+ const ZERO: Self = Decimal::ZERO;
1276
+ const ONE: Self = Decimal::ONE;
1277
+
1278
+ #[inline]
1279
+ fn abs(self) -> Self {
1280
+ Decimal::abs(&self)
1281
+ }
1282
+
1283
+ #[inline]
1284
+ fn min(self, other: Self) -> Self {
1285
+ Decimal::min(self, other)
1286
+ }
1287
+
1288
+ #[inline]
1289
+ fn max(self, other: Self) -> Self {
1290
+ Decimal::max(self, other)
1291
+ }
1292
+
1293
+ #[inline]
1294
+ fn rem_euclid(self, rhs: Self) -> Self {
1295
+ let r = self % rhs;
1296
+ if r < Decimal::ZERO {
1297
+ r + rhs.abs()
1298
+ } else {
1299
+ r
1300
+ }
1301
+ }
1302
+ }
1303
+
1304
+ impl Exact for Decimal {
1305
+ #[inline]
1306
+ fn to_f64_approx(self) -> f64 {
1307
+ use rust_decimal::prelude::ToPrimitive;
1308
+ ToPrimitive::to_f64(&self).unwrap_or(0.0)
1309
+ }
1310
+
1311
+ #[inline]
1312
+ fn from_f64_approx(value: f64) -> Self {
1313
+ Decimal::try_from(value).unwrap_or(Decimal::ZERO)
1314
+ }
1315
+ }
1316
+
1317
+ // Note: Decimal implements a limited Real interface.
1318
+ // Transcendental functions are not available.
1319
+ impl Real for Decimal {
1320
+ const PI: Self = Decimal::PI;
1321
+ const TAU: Self = Decimal::TWO_PI;
1322
+ const E: Self = Decimal::E;
1323
+ // Decimal doesn't have infinity/NaN, use MAX/MIN as approximations
1324
+ const INFINITY: Self = Decimal::MAX;
1325
+ const NEG_INFINITY: Self = Decimal::MIN;
1326
+ const NAN: Self = Decimal::ZERO; // No NaN representation
1327
+
1328
+ #[inline]
1329
+ fn from_f64(value: f64) -> Self {
1330
+ Decimal::try_from(value).unwrap_or(Decimal::ZERO)
1331
+ }
1332
+
1333
+ #[inline]
1334
+ fn to_f64(self) -> f64 {
1335
+ use rust_decimal::prelude::ToPrimitive;
1336
+ ToPrimitive::to_f64(&self).unwrap_or(0.0)
1337
+ }
1338
+
1339
+ #[inline]
1340
+ fn signum(self) -> Self {
1341
+ if self > Decimal::ZERO {
1342
+ Decimal::ONE
1343
+ } else if self < Decimal::ZERO {
1344
+ Decimal::NEGATIVE_ONE
1345
+ } else {
1346
+ Decimal::ZERO
1347
+ }
1348
+ }
1349
+
1350
+ #[inline]
1351
+ fn is_nan(self) -> bool {
1352
+ false // Decimal has no NaN
1353
+ }
1354
+
1355
+ #[inline]
1356
+ fn is_infinite(self) -> bool {
1357
+ false // Decimal has no infinity
1358
+ }
1359
+
1360
+ #[inline]
1361
+ fn is_finite(self) -> bool {
1362
+ true // Decimal is always finite
1363
+ }
1364
+
1365
+ #[inline]
1366
+ fn mul_add(self, a: Self, b: Self) -> Self {
1367
+ self * a + b
1368
+ }
1369
+
1370
+ #[inline]
1371
+ fn floor(self) -> Self {
1372
+ Decimal::floor(&self)
1373
+ }
1374
+
1375
+ #[inline]
1376
+ fn ceil(self) -> Self {
1377
+ Decimal::ceil(&self)
1378
+ }
1379
+
1380
+ #[inline]
1381
+ fn round(self) -> Self {
1382
+ Decimal::round(&self)
1383
+ }
1384
+
1385
+ #[inline]
1386
+ fn trunc(self) -> Self {
1387
+ Decimal::trunc(&self)
1388
+ }
1389
+
1390
+ #[inline]
1391
+ fn fract(self) -> Self {
1392
+ Decimal::fract(&self)
1393
+ }
1394
+
1395
+ #[inline]
1396
+ fn powf(self, exp: Self) -> Self {
1397
+ // Decimal::powd may panic for non-integer exponents
1398
+ // Fall back to conversion through f64
1399
+ Self::from_f64(self.to_f64().powf(exp.to_f64()))
1400
+ }
1401
+
1402
+ #[inline]
1403
+ fn powi(self, exp: i32) -> Self {
1404
+ use rust_decimal::MathematicalOps;
1405
+ MathematicalOps::powi(&self, exp as i64)
1406
+ }
1407
+
1408
+ #[inline]
1409
+ fn sqrt(self) -> Self {
1410
+ use rust_decimal::MathematicalOps;
1411
+ MathematicalOps::sqrt(&self).unwrap_or(Decimal::ZERO)
1412
+ }
1413
+
1414
+ #[inline]
1415
+ fn cbrt(self) -> Self {
1416
+ // No native cbrt, use powf
1417
+ Self::from_f64(self.to_f64().cbrt())
1418
+ }
1419
+
1420
+ #[inline]
1421
+ fn ln(self) -> Self {
1422
+ use rust_decimal::MathematicalOps;
1423
+ MathematicalOps::ln(&self)
1424
+ }
1425
+
1426
+ #[inline]
1427
+ fn log10(self) -> Self {
1428
+ use rust_decimal::MathematicalOps;
1429
+ MathematicalOps::log10(&self)
1430
+ }
1431
+
1432
+ #[inline]
1433
+ fn log2(self) -> Self {
1434
+ use rust_decimal::MathematicalOps;
1435
+ // No native log2, compute as ln(self) / ln(2)
1436
+ MathematicalOps::ln(&self) / MathematicalOps::ln(&Decimal::TWO)
1437
+ }
1438
+
1439
+ #[inline]
1440
+ fn log(self, base: Self) -> Self {
1441
+ use rust_decimal::MathematicalOps;
1442
+ MathematicalOps::ln(&self) / MathematicalOps::ln(&base)
1443
+ }
1444
+
1445
+ #[inline]
1446
+ fn exp(self) -> Self {
1447
+ use rust_decimal::MathematicalOps;
1448
+ MathematicalOps::exp(&self)
1449
+ }
1450
+
1451
+ #[inline]
1452
+ fn exp2(self) -> Self {
1453
+ use rust_decimal::MathematicalOps;
1454
+ // 2^self = exp(self * ln(2))
1455
+ MathematicalOps::exp(&(self * MathematicalOps::ln(&Decimal::TWO)))
1456
+ }
1457
+
1458
+ #[inline]
1459
+ fn hypot(self, other: Self) -> Self {
1460
+ (self * self + other * other).sqrt()
1461
+ }
1462
+ }
1463
+ }
1464
+
1465
+ // ─────────────────────────────────────────────────────────────────────────────
1466
+ // Rational implementations (feature-gated)
1467
+ // ─────────────────────────────────────────────────────────────────────────────
1468
+
1469
+ #[cfg(feature = "scalar-rational")]
1470
+ mod rational_impl {
1471
+ use super::*;
1472
+ use num_rational::{Rational32, Rational64};
1473
+
1474
+ impl Scalar for Rational64 {
1475
+ const ZERO: Self = Rational64::new_raw(0, 1);
1476
+ const ONE: Self = Rational64::new_raw(1, 1);
1477
+
1478
+ #[inline]
1479
+ fn abs(self) -> Self {
1480
+ if self < Self::ZERO {
1481
+ -self
1482
+ } else {
1483
+ self
1484
+ }
1485
+ }
1486
+
1487
+ #[inline]
1488
+ fn min(self, other: Self) -> Self {
1489
+ if self < other {
1490
+ self
1491
+ } else {
1492
+ other
1493
+ }
1494
+ }
1495
+
1496
+ #[inline]
1497
+ fn max(self, other: Self) -> Self {
1498
+ if self > other {
1499
+ self
1500
+ } else {
1501
+ other
1502
+ }
1503
+ }
1504
+
1505
+ #[inline]
1506
+ fn rem_euclid(self, rhs: Self) -> Self {
1507
+ let r = self - (self / rhs).trunc() * rhs;
1508
+ if r < Self::ZERO {
1509
+ r + rhs.abs()
1510
+ } else {
1511
+ r
1512
+ }
1513
+ }
1514
+ }
1515
+
1516
+ impl Exact for Rational64 {
1517
+ #[inline]
1518
+ fn to_f64_approx(self) -> f64 {
1519
+ *self.numer() as f64 / *self.denom() as f64
1520
+ }
1521
+
1522
+ #[inline]
1523
+ fn from_f64_approx(value: f64) -> Self {
1524
+ Rational64::approximate_float(value).unwrap_or(Rational64::new_raw(0, 1))
1525
+ }
1526
+ }
1527
+
1528
+ impl Scalar for Rational32 {
1529
+ const ZERO: Self = Rational32::new_raw(0, 1);
1530
+ const ONE: Self = Rational32::new_raw(1, 1);
1531
+
1532
+ #[inline]
1533
+ fn abs(self) -> Self {
1534
+ if self < Self::ZERO {
1535
+ -self
1536
+ } else {
1537
+ self
1538
+ }
1539
+ }
1540
+
1541
+ #[inline]
1542
+ fn min(self, other: Self) -> Self {
1543
+ if self < other {
1544
+ self
1545
+ } else {
1546
+ other
1547
+ }
1548
+ }
1549
+
1550
+ #[inline]
1551
+ fn max(self, other: Self) -> Self {
1552
+ if self > other {
1553
+ self
1554
+ } else {
1555
+ other
1556
+ }
1557
+ }
1558
+
1559
+ #[inline]
1560
+ fn rem_euclid(self, rhs: Self) -> Self {
1561
+ let r = self - (self / rhs).trunc() * rhs;
1562
+ if r < Self::ZERO {
1563
+ r + rhs.abs()
1564
+ } else {
1565
+ r
1566
+ }
1567
+ }
1568
+ }
1569
+
1570
+ impl Exact for Rational32 {
1571
+ #[inline]
1572
+ fn to_f64_approx(self) -> f64 {
1573
+ *self.numer() as f64 / *self.denom() as f64
1574
+ }
1575
+
1576
+ #[inline]
1577
+ fn from_f64_approx(value: f64) -> Self {
1578
+ Rational32::approximate_float(value).unwrap_or(Rational32::new_raw(0, 1))
1579
+ }
1580
+ }
1581
+ }
1582
+
1583
+ // NOTE: BigRational (Ratio<BigInt>) is NOT supported because BigInt does not implement Copy,
1584
+ // which is required by the Scalar trait. Supporting arbitrary-precision rationals would require
1585
+ // a different design using Clone instead of Copy.
1586
+
1587
+ // ─────────────────────────────────────────────────────────────────────────────
1588
+ // Signed integer implementations
1589
+ // ─────────────────────────────────────────────────────────────────────────────
1590
+
1591
+ macro_rules! impl_scalar_for_signed_int {
1592
+ ($($t:ty),*) => { $(
1593
+ impl Scalar for $t {
1594
+ const ZERO: Self = 0;
1595
+ const ONE: Self = 1;
1596
+
1597
+ #[inline]
1598
+ fn abs(self) -> Self {
1599
+ self.abs()
1600
+ }
1601
+
1602
+ #[inline]
1603
+ fn min(self, other: Self) -> Self {
1604
+ Ord::min(self, other)
1605
+ }
1606
+
1607
+ #[inline]
1608
+ fn max(self, other: Self) -> Self {
1609
+ Ord::max(self, other)
1610
+ }
1611
+
1612
+ #[inline]
1613
+ fn rem_euclid(self, rhs: Self) -> Self {
1614
+ self.rem_euclid(rhs)
1615
+ }
1616
+ }
1617
+
1618
+ impl Exact for $t {
1619
+ #[inline]
1620
+ fn to_f64_approx(self) -> f64 {
1621
+ self as f64
1622
+ }
1623
+
1624
+ #[inline]
1625
+ fn from_f64_approx(value: f64) -> Self {
1626
+ value as Self
1627
+ }
1628
+ }
1629
+
1630
+ impl IntegerScalar for $t {}
1631
+ )* };
1632
+ }
1633
+
1634
+ impl_scalar_for_signed_int!(i8, i16, i32, i64, i128);
1635
+
1636
+ #[cfg(test)]
1637
+ mod tests {
1638
+ use super::*;
1639
+
1640
+ #[test]
1641
+ fn test_f64_scalar_basic() {
1642
+ assert_eq!(f64::ZERO, 0.0);
1643
+ assert_eq!(f64::ONE, 1.0);
1644
+ assert_eq!((-5.0_f64).abs(), 5.0);
1645
+ assert_eq!(3.0_f64.min(5.0), 3.0);
1646
+ assert_eq!(3.0_f64.max(5.0), 5.0);
1647
+ }
1648
+
1649
+ #[test]
1650
+ fn test_f64_real() {
1651
+ assert!((f64::PI - core::f64::consts::PI).abs() < 1e-15);
1652
+ assert_eq!(f64::from_f64(42.5), 42.5);
1653
+ assert_eq!(42.5_f64.to_f64(), 42.5);
1654
+ assert!(f64::NAN.is_nan());
1655
+ assert!(f64::INFINITY.is_infinite());
1656
+ }
1657
+
1658
+ #[test]
1659
+ fn test_f64_transcendental() {
1660
+ let angle = core::f64::consts::FRAC_PI_2;
1661
+ assert!((angle.sin() - 1.0).abs() < 1e-15);
1662
+ assert!(angle.cos().abs() < 1e-15);
1663
+ }
1664
+
1665
+ #[test]
1666
+ fn test_f32_scalar_basic() {
1667
+ assert_eq!(f32::ZERO, 0.0);
1668
+ assert_eq!(f32::ONE, 1.0);
1669
+ assert_eq!((-5.0_f32).abs(), 5.0);
1670
+ }
1671
+
1672
+ #[test]
1673
+ fn test_f32_real() {
1674
+ assert!((f32::PI - core::f32::consts::PI).abs() < 1e-6);
1675
+ assert_eq!(f32::from_f64(42.5), 42.5);
1676
+ }
1677
+
1678
+ #[test]
1679
+ fn test_f32_transcendental() {
1680
+ let angle = core::f32::consts::FRAC_PI_2;
1681
+ assert!((angle.sin() - 1.0).abs() < 1e-6);
1682
+ }
1683
+
1684
+ // ── Integer scalar tests ──────────────────────────────────────────────
1685
+
1686
+ #[test]
1687
+ fn test_i32_scalar_basic() {
1688
+ assert_eq!(i32::ZERO, 0);
1689
+ assert_eq!(i32::ONE, 1);
1690
+ assert_eq!((-5_i32).abs(), 5);
1691
+ assert_eq!(Scalar::min(3_i32, 5), 3);
1692
+ assert_eq!(Scalar::max(3_i32, 5), 5);
1693
+ assert_eq!(7_i32.rem_euclid(4), 3);
1694
+ assert_eq!((-7_i32).rem_euclid(4), 1);
1695
+ }
1696
+
1697
+ #[test]
1698
+ fn test_i64_scalar_basic() {
1699
+ assert_eq!(i64::ZERO, 0);
1700
+ assert_eq!(i64::ONE, 1);
1701
+ assert_eq!((-100_i64).abs(), 100);
1702
+ assert_eq!(Scalar::min(10_i64, 20), 10);
1703
+ assert_eq!(Scalar::max(10_i64, 20), 20);
1704
+ }
1705
+
1706
+ #[test]
1707
+ fn test_i8_scalar_basic() {
1708
+ assert_eq!(i8::ZERO, 0);
1709
+ assert_eq!(i8::ONE, 1);
1710
+ assert_eq!((-5_i8).abs(), 5);
1711
+ assert_eq!(Scalar::max(127_i8, -128), 127);
1712
+ }
1713
+
1714
+ #[test]
1715
+ fn test_i16_scalar_basic() {
1716
+ assert_eq!(i16::ZERO, 0);
1717
+ assert_eq!(i16::ONE, 1);
1718
+ assert_eq!((-1000_i16).abs(), 1000);
1719
+ }
1720
+
1721
+ #[test]
1722
+ fn test_i128_scalar_basic() {
1723
+ assert_eq!(i128::ZERO, 0);
1724
+ assert_eq!(i128::ONE, 1);
1725
+ assert_eq!((-42_i128).abs(), 42);
1726
+ }
1727
+
1728
+ #[test]
1729
+ fn test_integer_exact_conversions() {
1730
+ // i32
1731
+ assert_eq!(42_i32.to_f64_approx(), 42.0);
1732
+ assert_eq!(i32::from_f64_approx(42.9), 42); // truncates toward zero
1733
+ assert_eq!(i32::from_f64_approx(-3.7), -3); // truncates toward zero
1734
+
1735
+ // i64
1736
+ assert_eq!(1000_i64.to_f64_approx(), 1000.0);
1737
+ assert_eq!(i64::from_f64_approx(1500.0), 1500);
1738
+
1739
+ // i8
1740
+ assert_eq!(i8::from_f64_approx(100.0), 100);
1741
+ }
1742
+ }