manifold_rust/robust/exact/
rational.rs1use num_bigint::BigUint;
14use num_rational::BigRational;
15use num_traits::{Signed, ToPrimitive, Zero};
16
17use crate::linalg::{Vec2, Vec3};
18
19#[inline]
26pub fn rat(v: f64) -> BigRational {
27 BigRational::from_float(v).expect("robust engine: coordinate must be finite")
28}
29
30#[inline]
34fn pow2(e: i64) -> f64 {
35 debug_assert!((-1074..=1023).contains(&e), "pow2 exponent out of range: {e}");
36 if e >= -1022 {
37 f64::from_bits(((e + 1023) as u64) << 52)
38 } else {
39 f64::from_bits(1u64 << (e + 1074))
40 }
41}
42
43pub fn rat_to_f64(r: &BigRational) -> f64 {
48 if r.is_zero() {
49 return 0.0;
50 }
51 let neg = r.is_negative();
52 let n: &BigUint = r.numer().magnitude();
53 let d: &BigUint = r.denom().magnitude();
54
55 let mut e = n.bits() as i64 - d.bits() as i64;
57 let ge = if e >= 0 {
58 *n >= (d << e as usize)
59 } else {
60 (n << (-e) as usize) >= *d
61 };
62 if !ge {
63 e -= 1;
64 }
65 if e > 1023 {
66 return if neg { f64::NEG_INFINITY } else { f64::INFINITY };
67 }
68
69 let lsb = (e - 52).max(-1074);
72
73 let (num, den) = if lsb >= 0 {
76 (n.clone(), d << lsb as usize)
77 } else {
78 (n << (-lsb) as usize, d.clone())
79 };
80 let q = &num / &den;
81 let rem = &num - &q * &den;
82 let mut m = q;
83 let twice_rem = &rem << 1usize;
84 match twice_rem.cmp(&den) {
85 std::cmp::Ordering::Greater => m += 1u32,
86 std::cmp::Ordering::Equal => {
87 if m.bit(0) {
88 m += 1u32;
89 }
90 }
91 std::cmp::Ordering::Less => {}
92 }
93
94 if m.is_zero() {
95 return if neg { -0.0 } else { 0.0 };
96 }
97 if m.bits() as i64 - 1 + lsb > 1023 {
100 return if neg { f64::NEG_INFINITY } else { f64::INFINITY };
101 }
102 let val = m.to_u64().expect("mantissa fits in u64") as f64 * pow2(lsb);
105 if neg {
106 -val
107 } else {
108 val
109 }
110}
111
112#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
117pub struct R2 {
118 pub x: BigRational,
119 pub y: BigRational,
120}
121
122impl R2 {
123 #[inline]
124 pub fn new(x: BigRational, y: BigRational) -> Self {
125 Self { x, y }
126 }
127
128 #[inline]
129 pub fn from_vec2(v: Vec2) -> Self {
130 Self::new(rat(v.x), rat(v.y))
131 }
132
133 pub fn to_vec2_rounded(&self) -> Vec2 {
134 Vec2::new(rat_to_f64(&self.x), rat_to_f64(&self.y))
135 }
136
137 pub fn sub(&self, o: &R2) -> R2 {
138 R2::new(&self.x - &o.x, &self.y - &o.y)
139 }
140
141 pub fn add(&self, o: &R2) -> R2 {
142 R2::new(&self.x + &o.x, &self.y + &o.y)
143 }
144
145 pub fn scale(&self, s: &BigRational) -> R2 {
146 R2::new(&self.x * s, &self.y * s)
147 }
148
149 pub fn dot(&self, o: &R2) -> BigRational {
150 &self.x * &o.x + &self.y * &o.y
151 }
152
153 pub fn cross(&self, o: &R2) -> BigRational {
155 &self.x * &o.y - &self.y * &o.x
156 }
157
158 pub fn is_zero(&self) -> bool {
159 self.x.is_zero() && self.y.is_zero()
160 }
161}
162
163#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
168pub struct R3 {
169 pub x: BigRational,
170 pub y: BigRational,
171 pub z: BigRational,
172}
173
174impl R3 {
175 #[inline]
176 pub fn new(x: BigRational, y: BigRational, z: BigRational) -> Self {
177 Self { x, y, z }
178 }
179
180 #[inline]
181 pub fn from_vec3(v: Vec3) -> Self {
182 Self::new(rat(v.x), rat(v.y), rat(v.z))
183 }
184
185 pub fn to_vec3_rounded(&self) -> Vec3 {
186 Vec3::new(rat_to_f64(&self.x), rat_to_f64(&self.y), rat_to_f64(&self.z))
187 }
188
189 pub fn sub(&self, o: &R3) -> R3 {
190 R3::new(&self.x - &o.x, &self.y - &o.y, &self.z - &o.z)
191 }
192
193 pub fn add(&self, o: &R3) -> R3 {
194 R3::new(&self.x + &o.x, &self.y + &o.y, &self.z + &o.z)
195 }
196
197 pub fn scale(&self, s: &BigRational) -> R3 {
198 R3::new(&self.x * s, &self.y * s, &self.z * s)
199 }
200
201 pub fn dot(&self, o: &R3) -> BigRational {
202 &self.x * &o.x + &self.y * &o.y + &self.z * &o.z
203 }
204
205 pub fn cross(&self, o: &R3) -> R3 {
206 R3::new(
207 &self.y * &o.z - &self.z * &o.y,
208 &self.z * &o.x - &self.x * &o.z,
209 &self.x * &o.y - &self.y * &o.x,
210 )
211 }
212
213 pub fn is_zero(&self) -> bool {
214 self.x.is_zero() && self.y.is_zero() && self.z.is_zero()
215 }
216
217 pub fn project_drop(&self, axis: usize) -> R2 {
221 match axis {
222 0 => R2::new(self.y.clone(), self.z.clone()),
223 1 => R2::new(self.z.clone(), self.x.clone()),
224 2 => R2::new(self.x.clone(), self.y.clone()),
225 _ => unreachable!("axis must be 0, 1, or 2"),
226 }
227 }
228}
229
230#[inline]
243fn rat_fields_eq(a: &BigRational, b: &BigRational) -> bool {
244 a.numer() == b.numer() && a.denom() == b.denom()
245}
246
247fn hash_rat<H: std::hash::Hasher>(r: &BigRational, state: &mut H) {
248 use std::hash::Hash;
249 (r.numer().sign() == num_bigint::Sign::Minus).hash(state);
250 for d in r.numer().iter_u64_digits() {
251 d.hash(state);
252 }
253 0xfeed_u64.hash(state); for d in r.denom().iter_u64_digits() {
255 d.hash(state);
256 }
257}
258
259#[derive(Clone, Debug)]
261pub struct R2Key(pub R2);
262
263impl PartialEq for R2Key {
264 #[inline]
265 fn eq(&self, other: &Self) -> bool {
266 rat_fields_eq(&self.0.x, &other.0.x) && rat_fields_eq(&self.0.y, &other.0.y)
267 }
268}
269impl Eq for R2Key {}
270impl std::hash::Hash for R2Key {
271 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
272 hash_rat(&self.0.x, state);
273 hash_rat(&self.0.y, state);
274 }
275}
276
277#[derive(Clone, Debug)]
279pub struct R3Key(pub R3);
280
281impl PartialEq for R3Key {
282 #[inline]
283 fn eq(&self, other: &Self) -> bool {
284 rat_fields_eq(&self.0.x, &other.0.x)
285 && rat_fields_eq(&self.0.y, &other.0.y)
286 && rat_fields_eq(&self.0.z, &other.0.z)
287 }
288}
289impl Eq for R3Key {}
290impl std::hash::Hash for R3Key {
291 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
292 hash_rat(&self.0.x, state);
293 hash_rat(&self.0.y, state);
294 hash_rat(&self.0.z, state);
295 }
296}
297
298#[inline]
300pub fn r2_eq(a: &R2, b: &R2) -> bool {
301 rat_fields_eq(&a.x, &b.x) && rat_fields_eq(&a.y, &b.y)
302}
303
304#[inline]
308pub fn r3_eq(a: &R3, b: &R3) -> bool {
309 rat_fields_eq(&a.x, &b.x) && rat_fields_eq(&a.y, &b.y) && rat_fields_eq(&a.z, &b.z)
310}