1use crate::{
7 Arg, BETWEEN_CHECKS, buffer::Element, convert::{float, int}, error::Error, float::{Float, Native}, interrupt::check, recipe::{self, Floats},
8 search::{self, Chains, Key, Links}, slice::{Total, pairs_into}
9};
10use num_bigint::BigInt;
11use num_complex::Complex;
12use num_rational::BigRational;
13use num_traits::{Signed, ToPrimitive, Zero};
14use std::{cmp::Ordering, marker::PhantomData};
15
16#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
18pub struct Tolerance(f64);
19
20impl Tolerance {
21 pub const EXACT: Self = Self(0.0);
23 pub const DEFAULT: Self = Self(1e-14);
25 #[inline]
27 pub fn new(t: f64) -> Option<Self> { (0.0..1.0).contains(&t).then_some(Self(t)) }
28 #[inline]
30 pub fn is_exact(self) -> bool { self.0 == 0.0 }
31 #[inline]
33 pub const fn value(self) -> f64 { self.0 }
34}
35
36pub trait Tolerant: Copy + Send + Sync {
38 fn value(self) -> f64;
40 fn close<F: Float>(self, x: F, y: F) -> bool;
42 #[inline]
44 fn equal<F: Float>(self, x: F, y: F) -> bool { recipe::equal::<F, _>(&Floats, x, y, |_| self.close(x, y)) }
45 #[inline]
47 fn floor<F: Float>(self, y: F) -> F { self.floor_in::<F, Floats>(&Floats, y) }
48 fn floor_in<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, y: O::V) -> O::V;
50 #[inline]
52 fn ceil<F: Float>(self, y: F) -> F { self.ceil_in::<F, Floats>(&Floats, y) }
53 #[inline]
55 fn ceil_in<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, y: O::V) -> O::V { recipe::ceil(o, y, |o, v| self.floor_in(o, v)) }
56 #[inline]
58 fn matches<F: Float>(self, x: F, y: F) -> bool { self.equal(x, y) | (x.is_nan() & y.is_nan()) }
59 #[inline]
61 fn less<F: Float>(self, x: F, y: F) -> bool { recipe::less::<F, _>(&Floats, x, y, |_| self.close(x, y)) }
62 #[inline]
64 fn less_equal<F: Float>(self, x: F, y: F) -> bool { recipe::less_equal::<F, _>(&Floats, x, y, |_| self.close(x, y)) }
65 #[inline]
67 fn greater<F: Float>(self, x: F, y: F) -> bool { self.less(y, x) }
68 #[inline]
70 fn greater_equal<F: Float>(self, x: F, y: F) -> bool { self.less_equal(y, x) }
71 #[inline]
73 fn compare<F: Float>(self, x: F, y: F) -> Option<Ordering> { if self.equal(x, y) { Some(Ordering::Equal) } else { x.partial_cmp(&y) } }
74 #[inline]
76 fn residue<F: Float>(self, x: F, y: F) -> F { self.residue_in::<F, Floats>(&Floats, x, y) }
77 #[inline]
79 fn residue_in<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::V {
80 let tolerance = (!exact(self, o)).then(|| o.tolerance(F::narrow(self.value())));
81 recipe::residue(o, tolerance, x, y, |o, v| self.floor_in(o, v))
82 }
83 #[inline]
85 fn nearest_whole<F: Float>(self, y: F) -> (F, bool) { self.nearest_whole_in(&Floats, y) }
86 #[inline]
87 fn nearest_whole_in<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, y: O::V) -> (O::V, O::B) {
88 let below = o.floor(y);
89 let n = o.select(o.less(o.number(F::narrow(0.5)), o.subtract(y, below)), o.add(below, o.number(F::one())), below);
90 let scale = o.multiply(o.number(F::narrow(self.value())), o.larger(o.magnitude(y), o.magnitude(n)));
91 (n, o.less_equal(o.magnitude(o.subtract(y, n)), scale))
92 }
93 #[inline]
95 fn whole<F: Float>(self, y: F) -> Option<F> { let (n, equal) = self.nearest_whole(y); equal.then_some(n) }
96 fn range<F: Float>(self, c: F) -> Option<(F, F)> {
98 if c.is_nan() { return None; }
99 if !c.is_finite() || c == F::zero() { return Some((c, c)); }
100 let t = F::narrow(self.value());
101 let (near, far) = (c * (F::one() - t), c / (F::one() - t));
102 let (lo, hi) = if c > F::zero() { (near, far) } else { (far, near) };
103 let end = |mut x: F, out: fn(F) -> F, back: fn(F) -> F| {
105 while !self.equal(x, c) { x = back(x); }
106 while self.equal(out(x), c) { x = out(x); }
107 x
108 };
109 Some((end(lo, F::next_down, F::next_up), end(hi, F::next_up, F::next_down)))
110 }
111 fn gcd<F: Float>(self, x: F, y: F) -> F {
113 let (x, y) = (x.wide(), y.wide());
114 if !(x.is_finite() && y.is_finite()) { return F::nan(); }
115 let (x, y) = (x.abs().max(y.abs()), x.abs().min(y.abs()));
116 if y == 0.0 { return F::narrow(x); }
117 let whole = x.fract() == 0.0 && y.fract() == 0.0 && x * y < 1e14 && self.value() <= Tolerance::DEFAULT.value();
118 if let Some(g) = whole.then(|| crate::integer::gcd(int(x)?, int(y)?)).flatten() { return float(g); }
119 let (t, (nx, dy)) = (self.value(), ratio_parts(x, y));
120 if t == 0.0 {
121 let g = crate::integer::gcd(nx.0 as i64, dy.0 as i64).unwrap() as f64;
123 let unit = if nx.1 == 0 { x / nx.0 as f64 } else { y / dy.0 as f64 };
124 return F::narrow(unit * g);
125 }
126 if let Some(g) = fixed_gcd(x, y, nx, dy, t) { return F::narrow(g); }
127 let (n, d) = (BigInt::from(nx.0) << nx.1, BigInt::from(dy.0) << dy.1);
129 let (m, e, _) = num_traits::Float::integer_decode(t);
130 let p = convergents(n.clone(), d.clone(), |p, q| {
131 let (gap, bound) = ((p * &d - q * &n).abs(), BigInt::from(m) * &n * q);
132 let within = gap.is_zero() || m != 0 && if e >= 0 { gap <= bound << e as usize } else { gap << (-e) as usize <= bound };
133 within.then(|| p.clone())
134 });
135 F::narrow((BigRational::from_float(x).unwrap() / BigRational::from_integer(p)).to_f64().unwrap())
136 }
137}
138
139fn ratio_parts(x: f64, y: f64) -> ((u64, u32), (u64, u32)) {
141 let odd = |v: f64| {
142 let (m, e, _) = num_traits::Float::integer_decode(v);
143 (m >> m.trailing_zeros(), i32::from(e) + m.trailing_zeros() as i32)
144 };
145 let ((mx, ex), (my, ey)) = (odd(x), odd(y));
146 if ex >= ey { ((mx, (ex - ey) as u32), (my, 0)) } else { ((mx, 0), (my, (ey - ex) as u32)) }
147}
148
149fn convergents<I: num_traits::Num + Clone, R>(n: I, d: I, mut done: impl FnMut(&I, &I) -> Option<R>) -> R {
151 let (mut a, mut b) = (n, d);
152 let (mut p0, mut p1, mut q0, mut q1) = (I::zero(), I::one(), I::one(), I::zero());
153 loop {
154 let k = a.clone() / b.clone();
155 let p = k.clone() * p1.clone() + p0;
156 p0 = std::mem::replace(&mut p1, p);
157 let q = k * q1.clone() + q0;
158 q0 = std::mem::replace(&mut q1, q);
159 if let Some(answer) = done(&p1, &q1) { return answer; }
160 let r = a % b.clone();
161 (a, b) = (b, r);
162 }
163}
164
165fn fixed_gcd(x: f64, y: f64, (mx, sx): (u64, u32), (my, sy): (u64, u32), t: f64) -> Option<f64> {
167 let shifted = |m: u64, s: u32| (m.leading_zeros() >= s).then(|| m << s);
168 let (n, d) = (shifted(mx, sx)?, shifted(my, sy)?);
169 convergents(n, d, |&p, &q| {
170 let gap = (u128::from(p) * u128::from(d)).abs_diff(u128::from(q) * u128::from(n));
172 let bound = t * (u128::from(n) * u128::from(q)) as f64;
173 let within = if gap == 0 { true }
174 else if bound < 0.5 { false } else {
175 let g = gap as f64;
176 if g <= bound * (1.0 - 1e-15) { true } else if g >= bound * (1.0 + 1e-15) { false } else { return Some(None); }
177 };
178 within.then(|| {
180 if p <= 1 << 53 { Some(x / p as f64) } else if gap == 0 && q <= 1 << 53 { Some(y / q as f64) } else { None }
181 })
182 })
183}
184
185#[inline(always)]
187fn nearest<F: Float>(y: F) -> F { Tolerance::EXACT.nearest_whole(y).0 }
188
189#[derive(Clone, Copy, Debug)]
191pub struct Exactly;
192
193impl Tolerant for Exactly {
194 #[inline]
195 fn value(self) -> f64 { 0.0 }
196 #[inline]
197 fn close<F: Float>(self, _: F, _: F) -> bool { false }
198 #[inline]
199 fn floor_in<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, y: O::V) -> O::V { recipe::exact_floor(o, y) }
200}
201
202impl Tolerant for Tolerance {
203 #[inline]
204 fn value(self) -> f64 { Tolerance::value(self) }
205 #[inline]
206 fn close<F: Float>(self, x: F, y: F) -> bool { recipe::close(&Floats, F::narrow(self.0), x, y) }
207 #[inline]
208 fn floor_in<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, y: O::V) -> O::V { recipe::floor(o, o.tolerance(F::narrow(self.0)), y) }
209}
210
211pub trait TolerantComplex: Tolerant {
213 #[inline]
215 fn complex_equal<R: Native>(self, x: Complex<R>, y: Complex<R>) -> bool {
216 let (equal, decided) = self.squared_equal(x, y);
217 if decided { equal } else { self.scaled_equal(x, y) }
218 }
219 #[inline(always)]
221 fn squared_equal<R: Native>(self, x: Complex<R>, y: Complex<R>) -> (bool, bool) {
222 let t = R::narrow(self.value());
223 let (low, high) = if R::WIDE { (2f64.powi(-800), 2f64.powi(1000)) } else { (2f64.powi(-80), 2f64.powi(120)) };
224 let size = x.norm_sqr().max(y.norm_sqr());
225 let near = (x - y).norm_sqr() <= t * t * size;
226 let exact = t == R::zero();
227 ((exact & (x == y)) | (!exact & near), exact | ((size >= R::narrow(low)) & (size <= R::narrow(high))))
228 }
229 fn scaled_equal<R: Native>(self, x: Complex<R>, y: Complex<R>) -> bool {
231 let finite = |z: Complex<R>| z.re.is_finite() & z.im.is_finite();
232 if !finite(x) || !finite(y) { return x == y; }
233 let t = R::narrow(self.value());
234 (x - y).norm() <= (x * t).norm().max((y * t).norm())
235 }
236 #[inline]
238 fn complex_floor<R: Native>(self, y: Complex<R>) -> Complex<R> {
239 let (a, b) = (self.floor(y.re), self.floor(y.im));
240 let (x, z) = (y.re - a, y.im - b);
241 if x + z < R::one() - R::narrow(self.value()) { Complex::new(a, b) }
242 else if x <= z { Complex::new(a, b + R::one()) }
243 else { Complex::new(a + R::one(), b) }
244 }
245 #[inline]
246 fn complex_ceil<R: Native>(self, y: Complex<R>) -> Complex<R> { -self.complex_floor(-y) }
247 #[inline]
249 fn complex_residue<R: Native>(self, x: Complex<R>, y: Complex<R>) -> Complex<R> {
250 if x.is_zero() { return y; }
251 let q = y / x;
252 if self.complex_equal(x * Complex::new(nearest(q.re), nearest(q.im)), y) { Complex::zero() } else { y - x * self.complex_floor(q) }
253 }
254 fn complex_gcd<R: Native>(self, x: Complex<R>, y: Complex<R>) -> Complex<R> {
256 if x.im == R::zero() && y.im == R::zero() { return Complex::new(self.gcd(x.re, y.re), R::zero()); }
257 let nan = Complex::new(R::nan(), R::nan());
258 let scale = x.norm().max(y.norm());
259 if !scale.is_finite() { return nan; }
260 let original = if x.norm() >= y.norm() { x } else { y };
261 let t = R::narrow(self.value()) * scale;
262 let whole = |z: Complex<R>| Complex::new(z.re.round(), z.im.round());
263 let small = |z: Complex<R>| Complex::new(if z.re.abs() <= t { R::zero() } else { z.re }, if z.im.abs() <= t { R::zero() } else { z.im });
264 let (mut x, mut y) = (x, y);
265 while y.norm() > t {
266 let r = x - y * whole(x / y);
267 if !(r.norm() < y.norm()) { return nan; }
268 (x, y) = (y, r);
269 }
270 let mut x = small(x);
271 if x.is_zero() { return x; }
272 while x.re <= R::zero() || x.im < R::zero() { x = Complex::new(-x.im, x.re); }
273 small(original / whole(original / x))
274 }
275}
276impl<T: Tolerant> TolerantComplex for T {}
277
278#[macro_export]
280macro_rules! with_tolerance {
281 ($tolerance:expr, |$t:ident| $body:expr) => {{
282 let tolerance: $crate::tolerant::Tolerance = $tolerance;
283 if tolerance.is_exact() {
284 let $t = $crate::tolerant::Exactly;
285 $body
286 } else {
287 let $t = tolerance;
288 $body
289 }
290 }};
291}
292
293macro_rules! tolerant {
295 ($(#[$($m:tt)+])* $object:ident at real $(=> $o:ty)?: $($rest:tt)+) => { tolerant!(@forms [$(#[$($m)+])*], $object, (real $(=> $o)?), $($rest)+); };
296 ($(#[$($m:tt)+])* $object:ident at $($e:ty $(=> $o:ty)?),+: $($rest:tt)+) => { tolerant!(@forms [$(#[$($m)+])*], $object, ($($e $(=> $o)?),+), $($rest)+); };
297 (@forms [$(#[$($m:tt)+])*], $object:ident, ($($at:tt)+), |$t:ident, [$($x:ident),+]| $body:expr) => {
298 forms!($(#[$($m)+])* $object at $($at)+: |k, [$($x),+]| { let $t = k.0; $body },
299 many |k, [$($x),+], n, out| with_tolerance!(k.0, |$t| each!([$($x),+], n, out, |[$($x),+]| $body)));
300 };
301}
302
303#[inline(always)]
305fn indicated<E: Element>(b: bool) -> E where bool: crate::buffer::Source<E> {
306 if b { crate::buffer::Source::<E>::read(true) } else { crate::buffer::Source::<E>::read(false) }
307}
308
309macro_rules! indicator {
311 ($object:ident: |$t:ident, [$x:ident, $y:ident]| $body:expr, $left:ident, $right:ident, $($e:ty),+) => {$(
312 forms!([Indicator<$object>] at $e: |k, [$x, $y]| crate::slice::Total(indicated::<$e>({ let $t = k.0.0; $body })),
313 many |k, [$x, $y], n, out| match against(k.0.0, $x, $y, n, out, Band::$left, Band::$right, |xs, lo, hi, band, out| in_band(xs, lo, hi, band, out, indicated::<$e>)) {
314 Some(ok) => ok,
315 None => with_tolerance!(k.0.0, |$t| each!([$x, $y], n, out, |[$x, $y]| crate::slice::Total(indicated::<$e>($body)))),
316 });
317 )+};
318}
319
320macro_rules! compared {
322 ($object:ident: |$t:ident, [$x:ident, $y:ident]| $body:expr, $left:ident, $right:ident) => {
323 forms!($object at real => bool: |k, [$x, $y]| { let $t = k.0; $body },
324 many |k, [$x, $y], n, out| match against(k.0, $x, $y, n, out, Band::$left, Band::$right, Banded::banded) {
325 Some(ok) => ok,
326 None => with_tolerance!(k.0, |$t| each!([$x, $y], n, out, |[$x, $y]| $body)),
327 });
328 indicator!($object: |$t, [$x, $y]| $body, $left, $right, f32, f64);
329 impl Banding for $object { const BANDS: (Band, Band) = (Band::$left, Band::$right); }
330 };
331}
332
333#[derive(Clone, Copy)]
335pub enum Band { Inside, Outside, Below, NotAbove, Above, NotBelow }
336
337impl Band {
338 #[inline(always)]
340 fn holds<F: Float>(self, lo: F, hi: F, v: F) -> bool { recipe::in_band(&Floats, self, lo, hi, v) }
341}
342
343pub trait Banding { const BANDS: (Band, Band); }
345
346pub fn against_steps<K: Banding, F: Float, O: recipe::Ops<F> + ?Sized>(_k: K, t: Tolerance, o: &O, c: F, v: O::V, left: bool) -> Option<O::B> {
348 let (lo, hi) = t.range(c)?;
349 let band = if left { K::BANDS.0 } else { K::BANDS.1 };
350 let (lo, hi) = (o.number(lo), o.number(hi));
351 Some(recipe::in_band(o, band, lo, hi, v))
352}
353
354macro_rules! with_band {
356 ($band:expr, |$b:ident| $body:expr) => {
357 match $band {
358 Band::Inside => { const $b: Band = Band::Inside; $body }
359 Band::Outside => { const $b: Band = Band::Outside; $body }
360 Band::Below => { const $b: Band = Band::Below; $body }
361 Band::NotAbove => { const $b: Band = Band::NotAbove; $body }
362 Band::Above => { const $b: Band = Band::Above; $body }
363 Band::NotBelow => { const $b: Band = Band::NotBelow; $body }
364 }
365 };
366}
367
368#[inline(always)]
370fn in_band<F: Float, O>(xs: &[F], lo: F, hi: F, band: Band, out: &mut [std::mem::MaybeUninit<O>], from: impl Fn(bool) -> O + Copy) {
371 with_band!(band, |B| pairs_into(
372 Arg::Many(xs), Arg::One(()), xs.len(), out, #[inline(always)] |v, ()| (from(B.holds(lo, hi, v)), true)
373 ));
374}
375
376pub trait Banded: Float {
378 #[inline(always)]
380 fn banded(xs: &[Self], lo: Self, hi: Self, band: Band, out: &mut [std::mem::MaybeUninit<bool>]) { in_band(xs, lo, hi, band, out, |b| b) }
381}
382#[cfg(not(all(target_arch = "wasm32", target_feature = "simd128")))]
383impl Banded for f32 {}
384#[cfg(not(all(target_arch = "wasm32", target_feature = "simd128")))]
385impl Banded for f64 {}
386
387#[inline(always)]
389pub fn against<F: Float, O>(
390 t: Tolerance, x: Arg<'_, F>, y: Arg<'_, F>, n: usize, out: &mut [std::mem::MaybeUninit<O>], left: Band, right: Band,
391 banded: impl FnOnce(&[F], F, F, Band, &mut [std::mem::MaybeUninit<O>])) -> Option<bool> {
392 let (items, (lo, hi), band) = match (x, y) {
393 (Arg::Many(xs), Arg::One(c)) => (xs, t.range(c)?, left), (Arg::One(c), Arg::Many(ys)) => (ys, t.range(c)?, right), _ => return None
394 };
395 banded(&items[..n], lo, hi, band, &mut out[..n]);
396 Some(true)
397}
398
399#[derive(Clone, Copy, Debug)]
401pub struct Indicator<K>(pub K);
402
403#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
405mod narrowed {
406 use super::{Band, Banded};
407 use core::arch::wasm32::*;
408 use std::mem::MaybeUninit;
409
410 macro_rules! narrowed {
412 ($($t:ty: $lanes:literal, $splat:ident, $lt:ident, $le:ident, $gt:ident, $ge:ident, $join:ident;)+) => {$(
413 impl Banded for $t {
414 fn banded(xs: &[$t], lo: $t, hi: $t, band: Band, out: &mut [MaybeUninit<bool>]) {
415 let (l, h) = ($splat(lo), $splat(hi));
416 with_band!(band, |B| blocks::<$t, $lanes, { 16 / $lanes }>(xs, out, $join, #[inline(always)] |v| match B {
417 Band::Inside => v128_and($le(l, v), $le(v, h)),
418 Band::Outside => v128_not(v128_and($le(l, v), $le(v, h))),
419 Band::Below => $lt(v, l),
420 Band::NotAbove => $le(v, h),
421 Band::Above => $gt(v, h),
422 Band::NotBelow => $ge(v, l),
423 }, #[inline(always)] |v| B.holds(lo, hi, v)))
424 }
425 }
426 )+};
427 }
428 narrowed! {
429 f64: 2, f64x2_splat, f64x2_lt, f64x2_le, f64x2_gt, f64x2_ge, join64;
430 f32: 4, f32x4_splat, f32x4_lt, f32x4_le, f32x4_gt, f32x4_ge, join32;
431 }
432
433 #[inline(always)]
435 fn blocks<T: Copy, const LANES: usize, const N: usize>(
436 xs: &[T], out: &mut [MaybeUninit<bool>], join: impl Fn([v128; N]) -> v128, mask: impl Fn(v128) -> v128, holds: impl Fn(T) -> bool) {
437 let (blocks, rest) = out.as_chunks_mut::<16>();
438 let (whole, tail) = xs.as_chunks::<16>();
439 for (block, x) in blocks.iter_mut().zip(whole) {
440 let masks = std::array::from_fn(|k| mask(unsafe { v128_load(x.as_ptr().add(LANES * k).cast()) }));
442 unsafe { v128_store(block.as_mut_ptr().cast(), v128_and(join(masks), u8x16_splat(1))) }
444 }
445 for (slot, &x) in rest.iter_mut().zip(tail) { slot.write(holds(x)); }
446 }
447
448 #[inline(always)]
449 fn join64(m: [v128; 8]) -> v128 {
450 let four = |a, b| i8x16_shuffle::<0, 8, 16, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(a, b);
451 let eight = |a, b| i8x16_shuffle::<0, 1, 2, 3, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0>(a, b);
452 let (low, high) = (eight(four(m[0], m[1]), four(m[2], m[3])), eight(four(m[4], m[5]), four(m[6], m[7])));
453 i8x16_shuffle::<0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23>(low, high)
454 }
455
456 #[inline(always)]
457 fn join32(m: [v128; 4]) -> v128 {
458 let eight = |a, b| i8x16_shuffle::<0, 4, 8, 12, 16, 20, 24, 28, 0, 0, 0, 0, 0, 0, 0, 0>(a, b);
459 i8x16_shuffle::<0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23>(eight(m[0], m[1]), eight(m[2], m[3]))
460 }
461}
462
463#[derive(Clone, Copy, Debug, PartialEq)]
465pub struct Equal(pub Tolerance);
466compared!(Equal: |t, [x, y]| t.equal(x, y), Inside, Inside);
467forms!(Equal at Complex<f32> => bool, Complex<f64> => bool: |k, [x, y]| k.0.complex_equal(x, y),
468 many |k, [x, y], n, out| with_tolerance!(k.0, |t| complex_equals(t, [x, y], n, out, false)));
469
470#[derive(Clone, Copy, Debug, PartialEq)]
472pub struct NotEqual(pub Tolerance);
473compared!(NotEqual: |t, [x, y]| !t.equal(x, y), Outside, Outside);
474forms!(NotEqual at Complex<f32> => bool, Complex<f64> => bool: |k, [x, y]| !k.0.complex_equal(x, y),
475 many |k, [x, y], n, out| with_tolerance!(k.0, |t| complex_equals(t, [x, y], n, out, true)));
476
477#[inline(always)]
479fn complex_equals<T: TolerantComplex, R: Native>(
480 t: T, [x, y]: [Arg<'_, Complex<R>>; 2], n: usize, out: &mut [std::mem::MaybeUninit<bool>], negate: bool) -> bool {
481 if pairs_into(x, y, n, out, #[inline(always)] |a, b| {
482 let (equal, decided) = t.squared_equal(a, b);
483 (equal != negate, decided)
484 }) { return true; }
485 let out = unsafe { crate::slice::written(out) };
486 for i in 0..n { let (a, b) = (x.nth(i), y.nth(i)); if !t.squared_equal(a, b).1 { out[i] = t.scaled_equal(a, b) != negate } }
487 true
488}
489
490#[derive(Clone, Copy, Debug, PartialEq)]
492pub struct Less(pub Tolerance);
493compared!(Less: |t, [x, y]| t.less(x, y), Below, Above);
494
495#[derive(Clone, Copy, Debug, PartialEq)]
497pub struct LessEqual(pub Tolerance);
498compared!(LessEqual: |t, [x, y]| t.less_equal(x, y), NotAbove, NotBelow);
499
500#[derive(Clone, Copy, Debug, PartialEq)]
502pub struct Greater(pub Tolerance);
503compared!(Greater: |t, [x, y]| t.greater(x, y), Above, Below);
504
505#[derive(Clone, Copy, Debug, PartialEq)]
507pub struct GreaterEqual(pub Tolerance);
508compared!(GreaterEqual: |t, [x, y]| t.greater_equal(x, y), NotBelow, NotAbove);
509
510pub trait Stepped {
512 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B;
514}
515
516#[inline(always)]
518fn close_steps<F: Float, O: recipe::Ops<F> + ?Sized>(t: Tolerance, o: &O, x: O::V, y: O::V) -> O::B {
519 recipe::close(o, o.tolerance(F::narrow(t.value())), x, y)
520}
521
522#[inline(always)]
524fn exact<F: Float, O: recipe::Ops<F> + ?Sized>(t: impl Tolerant, o: &O) -> bool {
525 o.constant(o.tolerance(F::narrow(t.value()))).is_some_and(|t| t == F::zero())
526}
527
528impl Stepped for Equal {
529 #[inline(always)]
530 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B {
531 if exact(self.0, o) { return o.equal(x, y); }
532 recipe::equal(o, x, y, |o| close_steps(self.0, o, x, y))
533 }
534}
535
536impl Stepped for NotEqual {
537 #[inline(always)]
538 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B { let equal = Equal(self.0).steps(o, x, y); o.not(equal) }
539}
540
541impl Stepped for Less {
542 #[inline(always)]
543 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B {
544 if exact(self.0, o) { return o.less(x, y); }
545 recipe::less(o, x, y, |o| close_steps(self.0, o, x, y))
546 }
547}
548
549impl Stepped for LessEqual {
550 #[inline(always)]
551 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B {
552 if exact(self.0, o) { return o.less_equal(x, y); }
553 recipe::less_equal(o, x, y, |o| close_steps(self.0, o, x, y))
554 }
555}
556
557impl Stepped for Greater {
558 #[inline(always)]
559 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B { Less(self.0).steps(o, y, x) }
560}
561
562impl Stepped for GreaterEqual {
563 #[inline(always)]
564 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B { LessEqual(self.0).steps(o, y, x) }
565}
566
567#[derive(Clone, Copy, Debug, PartialEq)]
569pub struct Matches(pub Tolerance);
570compared!(Matches: |t, [x, y]| t.matches(x, y), Inside, Inside);
571
572#[derive(Clone, Copy, Debug, PartialEq)]
574pub struct Compare(pub Tolerance);
575tolerant!(Compare at real => Ordering: |t, [x, y]| t.compare(x, y).unwrap_or(x.is_nan().cmp(&y.is_nan())));
576
577#[derive(Clone, Copy, Debug, PartialEq)]
579pub struct Floor(pub Tolerance);
580tolerant!(#[inline] Floor at real: |t, [y]| Total(t.floor(y)));
581tolerant!(Floor at Complex<f32>, Complex<f64>: |t, [y]| Total(t.complex_floor(y)));
582
583#[derive(Clone, Copy, Debug, PartialEq)]
585pub struct Ceil(pub Tolerance);
586tolerant!(#[inline] Ceil at real: |t, [y]| Total(t.ceil(y)));
587tolerant!(Ceil at Complex<f32>, Complex<f64>: |t, [y]| Total(t.complex_ceil(y)));
588
589#[derive(Clone, Copy, Debug, PartialEq)]
591pub struct Residue(pub Tolerance);
592forms!(#[inline] Residue at real: |k, [x, y]| Total(if k.0.is_exact() { exact_residue(x, y) } else { k.0.residue(x, y) }),
593many |k, [x, y], n, out| with_tolerance!(k.0, |t| {
594 if t.value() != 0.0 { return each!([x, y], n, out, |[x, y]| Total(t.residue(x, y))); }
595 if !each!([x, y], n, out, |[x, y]| (t.residue(x, y), near(x, y))) { replace_far(x, y, n, out); }
596 true
597}));
598tolerant!(Residue at Complex<f32>, Complex<f64>: |t, [x, y]| t.complex_residue(x, y));
599
600fn replace_far<F: Float>(x: Arg<'_, F>, y: Arg<'_, F>, n: usize, out: &mut [std::mem::MaybeUninit<F>]) {
602 for (i, r) in unsafe { crate::slice::written(&mut out[..n]) }.iter_mut().enumerate() {
603 let (x, y) = (x.nth(i), y.nth(i));
604 if !near(x, y) { *r = exact_residue(x, y); }
605 }
606}
607
608#[inline(always)]
610fn far<F: Float>(x: F, y: F) -> bool { (x != F::zero()) & (y.abs() >= F::narrow(2.0) / F::epsilon() * x.abs()) }
611
612#[inline(always)]
614fn near<F: Float>(x: F, y: F) -> bool { y.abs() < F::narrow(2.0) / F::epsilon() * x.abs() }
615
616pub(crate) fn exact_residue<F: Float>(x: F, y: F) -> F { if far(x, y) { far_residue(x, y) } else { Exactly.residue(x, y) } }
618
619#[cold]
621#[inline(never)]
622fn far_residue<F: Float>(x: F, y: F) -> F { let r = y % x; if r == F::zero() { F::zero() } else if (r < F::zero()) != (x < F::zero()) { r + x } else { r } }
623
624#[derive(Clone, Copy, Debug, PartialEq)]
626pub struct FloorDivide(pub Tolerance);
627
628#[derive(Clone, Copy, Debug, PartialEq)]
630pub struct MathishFloorDivide(pub Tolerance);
631
632#[derive(Clone, Copy, Debug, PartialEq)]
634pub struct Gcd(pub Tolerance);
635tolerant!(Gcd at real: |t, [x, y]| t.gcd(x, y));
636tolerant!(Gcd at Complex<f32>, Complex<f64>: |t, [x, y]| t.complex_gcd(x, y));
637
638#[derive(Clone, Copy, Debug, PartialEq)]
640pub struct Lcm(pub Tolerance);
641tolerant!(Lcm at real: |t, [x, y]| lcm_from_gcd(x, y, t.gcd(x, y)));
642tolerant!(Lcm at Complex<f32>, Complex<f64>: |t, [x, y]| lcm_from_gcd(x, y, t.complex_gcd(x, y)));
643
644#[inline]
645pub(crate) fn lcm_from_gcd<T: Copy + num_traits::Num>(x: T, y: T, g: T) -> T { if g.is_zero() { g } else { x / g * y } }
646
647const ROUND_UNITS: usize = 16;
649
650#[inline(never)]
652pub fn index_of<F: Float>(t: impl Tolerant, x: &[F], y: &[F]) -> Result<Vec<u32>, Error> {
653 if y.len() <= search::SCANNED { return Ok(y.iter().map(|&v| scan(t, x, v) as u32).collect()); }
654 if let Some((keys, exact)) = wholes(t, x, whole_limit(t)) && let Some((whole_y, _)) = wholes(t, y, whole_limit(t)) {
655 let found = search::index_of(&keys, &whole_y);
657 if exact || found.iter().zip(y).all(|(&p, &v)| p as usize == x.len() || x[p as usize] == v || t.matches(x[p as usize], v)) { return Ok(found); }
658 }
659 if t.value() == 0.0 { return Ok(search::index_of(&order_keys(x), &order_keys(y))); }
660 let near = Near::new(t, x, |j, i| x[j].order_key() == x[i].order_key());
661 let mut found = Vec::with_capacity(y.len());
662 for round in y.chunks(ROUND_UNITS * BETWEEN_CHECKS * crate::parallel::threads()) {
663 check()?;
664 search::look_up(&mut found, round, |v| {
665 let mut first = x.len();
666 for i in near.near(v) { if i < first && t.matches(x[i], v) { first = i; } }
667 first as u32
668 });
669 }
670 Ok(found)
671}
672
673#[inline(never)]
675pub fn classes<F: Float>(t: impl Tolerant, x: &[F]) -> Result<Vec<u32>, Error> {
676 if let Some((keys, _)) = wholes(t, x, whole_limit(t)) { return Ok(search::classes(&keys)); }
677 if t.value() == 0.0 { return Ok(search::first_classes(x, F::order_key)); }
678 let mut classes = vec![0; x.len()];
679 checked_runs(t, x, |run| { for &i in run { classes[i] = run[0] as u32 } })?;
680 Ok(classes)
681}
682
683#[inline(never)]
685pub fn firsts<F: Float>(t: impl Tolerant, x: &[F]) -> Result<Vec<bool>, Error> {
686 if let Some((keys, _)) = wholes(t, x, whole_limit(t)) { return Ok(search::firsts(&keys)); }
687 if t.value() == 0.0 { return Ok(search::first_mask(x, F::order_key)); }
688 let mut firsts = vec![false; x.len()];
689 checked_runs(t, x, |run| firsts[run[0]] = true)?;
690 Ok(firsts)
691}
692
693fn checked_runs<F: Float>(t: impl Tolerant, x: &[F], mut each: impl FnMut(&[usize])) -> Result<(), Error> {
695 let mut since = 0;
696 runs(t, x, |run| {
697 since += run.len();
698 if since >= BETWEEN_CHECKS {
699 since = 0;
700 check()?;
701 }
702 each(run);
703 Ok(())
704 })
705}
706
707#[inline(never)]
709pub fn runs<F: Float, E>(t: impl Tolerant, x: &[F], mut each: impl FnMut(&[usize]) -> Result<(), E>) -> Result<(), E> {
710 let (keys, order) = search::sorted(x.iter().map(|&v| v.order_key()).collect());
711 let (mut run, mut start) = (Vec::new(), 0);
712 for end in 1..=keys.len() {
713 if end < keys.len() && t.matches(F::from_order_key(keys[end - 1]), F::from_order_key(keys[end])) { continue; }
714 if end - start == 1 { each(&[order[start] as usize])?; } else {
716 run.clear();
717 run.extend(order[start..end].iter().map(|&i| i as usize));
718 run.sort_unstable();
719 each(&run)?;
720 }
721 start = end;
722 }
723 Ok(())
724}
725
726pub struct Near<F> { chains: Chains, shift: u32, width: PhantomData<F> }
728
729impl<F: Float> Near<F> {
730 pub fn new(t: impl Tolerant, x: &[F], copies: impl Fn(usize, usize) -> bool) -> Self {
732 let (t, epsilon) = (t.value(), F::epsilon().to_f64().unwrap_or(1.0));
733 let apart = (4.0 * t / ((1.0 - t) * epsilon)).ceil() + 1.0;
734 let shift = (2.0 * (apart + 1.0)).log2().ceil().min(64.0) as u32;
735 let mut near = Self { chains: Chains::new(x.len()), shift, width: PhantomData };
736 for (i, &v) in x.iter().enumerate() { near.chains.push_new(near.bucket(v).0, i, |j| copies(j, i)); }
737 near
738 }
739 #[inline]
741 fn bucket(&self, y: F) -> (u64, bool) {
742 let k = y.order_key().key() as u64;
743 (k.checked_shr(self.shift).unwrap_or(0), self.shift > 0 && (k >> (self.shift - 1)) & 1 == 1)
744 }
745 #[inline]
747 pub fn near(&self, y: F) -> Links<'_> { let (b, upper) = self.bucket(y); self.chains.chains(b, if upper { b.checked_add(1) } else { b.checked_sub(1) }) }
748}
749
750pub(crate) fn order_keys<F: Float>(x: &[F]) -> Vec<F::OrderKey> { x.iter().map(|v| v.order_key()).collect() }
752
753pub fn whole_limit(t: impl Tolerant) -> f64 { (0.25 / t.value()).min(9_007_199_254_740_992.0) }
755
756pub fn integer_limit<F: Float>(t: impl Tolerant) -> f64 { whole_limit(t).min(2.0 / F::epsilon().wide()) }
758
759pub fn integer_of<F: Float>(t: impl Tolerant, v: F) -> Option<i64> {
761 let v = v.wide();
762 (v.fract() == 0.0 && v.abs() < integer_limit::<F>(t)).then_some(v as i64)
763}
764
765pub fn wholes<F: Float>(t: impl Tolerant, x: &[F], limit: f64) -> Option<(Vec<i64>, bool)> {
767 const WHOLES: usize = 256;
768 let (limit, mut keys, mut exact) = (F::narrow(limit), Vec::with_capacity(x.len()), true);
769 for block in x.chunks(WHOLES) {
770 let (fits, whole) = unsafe {
771 crate::slice::appended(&mut keys, block.len(), |slots| {
772 const MAGIC: f64 = 6_755_399_441_055_744.0;
774 let (mut fits, mut whole, mut small) = (true, true, true);
775 for (slot, &y) in slots.iter_mut().zip(block) {
776 let n = nearest(y);
777 fits &= n.abs() < limit;
778 whole &= n == y;
779 small &= n.wide().abs() < 2_251_799_813_685_248.0;
780 slot.write(((n.wide() + MAGIC).to_bits() as i64).wrapping_sub(MAGIC.to_bits() as i64));
781 }
782 if !small { for (slot, &y) in slots.iter_mut().zip(block) { slot.write(nearest(y).wide() as i64); } }
783 (fits, whole)
784 })
785 };
786 if !fits || !whole && !block.iter().fold(true, |equal, &y| equal & t.nearest_whole(y).1) { return None; }
787 exact &= whole;
788 }
789 Some((keys, exact))
790}
791
792fn scan<F: Float>(t: impl Tolerant, x: &[F], v: F) -> usize {
794 let found = match t.range(v) { Some((lo, hi)) => search::first(x, |h| (lo <= h) & (h <= hi)), None => search::first(x, |h: F| h.is_nan()) };
795 found.unwrap_or(x.len())
796}