1use 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
51pub 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 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 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
102pub 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 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 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
166macro_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
182macro_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
199macro_rules! sep_xyzw6 {
202(($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 }
232
233macro_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
262pub fn orient_3d(a: Vec3, b: Vec3, c: Vec3, d: Vec3) -> f64 {
289 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 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 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
345pub 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 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 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
403pub fn in_sphere(a: Vec3, b: Vec3, c: Vec3, d: Vec3, e: Vec3) -> f64 {
409 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); 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 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
484pub 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
506pub 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
528pub 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
554pub 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 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
587pub 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 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
620pub 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 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
657pub 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 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
705pub 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 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
746pub 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
775pub 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 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
816pub 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 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 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 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 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; 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; 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; 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; 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; 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}