robust_geo/
geo.rs

1//! Contains the geometric tests
2
3use crate::Expansion;
4use crate::{square, two_product, two_sum};
5
6use nalgebra::{Vector2, Vector3};
7type Vec2 = Vector2<f64>;
8type Vec3 = Vector3<f64>;
9
10const EPSILON: f64 = f64::EPSILON / 2.0;
11
12const ORIENT_2D_BOUND_A: f64 = (3.0 + 16.0 * EPSILON) * EPSILON;
13const ORIENT_2D_BOUND_B: f64 = (2.0 + 12.0 * EPSILON) * EPSILON;
14const ORIENT_2D_BOUND_C1: f64 = (3.0 + 8.0 * EPSILON) * EPSILON;
15const ORIENT_2D_BOUND_C2: f64 = (9.0 + 64.0 * EPSILON) * EPSILON * EPSILON;
16
17const ORIENT_3D_BOUND_A: f64 = (7.0 + 56.0 * EPSILON) * EPSILON;
18const ORIENT_3D_BOUND_B: f64 = (3.0 + 28.0 * EPSILON) * EPSILON;
19const ORIENT_3D_BOUND_C1: f64 = (3.0 + 8.0 * EPSILON) * EPSILON;
20const ORIENT_3D_BOUND_C2: f64 = (26.0 + 288.0 * EPSILON) * EPSILON * EPSILON;
21
22const IN_CIRCLE_BOUND_A: f64 = (10.0 + 96.0 * EPSILON) * EPSILON;
23const IN_CIRCLE_BOUND_B: f64 = (4.0 + 48.0 * EPSILON) * EPSILON;
24const IN_CIRCLE_BOUND_C1: f64 = (3.0 + 8.0 * EPSILON) * EPSILON;
25const IN_CIRCLE_BOUND_C2: f64 = (44.0 + 576.0 * EPSILON) * EPSILON * EPSILON;
26
27const IN_SPHERE_BOUND_A: f64 = (16.0 + 224.0 * EPSILON) * EPSILON;
28const IN_SPHERE_BOUND_B: f64 = (5.0 + 72.0 * EPSILON) * EPSILON;
29const IN_SPHERE_BOUND_C1: f64 = (3.0 + 8.0 * EPSILON) * EPSILON;
30const IN_SPHERE_BOUND_C2: f64 = (71.0 + 1408.0 * EPSILON) * EPSILON * EPSILON;
31
32const MAGNITUDE_CMP_2D_BOUND_A: f64 = (2.0 + 8.0 * EPSILON) * EPSILON;
33
34const MAGNITUDE_CMP_3D_BOUND_A: f64 = (3.0 + 12.0 * EPSILON) * EPSILON;
35
36const DISTANCE_CMP_3D_BOUND_A: f64 = (6.0 + 32.0 * EPSILON) * EPSILON;
37
38const SIGN_DET_X_X2Y2_BOUND_A: f64 = (5.0 + 32.0 * EPSILON) * EPSILON;
39
40const SIGN_DET_X_X2Y2Z2_BOUND_A: f64 = (6.0 + 32.0 * EPSILON) * EPSILON;
41
42const SIGN_DET_X_Y_X2Y2Z2_BOUND_A: f64 = (9.0 + 64.0 * EPSILON) * EPSILON;
43
44const SIGN_DET_X_Y_X2Y2Z2_PLUS_2X_DET_X_Y_Z_BOUND_A: f64 = (10.0 + 80.0 * EPSILON) * EPSILON;
45const SIGN_DET_X_X2Y2Z2_PLUS_2X_DET_X_Y_BOUND_A: f64 = (7.0 + 48.0 * EPSILON) * EPSILON;
46const SIGN_DET_X2Y2Z2_PLUS_2X_DET_X_BOUND_A: f64 = (4.0 + 24.0 * EPSILON) * EPSILON;
47
48const SIGN_DET_X_X2Y2_PLUS_2X_DET_X_Y_BOUND_A: f64 = (6.0 + 40.0 * EPSILON) * EPSILON;
49const SIGN_DET_X2Y2_PLUS_2X_DET_X_BOUND_A: f64 = (3.0 + 16.0 * EPSILON) * EPSILON;
50
51/// Calculates the orientation of points `a`, `b`, `c` in a plane.
52/// Returns twice the area of the triangle formed by `a`, `b`, and `c`,
53/// which is a positive number if they define a left turn,
54/// a negative number if they define a right turn,
55/// and 0 if they are collinear.
56pub fn orient_2d(a: Vec2, b: Vec2, c: Vec2) -> f64 {
57    let diag1 = (a.x - c.x) * (b.y - c.y);
58    let diag2 = (a.y - c.y) * (b.x - c.x);
59    let det = diag1 - diag2;
60    let det_sum = diag1.abs() + diag2.abs();
61
62    if det.abs() >= det_sum * ORIENT_2D_BOUND_A {
63        det
64    } else {
65        orient_2d_adapt(a, b, c, det_sum)
66    }
67}
68
69fn orient_2d_adapt(a: Vec2, b: Vec2, c: Vec2, det_sum: f64) -> f64 {
70    let diag1 = two_product(a.x - c.x, b.y - c.y);
71    let diag2 = two_product(a.y - c.y, b.x - c.x);
72    let det_hi = diag1 - diag2;
73    let det_approx = det_hi.approximate();
74
75    if det_approx.abs() >= det_sum * ORIENT_2D_BOUND_B {
76        return det_approx;
77    }
78
79    // Calculate correction factor, but don't go for the full exact value yet
80    let ax = two_sum(a.x, -c.x);
81    let ay = two_sum(a.y, -c.y);
82    let bx = two_sum(b.x, -c.x);
83    let by = two_sum(b.y, -c.y);
84
85    let det_mid1 = ax[0] * by[1] - ay[0] * bx[1];
86    let det_mid2 = ax[1] * by[0] - ay[1] * bx[0];
87    let det = det_mid1 + det_mid2 + det_approx;
88
89    if det.abs() >= ORIENT_2D_BOUND_C1 * det_approx.abs() + ORIENT_2D_BOUND_C2 * det_sum {
90        return det;
91    }
92
93    // Determinant is likely 0; go for exact value
94    let det_hi = det_hi.dynamic();
95    let det_m1 = (two_product(ax[0], by[1]) - two_product(ay[0], bx[1])).dynamic();
96    let det_m2 = (two_product(ax[1], by[0]) - two_product(ay[1], bx[0])).dynamic();
97    let det_lo = (two_product(ax[0], by[0]) - two_product(ay[0], bx[0])).dynamic();
98    let det = (det_hi + det_m1) + (det_m2 + det_lo);
99    det.approximate()
100}
101
102/// Computes the determinant of the following matrix
103/// ```notrust
104/// ┌─                     ─┐
105/// │ b.x - a.x   b.y - a.y │
106/// │ d.x - c.x   d.y - c.y │
107/// └─                     ─┘
108/// ```
109pub fn cross_2d(a: Vec2, b: Vec2, c: Vec2, d: Vec2) -> f64 {
110    let diag1 = (b.x - a.x) * (d.y - c.y);
111    let diag2 = (b.y - a.y) * (d.x - c.x);
112    let det = diag1 - diag2;
113    let det_sum = diag1.abs() + diag2.abs();
114
115    if det.abs() >= det_sum * ORIENT_2D_BOUND_A {
116        det
117    } else {
118        cross_2d_adapt(a, b, c, d, det_sum)
119    }
120}
121
122fn cross_2d_adapt(a: Vec2, b: Vec2, c: Vec2, d: Vec2, det_sum: f64) -> f64 {
123    let diag1 = two_product(b.x - a.x, d.y - c.y);
124    let diag2 = two_product(b.y - a.y, d.x - c.x);
125    let det_hi = diag1 - diag2;
126    let det_approx = det_hi.approximate();
127
128    if det_approx.abs() >= det_sum * ORIENT_2D_BOUND_B {
129        return det_approx;
130    }
131
132    // Calculate correction factor, but don't go for the full exact value yet
133    let abx = two_sum(b.x, -a.x);
134    let aby = two_sum(b.y, -a.y);
135    let cdx = two_sum(d.x, -c.x);
136    let cdy = two_sum(d.y, -c.y);
137
138    let det_mid1 = abx[0] * cdy[1] - aby[0] * cdx[1];
139    let det_mid2 = abx[1] * cdy[0] - aby[1] * cdx[0];
140    let det = det_mid1 + det_mid2 + det_approx;
141
142    if det.abs() >= ORIENT_2D_BOUND_C1 * det_approx.abs() + ORIENT_2D_BOUND_C2 * det_sum {
143        return det;
144    }
145
146    // Determinant is likely 0; go for exact value
147    let det_hi = det_hi.dynamic();
148    let det_m1 = (two_product(abx[0], cdy[1]) - two_product(aby[0], cdx[1])).dynamic();
149    let det_m2 = (two_product(abx[1], cdy[0]) - two_product(aby[1], cdx[0])).dynamic();
150    let det_lo = (two_product(abx[0], cdy[0]) - two_product(aby[0], cdx[0])).dynamic();
151    let det = (det_hi + det_m1) + (det_m2 + det_lo);
152    det.approximate()
153}
154
155macro_rules! sep_let {
156    (($($d:tt $var:ident),*) => let $($name:ident : [$($vals:tt)*]),* $(,)? = $($expr:tt)*) => {
157        macro_rules! __mac {
158            ($($d $var:ident),*) => { $($expr)* };
159            ($($d $var:expr),*) => { $($expr)* };
160        }
161
162        $(let $name = __mac!($($vals)*);)*
163    };
164}
165
166// xyz cyclic permutation shortcut.
167// Simplifies cofactor repetition.
168macro_rules! sep_xyz {
169    (($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident $(, $d:tt $var:ident)*)
170        => let $n1:ident : [$($v1:tt)*], $n2:ident : [$($v2:tt)*], $n3:ident : [$($v3:tt)*] = $($expr:tt)*) =>
171    {
172        sep_let!(($dx $x, $dy $y, $dz $z $(, $d $var)*) =>
173            let $n1: [$x, $y, $z, $($v1)*], $n2: [$y, $z, $x, $($v2)*], $n3: [$z, $x, $y, $($v3)*] = $($expr)*);
174    };
175
176    (($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident) => let $n1:ident, $n2:ident, $n3:ident = $($expr:tt)*) => {
177        sep_let!(($dx $x, $dy $y, $dz $z) =>
178            let $n1: [$x, $y, $z], $n2: [$y, $z, $x], $n3: [$z, $x, $y] = $($expr)*);
179    };
180}
181
182// xyzw cyclic permutation shortcut.
183// Simplifies cofactor repetition.
184macro_rules! sep_xyzw {
185    (($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident, $dw:tt $w:ident $(, $d:tt $var:ident)*)
186        => let $n1:ident : [$($v1:tt)*], $n2:ident : [$($v2:tt)*], $n3:ident : [$($v3:tt)*], $n4:ident : [$($v4:tt)*] = $($expr:tt)*) =>
187    {
188        sep_let!(($dx $x, $dy $y, $dz $z, $dw $w $(, $d $var)*) =>
189            let $n1: [$x, $y, $z, $w, $($v1)*], $n2: [$y, $z, $w, $x, $($v2)*], $n3: [$z, $w, $x, $y, $($v3)*], $n4: [$w, $x, $y, $z, $($v4)*]
190                = $($expr)*);
191    };
192
193    (($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident, $dw:tt $w:ident) => let $n1:ident, $n2:ident, $n3:ident, $n4:ident = $($expr:tt)*) => {
194        sep_let!(($dx $x, $dy $y, $dz $z, $dw $w) =>
195            let $n1: [$x, $y, $z, $w], $n2: [$y, $z, $w, $x], $n3: [$z, $w, $x, $y], $n4: [$w, $x, $y, $z] = $($expr)*);
196    };
197}
198
199// xyzw half even permutation shortcut.
200// Simplifies cofactor repitition.
201macro_rules! sep_xyzw6 {
202//    (($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident, $dw:tt $w:ident $(, $d:tt $var:ident)*)
203//        => let $nxy:ident : [$($vxy:tt)*], $nxz:ident : [$($vxz:tt)*], $nxw:ident : [$($vxw:tt)*]
204//               $nyz:ident : [$($vyz:tt)*], $nyw:ident : [$($vyw:tt)*], $nyx:ident : [$($vyx:tt)*]
205//               $nzw:ident : [$($vzw:tt)*], $nzx:ident : [$($vzx:tt)*], $nzy:ident : [$($vzy:tt)*]
206//               $nwx:ident : [$($vwz:tt)*], $nwy:ident : [$($vwy:tt)*], $nwz:ident : [$($vwz:tt)*]
207//        = $($expr:tt)*) =>
208//    {
209//        sep_let!(($dx $x, $dy $y, $dz $z $(, $d $var)*) =>
210//            let $nxy: [$x, $y, $z, $w, $($vxy)*], $nxz: [$x, $z, $w, $y, $($vxz)*], $nxw: [ $($v3)*] = $($expr)*);
211//    };
212
213    (($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident, $dw:tt $w:ident)
214        => let $nxy:ident, $nxz:ident, $nxw:ident, $nyz:ident, $nyw:ident, $nzw:ident = $($expr:tt)*) => {
215        sep_let!(($dx $x, $dy $y, $dz $z, $dw $w) =>
216            let $nxy: [$x, $y, $z, $w], $nxz: [$x, $z, $w, $y], $nxw: [$x, $w, $y, $z],
217                $nyz: [$y, $z, $x, $w], $nyw: [$y, $w, $z, $x], $nzw: [$z, $w, $x, $y],
218             = $($expr)*);
219    };
220
221    //// Because writing out all 12 terms is annoying
222    //(($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident, $dw:tt $w:ident)
223    //    => let $n:ident = $($expr:tt)*) => {
224    //    sep_let!(($dx $x, $dy $y, $dz $z) =>
225    //        let paste!([<$n$x$y>]): [$x, $y, $z, $w], paste!([<$n$x$z>]): [$x, $z, $w, $y], paste!([<$n$x$w>]): [$x, $w, $y, $z],
226    //            paste!([<$n$y$z>]): [$y, $z, $w, $x], paste!([<$n$y$w>]): [$y, $w, $x, $z], paste!([<$n$y$x>]): [$y, $x, $z, $w],
227    //            paste!([<$n$z$w>]): [$z, $w, $x, $y], paste!([<$n$z$x>]): [$z, $x, $y, $w], paste!([<$n$z$y>]): [$z, $y, $w, $x],
228    //            paste!([<$n$w$x>]): [$w, $x, $y, $z], paste!([<$n$w$y>]): [$w, $y, $z, $x], paste!([<$n$w$z>]): [$w, $z, $x, $y],
229    //         = $($expr)*);
230    //};
231}
232
233// 5 cyclic permutation shortcut.
234// Simplifies cofactor repetition.
235macro_rules! sep_5 {
236    (($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident, $dw:tt $w:ident, $dv:tt $u:ident $(, $d:tt $var:ident)*)
237        => let $n1:ident : [$($v1:tt)*],
238               $n2:ident : [$($v2:tt)*],
239               $n3:ident : [$($v3:tt)*],
240               $n4:ident : [$($v4:tt)*]
241               $n5:ident : [$($v5:tt)*] = $($expr:tt)*) =>
242    {
243        sep_let!(($dx $x, $dy $y, $dz $z, $dw $w, $du $u $(, $d $var)*) =>
244            let $n1: [$x, $y, $z, $w, $u, $($v1)*],
245                $n2: [$y, $z, $w, $u, $x, $($v2)*],
246                $n3: [$z, $w, $u, $x, $y, $($v3)*],
247                $n4: [$w, $u, $x, $y, $z, $($v4)*],
248                $n5: [$u, $x, $y, $z, $w, $($v5)*] = $($expr)*);
249    };
250
251    (($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident, $dw:tt $w:ident, $du:tt $u:ident)
252        => let $n1:ident, $n2:ident, $n3:ident, $n4:ident, $n5:ident = $($expr:tt)*) => {
253        sep_let!(($dx $x, $dy $y, $dz $z, $dw $w, $du $u) =>
254            let $n1: [$x, $y, $z, $w, $u],
255                $n2: [$y, $z, $w, $u, $x],
256                $n3: [$z, $w, $u, $x, $y],
257                $n4: [$w, $u, $x, $y, $z],
258                $n5: [$u, $x, $y, $z, $w] = $($expr)*);
259    };
260}
261
262// These regress performance, unfortunately
263//macro_rules! arr_let {
264//    (($($d:tt $var:ident),*) => let $name:ident : $([$($vals:tt)*]),* = $($expr:tt)*) => {
265//        macro_rules! __mac {
266//            ($($d $var:ident),*) => { $($expr)* };
267//            ($($d $var:expr),*) => { $($expr)* };
268//        }
269//
270//        let $name = [
271//            $(__mac!($($vals)*)),*
272//        ];
273//    };
274//}
275//
276//macro_rules! arr_xyz {
277//    (($dx:tt $x:ident, $dy:tt $y:ident, $dz:tt $z:ident) => let $name:ident = $($expr:tt)*) => {
278//        arr_let!(($dx $x, $dy $y, $dz $z) =>
279//            let $name: [[0, 1, 2], [1, 2, 0], [2, 0, 1]] = $($expr)*);
280//    };
281//}
282
283/// Calculates the orientation of points `a`, `b`, `c`, `d` in a space.
284/// Returns 6 times the volume of the tetrahedron formed by `a`, `b`, `c`, `d`,
285/// which is a positive number if `b`→`c`→`d` defines a left turn when looked at from `a`,
286/// a negative number if they define a right turn,
287/// and 0 if `a`, `b`, `c`, `d` are coplanar.
288pub fn orient_3d(a: Vec3, b: Vec3, c: Vec3, d: Vec3) -> f64 {
289    // vec![[0, 1, 2], [1, 2, 0], [2, 0, 1]].into_iter().map(|[x, y, z] stuff).sum::<f64>() regressed performance a lot
290    sep_xyz!(($x, $y, $z) => let cof1, cof2, cof3 = 
291        (a.$z - d.$z) * ((b.$x - d.$x) * (c.$y - d.$y) - (b.$y - d.$y) * (c.$x - d.$x)));
292    let det = cof1 + cof2 + cof3;
293
294    sep_xyz!(($x, $y, $z) => let cof1_sum, cof2_sum, cof3_sum = 
295        (a.$z - d.$z).abs() * (((b.$x - d.$x) * (c.$y - d.$y)).abs() + ((b.$y - d.$y) * (c.$x - d.$x)).abs()));
296    let det_sum = cof1_sum + cof2_sum + cof3_sum;
297
298    if det.abs() >= det_sum * ORIENT_3D_BOUND_A {
299        det
300    } else {
301        orient_3d_adapt(a, b, c, d, det_sum)
302    }
303}
304
305fn orient_3d_adapt(a: Vec3, b: Vec3, c: Vec3, d: Vec3, det_sum: f64) -> f64 {
306    sep_xyz!(($x, $y, $z) => let sub1, sub2, sub3 = (two_product(b.$x - d.$x, c.$y - d.$y) - two_product(b.$y - d.$y, c.$x - d.$x)).dynamic());
307    sep_xyz!(($x, $y, $z, $s) => let cof1: [sub1], cof2: [sub2], cof3: [sub3] = $s.scale_expansion(a.$z - d.$z));
308    let det_hi = cof1 + cof2 + cof3;
309    let det_approx = det_hi.approximate();
310
311    if det_approx.abs() >= det_sum * ORIENT_3D_BOUND_B {
312        return det_approx;
313    }
314
315    // Correction factor for order ε² error bound
316    sep_xyz!(($x, $y, $z) => let ax, ay, az = two_sum(a.$x, -d.$x));
317    sep_xyz!(($x, $y, $z) => let bx, by, bz = two_sum(b.$x, -d.$x));
318    sep_xyz!(($x, $y, $z) => let cx, cy, cz = two_sum(c.$x, -d.$x));
319    sep_xyz!(($x, $y, $z) => let cof1_m1, cof2_m1, cof3_m1 =
320        paste!([<a$z>][0] * ([<b$x>][1] * [<c$y>][1] - [<b$y>][1] * [<c$x>][1])));
321    sep_xyz!(($x, $y, $z) => let cof1_m2, cof2_m2, cof3_m2 =
322        paste!([<a$z>][1] * (([<b$x>][0] * [<c$y>][1] + [<b$x>][1] * [<c$y>][0]) - ([<b$y>][0] * [<c$x>][1] + [<b$y>][1] * [<c$x>][0]))));
323    let det = cof1_m1 + cof2_m1 + cof3_m1 + cof1_m2 + cof2_m2 + cof3_m2 + det_approx;
324
325    if det.abs() >= ORIENT_3D_BOUND_C1 * det_approx.abs() + ORIENT_3D_BOUND_C2 * det_sum {
326        return det;
327    }
328
329    // Exact result time!
330    let det_m1 =
331        sub1.scale_expansion(az[0]) + sub2.scale_expansion(ax[0]) + sub3.scale_expansion(ay[0]);
332    sep_xyz!(($x, $y, $z) => let cof1x, cof1y, cof1z =
333        paste!((two_product([<b$x>][0], [<c$y>][1]) + two_product([<b$x>][1], [<c$y>][0]) + two_product([<b$x>][0], [<c$y>][0])).dynamic()));
334    sep_xyz!(($x, $y, $z) => let cof2x, cof2y, cof2z =
335        paste!((two_product([<b$y>][0], [<c$x>][1]) + two_product([<b$y>][1], [<c$x>][0]) + two_product([<b$y>][0], [<c$x>][0])).dynamic()));
336    sep_xyz!(($x, $y, $z) => let cof1, cof2, cof3 = paste!([<cof1$x>] - [<cof2$x>]));
337    let det_m2 =
338        cof1.scale_expansion(az[1]) + cof2.scale_expansion(ax[1]) + cof3.scale_expansion(ay[1]);
339    let det_lo =
340        cof1.scale_expansion(az[0]) + cof2.scale_expansion(ax[0]) + cof3.scale_expansion(ay[0]);
341    let det = (det_hi + det_m1) + (det_m2 + det_lo);
342    det.approximate()
343}
344
345/// Returns a positive number if `d` is inside the oriented circle that goes through `a`, `b`, `c`,
346/// a negative number if it lies outside,
347/// and 0 if `a`, `b`, `c`, `d` are cocircular or `a`, `b`, `c` are collinear.
348/// If `a`, `b`, `c` are in counterclockwise order, "inside the circle" is the inside.
349/// If `a`, `b`, `c` are in clockwise order, "inside the circle" is the outside.
350pub fn in_circle(a: Vec2, b: Vec2, c: Vec2, d: Vec2) -> f64 {
351    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = ($a.x - d.x) * ($a.x - d.x) + ($a.y - d.y) * ($a.y - d.y));
352    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 =
353        paste!([<sq$a>]) * (($b.x - d.x) * ($c.y - d.y) - ($b.y - d.y) * ($c.x - d.x)));
354    let det = cof1 + cof2 + cof3;
355
356    sep_xyz!(($a, $b, $c) => let cof1_sum, cof2_sum, cof3_sum =
357        paste!([<sq$a>]) * ((($b.x - d.x) * ($c.y - d.y)).abs() + (($b.y - d.y) * ($c.x - d.x)).abs()));
358    let det_sum = cof1_sum + cof2_sum + cof3_sum;
359
360    if det.abs() >= det_sum * IN_CIRCLE_BOUND_A {
361        det
362    } else {
363        in_circle_adapt(a, b, c, d, det_sum)
364    }
365}
366
367fn in_circle_adapt(a: Vec2, b: Vec2, c: Vec2, d: Vec2, det_sum: f64) -> f64 {
368    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = (square($a.x - d.x) + square($a.y - d.y)).dynamic());
369    sep_xyz!(($a, $b, $c) => let suba, subb, subc = (two_product($b.x - d.x, $c.y - d.y) - two_product($b.y - d.y, $c.x - d.x)).dynamic());
370    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!([<sq$a>] * [<sub$a>]));
371    let det = cof1 + cof2 + cof3;
372    let det_approx = det.approximate();
373
374    if det_approx.abs() >= det_sum * IN_CIRCLE_BOUND_B {
375        return det_approx;
376    }
377
378    // Correction factor for order ε² error bound
379    sep_xyz!(($a, $b, $c) => let xa, xb, xc = two_sum($a.x, -d.x));
380    sep_xyz!(($a, $b, $c) => let ya, yb, yc = two_sum($a.y, -d.y));
381    sep_xyz!(($a, $b, $c) => let cof1_m1, cof2_m1, cof3_m1 =
382        paste!(([<x$a>][0] * [<x$a>][1] + [<y$a>][0] * [<y$a>][1])
383            * ([<x$b>][1] * [<y$c>][1] - [<y$b>][1] * [<x$c>][1])));
384    sep_xyz!(($a, $b, $c) => let cof1_m2, cof2_m2, cof3_m2 =
385        paste!(([<x$a>][1] * [<x$a>][1] + [<y$a>][1] * [<y$a>][1])
386            * (([<x$b>][0] * [<y$c>][1] + [<x$b>][1] * [<y$c>][0]) - ([<y$b>][0] * [<x$c>][1] + [<y$b>][1] * [<x$c>][0]))));
387    let det = (cof1_m1 + cof2_m1 + cof3_m1) * 2.0 + cof1_m2 + cof2_m2 + cof3_m2 + det_approx;
388
389    if det.abs() >= IN_CIRCLE_BOUND_C1 * det_approx.abs() + IN_CIRCLE_BOUND_C2 * det_sum {
390        return det;
391    }
392
393    // Exact result time!
394    // Let's be lazy
395    sep_xyz!(($a, $b, $c) => let xa, xb, xc = paste!([<x$a>].dynamic()));
396    sep_xyz!(($a, $b, $c) => let ya, yb, yc = paste!([<y$a>].dynamic()));
397    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = paste!(&[<x$a>] * &[<x$a>] + &[<y$a>] * &[<y$a>]));
398    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!(&[<sq$a>] * (&[<x$b>] * &[<y$c>] - &[<y$b>] * &[<x$c>])));
399    let det = cof1 + cof2 + cof3;
400    det.highest_magnitude()
401}
402
403/// Returns a positive number if `e` is inside the oriented sphere that goes through `a`, `b`, `c`, `d`,
404/// a negative number if it lies outside,
405/// and 0 if `a`, `b`, `c`, `d`, `e` are cospherical or `a`, `b`, `c`, `d` are coplanar.
406/// If `a`, `b`, `c`, `d` are oriented positive, "inside the sphere" is the inside.
407/// If `a`, `b`, `c`, `d` are oriented negative, "inside the sphere" is the outside.
408pub fn in_sphere(a: Vec3, b: Vec3, c: Vec3, d: Vec3, e: Vec3) -> f64 {
409    // Oh boy. 4x4 determinant, *lots* of cofactors.
410    sep_xyzw!(($a, $b, $c, $d) => let sqa, sqb, sqc, sqd =
411        ($a.x - e.x) * ($a.x - e.x) + ($a.y - e.y) * ($a.y - e.y) + ($a.z - e.z) * ($a.z - e.z));
412    sep_xyzw6!(($a, $b, $c, $d) => let cof1, cof2, cof3, cof4, cof5, cof6 =
413        paste!((($a.x - e.x) * ($b.y - e.y) - ($a.y - e.y) * ($b.x - e.x)) *
414               (($c.z - e.z) * [<sq$d>] - [<sq$c>] * ($d.z - e.z))));
415    let det = (cof1 + cof2 + cof3) + (cof4 + cof5 + cof6);
416
417    sep_xyzw6!(($a, $b, $c, $d) => let cof1_sum, cof2_sum, cof3_sum, cof4_sum, cof5_sum, cof6_sum =
418        paste!(((($a.x - e.x) * ($b.y - e.y)).abs() + (($a.y - e.y) * ($b.x - e.x)).abs()) *
419               ((($c.z - e.z) * [<sq$d>]).abs() + ([<sq$c>] * ($d.z - e.z)).abs())));
420    let det_sum = (cof1_sum + cof2_sum + cof3_sum) + (cof4_sum + cof5_sum + cof6_sum);
421
422    if det.abs() >= det_sum * IN_SPHERE_BOUND_A {
423        det
424    } else {
425        in_sphere_adapt(a, b, c, d, e, det_sum)
426    }
427}
428
429fn in_sphere_adapt(a: Vec3, b: Vec3, c: Vec3, d: Vec3, e: Vec3, det_sum: f64) -> f64 {
430    sep_xyzw!(($a, $b, $c, $d) => let sqa, sqb, sqc, sqd = (square($a.x - e.x) + square($a.y - e.y)).dynamic() + square($a.z - e.z).dynamic());
431    sep_xyzw6!(($a, $b, $c, $d) => let cof1, cof2, cof3, cof4, cof5, cof6 =
432        paste!((two_product(($a.x - e.x), ($b.y - e.y)) - two_product(($a.y - e.y), ($b.x - e.x))).dynamic() *
433               ([<sq$d>].scale_expansion($c.z - e.z) - [<sq$c>].scale_expansion($d.z - e.z))));
434    let det = (cof1 + cof2 + cof3) + (cof4 + cof5 + cof6); // Balanced summation is faster
435    let det_approx = det.approximate();
436
437    if det_approx.abs() >= det_sum * IN_SPHERE_BOUND_B {
438        return det_approx;
439    }
440
441    sep_xyzw!(($a, $b, $c, $d) => let xa, xb, xc, xd = two_sum($a.x, -e.x));
442    sep_xyzw!(($a, $b, $c, $d) => let ya, yb, yc, yd = two_sum($a.y, -e.y));
443    sep_xyzw!(($a, $b, $c, $d) => let za, zb, zc, zd = two_sum($a.z, -e.z));
444
445    sep_xyzw!(($a, $b, $c, $d) => let sqa, sqb, sqc, sqd =
446        paste!([<x$a>][1] * [<x$a>][1] + [<y$a>][1] * [<y$a>][1] + [<z$a>][1] * [<z$a>][1]));
447    sep_xyzw!(($a, $b, $c, $d) => let sra, srb, src, srd =
448        paste!(([<x$a>][0] * [<x$a>][1] + [<y$a>][0] * [<y$a>][1] + [<z$a>][0] * [<z$a>][1]) * 2.0));
449
450    sep_xyzw6!(($a, $b, $c, $d) => let c11, c21, c31, c41, c51, c61 =
451        paste!((([<x$a>][0] * [<y$b>][1] + [<x$a>][1] * [<y$b>][0]) - ([<y$a>][0] * [<x$b>][1] + [<y$a>][1] * [<x$b>][0])) *
452               ([<z$c>][1] * [<sq$d>] - [<sq$c>] * [<z$d>][1])));
453    sep_xyzw6!(($a, $b, $c, $d) => let c12, c22, c32, c42, c52, c62 =
454        paste!(([<x$a>][1] * [<y$b>][1] - [<y$a>][1] * [<x$b>][1]) *
455               (([<z$c>][0] * [<sq$d>] + [<z$c>][1] * [<sr$d>]) - ([<sr$c>] * [<z$d>][1] + [<sq$c>] * [<z$d>][0]))));
456    let det = ((c11 + c21 + c31) + (c41 + c51 + c61))
457        + ((c12 + c22 + c32) + (c42 + c52 + c62))
458        + det_approx;
459
460    if det.abs() >= IN_SPHERE_BOUND_C1 * det_approx.abs() + IN_SPHERE_BOUND_C2 * det_sum {
461        det
462    } else {
463        in_sphere_adapt2(a, b, c, d, e)
464    }
465}
466
467fn in_sphere_adapt2(a: Vec3, b: Vec3, c: Vec3, d: Vec3, e: Vec3) -> f64 {
468    // Exact result time!
469    // Calculating the 5x5 determinant because it's less straining on the stack
470    sep_5!(($a, $b, $c, $d, $e) => let sqa, sqb, sqc, sqd, sqe =
471        (square($a.x) + square($a.y)).dynamic() + square($a.z).dynamic());
472    sep_5!(($a, $b, $c, $d, $e) => let ab, bc, cd, de, ea = (two_product($a.x, $b.y) - two_product($b.x, $a.y)).dynamic());
473    sep_5!(($a, $b, $c, $d, $e) => let ac, bd, ce, da, eb = (two_product($a.x, $c.y) - two_product($c.x, $a.y)).dynamic());
474    sep_5!(($a, $b, $c, $d, $e) => let nab, nbc, ncd, nde, nea = paste!(
475        [<$c$d>].scale_expansion($e.z) + [<$d$e>].scale_expansion($c.z) + [<$c$e>].scale_expansion(-$d.z)));
476    sep_5!(($a, $b, $c, $d, $e) => let nad, nbe, nca, ndb, nec = paste!(
477        [<$b$c>].scale_expansion($e.z) + [<$c$e>].scale_expansion($b.z) + [<$e$b>].scale_expansion($c.z)));
478    sep_5!(($a, $b, $c, $d, $e) => let cof1, cof2, cof3, cof4, cof5 = paste!(
479        (&[<n$e$a>] * &[<sq$e>] - &[<n$a$d>] * &[<sq$d>]) + (&[<n$c$a>] * &[<sq$c>] - &[<n$a$b>] * &[<sq$b>])));
480    let det = (cof1 + cof2) + (cof3 + cof4) + cof5;
481    det.highest_magnitude()
482}
483
484/// Compares the magnitude of `a` and `b`
485/// and returns a positive number if `a`'s magnitude is greater,
486/// a negative number if `b`'s magnitude is greater,
487/// and 0 if their magnitudes equal.
488pub fn magnitude_cmp_2d(a: Vec2, b: Vec2) -> f64 {
489    let sqa = a.x * a.x + a.y * a.y;
490    let sqb = b.x * b.x + b.y * b.y;
491    let det = sqa - sqb;
492
493    if det.abs() >= (sqa + sqb) * MAGNITUDE_CMP_2D_BOUND_A {
494        det
495    } else {
496        magnitude_cmp_2d_adapt(a, b)
497    }
498}
499
500fn magnitude_cmp_2d_adapt(a: Vec2, b: Vec2) -> f64 {
501    let sqa = (square(a.x) + square(a.y)).dynamic();
502    let sqb = (square(b.x) + square(b.y)).dynamic();
503    (sqa - sqb).highest_magnitude()
504}
505
506/// Compares the magnitude of `a` and `b`
507/// and returns a positive number if `a`'s magnitude is greater,
508/// a negative number if `b`'s magnitude is greater,
509/// and 0 if their magnitudes equal.
510pub fn magnitude_cmp_3d(a: Vec3, b: Vec3) -> f64 {
511    let sqa = a.x * a.x + a.y * a.y + a.z * a.z;
512    let sqb = b.x * b.x + b.y * b.y + b.z * b.z;
513    let det = sqa - sqb;
514
515    if det.abs() >= (sqa + sqb) * MAGNITUDE_CMP_3D_BOUND_A {
516        det
517    } else {
518        magnitude_cmp_3d_adapt(a, b)
519    }
520}
521
522fn magnitude_cmp_3d_adapt(a: Vec3, b: Vec3) -> f64 {
523    let sqa = (square(a.x) + square(a.y)).dynamic() + square(a.z).dynamic();
524    let sqb = (square(b.x) + square(b.y)).dynamic() + square(b.z).dynamic();
525    (sqa - sqb).highest_magnitude()
526}
527
528/// Compares the distance of `a` and `b` to `c`
529/// and returns a positive number if `a`'s distance to `c` is greater,
530/// a negative number if `b`'s distance to `c` is greater,
531/// and 0 if their distances equal.
532pub fn distance_cmp_3d(a: Vec3, b: Vec3, c: Vec3) -> f64 {
533    let ac = a - c;
534    let bc = b - c;
535    let sqa = ac.x * ac.x + ac.y * ac.y + ac.z * ac.z;
536    let sqb = bc.x * bc.x + bc.y * bc.y + bc.z * bc.z;
537    let det = sqa - sqb;
538
539    if det.abs() >= (sqa + sqb) * DISTANCE_CMP_3D_BOUND_A {
540        det
541    } else {
542        distance_cmp_3d_adapt(a, b, c)
543    }
544}
545
546fn distance_cmp_3d_adapt(a: Vec3, b: Vec3, c: Vec3) -> f64 {
547    sep_xyz!(($x, $y, $z) => let ax, ay, az = two_sum(a.$x, -c.$x).dynamic());
548    sep_xyz!(($x, $y, $z) => let bx, by, bz = two_sum(b.$x, -c.$x).dynamic());
549    let sqa = &ax * &ax + &ay * &ay + &az * &az;
550    let sqb = &bx * &bx + &by * &by + &bz * &bz;
551    (sqa - sqb).highest_magnitude()
552}
553
554/// Computes the determinant of the following matrix
555/// ```notrust
556/// ┌─                       ─┐
557/// │ a.x   a.x^2 + a.y^2   1 │
558/// │ b.x   b.x^2 + b.y^2   1 │
559/// │ c.x   c.x^2 + c.y^2   1 │
560/// └─                       ─┘
561/// ```
562/// and returns a number with the same sign as the determinant,
563/// or 0 if the determinant is 0
564pub fn sign_det_x_x2y2(a: Vec2, b: Vec2, c: Vec2) -> f64 {
565    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = $a.x * $a.x + $a.y * $a.y);
566    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!([<sq$a>] * ($c.x - $b.x)));
567    let det = cof1 + cof2 + cof3;
568
569    sep_xyz!(($a, $b, $c) => let cof1_sum, cof2_sum, cof3_sum = paste!([<sq$a>] * ($c.x - $b.x).abs()));
570    let det_sum = cof1_sum + cof2_sum + cof3_sum;
571
572    if det.abs() >= det_sum * SIGN_DET_X_X2Y2_BOUND_A {
573        det
574    } else {
575        sign_det_x_x2y2_adapt(a, b, c)
576    }
577}
578
579fn sign_det_x_x2y2_adapt(a: Vec2, b: Vec2, c: Vec2) -> f64 {
580    // Compute exact value at least for now
581    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = (square($a.x) + square($a.y)).dynamic());
582    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!([<sq$a>] * two_sum($c.x, -$b.x).dynamic()));
583    let det = cof1 + cof2 + cof3;
584    det.highest_magnitude()
585}
586
587/// Computes the determinant of the following matrix
588/// ```notrust
589/// ┌─                               ─┐
590/// │ a.x   a.x^2 + a.y^2 + a.z^2   1 │
591/// │ b.x   b.x^2 + b.y^2 + b.z^2   1 │
592/// │ c.x   c.x^2 + c.y^2 + c.z^2   1 │
593/// └─                               ─┘
594/// ```
595/// and returns a number with the same sign as the determinant,
596/// or 0 if the determinant is 0
597pub fn sign_det_x_x2y2z2(a: Vec3, b: Vec3, c: Vec3) -> f64 {
598    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = $a.x * $a.x + $a.y * $a.y + $a.z * $a.z);
599    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!([<sq$a>] * ($c.x - $b.x)));
600    let det = cof1 + cof2 + cof3;
601
602    sep_xyz!(($a, $b, $c) => let cof1_sum, cof2_sum, cof3_sum = paste!([<sq$a>] * ($c.x - $b.x).abs()));
603    let det_sum = cof1_sum + cof2_sum + cof3_sum;
604
605    if det.abs() >= det_sum * SIGN_DET_X_X2Y2Z2_BOUND_A {
606        det
607    } else {
608        sign_det_x_x2y2z2_adapt(a, b, c)
609    }
610}
611
612fn sign_det_x_x2y2z2_adapt(a: Vec3, b: Vec3, c: Vec3) -> f64 {
613    // Compute exact value at least for now
614    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = (square($a.x) + square($a.y)).dynamic() + square($a.z).dynamic());
615    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!([<sq$a>] * two_sum($c.x, -$b.x).dynamic()));
616    let det = cof1 + cof2 + cof3;
617    det.highest_magnitude()
618}
619
620/// Computes the determinant of the following matrix
621/// ```notrust
622/// ┌─                                     ─┐
623/// │ a.x   a.y   a.x^2 + a.y^2 + a.z^2   1 │
624/// │ b.x   b.y   b.x^2 + b.y^2 + b.z^2   1 │
625/// │ c.x   c.y   c.x^2 + c.y^2 + c.z^2   1 │
626/// │ d.x   d.y   d.x^2 + d.y^2 + d.z^2   1 │
627/// └─                                     ─┘
628/// ```
629/// and returns a number with the same sign as the determinant,
630/// or 0 if the determinant is 0
631pub fn sign_det_x_y_x2y2z2(a: Vec3, b: Vec3, c: Vec3, d: Vec3) -> f64 {
632    sep_xyzw!(($a, $b, $c, $d) => let sqa, sqb, sqc, sqd = $a.x * $a.x + $a.y * $a.y + $a.z * $a.z);
633    sep_xyzw6!(($a, $b, $c, $d) => let cof1, cof2, cof3, cof4, cof5, cof6 =
634        paste!(($a.x * $b.y - $a.y * $b.x) * ([<sq$c>] - [<sq$d>])));
635    let det = (cof1 + cof2 + cof3) + (cof4 + cof5 + cof6);
636
637    sep_xyzw6!(($a, $b, $c, $d) => let cof1_sum, cof2_sum, cof3_sum, cof4_sum, cof5_sum, cof6_sum =
638        paste!((($a.x * $b.y).abs() + ($a.y * $b.x).abs()) * ([<sq$c>] + [<sq$d>])));
639    let det_sum = (cof1_sum + cof2_sum + cof3_sum) + (cof4_sum + cof5_sum + cof6_sum);
640
641    if det.abs() >= det_sum * SIGN_DET_X_Y_X2Y2Z2_BOUND_A {
642        det
643    } else {
644        sign_det_x_y_x2y2z2_adapt(a, b, c, d)
645    }
646}
647
648fn sign_det_x_y_x2y2z2_adapt(a: Vec3, b: Vec3, c: Vec3, d: Vec3) -> f64 {
649    // Compute exact value at least for now
650    sep_xyzw!(($a, $b, $c, $d) => let sqa, sqb, sqc, sqd = (square($a.x) + square($a.y)).dynamic() + square($a.z).dynamic());
651    sep_xyzw6!(($a, $b, $c, $d) => let cof1, cof2, cof3, cof4, cof5, cof6 =
652        paste!((two_product($a.x, $b.y) - two_product($a.y, $b.x)).dynamic() * (&[<sq$c>] - &[<sq$d>])));
653    let det = (cof1 + cof2 + cof3) + (cof4 + cof5 + cof6);
654    det.highest_magnitude()
655}
656
657/// Computes the following sum of determinants
658/// ```notrust
659/// │ a.x   a.y   a.x^2 + a.y^2 + a.z^2   1 │     │ i.x   i.y   i.z   1 │
660/// │ b.x   b.y   b.x^2 + b.y^2 + b.z^2   1 │ + 2u│ j.x   j.y   j.z   1 │
661/// │ c.x   c.y   c.x^2 + c.y^2 + c.z^2   1 │     │ k.x   k.y   k.z   1 │
662/// │ d.x   d.y   d.x^2 + d.y^2 + d.z^2   1 │     │ l.x   l.y   l.z   1 │
663/// ```
664/// and returns a number with the same sign as the sum,
665/// or 0 if the sum is 0
666pub fn sign_det_x_y_x2y2z2_plus_2x_det_x_y_z(a: Vec3, b: Vec3, c: Vec3, d: Vec3, u: f64, i: Vec3, j: Vec3, k: Vec3, l: Vec3) -> f64 {
667    sep_xyzw!(($a, $b, $c, $d) => let sqa, sqb, sqc, sqd = $a.x * $a.x + $a.y * $a.y + $a.z * $a.z);
668    sep_xyzw6!(($a, $b, $c, $d) => let cof1, cof2, cof3, cof4, cof5, cof6 =
669        paste!(($a.x * $b.y - $a.y * $b.x) * ([<sq$c>] - [<sq$d>])));
670    let det1 = (cof1 + cof2 + cof3) + (cof4 + cof5 + cof6);
671
672    sep_xyzw6!(($a, $b, $c, $d) => let cof1_sum, cof2_sum, cof3_sum, cof4_sum, cof5_sum, cof6_sum =
673        paste!((($a.x * $b.y).abs() + ($a.y * $b.x).abs()) * ([<sq$c>] + [<sq$d>])));
674    let det_sum1 = (cof1_sum + cof2_sum + cof3_sum) + (cof4_sum + cof5_sum + cof6_sum);
675
676    sep_xyzw6!(($i, $j, $k, $l) => let cof1, cof2, cof3, cof4, cof5, cof6 =
677        paste!(($i.x * $j.y - $i.y * $j.x) * ($k.z - $l.z)));
678    let det2 = ((cof1 + cof2 + cof3) + (cof4 + cof5 + cof6)) * u * 2.0;
679
680    sep_xyzw6!(($i, $j, $k, $l) => let cof1_sum, cof2_sum, cof3_sum, cof4_sum, cof5_sum, cof6_sum =
681        paste!((($i.x * $j.y).abs() + ($i.y * $j.x).abs()) * ($k.z - $l.z).abs()));
682    let det_sum2 = ((cof1_sum + cof2_sum + cof3_sum) + (cof4_sum + cof5_sum + cof6_sum)) * u.abs() * 2.0;
683
684    if (det1 + det2).abs() >= (det_sum1 + det_sum2) * SIGN_DET_X_Y_X2Y2Z2_PLUS_2X_DET_X_Y_Z_BOUND_A {
685        det1 + det2
686    } else {
687        sign_det_x_y_x2y2z2_plus_2x_det_x_y_z_adapt(a, b, c, d, u, i, j, k, l)
688    }
689}
690
691fn sign_det_x_y_x2y2z2_plus_2x_det_x_y_z_adapt(a: Vec3, b: Vec3, c: Vec3, d: Vec3, u: f64, i: Vec3, j: Vec3, k: Vec3, l: Vec3) -> f64 {
692    // Compute exact value at least for now
693    sep_xyzw!(($a, $b, $c, $d) => let sqa, sqb, sqc, sqd = (square($a.x) + square($a.y)).dynamic() + square($a.z).dynamic());
694    sep_xyzw6!(($a, $b, $c, $d) => let cof1, cof2, cof3, cof4, cof5, cof6 =
695        paste!((two_product($a.x, $b.y) - two_product($a.y, $b.x)).dynamic() * (&[<sq$c>] - &[<sq$d>])));
696    let det1 = (cof1 + cof2 + cof3) + (cof4 + cof5 + cof6);
697
698    sep_xyzw6!(($i, $j, $k, $l) => let cof1, cof2, cof3, cof4, cof5, cof6 =
699        paste!((two_product($i.x, $j.y) - two_product($i.y, $j.x)).dynamic() * two_sum($k.z, -$l.z).dynamic()));
700    let det2 = ((cof1 + cof2 + cof3) + (cof4 + cof5 + cof6)).scale_expansion(u * 2.0);
701
702    (det1 + det2).highest_magnitude()
703}
704
705/// Computes the following sum of determinants. 
706/// ```notrust
707/// │ a.x   a.x^2 + a.y^2 + a.z^2   1 │     │ i.x   i.y   1 │
708/// │ b.x   b.x^2 + b.y^2 + b.z^2   1 │ + 2u│ j.x   j.y   1 │
709/// │ c.x   c.x^2 + c.y^2 + c.z^2   1 │     │ k.x   k.y   1 │
710/// ```
711/// and returns a number with the same sign as the sum,
712/// or 0 if the sum is 0
713pub fn sign_det_x_x2y2z2_plus_2x_det_x_y(a: Vec3, b: Vec3, c: Vec3, u: f64, i: Vec2, j: Vec2, k: Vec2) -> f64 {
714    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = $a.x * $a.x + $a.y * $a.y + $a.z * $a.z);
715    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!([<sq$a>] * ($c.x - $b.x)));
716    let det1 = cof1 + cof2 + cof3;
717
718    sep_xyz!(($a, $b, $c) => let cof1_sum, cof2_sum, cof3_sum = paste!([<sq$a>] * ($c.x - $b.x).abs()));
719    let det_sum1 = cof1_sum + cof2_sum + cof3_sum;
720
721    sep_xyz!(($i, $j, $k) => let cof1, cof2, cof3 = paste!($i.x * ($j.y - $k.y)));
722    let det2 = (cof1 + cof2 + cof3) * u * 2.0;
723
724    sep_xyz!(($i, $j, $k) => let cof1_sum, cof2_sum, cof3_sum = paste!($i.x.abs() * ($j.y - $k.y).abs()));
725    let det_sum2 = (cof1_sum + cof2_sum + cof3_sum) * u.abs() * 2.0;
726
727    if (det1 + det2).abs() >= (det_sum1 + det_sum2) * SIGN_DET_X_X2Y2Z2_PLUS_2X_DET_X_Y_BOUND_A {
728        det1 + det2
729    } else {
730        sign_det_x_x2y2z2_plus_2x_det_x_y_adapt(a, b, c, u, i, j, k)
731    }
732}
733
734fn sign_det_x_x2y2z2_plus_2x_det_x_y_adapt(a: Vec3, b: Vec3, c: Vec3, u: f64, i: Vec2, j: Vec2, k: Vec2) -> f64 {
735    // Compute exact value at least for now
736    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = (square($a.x) + square($a.y)).dynamic() + square($a.z).dynamic());
737    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!([<sq$a>] * two_sum($c.x, -$b.x).dynamic()));
738    let det1 = cof1 + cof2 + cof3;
739
740    sep_xyz!(($i, $j, $k) => let cof1, cof2, cof3 = paste!(two_sum($j.y, -$k.y).dynamic().scale_expansion($i.x)));
741    let det2 = (cof1 + cof2 + cof3).scale_expansion(2.0 * u);
742
743    (det1 + det2).highest_magnitude()
744}
745
746/// Computes the following sum of determinants. 
747/// ```notrust
748/// │ a.x^2 + a.y^2 + a.z^2   1 │ + 2u│ i   1 │
749/// │ b.x^2 + b.y^2 + b.z^2   1 │     │ j   1 │
750/// ```
751/// and returns a number with the same sign as the sum,
752/// or 0 if the sum is 0
753pub fn sign_det_x2y2z2_plus_2x_det_x(a: Vec3, b: Vec3, u: f64, i: f64, j: f64) -> f64 {
754    let sqa = a.x * a.x + a.y * a.y + a.z * a.z;
755    let sqb = b.x * b.x + b.y * b.y + b.z * b.z;
756    let det1 = sqa - sqb;
757    let det2 = (i - j) * u * 2.0;
758    let det_sum = sqa + sqb + det2.abs();
759
760    if (det1 + det2).abs() >= det_sum * SIGN_DET_X2Y2Z2_PLUS_2X_DET_X_BOUND_A {
761        det1 + det2
762    } else {
763        sign_det_x2y2z2_plus_2x_det_x_adapt(a, b, u, i, j)
764    }
765}
766
767fn sign_det_x2y2z2_plus_2x_det_x_adapt(a: Vec3, b: Vec3, u: f64, i: f64, j: f64) -> f64 {
768    let sqa = (square(a.x) + square(a.y)).dynamic() + square(a.z).dynamic();
769    let sqb = (square(b.x) + square(b.y)).dynamic() + square(b.z).dynamic();
770    let diff = two_sum(i, -j).scale_expansion(u * 2.0).dynamic();
771    (sqa - sqb + diff).highest_magnitude()
772}
773
774
775/// Computes the following sum of determinants. 
776/// ```notrust
777/// │ a.x   a.x^2 + a.y^2   1 │     │ i.x   i.y   1 │
778/// │ b.x   b.x^2 + b.y^2   1 │ + 2u│ j.x   j.y   1 │
779/// │ c.x   c.x^2 + c.y^2   1 │     │ k.x   k.y   1 │
780/// ```
781/// and returns a number with the same sign as the sum,
782/// or 0 if the sum is 0
783pub fn sign_det_x_x2y2_plus_2x_det_x_y(a: Vec2, b: Vec2, c: Vec2, u: f64, i: Vec2, j: Vec2, k: Vec2) -> f64 {
784    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = $a.x * $a.x + $a.y * $a.y);
785    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!([<sq$a>] * ($c.x - $b.x)));
786    let det1 = cof1 + cof2 + cof3;
787
788    sep_xyz!(($a, $b, $c) => let cof1_sum, cof2_sum, cof3_sum = paste!([<sq$a>] * ($c.x - $b.x).abs()));
789    let det_sum1 = cof1_sum + cof2_sum + cof3_sum;
790
791    sep_xyz!(($i, $j, $k) => let cof1, cof2, cof3 = paste!($i.x * ($j.y - $k.y)));
792    let det2 = (cof1 + cof2 + cof3) * u * 2.0;
793
794    sep_xyz!(($i, $j, $k) => let cof1_sum, cof2_sum, cof3_sum = paste!($i.x.abs() * ($j.y - $k.y).abs()));
795    let det_sum2 = (cof1_sum + cof2_sum + cof3_sum) * u.abs() * 2.0;
796
797    if (det1 + det2).abs() >= (det_sum1 + det_sum2) * SIGN_DET_X_X2Y2_PLUS_2X_DET_X_Y_BOUND_A {
798        det1 + det2
799    } else {
800        sign_det_x_x2y2_plus_2x_det_x_y_adapt(a, b, c, u, i, j, k)
801    }
802}
803
804fn sign_det_x_x2y2_plus_2x_det_x_y_adapt(a: Vec2, b: Vec2, c: Vec2, u: f64, i: Vec2, j: Vec2, k: Vec2) -> f64 {
805    // Compute exact value at least for now
806    sep_xyz!(($a, $b, $c) => let sqa, sqb, sqc = (square($a.x) + square($a.y)).dynamic());
807    sep_xyz!(($a, $b, $c) => let cof1, cof2, cof3 = paste!([<sq$a>] * two_sum($c.x, -$b.x).dynamic()));
808    let det1 = cof1 + cof2 + cof3;
809
810    sep_xyz!(($i, $j, $k) => let cof1, cof2, cof3 = paste!(two_sum($j.y, -$k.y).dynamic().scale_expansion($i.x)));
811    let det2 = (cof1 + cof2 + cof3).scale_expansion(2.0 * u);
812
813    (det1 + det2).highest_magnitude()
814}
815
816/// Computes the following sum of determinants. 
817/// ```notrust
818/// │ a.x^2 + a.y^2   1 │ + 2u│ i   1 │
819/// │ b.x^2 + b.y^2   1 │     │ j   1 │
820/// ```
821/// and returns a number with the same sign as the sum,
822/// or 0 if the sum is 0
823pub fn sign_det_x2y2_plus_2x_det_x(a: Vec2, b: Vec2, u: f64, i: f64, j: f64) -> f64 {
824    let sqa = a.x * a.x + a.y * a.y;
825    let sqb = b.x * b.x + b.y * b.y;
826    let det1 = sqa - sqb;
827    let det2 = (i - j) * u * 2.0;
828    let det_sum = sqa + sqb + det2.abs();
829
830    if (det1 + det2).abs() >= det_sum * SIGN_DET_X2Y2_PLUS_2X_DET_X_BOUND_A {
831        det1 + det2
832    } else {
833        sign_det_x2y2_plus_2x_det_x_adapt(a, b, u, i, j)
834    }
835}
836
837fn sign_det_x2y2_plus_2x_det_x_adapt(a: Vec2, b: Vec2, u: f64, i: f64, j: f64) -> f64 {
838    let sqa = (square(a.x) + square(a.y)).dynamic();
839    let sqb = (square(b.x) + square(b.y)).dynamic();
840    let diff = two_sum(i, -j).scale_expansion(u * 2.0).dynamic();
841    (sqa - sqb + diff).highest_magnitude()
842}
843
844#[cfg(test)]
845mod test {
846    use super::*;
847    use nalgebra::{Matrix2, Matrix3};
848    use rand::distributions::{Distribution, Uniform};
849    use rand::seq::SliceRandom;
850    use rand::Rng;
851    use rand_distr::{UnitCircle, UnitSphere};
852    use rand_pcg::Pcg64;
853    use rug::Float;
854
855    type Mtx2 = Matrix2<f64>;
856    type Mtx3 = Matrix3<f64>;
857
858    const PCG_STATE: u128 = 0xcafef00dd15ea5e5;
859    const PCG_STREAM: u128 = 0xa02bdbf7bb3c0a7ac28fa16a64abf96;
860
861    fn orient_2d_exact(a: Vec2, b: Vec2, c: Vec2) -> Float {
862        const PREC: u32 = (f64::MANTISSA_DIGITS + 1) * 2 + 1;
863        let ax = Float::with_val(
864            PREC,
865            &Float::with_val(PREC, a.x) - &Float::with_val(PREC, c.x),
866        );
867        let ay = Float::with_val(
868            PREC,
869            &Float::with_val(PREC, a.y) - &Float::with_val(PREC, c.y),
870        );
871        let bx = Float::with_val(
872            PREC,
873            &Float::with_val(PREC, b.x) - &Float::with_val(PREC, c.x),
874        );
875        let by = Float::with_val(
876            PREC,
877            &Float::with_val(PREC, b.y) - &Float::with_val(PREC, c.y),
878        );
879        Float::with_val(PREC, &ax * &by - &ay * &bx)
880    }
881
882    fn check_orient_2d(a: Vec2, b: Vec2, c: Vec2) {
883        let adapt = orient_2d(a, b, c);
884        let exact = orient_2d_exact(a, b, c);
885        assert_eq!(
886            adapt.partial_cmp(&0.0),
887            exact.partial_cmp(&0.0),
888            "({}, {}, {}) gave wrong result: {} vs {}",
889            a,
890            b,
891            c,
892            adapt,
893            exact
894        );
895    }
896
897    #[test]
898    fn test_orient_2d_uniform_random() {
899        // Deterministic, portable RNG
900        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
901        let dist = Uniform::new(-10.0, 10.0);
902
903        for _ in 0..10000 {
904            let vals = dist.sample_iter(&mut rng).take(6).collect::<Vec<_>>();
905            let a = Vec2::new(vals[0], vals[1]);
906            let b = Vec2::new(vals[2], vals[3]);
907            let c = Vec2::new(vals[4], vals[5]);
908            check_orient_2d(a, b, c);
909        }
910    }
911
912    #[test]
913    fn test_orient_2d_geometric_random() {
914        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
915        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
916        let dist = Uniform::new(-30.0, 30.0);
917
918        for _ in 0..10000 {
919            let vals = dist
920                .sample_iter(&mut rng)
921                .take(6)
922                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
923                .collect::<Vec<_>>();
924            let a = Vec2::new(vals[0], vals[1]);
925            let b = Vec2::new(vals[2], vals[3]);
926            let c = Vec2::new(vals[4], vals[5]);
927            check_orient_2d(a, b, c);
928        }
929    }
930
931    #[test]
932    fn test_orient_2d_near_collinear() {
933        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
934        let dist = Uniform::new(-1.0, 1.0);
935        let fac_dist = Uniform::new(-1.0, 2.0);
936
937        for _ in 0..10000 {
938            let vals = dist.sample_iter(&mut rng).take(4).collect::<Vec<_>>();
939            let a = Vec2::new(vals[0], vals[1]);
940            let b = Vec2::new(vals[2], vals[3]);
941
942            let fac = fac_dist.sample(&mut rng);
943            let c = a + (b - a) * fac;
944
945            check_orient_2d(a, b, c);
946        }
947    }
948
949    #[test]
950    fn test_orient_2d_collinear() {
951        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
952        let dist = Uniform::new_inclusive(-4096, 4096);
953        let fac_dist = Uniform::new_inclusive(-4095, 4096);
954
955        for _ in 0..10000 {
956            let vals = dist
957                .sample_iter(&mut rng)
958                .take(4)
959                .map(|x| (x as f64) / 4096.0)
960                .collect::<Vec<_>>();
961            let a = Vec2::new(vals[0], vals[1]);
962            let b = Vec2::new(vals[2], vals[3]);
963
964            let fac = fac_dist.sample(&mut rng);
965            let c = a + (b - a) * (fac as f64) / 16.0;
966
967            check_orient_2d(a, b, c);
968        }
969    }
970
971    fn cross_2d_exact(a: Vec2, b: Vec2, c: Vec2, d: Vec2) -> Float {
972        const PREC: u32 = (f64::MANTISSA_DIGITS + 1) * 2 + 1;
973        let abx = Float::with_val(
974            PREC,
975            &Float::with_val(PREC, b.x) - &Float::with_val(PREC, a.x),
976        );
977        let aby = Float::with_val(
978            PREC,
979            &Float::with_val(PREC, b.y) - &Float::with_val(PREC, a.y),
980        );
981        let cdx = Float::with_val(
982            PREC,
983            &Float::with_val(PREC, d.x) - &Float::with_val(PREC, c.x),
984        );
985        let cdy = Float::with_val(
986            PREC,
987            &Float::with_val(PREC, d.y) - &Float::with_val(PREC, c.y),
988        );
989        Float::with_val(PREC, &abx * &cdy - &aby * &cdx)
990    }
991
992    fn check_cross_2d(a: Vec2, b: Vec2, c: Vec2, d: Vec2,) {
993        let adapt = cross_2d(a, b, c, d);
994        let exact = cross_2d_exact(a, b, c, d);
995        assert_eq!(
996            adapt.partial_cmp(&0.0),
997            exact.partial_cmp(&0.0),
998            "({}, {}, {}, {}) gave wrong result: {} vs {}",
999            a,
1000            b,
1001            c,
1002            d,
1003            adapt,
1004            exact
1005        );
1006    }
1007
1008    #[test]
1009    fn test_cross_2d_uniform_random() {
1010        // Deterministic, portable RNG
1011        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1012        let dist = Uniform::new(-10.0, 10.0);
1013
1014        for _ in 0..10000 {
1015            let vals = dist.sample_iter(&mut rng).take(8).collect::<Vec<_>>();
1016            let a = Vec2::new(vals[0], vals[1]);
1017            let b = Vec2::new(vals[2], vals[3]);
1018            let c = Vec2::new(vals[4], vals[5]);
1019            let d = Vec2::new(vals[6], vals[7]);
1020            check_cross_2d(a, b, c, d);
1021        }
1022    }
1023
1024    #[test]
1025    fn test_cross_2d_geometric_random() {
1026        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1027        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
1028        let dist = Uniform::new(-30.0, 30.0);
1029
1030        for _ in 0..10000 {
1031            let vals = dist
1032                .sample_iter(&mut rng)
1033                .take(8)
1034                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
1035                .collect::<Vec<_>>();
1036            let a = Vec2::new(vals[0], vals[1]);
1037            let b = Vec2::new(vals[2], vals[3]);
1038            let c = Vec2::new(vals[4], vals[5]);
1039            let d = Vec2::new(vals[6], vals[7]);
1040            check_cross_2d(a, b, c, d);
1041        }
1042    }
1043
1044    #[test]
1045    fn test_cross_2d_near_parallel() {
1046        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1047        let dist = Uniform::new(-1.0, 1.0);
1048        let fac_dist = Uniform::new(-1.0, 2.0);
1049
1050        for _ in 0..10000 {
1051            let vals = dist.sample_iter(&mut rng).take(6).collect::<Vec<_>>();
1052            let a = Vec2::new(vals[0], vals[1]);
1053            let b = Vec2::new(vals[2], vals[3]);
1054            let c = Vec2::new(vals[4], vals[5]);
1055
1056            let fac = fac_dist.sample(&mut rng);
1057            let d = c + (b - a) * fac;
1058
1059            check_cross_2d(a, b, c, d);
1060        }
1061    }
1062
1063    #[test]
1064    fn test_cross_2d_collinear() {
1065        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1066        let dist = Uniform::new_inclusive(-4096, 4096);
1067        let fac_dist = Uniform::new_inclusive(-4095, 4096);
1068
1069        for _ in 0..10000 {
1070            let vals = dist
1071                .sample_iter(&mut rng)
1072                .take(6)
1073                .map(|x| (x as f64) / 4096.0)
1074                .collect::<Vec<_>>();
1075            let a = Vec2::new(vals[0], vals[1]);
1076            let b = Vec2::new(vals[2], vals[3]);
1077            let c = Vec2::new(vals[4], vals[5]);
1078
1079            let fac = fac_dist.sample(&mut rng);
1080            let d = c + (b - a) * (fac as f64) / 16.0;
1081
1082            check_cross_2d(a, b, c, d);
1083        }
1084    }
1085
1086    fn orient_3d_exact(a: Vec3, b: Vec3, c: Vec3, d: Vec3) -> Float {
1087        const PREC: u32 = (f64::MANTISSA_DIGITS + 1) * 3 + 3;
1088        let ax = Float::with_val(
1089            PREC,
1090            &Float::with_val(PREC, a.x) - &Float::with_val(PREC, d.x),
1091        );
1092        let ay = Float::with_val(
1093            PREC,
1094            &Float::with_val(PREC, a.y) - &Float::with_val(PREC, d.y),
1095        );
1096        let az = Float::with_val(
1097            PREC,
1098            &Float::with_val(PREC, a.z) - &Float::with_val(PREC, d.z),
1099        );
1100        let bx = Float::with_val(
1101            PREC,
1102            &Float::with_val(PREC, b.x) - &Float::with_val(PREC, d.x),
1103        );
1104        let by = Float::with_val(
1105            PREC,
1106            &Float::with_val(PREC, b.y) - &Float::with_val(PREC, d.y),
1107        );
1108        let bz = Float::with_val(
1109            PREC,
1110            &Float::with_val(PREC, b.z) - &Float::with_val(PREC, d.z),
1111        );
1112        let cx = Float::with_val(
1113            PREC,
1114            &Float::with_val(PREC, c.x) - &Float::with_val(PREC, d.x),
1115        );
1116        let cy = Float::with_val(
1117            PREC,
1118            &Float::with_val(PREC, c.y) - &Float::with_val(PREC, d.y),
1119        );
1120        let cz = Float::with_val(
1121            PREC,
1122            &Float::with_val(PREC, c.z) - &Float::with_val(PREC, d.z),
1123        );
1124        let xy = Float::with_val(PREC, &bx * &cy - &by * &cx);
1125        let yz = Float::with_val(PREC, &by * &cz - &bz * &cy);
1126        let zx = Float::with_val(PREC, &bz * &cx - &bx * &cz);
1127        let ab = Float::with_val(PREC, &az * &xy + &ax * &yz);
1128        Float::with_val(PREC, &ab + &ay * &zx)
1129    }
1130
1131    fn check_orient_3d(a: Vec3, b: Vec3, c: Vec3, d: Vec3) {
1132        let adapt = orient_3d(a, b, c, d);
1133        let exact = orient_3d_exact(a, b, c, d);
1134        assert_eq!(
1135            adapt.partial_cmp(&0.0),
1136            exact.partial_cmp(&0.0),
1137            "({}, {}, {}, {}) gave wrong result: {} vs {}",
1138            a,
1139            b,
1140            c,
1141            d,
1142            adapt,
1143            exact
1144        );
1145    }
1146
1147    #[test]
1148    fn test_orient_3d_uniform_random() {
1149        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1150        let dist = Uniform::new(-10.0, 10.0);
1151
1152        for _ in 0..1000 {
1153            let vals = dist.sample_iter(&mut rng).take(12).collect::<Vec<_>>();
1154            let a = Vec3::new(vals[0], vals[1], vals[2]);
1155            let b = Vec3::new(vals[3], vals[4], vals[5]);
1156            let c = Vec3::new(vals[6], vals[7], vals[8]);
1157            let d = Vec3::new(vals[9], vals[10], vals[11]);
1158            check_orient_3d(a, b, c, d);
1159        }
1160    }
1161
1162    #[test]
1163    fn test_orient_3d_geometric_random() {
1164        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1165        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
1166        let dist = Uniform::new(-30.0, 30.0);
1167
1168        for _ in 0..1000 {
1169            let vals = dist
1170                .sample_iter(&mut rng)
1171                .take(12)
1172                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
1173                .collect::<Vec<_>>();
1174            let a = Vec3::new(vals[0], vals[1], vals[2]);
1175            let b = Vec3::new(vals[3], vals[4], vals[5]);
1176            let c = Vec3::new(vals[6], vals[7], vals[8]);
1177            let d = Vec3::new(vals[9], vals[10], vals[11]);
1178            check_orient_3d(a, b, c, d);
1179        }
1180    }
1181
1182    #[test]
1183    fn test_orient_3d_near_coplanar() {
1184        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1185        let dist = Uniform::new(-1.0, 1.0);
1186        let fac_dist = Uniform::new(-1.0, 2.0);
1187
1188        for _ in 0..1000 {
1189            let vals = dist.sample_iter(&mut rng).take(9).collect::<Vec<_>>();
1190            let a = Vec3::new(vals[0], vals[1], vals[2]);
1191            let b = Vec3::new(vals[3], vals[4], vals[5]);
1192            let c = Vec3::new(vals[6], vals[7], vals[8]);
1193
1194            let fac = fac_dist.sample(&mut rng);
1195            let fac2 = fac_dist.sample(&mut rng);
1196            let d = c + (a + (b - a) * fac - c) * fac2;
1197
1198            check_orient_3d(a, b, c, d);
1199        }
1200    }
1201
1202    #[test]
1203    fn test_orient_3d_coplanar() {
1204        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1205        let dist = Uniform::new_inclusive(-4096, 4096);
1206        let fac_dist = Uniform::new_inclusive(-4095, 4096);
1207
1208        for _ in 0..1000 {
1209            let vals = dist
1210                .sample_iter(&mut rng)
1211                .take(9)
1212                .map(|x| (x as f64) / 4096.0)
1213                .collect::<Vec<_>>();
1214            let a = Vec3::new(vals[0], vals[1], vals[2]);
1215            let b = Vec3::new(vals[3], vals[4], vals[5]);
1216            let c = Vec3::new(vals[6], vals[7], vals[8]);
1217
1218            let fac = fac_dist.sample(&mut rng) as f64 / 16.0;
1219            let fac2 = fac_dist.sample(&mut rng) as f64 / 16.0;
1220            let d = c + (a + (b - a) * fac - c) * fac2;
1221
1222            check_orient_3d(a, b, c, d);
1223        }
1224    }
1225
1226    fn in_circle_exact(a: Vec2, b: Vec2, c: Vec2, d: Vec2) -> Float {
1227        const PREC: u32 = ((f64::MANTISSA_DIGITS + 1) * 2 + 1) * 2 + 2;
1228        macro_rules! f {
1229            ($expr:expr) => {
1230                Float::with_val(PREC, $expr)
1231            };
1232        }
1233
1234        let ax = f!(&f!(a.x) - &f!(d.x));
1235        let ay = f!(&f!(a.y) - &f!(d.y));
1236        let bx = f!(&f!(b.x) - &f!(d.x));
1237        let by = f!(&f!(b.y) - &f!(d.y));
1238        let cx = f!(&f!(c.x) - &f!(d.x));
1239        let cy = f!(&f!(c.y) - &f!(d.y));
1240        let cof1 = f!(&f!(&ax * &ax + &ay * &ay) * &f!(&bx * &cy - &by * &cx));
1241        let cof2 = f!(&f!(&bx * &bx + &by * &by) * &f!(&cx * &ay - &cy * &ax));
1242        let cof3 = f!(&f!(&cx * &cx + &cy * &cy) * &f!(&ax * &by - &ay * &bx));
1243        f!(&f!(&cof1 + &cof2) + &cof3)
1244    }
1245
1246    fn check_in_circle(a: Vec2, b: Vec2, c: Vec2, d: Vec2) {
1247        let adapt = in_circle(a, b, c, d);
1248        let exact = in_circle_exact(a, b, c, d);
1249        assert_eq!(
1250            adapt.partial_cmp(&0.0),
1251            exact.partial_cmp(&0.0),
1252            "({}, {}, {}, {}) gave wrong result: {} vs {}",
1253            a,
1254            b,
1255            c,
1256            d,
1257            adapt,
1258            exact
1259        );
1260    }
1261
1262    #[test]
1263    fn test_in_circle_uniform_random() {
1264        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1265        let dist = Uniform::new(-10.0, 10.0);
1266
1267        for _ in 0..1000 {
1268            let vals = dist.sample_iter(&mut rng).take(8).collect::<Vec<_>>();
1269            let a = Vec2::new(vals[0], vals[1]);
1270            let b = Vec2::new(vals[2], vals[3]);
1271            let c = Vec2::new(vals[4], vals[5]);
1272            let d = Vec2::new(vals[6], vals[7]);
1273            check_in_circle(a, b, c, d);
1274        }
1275    }
1276
1277    #[test]
1278    fn test_in_circle_geometric_random() {
1279        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1280        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
1281        let dist = Uniform::new(-30.0, 30.0);
1282
1283        for _ in 0..1000 {
1284            let vals = dist
1285                .sample_iter(&mut rng)
1286                .take(12)
1287                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
1288                .collect::<Vec<_>>();
1289            let a = Vec2::new(vals[0], vals[1]);
1290            let b = Vec2::new(vals[2], vals[3]);
1291            let c = Vec2::new(vals[4], vals[5]);
1292            let d = Vec2::new(vals[6], vals[7]);
1293            check_in_circle(a, b, c, d);
1294        }
1295    }
1296
1297    #[test]
1298    fn test_in_circle_near_cocircular() {
1299        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1300        let dist = Uniform::new(-1.0, 1.0);
1301
1302        for _ in 0..1000 {
1303            let vals = UnitCircle.sample_iter(&mut rng).take(4).collect::<Vec<_>>();
1304            let radius = dist.sample(&mut rng);
1305            let x = dist.sample(&mut rng);
1306            let y = dist.sample(&mut rng);
1307            let offset = Vec2::new(x, y);
1308
1309            let a = Vec2::new(vals[0][0], vals[0][1]) * radius + offset;
1310            let b = Vec2::new(vals[1][0], vals[1][1]) * radius + offset;
1311            let c = Vec2::new(vals[2][0], vals[2][1]) * radius + offset;
1312            let d = Vec2::new(vals[3][0], vals[3][1]) * radius + offset;
1313
1314            check_in_circle(a, b, c, d);
1315        }
1316    }
1317
1318    #[test]
1319    fn test_in_circle_cocircular() {
1320        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1321        let dist = Uniform::new_inclusive(-4096, 4096);
1322        let abs = Uniform::new_inclusive(0, 4096);
1323        let mtxs = [
1324            Mtx2::new(1.0, 0.0, 0.0, 1.0),
1325            Mtx2::new(-1.0, 0.0, 0.0, 1.0),
1326            Mtx2::new(0.0, -1.0, 1.0, 0.0),
1327            Mtx2::new(0.0, 1.0, 1.0, 0.0),
1328            Mtx2::new(-1.0, 0.0, 0.0, -1.0),
1329            Mtx2::new(1.0, 0.0, 0.0, -1.0),
1330            Mtx2::new(0.0, 1.0, -1.0, 0.0),
1331            Mtx2::new(0.0, -1.0, -1.0, 0.0),
1332        ];
1333
1334        for _ in 0..1000 {
1335            let x = abs.sample(&mut rng) as f64 / 4096.0;
1336            let y = abs.sample(&mut rng) as f64 / 4096.0;
1337            let v = Vec2::new(x, y);
1338            let cx = dist.sample(&mut rng) as f64 / 4096.0;
1339            let cy = dist.sample(&mut rng) as f64 / 4096.0;
1340            let offset = Vec2::new(cx, cy);
1341
1342            // Reminder to use choose_multiple for the benchmark to not mix
1343            // cases that have identical points and are thus more likely to
1344            // get stopped at the initial floating-point calculation
1345            let a = mtxs.choose(&mut rng).unwrap() * v + offset;
1346            let b = mtxs.choose(&mut rng).unwrap() * v + offset;
1347            let c = mtxs.choose(&mut rng).unwrap() * v + offset;
1348            let d = mtxs.choose(&mut rng).unwrap() * v + offset;
1349
1350            check_in_circle(a, b, c, d);
1351        }
1352    }
1353
1354    fn in_sphere_exact(a: Vec3, b: Vec3, c: Vec3, d: Vec3, e: Vec3) -> Float {
1355        const PREC: u32 = ((f64::MANTISSA_DIGITS + 1) * 2
1356            + 1
1357            + f64::MANTISSA_DIGITS
1358            + 1
1359            + (f64::MANTISSA_DIGITS + 1) * 2
1360            + 2)
1361            + 5;
1362        macro_rules! f {
1363            ($expr:expr) => {
1364                Float::with_val(PREC, $expr)
1365            };
1366        }
1367
1368        let ax = f!(&f!(a.x) - &f!(e.x));
1369        let ay = f!(&f!(a.y) - &f!(e.y));
1370        let az = f!(&f!(a.z) - &f!(e.z));
1371        let bx = f!(&f!(b.x) - &f!(e.x));
1372        let by = f!(&f!(b.y) - &f!(e.y));
1373        let bz = f!(&f!(b.z) - &f!(e.z));
1374        let cx = f!(&f!(c.x) - &f!(e.x));
1375        let cy = f!(&f!(c.y) - &f!(e.y));
1376        let cz = f!(&f!(c.z) - &f!(e.z));
1377        let dx = f!(&f!(d.x) - &f!(e.x));
1378        let dy = f!(&f!(d.y) - &f!(e.y));
1379        let dz = f!(&f!(d.z) - &f!(e.z));
1380        let aq = f!(&f!(&ax * &ax + &ay * &ay) + &az * &az);
1381        let bq = f!(&f!(&bx * &bx + &by * &by) + &bz * &bz);
1382        let cq = f!(&f!(&cx * &cx + &cy * &cy) + &cz * &cz);
1383        let dq = f!(&f!(&dx * &dx + &dy * &dy) + &dz * &dz);
1384        let cof1 = f!(&f!(&ax * &by - &bx * &ay) * &f!(&cz * &dq - &dz * &cq));
1385        let cof2 = f!(&f!(&ax * &cy - &cx * &ay) * &f!(&dz * &bq - &bz * &dq));
1386        let cof3 = f!(&f!(&ax * &dy - &dx * &ay) * &f!(&bz * &cq - &cz * &bq));
1387        let cof4 = f!(&f!(&bx * &cy - &cx * &by) * &f!(&az * &dq - &dz * &aq));
1388        let cof5 = f!(&f!(&bx * &dy - &dx * &by) * &f!(&cz * &aq - &az * &cq));
1389        let cof6 = f!(&f!(&cx * &dy - &dx * &cy) * &f!(&az * &bq - &bz * &aq));
1390        f!(f!(&f!(&cof1 + &cof2) + &cof3) + f!(&f!(&cof4 + &cof5) + &cof6))
1391    }
1392
1393    fn check_in_sphere(a: Vec3, b: Vec3, c: Vec3, d: Vec3, e: Vec3) {
1394        let adapt = in_sphere(a, b, c, d, e);
1395        let exact = in_sphere_exact(a, b, c, d, e);
1396        assert_eq!(
1397            adapt.partial_cmp(&0.0),
1398            exact.partial_cmp(&0.0),
1399            "({}, {}, {}, {}, {}) gave wrong result: {} vs {}",
1400            a,
1401            b,
1402            c,
1403            d,
1404            e,
1405            adapt,
1406            exact
1407        );
1408    }
1409
1410    #[test]
1411    fn test_in_sphere_uniform_random() {
1412        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1413        let dist = Uniform::new(-10.0, 10.0);
1414
1415        for _ in 0..1000 {
1416            let vals = dist.sample_iter(&mut rng).take(15).collect::<Vec<_>>();
1417            let a = Vec3::new(vals[0], vals[1], vals[2]);
1418            let b = Vec3::new(vals[3], vals[4], vals[5]);
1419            let c = Vec3::new(vals[6], vals[7], vals[8]);
1420            let d = Vec3::new(vals[9], vals[10], vals[11]);
1421            let e = Vec3::new(vals[12], vals[13], vals[14]);
1422            check_in_sphere(a, b, c, d, e);
1423        }
1424    }
1425
1426    #[test]
1427    fn test_in_sphere_geometric_random() {
1428        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1429        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
1430        let dist = Uniform::new(-30.0, 30.0);
1431
1432        for _ in 0..1000 {
1433            let vals = dist
1434                .sample_iter(&mut rng)
1435                .take(15)
1436                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
1437                .collect::<Vec<_>>();
1438            let a = Vec3::new(vals[0], vals[1], vals[2]);
1439            let b = Vec3::new(vals[3], vals[4], vals[5]);
1440            let c = Vec3::new(vals[6], vals[7], vals[8]);
1441            let d = Vec3::new(vals[9], vals[10], vals[11]);
1442            let e = Vec3::new(vals[12], vals[13], vals[14]);
1443            check_in_sphere(a, b, c, d, e);
1444        }
1445    }
1446
1447    #[test]
1448    fn test_in_sphere_near_cospherical() {
1449        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1450        let dist = Uniform::new(-1.0, 1.0);
1451
1452        for _ in 0..1000 {
1453            let vals = UnitSphere.sample_iter(&mut rng).take(5).collect::<Vec<_>>();
1454            let radius = dist.sample(&mut rng);
1455            let x = dist.sample(&mut rng);
1456            let y = dist.sample(&mut rng);
1457            let z = dist.sample(&mut rng);
1458            let offset = Vec3::new(x, y, z);
1459
1460            let a = Vec3::new(vals[0][0], vals[0][1], vals[0][2]) * radius + offset;
1461            let b = Vec3::new(vals[1][0], vals[1][1], vals[1][2]) * radius + offset;
1462            let c = Vec3::new(vals[2][0], vals[2][1], vals[2][2]) * radius + offset;
1463            let d = Vec3::new(vals[3][0], vals[3][1], vals[3][2]) * radius + offset;
1464            let e = Vec3::new(vals[4][0], vals[4][1], vals[4][2]) * radius + offset;
1465
1466            check_in_sphere(a, b, c, d, e);
1467        }
1468    }
1469
1470    #[test]
1471    fn test_in_sphere_cospherical() {
1472        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1473        let dist = Uniform::new_inclusive(-4096, 4096);
1474        let abs = Uniform::new_inclusive(0, 4096);
1475        let flip = Mtx3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
1476        let rot4 = Mtx3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
1477        let rot3 = Mtx3::new(0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0);
1478        let mut mtxs = vec![];
1479
1480        for i in 0..2 {
1481            for j in 0..4 {
1482                for k in 0..3 {
1483                    mtxs.push(
1484                        std::iter::repeat(rot3).take(k).product::<Mtx3>()
1485                            * std::iter::repeat(rot4).take(j).product::<Mtx3>()
1486                            * std::iter::repeat(flip).take(i).product::<Mtx3>(),
1487                    );
1488                }
1489            }
1490        }
1491
1492        for _ in 0..1000 {
1493            let x = abs.sample(&mut rng) as f64 / 4096.0;
1494            let y = abs.sample(&mut rng) as f64 / 4096.0;
1495            let z = abs.sample(&mut rng) as f64 / 4096.0;
1496            let v = Vec3::new(x, y, z);
1497            let cx = dist.sample(&mut rng) as f64 / 4096.0;
1498            let cy = dist.sample(&mut rng) as f64 / 4096.0;
1499            let cz = dist.sample(&mut rng) as f64 / 4096.0;
1500            let offset = Vec3::new(cx, cy, cz);
1501
1502            // Reminder to use choose_multiple for the benchmark to not mix
1503            // cases that have identical points and are thus more likely to
1504            // get stopped at the initial floating-point calculation
1505            let a = mtxs.choose(&mut rng).unwrap() * v + offset;
1506            let b = mtxs.choose(&mut rng).unwrap() * v + offset;
1507            let c = mtxs.choose(&mut rng).unwrap() * v + offset;
1508            let d = mtxs.choose(&mut rng).unwrap() * v + offset;
1509            let e = mtxs.choose(&mut rng).unwrap() * v + offset;
1510
1511            check_in_sphere(a, b, c, d, e);
1512        }
1513    }
1514
1515    fn magnitude_cmp_2d_exact(a: Vec2, b: Vec2) -> Float {
1516        const PREC: u32 = f64::MANTISSA_DIGITS * 2 + 2;
1517        macro_rules! f {
1518            ($expr:expr) => {
1519                Float::with_val(PREC, $expr)
1520            };
1521        }
1522
1523        let ax = f!(a.x);
1524        let ay = f!(a.y);
1525        let bx = f!(b.x);
1526        let by = f!(b.y);
1527        f!(&f!(&ax * &ax + &ay * &ay) - &f!(&bx * &bx + &by * &by))
1528    }
1529
1530    fn check_magnitude_cmp_2d(a: Vec2, b: Vec2) {
1531        let adapt = magnitude_cmp_2d(a, b);
1532        let exact = magnitude_cmp_2d_exact(a, b);
1533        assert_eq!(
1534            adapt.partial_cmp(&0.0),
1535            exact.partial_cmp(&0.0),
1536            "({}, {}) gave wrong result: {} vs {}",
1537            a,
1538            b,
1539            adapt,
1540            exact
1541        );
1542    }
1543
1544    #[test]
1545    fn test_magnitude_cmp_2d_uniform_random() {
1546        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1547        let dist = Uniform::new(-10.0, 10.0);
1548
1549        for _ in 0..1000 {
1550            let vals = dist.sample_iter(&mut rng).take(4).collect::<Vec<_>>();
1551            let a = Vec2::new(vals[0], vals[1]);
1552            let b = Vec2::new(vals[2], vals[3]);
1553            check_magnitude_cmp_2d(a, b);
1554        }
1555    }
1556
1557    #[test]
1558    fn test_magnitude_cmp_2d_geometric_random() {
1559        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1560        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
1561        let dist = Uniform::new(-30.0, 30.0);
1562
1563        for _ in 0..1000 {
1564            let vals = dist
1565                .sample_iter(&mut rng)
1566                .take(4)
1567                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
1568                .collect::<Vec<_>>();
1569            let a = Vec2::new(vals[0], vals[1]);
1570            let b = Vec2::new(vals[2], vals[3]);
1571            check_magnitude_cmp_2d(a, b);
1572        }
1573    }
1574
1575    #[test]
1576    fn test_magnitude_cmp_2d_near_zero() {
1577        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1578        let dist = Uniform::new(-1.0, 1.0);
1579
1580        for _ in 0..1000 {
1581            let vals = UnitCircle.sample_iter(&mut rng).take(4).collect::<Vec<_>>();
1582            let radius = dist.sample(&mut rng);
1583
1584            let a = Vec2::new(vals[0][0], vals[0][1]) * radius;
1585            let b = Vec2::new(vals[1][0], vals[1][1]) * radius;
1586
1587            check_magnitude_cmp_2d(a, b);
1588        }
1589    }
1590
1591    #[test]
1592    fn test_magnitude_cmp_2d_zero() {
1593        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1594        let abs = Uniform::new_inclusive(0, 4096);
1595        let mtxs = [
1596            Mtx2::new(1.0, 0.0, 0.0, 1.0),
1597            Mtx2::new(-1.0, 0.0, 0.0, 1.0),
1598            Mtx2::new(0.0, -1.0, 1.0, 0.0),
1599            Mtx2::new(0.0, 1.0, 1.0, 0.0),
1600            Mtx2::new(-1.0, 0.0, 0.0, -1.0),
1601            Mtx2::new(1.0, 0.0, 0.0, -1.0),
1602            Mtx2::new(0.0, 1.0, -1.0, 0.0),
1603            Mtx2::new(0.0, -1.0, -1.0, 0.0),
1604        ];
1605
1606        for _ in 0..1000 {
1607            let x = abs.sample(&mut rng) as f64 / 4096.0;
1608            let y = abs.sample(&mut rng) as f64 / 4096.0;
1609            let v = Vec2::new(x, y);
1610
1611            let a = mtxs.choose(&mut rng).unwrap() * v;
1612            let b = mtxs.choose(&mut rng).unwrap() * v;
1613
1614            check_magnitude_cmp_2d(a, b);
1615        }
1616    }
1617
1618    fn magnitude_cmp_3d_exact(a: Vec3, b: Vec3) -> Float {
1619        const PREC: u32 = f64::MANTISSA_DIGITS * 2 + 2;
1620        macro_rules! f {
1621            ($expr:expr) => {
1622                Float::with_val(PREC, $expr)
1623            };
1624        }
1625
1626        let ax = f!(a.x);
1627        let ay = f!(a.y);
1628        let az = f!(a.z);
1629        let bx = f!(b.x);
1630        let by = f!(b.y);
1631        let bz = f!(b.z);
1632        f!(&f!(&f!(&ax * &ax + &ay * &ay) + &az * &az)
1633            - &f!(&f!(&bx * &bx + &by * &by) + &bz * &bz))
1634    }
1635
1636    fn check_magnitude_cmp_3d(a: Vec3, b: Vec3) {
1637        let adapt = magnitude_cmp_3d(a, b);
1638        let exact = magnitude_cmp_3d_exact(a, b);
1639        assert_eq!(
1640            adapt.partial_cmp(&0.0),
1641            exact.partial_cmp(&0.0),
1642            "({}, {}) gave wrong result: {} vs {}",
1643            a,
1644            b,
1645            adapt,
1646            exact
1647        );
1648    }
1649
1650    #[test]
1651    fn test_magnitude_cmp_3d_uniform_random() {
1652        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1653        let dist = Uniform::new(-10.0, 10.0);
1654
1655        for _ in 0..1000 {
1656            let vals = dist.sample_iter(&mut rng).take(6).collect::<Vec<_>>();
1657            let a = Vec3::new(vals[0], vals[1], vals[2]);
1658            let b = Vec3::new(vals[3], vals[4], vals[5]);
1659            check_magnitude_cmp_3d(a, b);
1660        }
1661    }
1662
1663    #[test]
1664    fn test_magnitude_cmp_3d_geometric_random() {
1665        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1666        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
1667        let dist = Uniform::new(-30.0, 30.0);
1668
1669        for _ in 0..1000 {
1670            let vals = dist
1671                .sample_iter(&mut rng)
1672                .take(6)
1673                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
1674                .collect::<Vec<_>>();
1675            let a = Vec3::new(vals[0], vals[1], vals[2]);
1676            let b = Vec3::new(vals[3], vals[4], vals[5]);
1677            check_magnitude_cmp_3d(a, b);
1678        }
1679    }
1680
1681    #[test]
1682    fn test_magnitude_cmp_3d_near_zero() {
1683        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1684        let dist = Uniform::new(-1.0, 1.0);
1685
1686        for _ in 0..1000 {
1687            let vals = UnitSphere.sample_iter(&mut rng).take(4).collect::<Vec<_>>();
1688            let radius = dist.sample(&mut rng);
1689
1690            let a = Vec3::new(vals[0][0], vals[0][1], vals[0][2]) * radius;
1691            let b = Vec3::new(vals[1][0], vals[1][1], vals[1][2]) * radius;
1692
1693            check_magnitude_cmp_3d(a, b);
1694        }
1695    }
1696
1697    #[test]
1698    fn test_magnitude_cmp_3d_zero() {
1699        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1700        let abs = Uniform::new_inclusive(0, 4096);
1701        let flip = Mtx3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);
1702        let rot4 = Mtx3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
1703        let rot3 = Mtx3::new(0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0);
1704        let mut mtxs = vec![];
1705
1706        for i in 0..2 {
1707            for j in 0..4 {
1708                for k in 0..3 {
1709                    mtxs.push(
1710                        std::iter::repeat(rot3).take(k).product::<Mtx3>()
1711                            * std::iter::repeat(rot4).take(j).product::<Mtx3>()
1712                            * std::iter::repeat(flip).take(i).product::<Mtx3>(),
1713                    );
1714                }
1715            }
1716        }
1717
1718        for _ in 0..1000 {
1719            let x = abs.sample(&mut rng) as f64 / 4096.0;
1720            let y = abs.sample(&mut rng) as f64 / 4096.0;
1721            let z = abs.sample(&mut rng) as f64 / 4096.0;
1722            let v = Vec3::new(x, y, z);
1723
1724            let a = mtxs.choose(&mut rng).unwrap() * v;
1725            let b = mtxs.choose(&mut rng).unwrap() * v;
1726
1727            check_magnitude_cmp_3d(a, b);
1728        }
1729    }
1730
1731    fn sign_det_x_x2y2_exact(a: Vec2, b: Vec2, c: Vec2) -> Float {
1732        const PREC: u32 = f64::MANTISSA_DIGITS * 2 + 1 + f64::MANTISSA_DIGITS + 3;
1733        macro_rules! f {
1734            ($expr:expr) => {
1735                Float::with_val(PREC, $expr)
1736            };
1737        }
1738
1739        let ax = f!(a.x);
1740        let ay = f!(a.y);
1741        let bx = f!(b.x);
1742        let by = f!(b.y);
1743        let cx = f!(c.x);
1744        let cy = f!(c.y);
1745        let sqa = f!(&ax * &ax + &ay * &ay);
1746        let sqb = f!(&bx * &bx + &by * &by);
1747        let sqc = f!(&cx * &cx + &cy * &cy);
1748        let cof1 = f!(&sqa * f!(&cx - &bx));
1749        let cof2 = f!(&sqb * f!(&ax - &cx));
1750        let cof3 = f!(&sqc * f!(&bx - &ax));
1751        f!(f!(&cof1 + &cof2) + &cof3)
1752    }
1753
1754    fn check_sign_det_x_x2y2(a: Vec2, b: Vec2, c: Vec2) {
1755        let adapt = sign_det_x_x2y2(a, b, c);
1756        let exact = sign_det_x_x2y2_exact(a, b, c);
1757        assert_eq!(
1758            adapt.partial_cmp(&0.0),
1759            exact.partial_cmp(&0.0),
1760            "({}, {}, {}) gave wrong result: {} vs {}",
1761            a,
1762            b,
1763            c,
1764            adapt,
1765            exact
1766        );
1767
1768        let adapt = sign_det_x_x2y2_adapt(a, b, c);
1769        let exact = sign_det_x_x2y2_exact(a, b, c);
1770        assert_eq!(
1771            adapt.partial_cmp(&0.0),
1772            exact.partial_cmp(&0.0),
1773            "({}, {}, {}) adapted gave wrong result: {} vs {}",
1774            a,
1775            b,
1776            c,
1777            adapt,
1778            exact
1779        );
1780    }
1781
1782    #[test]
1783    fn test_sign_det_x_x2y2_uniform_random() {
1784        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1785        let dist = Uniform::new(-10.0, 10.0);
1786
1787        for _ in 0..1000 {
1788            let vals = dist.sample_iter(&mut rng).take(6).collect::<Vec<_>>();
1789            let a = Vec2::new(vals[0], vals[1]);
1790            let b = Vec2::new(vals[2], vals[3]);
1791            let c = Vec2::new(vals[4], vals[5]);
1792            check_sign_det_x_x2y2(a, b, c);
1793        }
1794    }
1795
1796    #[test]
1797    fn test_sign_det_x_x2y2_geometric_random() {
1798        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1799        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
1800        let dist = Uniform::new(-30.0, 30.0);
1801
1802        for _ in 0..1000 {
1803            let vals = dist
1804                .sample_iter(&mut rng)
1805                .take(6)
1806                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
1807                .collect::<Vec<_>>();
1808            let a = Vec2::new(vals[0], vals[1]);
1809            let b = Vec2::new(vals[2], vals[3]);
1810            let c = Vec2::new(vals[4], vals[5]);
1811            check_sign_det_x_x2y2(a, b, c);
1812        }
1813    }
1814
1815    #[test]
1816    fn test_sign_det_x_x2y2_zero() {
1817        let a = Vec2::new(1.0, 0.0);
1818        let b = Vec2::new(2.0, -1.0);
1819        let c = Vec2::new(3.0, 0.0);
1820        assert_eq!(sign_det_x_x2y2(a, b, c), 0.0);
1821    }
1822
1823    fn sign_det_x_x2y2z2_exact(a: Vec3, b: Vec3, c: Vec3) -> Float {
1824        const PREC: u32 = f64::MANTISSA_DIGITS * 2 + 2 + f64::MANTISSA_DIGITS + 3;
1825        macro_rules! f {
1826            ($expr:expr) => {
1827                Float::with_val(PREC, $expr)
1828            };
1829        }
1830
1831        let ax = f!(a.x);
1832        let ay = f!(a.y);
1833        let az = f!(a.z);
1834        let bx = f!(b.x);
1835        let by = f!(b.y);
1836        let bz = f!(b.z);
1837        let cx = f!(c.x);
1838        let cy = f!(c.y);
1839        let cz = f!(c.z);
1840        let sqa = f!(&f!(&ax * &ax + &ay * &ay) + &f!(&az * &az));
1841        let sqb = f!(&f!(&bx * &bx + &by * &by) + &f!(&bz * &bz));
1842        let sqc = f!(&f!(&cx * &cx + &cy * &cy) + &f!(&cz * &cz));
1843        let cof1 = f!(&sqa * f!(&cx - &bx));
1844        let cof2 = f!(&sqb * f!(&ax - &cx));
1845        let cof3 = f!(&sqc * f!(&bx - &ax));
1846        f!(f!(&cof1 + &cof2) + &cof3)
1847    }
1848
1849    fn check_sign_det_x_x2y2z2(a: Vec3, b: Vec3, c: Vec3) {
1850        let adapt = sign_det_x_x2y2z2(a, b, c);
1851        let exact = sign_det_x_x2y2z2_exact(a, b, c);
1852        assert_eq!(
1853            adapt.partial_cmp(&0.0),
1854            exact.partial_cmp(&0.0),
1855            "({}, {}, {}) gave wrong result: {} vs {}",
1856            a,
1857            b,
1858            c,
1859            adapt,
1860            exact
1861        );
1862
1863        let adapt = sign_det_x_x2y2z2_adapt(a, b, c);
1864        let exact = sign_det_x_x2y2z2_exact(a, b, c);
1865        assert_eq!(
1866            adapt.partial_cmp(&0.0),
1867            exact.partial_cmp(&0.0),
1868            "({}, {}, {}) adapted gave wrong result: {} vs {}",
1869            a,
1870            b,
1871            c,
1872            adapt,
1873            exact
1874        );
1875    }
1876
1877    #[test]
1878    fn test_sign_det_x_x2y2z2_uniform_random() {
1879        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1880        let dist = Uniform::new(-10.0, 10.0);
1881
1882        for _ in 0..1000 {
1883            let vals = dist.sample_iter(&mut rng).take(9).collect::<Vec<_>>();
1884            let a = Vec3::new(vals[0], vals[1], vals[2]);
1885            let b = Vec3::new(vals[3], vals[4], vals[5]);
1886            let c = Vec3::new(vals[6], vals[7], vals[8]);
1887            check_sign_det_x_x2y2z2(a, b, c);
1888        }
1889    }
1890
1891    #[test]
1892    fn test_sign_det_x_x2y2z2_geometric_random() {
1893        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1894        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
1895        let dist = Uniform::new(-30.0, 30.0);
1896
1897        for _ in 0..1000 {
1898            let vals = dist
1899                .sample_iter(&mut rng)
1900                .take(9)
1901                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
1902                .collect::<Vec<_>>();
1903            let a = Vec3::new(vals[0], vals[1], vals[2]);
1904            let b = Vec3::new(vals[3], vals[4], vals[5]);
1905            let c = Vec3::new(vals[6], vals[7], vals[8]);
1906            check_sign_det_x_x2y2z2(a, b, c);
1907        }
1908    }
1909
1910    #[test]
1911    fn test_sign_det_x_x2y2z2_zero() {
1912        let a = Vec3::new(1.0, 0.0, 4.0);
1913        let b = Vec3::new(2.0, -3.0, 3.0);
1914        let c = Vec3::new(4.0, -4.0, 0.0);
1915        assert_eq!(sign_det_x_x2y2z2(a, b, c), 0.0);
1916    }
1917
1918    fn sign_det_x_y_x2y2z2_exact(a: Vec3, b: Vec3, c: Vec3, d: Vec3) -> Float {
1919        const PREC: u32 = f64::MANTISSA_DIGITS * 2 + 2 + f64::MANTISSA_DIGITS * 2 + 4;
1920        macro_rules! f {
1921            ($expr:expr) => {
1922                Float::with_val(PREC, $expr)
1923            };
1924        }
1925
1926        let ax = f!(a.x);
1927        let ay = f!(a.y);
1928        let az = f!(a.z);
1929        let bx = f!(b.x);
1930        let by = f!(b.y);
1931        let bz = f!(b.z);
1932        let cx = f!(c.x);
1933        let cy = f!(c.y);
1934        let cz = f!(c.z);
1935        let dx = f!(d.x);
1936        let dy = f!(d.y);
1937        let dz = f!(d.z);
1938        let sqa = f!(&f!(&ax * &ax + &ay * &ay) + &f!(&az * &az));
1939        let sqb = f!(&f!(&bx * &bx + &by * &by) + &f!(&bz * &bz));
1940        let sqc = f!(&f!(&cx * &cx + &cy * &cy) + &f!(&cz * &cz));
1941        let sqd = f!(&f!(&dx * &dx + &dy * &dy) + &f!(&dz * &dz));
1942        let cof1 = f!(&f!(&ax * &by - &bx * &ay) * &f!(&sqc - &sqd));
1943        let cof2 = f!(&f!(&ax * &cy - &cx * &ay) * &f!(&sqd - &sqb));
1944        let cof3 = f!(&f!(&ax * &dy - &dx * &ay) * &f!(&sqb - &sqc));
1945        let cof4 = f!(&f!(&bx * &cy - &cx * &by) * &f!(&sqa - &sqd));
1946        let cof5 = f!(&f!(&bx * &dy - &dx * &by) * &f!(&sqc - &sqa));
1947        let cof6 = f!(&f!(&cx * &dy - &dx * &cy) * &f!(&sqa - &sqb));
1948        f!(f!(&f!(&cof1 + &cof2) + &cof3) + f!(&f!(&cof4 + &cof5) + &cof6))
1949    }
1950
1951    fn check_sign_det_x_y_x2y2z2(a: Vec3, b: Vec3, c: Vec3, d: Vec3) {
1952        let adapt = sign_det_x_y_x2y2z2(a, b, c, d);
1953        let exact = sign_det_x_y_x2y2z2_exact(a, b, c, d);
1954        assert_eq!(
1955            adapt.partial_cmp(&0.0),
1956            exact.partial_cmp(&0.0),
1957            "({}, {}, {}, {}) gave wrong result: {} vs {}",
1958            a,
1959            b,
1960            c,
1961            d,
1962            adapt,
1963            exact
1964        );
1965
1966        let adapt = sign_det_x_y_x2y2z2_adapt(a, b, c, d);
1967        let exact = sign_det_x_y_x2y2z2_exact(a, b, c, d);
1968        assert_eq!(
1969            adapt.partial_cmp(&0.0),
1970            exact.partial_cmp(&0.0),
1971            "({}, {}, {}, {}) adapted gave wrong result: {} vs {}",
1972            a,
1973            b,
1974            c,
1975            d,
1976            adapt,
1977            exact
1978        );
1979    }
1980
1981    #[test]
1982    fn test_sign_det_x_y_x2y2z2_uniform_random() {
1983        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1984        let dist = Uniform::new(-10.0, 10.0);
1985
1986        for _ in 0..1000 {
1987            let vals = dist.sample_iter(&mut rng).take(12).collect::<Vec<_>>();
1988            let a = Vec3::new(vals[0], vals[1], vals[2]);
1989            let b = Vec3::new(vals[3], vals[4], vals[5]);
1990            let c = Vec3::new(vals[6], vals[7], vals[8]);
1991            let d = Vec3::new(vals[9], vals[10], vals[11]);
1992            check_sign_det_x_y_x2y2z2(a, b, c, d);
1993        }
1994    }
1995
1996    #[test]
1997    fn test_sign_det_x_y_x2y2z2_geometric_random() {
1998        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
1999        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
2000        let dist = Uniform::new(-30.0, 30.0);
2001
2002        for _ in 0..1000 {
2003            let vals = dist
2004                .sample_iter(&mut rng)
2005                .take(12)
2006                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
2007                .collect::<Vec<_>>();
2008            let a = Vec3::new(vals[0], vals[1], vals[2]);
2009            let b = Vec3::new(vals[3], vals[4], vals[5]);
2010            let c = Vec3::new(vals[6], vals[7], vals[8]);
2011            let d = Vec3::new(vals[9], vals[10], vals[11]);
2012            check_sign_det_x_y_x2y2z2(a, b, c, d);
2013        }
2014    }
2015
2016    #[test]
2017    fn test_sign_det_x_y_x2y2z2_zero() {
2018        let a = Vec3::new(1.0, 0.0, 4.0);
2019        let b = Vec3::new(2.0, -3.0, 3.0);
2020        let c = Vec3::new(4.0, -4.0, 0.0);
2021        let d = Vec3::new(2.0, -3.0, -3.0);
2022        assert_eq!(sign_det_x_y_x2y2z2(a, b, c, d), 0.0);
2023    }
2024
2025    fn sign_det_x_y_x2y2z2_plus_2x_det_x_y_z_exact(a: Vec3, b: Vec3, c: Vec3, d: Vec3, u: f64, i: Vec3, j: Vec3, k: Vec3, l: Vec3) -> Float {
2026        const PREC: u32 = 1000; // lazy; this should be enough
2027        macro_rules! f {
2028            ($expr:expr) => {
2029                Float::with_val(PREC, $expr)
2030            };
2031        }
2032        f!(&sign_det_x_y_x2y2z2_exact(a, b, c, d) + &f!(2.0 * u) * &orient_3d_exact(i, j, k, l))
2033    }
2034
2035    fn check_sign_det_x_y_x2y2z2_plus_2x_det_x_y_z(a: Vec3, b: Vec3, c: Vec3, d: Vec3, u: f64, i: Vec3, j: Vec3, k: Vec3, l: Vec3) {
2036        let adapt = sign_det_x_y_x2y2z2_plus_2x_det_x_y_z(a, b, c, d, u, i, j, k, l);
2037        let exact = sign_det_x_y_x2y2z2_plus_2x_det_x_y_z_exact(a, b, c, d, u, i, j, k, l);
2038        assert_eq!(
2039            adapt.partial_cmp(&0.0),
2040            exact.partial_cmp(&0.0),
2041            "({}, {}, {}, {}, {}, {}, {}, {}, {}) gave wrong result: {} vs {}",
2042            a,
2043            b,
2044            c,
2045            d,
2046            u,
2047            i,
2048            j,
2049            k,
2050            l,
2051            adapt,
2052            exact
2053        );
2054
2055        let adapt = sign_det_x_y_x2y2z2_plus_2x_det_x_y_z_adapt(a, b, c, d, u, i, j, k, l);
2056        let exact = sign_det_x_y_x2y2z2_plus_2x_det_x_y_z_exact(a, b, c, d, u, i, j, k, l);
2057        assert_eq!(
2058            adapt.partial_cmp(&0.0),
2059            exact.partial_cmp(&0.0),
2060            "({}, {}, {}, {}, {}, {}, {}, {}, {}) adapted gave wrong result: {} vs {}",
2061            a,
2062            b,
2063            c,
2064            d,
2065            u,
2066            i,
2067            j,
2068            k,
2069            l,
2070            adapt,
2071            exact
2072        );
2073    }
2074
2075    #[test]
2076    fn test_sign_det_x_y_x2y2z2_plus_2x_det_x_y_z_uniform_random() {
2077        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2078        let dist = Uniform::new(-10.0, 10.0);
2079
2080        for _ in 0..1000 {
2081            let vals = dist.sample_iter(&mut rng).take(25).collect::<Vec<_>>();
2082            let a = Vec3::new(vals[0], vals[1], vals[2]);
2083            let b = Vec3::new(vals[3], vals[4], vals[5]);
2084            let c = Vec3::new(vals[6], vals[7], vals[8]);
2085            let d = Vec3::new(vals[9], vals[10], vals[11]);
2086            let u = vals[12];
2087            let i = Vec3::new(vals[13], vals[14], vals[15]);
2088            let j = Vec3::new(vals[16], vals[17], vals[18]);
2089            let k = Vec3::new(vals[19], vals[20], vals[21]);
2090            let l = Vec3::new(vals[22], vals[23], vals[24]);
2091            check_sign_det_x_y_x2y2z2_plus_2x_det_x_y_z(a, b, c, d, u, i, j, k, l);
2092        }
2093    }
2094
2095    #[test]
2096    fn test_sign_det_x_y_x2y2z2_plus_2x_det_x_y_z_geometric_random() {
2097        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2098        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
2099        let dist = Uniform::new(-30.0, 30.0);
2100
2101        for _ in 0..1000 {
2102            let vals = dist
2103                .sample_iter(&mut rng)
2104                .take(25)
2105                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
2106                .collect::<Vec<_>>();
2107            let a = Vec3::new(vals[0], vals[1], vals[2]);
2108            let b = Vec3::new(vals[3], vals[4], vals[5]);
2109            let c = Vec3::new(vals[6], vals[7], vals[8]);
2110            let d = Vec3::new(vals[9], vals[10], vals[11]);
2111            let u = vals[12];
2112            let i = Vec3::new(vals[13], vals[14], vals[15]);
2113            let j = Vec3::new(vals[16], vals[17], vals[18]);
2114            let k = Vec3::new(vals[19], vals[20], vals[21]);
2115            let l = Vec3::new(vals[22], vals[23], vals[24]);
2116            check_sign_det_x_y_x2y2z2_plus_2x_det_x_y_z(a, b, c, d, u, i, j, k, l);
2117        }
2118    }
2119
2120    fn sign_det_x_x2y2z2_plus_2x_det_x_y_exact(a: Vec3, b: Vec3, c: Vec3, u: f64, i: Vec2, j: Vec2, k: Vec2) -> Float {
2121        const PREC: u32 = 1000; // lazy; this should be enough
2122        macro_rules! f {
2123            ($expr:expr) => {
2124                Float::with_val(PREC, $expr)
2125            };
2126        }
2127        f!(&sign_det_x_x2y2z2_exact(a, b, c) + &f!(2.0 * u) * &orient_2d_exact(i, j, k))
2128    }
2129
2130    fn check_sign_det_x_x2y2z2_plus_2x_det_x_y(a: Vec3, b: Vec3, c: Vec3, u: f64, i: Vec2, j: Vec2, k: Vec2) {
2131        let adapt = sign_det_x_x2y2z2_plus_2x_det_x_y(a, b, c, u, i, j, k);
2132        let exact = sign_det_x_x2y2z2_plus_2x_det_x_y_exact(a, b, c, u, i, j, k);
2133        assert_eq!(
2134            adapt.partial_cmp(&0.0),
2135            exact.partial_cmp(&0.0),
2136            "({}, {}, {}, {}, {}, {}, {}) gave wrong result: {} vs {}",
2137            a,
2138            b,
2139            c,
2140            u,
2141            i,
2142            j,
2143            k,
2144            adapt,
2145            exact
2146        );
2147
2148        let adapt = sign_det_x_x2y2z2_plus_2x_det_x_y_adapt(a, b, c, u, i, j, k);
2149        let exact = sign_det_x_x2y2z2_plus_2x_det_x_y_exact(a, b, c, u, i, j, k);
2150        assert_eq!(
2151            adapt.partial_cmp(&0.0),
2152            exact.partial_cmp(&0.0),
2153            "({}, {}, {}, {}, {}, {}, {}) adapted gave wrong result: {} vs {}",
2154            a,
2155            b,
2156            c,
2157            u,
2158            i,
2159            j,
2160            k,
2161            adapt,
2162            exact
2163        );
2164    }
2165
2166    #[test]
2167    fn test_sign_det_x_x2y2z2_plus_2x_det_x_y_uniform_random() {
2168        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2169        let dist = Uniform::new(-10.0, 10.0);
2170
2171        for _ in 0..1000 {
2172            let vals = dist.sample_iter(&mut rng).take(16).collect::<Vec<_>>();
2173            let a = Vec3::new(vals[0], vals[1], vals[2]);
2174            let b = Vec3::new(vals[3], vals[4], vals[5]);
2175            let c = Vec3::new(vals[6], vals[7], vals[8]);
2176            let u = vals[9];
2177            let i = Vec2::new(vals[10], vals[11]);
2178            let j = Vec2::new(vals[12], vals[13]);
2179            let k = Vec2::new(vals[14], vals[15]);
2180            check_sign_det_x_x2y2z2_plus_2x_det_x_y(a, b, c, u, i, j, k);
2181        }
2182    }
2183
2184    #[test]
2185    fn test_sign_det_x_x2y2z2_plus_2x_det_x_y_geometric_random() {
2186        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2187        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
2188        let dist = Uniform::new(-30.0, 30.0);
2189
2190        for _ in 0..1000 {
2191            let vals = dist
2192                .sample_iter(&mut rng)
2193                .take(16)
2194                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
2195                .collect::<Vec<_>>();
2196            let a = Vec3::new(vals[0], vals[1], vals[2]);
2197            let b = Vec3::new(vals[3], vals[4], vals[5]);
2198            let c = Vec3::new(vals[6], vals[7], vals[8]);
2199            let u = vals[9];
2200            let i = Vec2::new(vals[10], vals[11]);
2201            let j = Vec2::new(vals[12], vals[13]);
2202            let k = Vec2::new(vals[14], vals[15]);
2203            check_sign_det_x_x2y2z2_plus_2x_det_x_y(a, b, c, u, i, j, k);
2204        }
2205    }
2206
2207    fn sign_det_x2y2z2_plus_2x_det_x_exact(a: Vec3, b: Vec3, u: f64, i: f64, j: f64) -> Float {
2208        const PREC: u32 = 1000; // lazy; this should be enough
2209        macro_rules! f {
2210            ($expr:expr) => {
2211                Float::with_val(PREC, $expr)
2212            };
2213        }
2214        f!(&magnitude_cmp_3d_exact(a, b) + &f!(2.0 * u) * &f!(&f!(i) - &f!(j)))
2215    }
2216
2217    fn check_sign_det_x2y2z2_plus_2x_det_x(a: Vec3, b: Vec3, u: f64, i: f64, j: f64) {
2218        let adapt = sign_det_x2y2z2_plus_2x_det_x(a, b, u, i, j);
2219        let exact = sign_det_x2y2z2_plus_2x_det_x_exact(a, b, u, i, j);
2220        assert_eq!(
2221            adapt.partial_cmp(&0.0),
2222            exact.partial_cmp(&0.0),
2223            "({}, {}, {}, {}, {}) gave wrong result: {} vs {}",
2224            a,
2225            b,
2226            u,
2227            i,
2228            j,
2229            adapt,
2230            exact
2231        );
2232
2233        let adapt = sign_det_x2y2z2_plus_2x_det_x_adapt(a, b, u, i, j);
2234        let exact = sign_det_x2y2z2_plus_2x_det_x_exact(a, b, u, i, j);
2235        assert_eq!(
2236            adapt.partial_cmp(&0.0),
2237            exact.partial_cmp(&0.0),
2238            "({}, {}, {}, {}, {}) adapted gave wrong result: {} vs {}",
2239            a,
2240            b,
2241            u,
2242            i,
2243            j,
2244            adapt,
2245            exact
2246        );
2247    }
2248
2249    #[test]
2250    fn test_sign_det_x2y2z2_plus_2x_det_x_uniform_random() {
2251        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2252        let dist = Uniform::new(-10.0, 10.0);
2253
2254        for _ in 0..1000 {
2255            let vals = dist.sample_iter(&mut rng).take(9).collect::<Vec<_>>();
2256            let a = Vec3::new(vals[0], vals[1], vals[2]);
2257            let b = Vec3::new(vals[3], vals[4], vals[5]);
2258            let u = vals[6];
2259            let i = vals[7];
2260            let j = vals[8];
2261            check_sign_det_x2y2z2_plus_2x_det_x(a, b, u, i, j);
2262        }
2263    }
2264
2265    #[test]
2266    fn test_sign_det_x2y2z2_plus_2x_det_x_geometric_random() {
2267        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2268        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
2269        let dist = Uniform::new(-30.0, 30.0);
2270
2271        for _ in 0..1000 {
2272            let vals = dist
2273                .sample_iter(&mut rng)
2274                .take(16)
2275                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
2276                .collect::<Vec<_>>();
2277            let a = Vec3::new(vals[0], vals[1], vals[2]);
2278            let b = Vec3::new(vals[3], vals[4], vals[5]);
2279            let u = vals[6];
2280            let i = vals[7];
2281            let j = vals[8];
2282            check_sign_det_x2y2z2_plus_2x_det_x(a, b, u, i, j);
2283        }
2284    }
2285
2286    fn sign_det_x_x2y2_plus_2x_det_x_y_exact(a: Vec2, b: Vec2, c: Vec2, u: f64, i: Vec2, j: Vec2, k: Vec2) -> Float {
2287        const PREC: u32 = 1000; // lazy; this should be enough
2288        macro_rules! f {
2289            ($expr:expr) => {
2290                Float::with_val(PREC, $expr)
2291            };
2292        }
2293        f!(&sign_det_x_x2y2_exact(a, b, c) + &f!(2.0 * u) * &orient_2d_exact(i, j, k))
2294    }
2295
2296    fn check_sign_det_x_x2y2_plus_2x_det_x_y(a: Vec2, b: Vec2, c: Vec2, u: f64, i: Vec2, j: Vec2, k: Vec2) {
2297        let adapt = sign_det_x_x2y2_plus_2x_det_x_y(a, b, c, u, i, j, k);
2298        let exact = sign_det_x_x2y2_plus_2x_det_x_y_exact(a, b, c, u, i, j, k);
2299        assert_eq!(
2300            adapt.partial_cmp(&0.0),
2301            exact.partial_cmp(&0.0),
2302            "({}, {}, {}, {}, {}, {}, {}) gave wrong result: {} vs {}",
2303            a,
2304            b,
2305            c,
2306            u,
2307            i,
2308            j,
2309            k,
2310            adapt,
2311            exact
2312        );
2313
2314        let adapt = sign_det_x_x2y2_plus_2x_det_x_y_adapt(a, b, c, u, i, j, k);
2315        let exact = sign_det_x_x2y2_plus_2x_det_x_y_exact(a, b, c, u, i, j, k);
2316        assert_eq!(
2317            adapt.partial_cmp(&0.0),
2318            exact.partial_cmp(&0.0),
2319            "({}, {}, {}, {}, {}, {}, {}) adapted gave wrong result: {} vs {}",
2320            a,
2321            b,
2322            c,
2323            u,
2324            i,
2325            j,
2326            k,
2327            adapt,
2328            exact
2329        );
2330    }
2331
2332    #[test]
2333    fn test_sign_det_x_x2y2_plus_2x_det_x_y_uniform_random() {
2334        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2335        let dist = Uniform::new(-10.0, 10.0);
2336
2337        for _ in 0..1000 {
2338            let vals = dist.sample_iter(&mut rng).take(13).collect::<Vec<_>>();
2339            let a = Vec2::new(vals[0], vals[1]);
2340            let b = Vec2::new(vals[2], vals[3]);
2341            let c = Vec2::new(vals[4], vals[5]);
2342            let u = vals[6];
2343            let i = Vec2::new(vals[7], vals[8]);
2344            let j = Vec2::new(vals[9], vals[10]);
2345            let k = Vec2::new(vals[11], vals[12]);
2346            check_sign_det_x_x2y2_plus_2x_det_x_y(a, b, c, u, i, j, k);
2347        }
2348    }
2349
2350    #[test]
2351    fn test_sign_det_x_x2y2_plus_2x_det_x_y_geometric_random() {
2352        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2353        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
2354        let dist = Uniform::new(-30.0, 30.0);
2355
2356        for _ in 0..1000 {
2357            let vals = dist
2358                .sample_iter(&mut rng)
2359                .take(16)
2360                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
2361                .collect::<Vec<_>>();
2362            let a = Vec2::new(vals[0], vals[1]);
2363            let b = Vec2::new(vals[2], vals[3]);
2364            let c = Vec2::new(vals[4], vals[5]);
2365            let u = vals[6];
2366            let i = Vec2::new(vals[7], vals[8]);
2367            let j = Vec2::new(vals[9], vals[10]);
2368            let k = Vec2::new(vals[11], vals[12]);
2369            check_sign_det_x_x2y2_plus_2x_det_x_y(a, b, c, u, i, j, k);
2370        }
2371    }
2372
2373    fn sign_det_x2y2_plus_2x_det_x_exact(a: Vec2, b: Vec2, u: f64, i: f64, j: f64) -> Float {
2374        const PREC: u32 = 1000; // lazy; this should be enough
2375        macro_rules! f {
2376            ($expr:expr) => {
2377                Float::with_val(PREC, $expr)
2378            };
2379        }
2380        f!(&magnitude_cmp_2d_exact(a, b) + &f!(2.0 * u) * &f!(&f!(i) - &f!(j)))
2381    }
2382
2383    fn check_sign_det_x2y2_plus_2x_det_x(a: Vec2, b: Vec2, u: f64, i: f64, j: f64) {
2384        let adapt = sign_det_x2y2_plus_2x_det_x(a, b, u, i, j);
2385        let exact = sign_det_x2y2_plus_2x_det_x_exact(a, b, u, i, j);
2386        assert_eq!(
2387            adapt.partial_cmp(&0.0),
2388            exact.partial_cmp(&0.0),
2389            "({}, {}, {}, {}, {}) gave wrong result: {} vs {}",
2390            a,
2391            b,
2392            u,
2393            i,
2394            j,
2395            adapt,
2396            exact
2397        );
2398
2399        let adapt = sign_det_x2y2_plus_2x_det_x_adapt(a, b, u, i, j);
2400        let exact = sign_det_x2y2_plus_2x_det_x_exact(a, b, u, i, j);
2401        assert_eq!(
2402            adapt.partial_cmp(&0.0),
2403            exact.partial_cmp(&0.0),
2404            "({}, {}, {}, {}, {}) adapted gave wrong result: {} vs {}",
2405            a,
2406            b,
2407            u,
2408            i,
2409            j,
2410            adapt,
2411            exact
2412        );
2413    }
2414
2415    #[test]
2416    fn test_sign_det_x2y2_plus_2x_det_x_uniform_random() {
2417        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2418        let dist = Uniform::new(-10.0, 10.0);
2419
2420        for _ in 0..1000 {
2421            let vals = dist.sample_iter(&mut rng).take(7).collect::<Vec<_>>();
2422            let a = Vec2::new(vals[0], vals[1]);
2423            let b = Vec2::new(vals[2], vals[3]);
2424            let u = vals[4];
2425            let i = vals[5];
2426            let j = vals[6];
2427            check_sign_det_x2y2_plus_2x_det_x(a, b, u, i, j);
2428        }
2429    }
2430
2431    #[test]
2432    fn test_sign_det_x2y2_plus_2x_det_x_geometric_random() {
2433        let mut rng = Pcg64::new(PCG_STATE, PCG_STREAM);
2434        let mut rng2 = Pcg64::new(PCG_STATE, PCG_STREAM);
2435        let dist = Uniform::new(-30.0, 30.0);
2436
2437        for _ in 0..1000 {
2438            let vals = dist
2439                .sample_iter(&mut rng)
2440                .take(16)
2441                .map(|x: f64| if rng2.gen() { -1.0 } else { 1.0 } * x.exp2())
2442                .collect::<Vec<_>>();
2443            let a = Vec2::new(vals[0], vals[1]);
2444            let b = Vec2::new(vals[2], vals[3]);
2445            let u = vals[4];
2446            let i = vals[5];
2447            let j = vals[6];
2448            check_sign_det_x2y2_plus_2x_det_x(a, b, u, i, j);
2449        }
2450    }
2451}