manifold_rust/robust/exact/
rational.rs1use super::backend::{
14 self, denom, numer_mag, rat_from_f64, rat_is_negative, rat_is_zero, Rational, ToPrimitive,
15 Uint,
16};
17
18use crate::linalg::{Vec2, Vec3};
19
20#[inline]
27pub fn rat(v: f64) -> Rational {
28 rat_from_f64(v).expect("robust engine: coordinate must be finite")
29}
30
31#[inline]
35fn pow2(e: i64) -> f64 {
36 debug_assert!((-1074..=1023).contains(&e), "pow2 exponent out of range: {e}");
37 if e >= -1022 {
38 f64::from_bits(((e + 1023) as u64) << 52)
39 } else {
40 f64::from_bits(1u64 << (e + 1074))
41 }
42}
43
44pub fn rat_to_f64(r: &Rational) -> f64 {
49 if rat_is_zero(r) {
50 return 0.0;
51 }
52 let neg = rat_is_negative(r);
53 let n_mag = numer_mag(r);
54 let n: &Uint = n_mag.as_ref();
55 let d: &Uint = denom(r);
56
57 let mut e = backend::uint_bits(n) as i64 - backend::uint_bits(d) as i64;
59 let ge = if e >= 0 {
60 *n >= (d << e as usize)
61 } else {
62 (n << (-e) as usize) >= *d
63 };
64 if !ge {
65 e -= 1;
66 }
67 if e > 1023 {
68 return if neg { f64::NEG_INFINITY } else { f64::INFINITY };
69 }
70
71 let lsb = (e - 52).max(-1074);
74
75 let (num, den) = if lsb >= 0 {
78 (n.clone(), d << lsb as usize)
79 } else {
80 (n << (-lsb) as usize, d.clone())
81 };
82 let q = &num / &den;
83 let rem = &num - &q * &den;
84 let mut m = q;
85 let twice_rem = &rem << 1usize;
86 match twice_rem.cmp(&den) {
87 std::cmp::Ordering::Greater => m += 1u32,
88 std::cmp::Ordering::Equal => {
89 if backend::uint_bit(&m, 0) {
90 m += 1u32;
91 }
92 }
93 std::cmp::Ordering::Less => {}
94 }
95
96 if m.is_zero() {
97 return if neg { -0.0 } else { 0.0 };
98 }
99 if backend::uint_bits(&m) as i64 - 1 + lsb > 1023 {
102 return if neg { f64::NEG_INFINITY } else { f64::INFINITY };
103 }
104 let val = m.to_u64().expect("mantissa fits in u64") as f64 * pow2(lsb);
107 if neg {
108 -val
109 } else {
110 val
111 }
112}
113
114#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
119pub struct R2 {
120 pub x: Rational,
121 pub y: Rational,
122}
123
124impl R2 {
125 #[inline]
126 pub fn new(x: Rational, y: Rational) -> Self {
127 Self { x, y }
128 }
129
130 #[inline]
131 pub fn from_vec2(v: Vec2) -> Self {
132 Self::new(rat(v.x), rat(v.y))
133 }
134
135 pub fn to_vec2_rounded(&self) -> Vec2 {
136 Vec2::new(rat_to_f64(&self.x), rat_to_f64(&self.y))
137 }
138
139 pub fn sub(&self, o: &R2) -> R2 {
140 R2::new(&self.x - &o.x, &self.y - &o.y)
141 }
142
143 pub fn add(&self, o: &R2) -> R2 {
144 R2::new(&self.x + &o.x, &self.y + &o.y)
145 }
146
147 pub fn scale(&self, s: &Rational) -> R2 {
148 R2::new(&self.x * s, &self.y * s)
149 }
150
151 pub fn dot(&self, o: &R2) -> Rational {
152 &self.x * &o.x + &self.y * &o.y
153 }
154
155 pub fn cross(&self, o: &R2) -> Rational {
157 &self.x * &o.y - &self.y * &o.x
158 }
159
160 pub fn is_zero(&self) -> bool {
161 rat_is_zero(&self.x) && rat_is_zero(&self.y)
162 }
163}
164
165#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
170pub struct R3 {
171 pub x: Rational,
172 pub y: Rational,
173 pub z: Rational,
174}
175
176impl R3 {
177 #[inline]
178 pub fn new(x: Rational, y: Rational, z: Rational) -> Self {
179 Self { x, y, z }
180 }
181
182 #[inline]
183 pub fn from_vec3(v: Vec3) -> Self {
184 Self::new(rat(v.x), rat(v.y), rat(v.z))
185 }
186
187 pub fn to_vec3_rounded(&self) -> Vec3 {
188 Vec3::new(rat_to_f64(&self.x), rat_to_f64(&self.y), rat_to_f64(&self.z))
189 }
190
191 pub fn sub(&self, o: &R3) -> R3 {
192 R3::new(&self.x - &o.x, &self.y - &o.y, &self.z - &o.z)
193 }
194
195 pub fn add(&self, o: &R3) -> R3 {
196 R3::new(&self.x + &o.x, &self.y + &o.y, &self.z + &o.z)
197 }
198
199 pub fn scale(&self, s: &Rational) -> R3 {
200 R3::new(&self.x * s, &self.y * s, &self.z * s)
201 }
202
203 pub fn dot(&self, o: &R3) -> Rational {
204 &self.x * &o.x + &self.y * &o.y + &self.z * &o.z
205 }
206
207 pub fn cross(&self, o: &R3) -> R3 {
208 R3::new(
209 &self.y * &o.z - &self.z * &o.y,
210 &self.z * &o.x - &self.x * &o.z,
211 &self.x * &o.y - &self.y * &o.x,
212 )
213 }
214
215 pub fn is_zero(&self) -> bool {
216 rat_is_zero(&self.x) && rat_is_zero(&self.y) && rat_is_zero(&self.z)
217 }
218
219 pub fn project_drop(&self, axis: usize) -> R2 {
223 match axis {
224 0 => R2::new(self.y.clone(), self.z.clone()),
225 1 => R2::new(self.z.clone(), self.x.clone()),
226 2 => R2::new(self.x.clone(), self.y.clone()),
227 _ => unreachable!("axis must be 0, 1, or 2"),
228 }
229 }
230}
231
232use backend::{hash_rational as hash_rat, rat_eq as rat_fields_eq};
244
245#[derive(Clone, Debug)]
247pub struct R2Key(pub R2);
248
249impl PartialEq for R2Key {
250 #[inline]
251 fn eq(&self, other: &Self) -> bool {
252 rat_fields_eq(&self.0.x, &other.0.x) && rat_fields_eq(&self.0.y, &other.0.y)
253 }
254}
255impl Eq for R2Key {}
256impl std::hash::Hash for R2Key {
257 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
258 hash_rat(&self.0.x, state);
259 hash_rat(&self.0.y, state);
260 }
261}
262
263#[derive(Clone, Debug)]
265pub struct R3Key(pub R3);
266
267impl PartialEq for R3Key {
268 #[inline]
269 fn eq(&self, other: &Self) -> bool {
270 rat_fields_eq(&self.0.x, &other.0.x)
271 && rat_fields_eq(&self.0.y, &other.0.y)
272 && rat_fields_eq(&self.0.z, &other.0.z)
273 }
274}
275impl Eq for R3Key {}
276impl std::hash::Hash for R3Key {
277 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
278 hash_rat(&self.0.x, state);
279 hash_rat(&self.0.y, state);
280 hash_rat(&self.0.z, state);
281 }
282}
283
284#[inline]
286pub fn r2_eq(a: &R2, b: &R2) -> bool {
287 rat_fields_eq(&a.x, &b.x) && rat_fields_eq(&a.y, &b.y)
288}
289
290#[inline]
294pub fn r3_eq(a: &R3, b: &R3) -> bool {
295 rat_fields_eq(&a.x, &b.x) && rat_fields_eq(&a.y, &b.y) && rat_fields_eq(&a.z, &b.z)
296}