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 let Some(g) = fixed_gcd(x, y, nx, dy, t) { return F::narrow(g); }
121 let (n, d) = (BigInt::from(nx.0) << nx.1, BigInt::from(dy.0) << dy.1);
123 let (m, e, _) = num_traits::Float::integer_decode(t);
124 let p = convergents(n.clone(), d.clone(), |p, q| {
125 let (gap, bound) = ((p * &d - q * &n).abs(), BigInt::from(m) * &n * q);
126 let within = gap.is_zero() || m != 0 && if e >= 0 { gap <= bound << e as usize } else { gap << (-e) as usize <= bound };
127 within.then(|| p.clone())
128 });
129 F::narrow((BigRational::from_float(x).unwrap() / BigRational::from_integer(p)).to_f64().unwrap())
130 }
131}
132
133fn ratio_parts(x: f64, y: f64) -> ((u64, u32), (u64, u32)) {
135 let odd = |v: f64| {
136 let (m, e, _) = num_traits::Float::integer_decode(v);
137 (m >> m.trailing_zeros(), i32::from(e) + m.trailing_zeros() as i32)
138 };
139 let ((mx, ex), (my, ey)) = (odd(x), odd(y));
140 if ex >= ey { ((mx, (ex - ey) as u32), (my, 0)) } else { ((mx, 0), (my, (ey - ex) as u32)) }
141}
142
143fn convergents<I: num_traits::Num + Clone, R>(n: I, d: I, mut done: impl FnMut(&I, &I) -> Option<R>) -> R {
145 let (mut a, mut b) = (n, d);
146 let (mut p0, mut p1, mut q0, mut q1) = (I::zero(), I::one(), I::one(), I::zero());
147 loop {
148 let k = a.clone() / b.clone();
149 let p = k.clone() * p1.clone() + p0;
150 p0 = std::mem::replace(&mut p1, p);
151 let q = k * q1.clone() + q0;
152 q0 = std::mem::replace(&mut q1, q);
153 if let Some(answer) = done(&p1, &q1) { return answer; }
154 let r = a % b.clone();
155 (a, b) = (b, r);
156 }
157}
158
159fn fixed_gcd(x: f64, y: f64, (mx, sx): (u64, u32), (my, sy): (u64, u32), t: f64) -> Option<f64> {
161 let shifted = |m: u64, s: u32| (m.leading_zeros() >= s).then(|| m << s);
162 let (n, d) = (shifted(mx, sx)?, shifted(my, sy)?);
163 convergents(n, d, |&p, &q| {
164 let gap = (u128::from(p) * u128::from(d)).abs_diff(u128::from(q) * u128::from(n));
166 let bound = t * (u128::from(n) * u128::from(q)) as f64;
167 let within = if gap == 0 { true }
168 else if bound < 0.5 { false } else {
169 let g = gap as f64;
170 if g <= bound * (1.0 - 1e-15) { true } else if g >= bound * (1.0 + 1e-15) { false } else { return Some(None); }
171 };
172 within.then(|| {
174 if p <= 1 << 53 { Some(x / p as f64) } else if gap == 0 && q <= 1 << 53 { Some(y / q as f64) } else { None }
175 })
176 })
177}
178
179#[inline(always)]
181fn nearest<F: Float>(y: F) -> F { Tolerance::EXACT.nearest_whole(y).0 }
182
183#[derive(Clone, Copy, Debug)]
185pub struct Exactly;
186
187impl Tolerant for Exactly {
188 #[inline]
189 fn value(self) -> f64 { 0.0 }
190 #[inline]
191 fn close<F: Float>(self, _: F, _: F) -> bool { false }
192 #[inline]
193 fn floor_in<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, y: O::V) -> O::V { recipe::exact_floor(o, y) }
194}
195
196impl Tolerant for Tolerance {
197 #[inline]
198 fn value(self) -> f64 { Tolerance::value(self) }
199 #[inline]
200 fn close<F: Float>(self, x: F, y: F) -> bool { recipe::close(&Floats, F::narrow(self.0), x, y) }
201 #[inline]
202 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) }
203}
204
205pub trait TolerantComplex: Tolerant {
207 #[inline]
209 fn complex_equal<R: Native>(self, x: Complex<R>, y: Complex<R>) -> bool {
210 let (equal, decided) = self.squared_equal(x, y);
211 if decided { equal } else { self.scaled_equal(x, y) }
212 }
213 #[inline(always)]
215 fn squared_equal<R: Native>(self, x: Complex<R>, y: Complex<R>) -> (bool, bool) {
216 let t = R::narrow(self.value());
217 let (low, high) = if R::WIDE { (2f64.powi(-800), 2f64.powi(1000)) } else { (2f64.powi(-80), 2f64.powi(120)) };
218 let size = x.norm_sqr().max(y.norm_sqr());
219 let near = (x - y).norm_sqr() <= t * t * size;
220 let exact = t == R::zero();
221 ((exact & (x == y)) | (!exact & near), exact | ((size >= R::narrow(low)) & (size <= R::narrow(high))))
222 }
223 fn scaled_equal<R: Native>(self, x: Complex<R>, y: Complex<R>) -> bool {
225 let finite = |z: Complex<R>| z.re.is_finite() & z.im.is_finite();
226 if !finite(x) || !finite(y) { return x == y; }
227 let t = R::narrow(self.value());
228 (x - y).norm() <= (x * t).norm().max((y * t).norm())
229 }
230 #[inline]
232 fn complex_floor<R: Native>(self, y: Complex<R>) -> Complex<R> {
233 let (a, b) = (self.floor(y.re), self.floor(y.im));
234 let (x, z) = (y.re - a, y.im - b);
235 if x + z < R::one() - R::narrow(self.value()) { Complex::new(a, b) }
236 else if x <= z { Complex::new(a, b + R::one()) }
237 else { Complex::new(a + R::one(), b) }
238 }
239 #[inline]
240 fn complex_ceil<R: Native>(self, y: Complex<R>) -> Complex<R> { -self.complex_floor(-y) }
241 #[inline]
243 fn complex_residue<R: Native>(self, x: Complex<R>, y: Complex<R>) -> Complex<R> {
244 if x.is_zero() { return y; }
245 let q = y / x;
246 if self.complex_equal(x * Complex::new(nearest(q.re), nearest(q.im)), y) { Complex::zero() } else { y - x * self.complex_floor(q) }
247 }
248 fn complex_gcd<R: Native>(self, x: Complex<R>, y: Complex<R>) -> Complex<R> {
250 if x.im == R::zero() && y.im == R::zero() { return Complex::new(self.gcd(x.re, y.re), R::zero()); }
251 let nan = Complex::new(R::nan(), R::nan());
252 let scale = x.norm().max(y.norm());
253 if !scale.is_finite() { return nan; }
254 let original = if x.norm() >= y.norm() { x } else { y };
255 let t = R::narrow(self.value()) * scale;
256 let whole = |z: Complex<R>| Complex::new(z.re.round(), z.im.round());
257 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 });
258 let (mut x, mut y) = (x, y);
259 while y.norm() > t {
260 let r = x - y * whole(x / y);
261 if !(r.norm() < y.norm()) { return nan; }
262 (x, y) = (y, r);
263 }
264 let mut x = small(x);
265 if x.is_zero() { return x; }
266 while x.re <= R::zero() || x.im < R::zero() { x = Complex::new(-x.im, x.re); }
267 small(original / whole(original / x))
268 }
269}
270impl<T: Tolerant> TolerantComplex for T {}
271
272#[macro_export]
274macro_rules! with_tolerance {
275 ($tolerance:expr, |$t:ident| $body:expr) => {{
276 let tolerance: $crate::tolerant::Tolerance = $tolerance;
277 if tolerance.is_exact() {
278 let $t = $crate::tolerant::Exactly;
279 $body
280 } else {
281 let $t = tolerance;
282 $body
283 }
284 }};
285}
286
287macro_rules! tolerant {
289 ($(#[$($m:tt)+])* $object:ident at real $(=> $o:ty)?: $($rest:tt)+) => { tolerant!(@forms [$(#[$($m)+])*], $object, (real $(=> $o)?), $($rest)+); };
290 ($(#[$($m:tt)+])* $object:ident at $($e:ty $(=> $o:ty)?),+: $($rest:tt)+) => { tolerant!(@forms [$(#[$($m)+])*], $object, ($($e $(=> $o)?),+), $($rest)+); };
291 (@forms [$(#[$($m:tt)+])*], $object:ident, ($($at:tt)+), |$t:ident, [$($x:ident),+]| $body:expr) => {
292 forms!($(#[$($m)+])* $object at $($at)+: |k, [$($x),+]| { let $t = k.0; $body },
293 many |k, [$($x),+], n, out| with_tolerance!(k.0, |$t| each!([$($x),+], n, out, |[$($x),+]| $body)));
294 };
295}
296
297#[inline(always)]
299fn indicated<E: Element>(b: bool) -> E where bool: crate::buffer::Source<E> {
300 if b { crate::buffer::Source::<E>::read(true) } else { crate::buffer::Source::<E>::read(false) }
301}
302
303macro_rules! indicator {
305 ($object:ident: |$t:ident, [$x:ident, $y:ident]| $body:expr, $left:ident, $right:ident, $($e:ty),+) => {$(
306 forms!([Indicator<$object>] at $e: |k, [$x, $y]| crate::slice::Total(indicated::<$e>({ let $t = k.0.0; $body })),
307 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>)) {
308 Some(ok) => ok,
309 None => with_tolerance!(k.0.0, |$t| each!([$x, $y], n, out, |[$x, $y]| crate::slice::Total(indicated::<$e>($body)))),
310 });
311 )+};
312}
313
314macro_rules! compared {
316 ($object:ident: |$t:ident, [$x:ident, $y:ident]| $body:expr, $left:ident, $right:ident) => {
317 forms!($object at real => bool: |k, [$x, $y]| { let $t = k.0; $body },
318 many |k, [$x, $y], n, out| match against(k.0, $x, $y, n, out, Band::$left, Band::$right, Banded::banded) {
319 Some(ok) => ok,
320 None => with_tolerance!(k.0, |$t| each!([$x, $y], n, out, |[$x, $y]| $body)),
321 });
322 indicator!($object: |$t, [$x, $y]| $body, $left, $right, f32, f64);
323 impl Banding for $object { const BANDS: (Band, Band) = (Band::$left, Band::$right); }
324 };
325}
326
327#[derive(Clone, Copy)]
329pub enum Band { Inside, Outside, Below, NotAbove, Above, NotBelow }
330
331impl Band {
332 #[inline(always)]
334 fn holds<F: Float>(self, lo: F, hi: F, v: F) -> bool { recipe::in_band(&Floats, self, lo, hi, v) }
335}
336
337pub trait Banding { const BANDS: (Band, Band); }
339
340pub 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> {
342 let (lo, hi) = t.range(c)?;
343 let band = if left { K::BANDS.0 } else { K::BANDS.1 };
344 let (lo, hi) = (o.number(lo), o.number(hi));
345 Some(recipe::in_band(o, band, lo, hi, v))
346}
347
348macro_rules! with_band {
350 ($band:expr, |$b:ident| $body:expr) => {
351 match $band {
352 Band::Inside => { const $b: Band = Band::Inside; $body }
353 Band::Outside => { const $b: Band = Band::Outside; $body }
354 Band::Below => { const $b: Band = Band::Below; $body }
355 Band::NotAbove => { const $b: Band = Band::NotAbove; $body }
356 Band::Above => { const $b: Band = Band::Above; $body }
357 Band::NotBelow => { const $b: Band = Band::NotBelow; $body }
358 }
359 };
360}
361
362#[inline(always)]
364fn 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) {
365 with_band!(band, |B| pairs_into(
366 Arg::Many(xs), Arg::One(()), xs.len(), out, #[inline(always)] |v, ()| (from(B.holds(lo, hi, v)), true)
367 ));
368}
369
370pub trait Banded: Float {
372 #[inline(always)]
374 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) }
375}
376#[cfg(not(all(target_arch = "wasm32", target_feature = "simd128")))]
377impl Banded for f32 {}
378#[cfg(not(all(target_arch = "wasm32", target_feature = "simd128")))]
379impl Banded for f64 {}
380
381#[inline(always)]
383pub fn against<F: Float, O>(
384 t: Tolerance, x: Arg<'_, F>, y: Arg<'_, F>, n: usize, out: &mut [std::mem::MaybeUninit<O>], left: Band, right: Band,
385 banded: impl FnOnce(&[F], F, F, Band, &mut [std::mem::MaybeUninit<O>])) -> Option<bool> {
386 let (items, (lo, hi), band) = match (x, y) {
387 (Arg::Many(xs), Arg::One(c)) => (xs, t.range(c)?, left), (Arg::One(c), Arg::Many(ys)) => (ys, t.range(c)?, right), _ => return None
388 };
389 banded(&items[..n], lo, hi, band, &mut out[..n]);
390 Some(true)
391}
392
393#[derive(Clone, Copy, Debug)]
395pub struct Indicator<K>(pub K);
396
397#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
399mod narrowed {
400 use super::{Band, Banded};
401 use core::arch::wasm32::*;
402 use std::mem::MaybeUninit;
403
404 macro_rules! narrowed {
406 ($($t:ty: $lanes:literal, $splat:ident, $lt:ident, $le:ident, $gt:ident, $ge:ident, $join:ident;)+) => {$(
407 impl Banded for $t {
408 fn banded(xs: &[$t], lo: $t, hi: $t, band: Band, out: &mut [MaybeUninit<bool>]) {
409 let (l, h) = ($splat(lo), $splat(hi));
410 with_band!(band, |B| blocks::<$t, $lanes, { 16 / $lanes }>(xs, out, $join, #[inline(always)] |v| match B {
411 Band::Inside => v128_and($le(l, v), $le(v, h)),
412 Band::Outside => v128_not(v128_and($le(l, v), $le(v, h))),
413 Band::Below => $lt(v, l),
414 Band::NotAbove => $le(v, h),
415 Band::Above => $gt(v, h),
416 Band::NotBelow => $ge(v, l),
417 }, #[inline(always)] |v| B.holds(lo, hi, v)))
418 }
419 }
420 )+};
421 }
422 narrowed! {
423 f64: 2, f64x2_splat, f64x2_lt, f64x2_le, f64x2_gt, f64x2_ge, join64;
424 f32: 4, f32x4_splat, f32x4_lt, f32x4_le, f32x4_gt, f32x4_ge, join32;
425 }
426
427 #[inline(always)]
429 fn blocks<T: Copy, const LANES: usize, const N: usize>(
430 xs: &[T], out: &mut [MaybeUninit<bool>], join: impl Fn([v128; N]) -> v128, mask: impl Fn(v128) -> v128, holds: impl Fn(T) -> bool) {
431 let (blocks, rest) = out.as_chunks_mut::<16>();
432 let (whole, tail) = xs.as_chunks::<16>();
433 for (block, x) in blocks.iter_mut().zip(whole) {
434 let masks = std::array::from_fn(|k| mask(unsafe { v128_load(x.as_ptr().add(LANES * k).cast()) }));
436 unsafe { v128_store(block.as_mut_ptr().cast(), v128_and(join(masks), u8x16_splat(1))) }
438 }
439 for (slot, &x) in rest.iter_mut().zip(tail) { slot.write(holds(x)); }
440 }
441
442 #[inline(always)]
443 fn join64(m: [v128; 8]) -> v128 {
444 let four = |a, b| i8x16_shuffle::<0, 8, 16, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(a, b);
445 let eight = |a, b| i8x16_shuffle::<0, 1, 2, 3, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0>(a, b);
446 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])));
447 i8x16_shuffle::<0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23>(low, high)
448 }
449
450 #[inline(always)]
451 fn join32(m: [v128; 4]) -> v128 {
452 let eight = |a, b| i8x16_shuffle::<0, 4, 8, 12, 16, 20, 24, 28, 0, 0, 0, 0, 0, 0, 0, 0>(a, b);
453 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]))
454 }
455}
456
457#[derive(Clone, Copy, Debug, PartialEq)]
459pub struct Equal(pub Tolerance);
460compared!(Equal: |t, [x, y]| t.equal(x, y), Inside, Inside);
461forms!(Equal at Complex<f32> => bool, Complex<f64> => bool: |k, [x, y]| k.0.complex_equal(x, y),
462 many |k, [x, y], n, out| with_tolerance!(k.0, |t| complex_equals(t, [x, y], n, out, false)));
463
464#[derive(Clone, Copy, Debug, PartialEq)]
466pub struct NotEqual(pub Tolerance);
467compared!(NotEqual: |t, [x, y]| !t.equal(x, y), Outside, Outside);
468forms!(NotEqual at Complex<f32> => bool, Complex<f64> => bool: |k, [x, y]| !k.0.complex_equal(x, y),
469 many |k, [x, y], n, out| with_tolerance!(k.0, |t| complex_equals(t, [x, y], n, out, true)));
470
471#[inline(always)]
473fn complex_equals<T: TolerantComplex, R: Native>(
474 t: T, [x, y]: [Arg<'_, Complex<R>>; 2], n: usize, out: &mut [std::mem::MaybeUninit<bool>], negate: bool) -> bool {
475 if pairs_into(x, y, n, out, #[inline(always)] |a, b| {
476 let (equal, decided) = t.squared_equal(a, b);
477 (equal != negate, decided)
478 }) { return true; }
479 let out = unsafe { crate::slice::written(out) };
480 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 } }
481 true
482}
483
484#[derive(Clone, Copy, Debug, PartialEq)]
486pub struct Less(pub Tolerance);
487compared!(Less: |t, [x, y]| t.less(x, y), Below, Above);
488
489#[derive(Clone, Copy, Debug, PartialEq)]
491pub struct LessEqual(pub Tolerance);
492compared!(LessEqual: |t, [x, y]| t.less_equal(x, y), NotAbove, NotBelow);
493
494#[derive(Clone, Copy, Debug, PartialEq)]
496pub struct Greater(pub Tolerance);
497compared!(Greater: |t, [x, y]| t.greater(x, y), Above, Below);
498
499#[derive(Clone, Copy, Debug, PartialEq)]
501pub struct GreaterEqual(pub Tolerance);
502compared!(GreaterEqual: |t, [x, y]| t.greater_equal(x, y), NotBelow, NotAbove);
503
504pub trait Stepped {
506 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B;
508}
509
510#[inline(always)]
512fn close_steps<F: Float, O: recipe::Ops<F> + ?Sized>(t: Tolerance, o: &O, x: O::V, y: O::V) -> O::B {
513 recipe::close(o, o.tolerance(F::narrow(t.value())), x, y)
514}
515
516#[inline(always)]
518fn exact<F: Float, O: recipe::Ops<F> + ?Sized>(t: impl Tolerant, o: &O) -> bool {
519 o.constant(o.tolerance(F::narrow(t.value()))).is_some_and(|t| t == F::zero())
520}
521
522impl Stepped for Equal {
523 #[inline(always)]
524 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B {
525 if exact(self.0, o) { return o.equal(x, y); }
526 recipe::equal(o, x, y, |o| close_steps(self.0, o, x, y))
527 }
528}
529
530impl Stepped for NotEqual {
531 #[inline(always)]
532 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) }
533}
534
535impl Stepped for Less {
536 #[inline(always)]
537 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B {
538 if exact(self.0, o) { return o.less(x, y); }
539 recipe::less(o, x, y, |o| close_steps(self.0, o, x, y))
540 }
541}
542
543impl Stepped for LessEqual {
544 #[inline(always)]
545 fn steps<F: Float, O: recipe::Ops<F> + ?Sized>(self, o: &O, x: O::V, y: O::V) -> O::B {
546 if exact(self.0, o) { return o.less_equal(x, y); }
547 recipe::less_equal(o, x, y, |o| close_steps(self.0, o, x, y))
548 }
549}
550
551impl Stepped for Greater {
552 #[inline(always)]
553 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) }
554}
555
556impl Stepped for GreaterEqual {
557 #[inline(always)]
558 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) }
559}
560
561#[derive(Clone, Copy, Debug, PartialEq)]
563pub struct Matches(pub Tolerance);
564compared!(Matches: |t, [x, y]| t.matches(x, y), Inside, Inside);
565
566#[derive(Clone, Copy, Debug, PartialEq)]
568pub struct Compare(pub Tolerance);
569tolerant!(Compare at real => Ordering: |t, [x, y]| t.compare(x, y).unwrap_or(x.is_nan().cmp(&y.is_nan())));
570
571#[derive(Clone, Copy, Debug, PartialEq)]
573pub struct Floor(pub Tolerance);
574tolerant!(#[inline] Floor at real: |t, [y]| Total(t.floor(y)));
575tolerant!(Floor at Complex<f32>, Complex<f64>: |t, [y]| Total(t.complex_floor(y)));
576
577#[derive(Clone, Copy, Debug, PartialEq)]
579pub struct Ceil(pub Tolerance);
580tolerant!(#[inline] Ceil at real: |t, [y]| Total(t.ceil(y)));
581tolerant!(Ceil at Complex<f32>, Complex<f64>: |t, [y]| Total(t.complex_ceil(y)));
582
583#[derive(Clone, Copy, Debug, PartialEq)]
585pub struct Residue(pub Tolerance);
586forms!(#[inline] Residue at real: |k, [x, y]| Total(if k.0.is_exact() { exact_residue(x, y) } else { k.0.residue(x, y) }),
587many |k, [x, y], n, out| with_tolerance!(k.0, |t| {
588 if t.value() != 0.0 { return each!([x, y], n, out, |[x, y]| Total(t.residue(x, y))); }
589 if !each!([x, y], n, out, |[x, y]| (t.residue(x, y), near(x, y))) { replace_far(x, y, n, out); }
590 true
591}));
592tolerant!(Residue at Complex<f32>, Complex<f64>: |t, [x, y]| t.complex_residue(x, y));
593
594fn replace_far<F: Float>(x: Arg<'_, F>, y: Arg<'_, F>, n: usize, out: &mut [std::mem::MaybeUninit<F>]) {
596 for (i, r) in unsafe { crate::slice::written(&mut out[..n]) }.iter_mut().enumerate() {
597 let (x, y) = (x.nth(i), y.nth(i));
598 if !near(x, y) { *r = exact_residue(x, y); }
599 }
600}
601
602#[inline(always)]
604fn far<F: Float>(x: F, y: F) -> bool { (x != F::zero()) & (y.abs() >= F::narrow(2.0) / F::epsilon() * x.abs()) }
605
606#[inline(always)]
608fn near<F: Float>(x: F, y: F) -> bool { y.abs() < F::narrow(2.0) / F::epsilon() * x.abs() }
609
610pub(crate) fn exact_residue<F: Float>(x: F, y: F) -> F { if far(x, y) { far_residue(x, y) } else { Exactly.residue(x, y) } }
612
613#[cold]
615#[inline(never)]
616fn 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 } }
617
618#[derive(Clone, Copy, Debug, PartialEq)]
620pub struct FloorDivide(pub Tolerance);
621
622#[derive(Clone, Copy, Debug, PartialEq)]
624pub struct MathishFloorDivide(pub Tolerance);
625
626#[derive(Clone, Copy, Debug, PartialEq)]
628pub struct Gcd(pub Tolerance);
629tolerant!(Gcd at real: |t, [x, y]| t.gcd(x, y));
630tolerant!(Gcd at Complex<f32>, Complex<f64>: |t, [x, y]| t.complex_gcd(x, y));
631
632#[derive(Clone, Copy, Debug, PartialEq)]
634pub struct Lcm(pub Tolerance);
635tolerant!(Lcm at real: |t, [x, y]| lcm_from_gcd(x, y, t.gcd(x, y)));
636tolerant!(Lcm at Complex<f32>, Complex<f64>: |t, [x, y]| lcm_from_gcd(x, y, t.complex_gcd(x, y)));
637
638#[inline]
639pub(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 } }
640
641const ROUND_UNITS: usize = 16;
643
644#[inline(never)]
646pub fn index_of<F: Float>(t: impl Tolerant, x: &[F], y: &[F]) -> Result<Vec<u32>, Error> {
647 if y.len() <= search::SCANNED { return Ok(y.iter().map(|&v| scan(t, x, v) as u32).collect()); }
648 if let Some((keys, exact)) = wholes(t, x, whole_limit(t)) && let Some((whole_y, _)) = wholes(t, y, whole_limit(t)) {
649 let found = search::index_of(&keys, &whole_y);
651 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); }
652 }
653 if t.value() == 0.0 { return Ok(search::index_of(&order_keys(x), &order_keys(y))); }
654 let near = Near::new(t, x, |j, i| x[j].order_key() == x[i].order_key());
655 let mut found = Vec::with_capacity(y.len());
656 for round in y.chunks(ROUND_UNITS * BETWEEN_CHECKS * crate::parallel::threads()) {
657 check()?;
658 search::look_up(&mut found, round, |v| {
659 let mut first = x.len();
660 for i in near.near(v) { if i < first && t.matches(x[i], v) { first = i; } }
661 first as u32
662 });
663 }
664 Ok(found)
665}
666
667#[inline(never)]
669pub fn classes<F: Float>(t: impl Tolerant, x: &[F]) -> Result<Vec<u32>, Error> {
670 if let Some((keys, _)) = wholes(t, x, whole_limit(t)) { return Ok(search::classes(&keys)); }
671 if t.value() == 0.0 { return Ok(search::first_classes(x, F::order_key)); }
672 let mut classes = vec![0; x.len()];
673 checked_runs(t, x, |run| { for &i in run { classes[i] = run[0] as u32 } })?;
674 Ok(classes)
675}
676
677#[inline(never)]
679pub fn firsts<F: Float>(t: impl Tolerant, x: &[F]) -> Result<Vec<bool>, Error> {
680 if let Some((keys, _)) = wholes(t, x, whole_limit(t)) { return Ok(search::firsts(&keys)); }
681 if t.value() == 0.0 { return Ok(search::first_mask(&search::first_classes(x, F::order_key))); }
682 let mut firsts = vec![false; x.len()];
683 checked_runs(t, x, |run| firsts[run[0]] = true)?;
684 Ok(firsts)
685}
686
687fn checked_runs<F: Float>(t: impl Tolerant, x: &[F], mut each: impl FnMut(&[usize])) -> Result<(), Error> {
689 let mut since = 0;
690 runs(t, x, |run| {
691 since += run.len();
692 if since >= BETWEEN_CHECKS {
693 since = 0;
694 check()?;
695 }
696 each(run);
697 Ok(())
698 })
699}
700
701#[inline(never)]
703pub fn runs<F: Float, E>(t: impl Tolerant, x: &[F], mut each: impl FnMut(&[usize]) -> Result<(), E>) -> Result<(), E> {
704 let (keys, order) = search::sorted(x.iter().map(|&v| v.order_key()).collect());
705 let (mut run, mut start) = (Vec::new(), 0);
706 for end in 1..=keys.len() {
707 if end < keys.len() && t.matches(F::from_order_key(keys[end - 1]), F::from_order_key(keys[end])) { continue; }
708 if end - start == 1 { each(&[order[start] as usize])?; } else {
710 run.clear();
711 run.extend(order[start..end].iter().map(|&i| i as usize));
712 run.sort_unstable();
713 each(&run)?;
714 }
715 start = end;
716 }
717 Ok(())
718}
719
720pub struct Near<F> { chains: Chains, shift: u32, width: PhantomData<F> }
722
723impl<F: Float> Near<F> {
724 pub fn new(t: impl Tolerant, x: &[F], copies: impl Fn(usize, usize) -> bool) -> Self {
726 let (t, epsilon) = (t.value(), F::epsilon().to_f64().unwrap_or(1.0));
727 let apart = (4.0 * t / ((1.0 - t) * epsilon)).ceil() + 1.0;
728 let shift = (2.0 * (apart + 1.0)).log2().ceil().min(64.0) as u32;
729 let mut near = Self { chains: Chains::new(x.len()), shift, width: PhantomData };
730 for (i, &v) in x.iter().enumerate() { near.chains.push_new(near.bucket(v).0, i, |j| copies(j, i)); }
731 near
732 }
733 #[inline]
735 fn bucket(&self, y: F) -> (u64, bool) {
736 let k = y.order_key().key() as u64;
737 (k.checked_shr(self.shift).unwrap_or(0), self.shift > 0 && (k >> (self.shift - 1)) & 1 == 1)
738 }
739 #[inline]
741 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) }) }
742}
743
744pub(crate) fn order_keys<F: Float>(x: &[F]) -> Vec<F::OrderKey> { x.iter().map(|v| v.order_key()).collect() }
746
747pub fn whole_limit(t: impl Tolerant) -> f64 { (0.25 / t.value()).min(9_007_199_254_740_992.0) }
749
750pub fn integer_limit<F: Float>(t: impl Tolerant) -> f64 { whole_limit(t).min(2.0 / F::epsilon().wide()) }
752
753pub fn integer_of<F: Float>(t: impl Tolerant, v: F) -> Option<i64> {
755 let v = v.wide();
756 (v.fract() == 0.0 && v.abs() < integer_limit::<F>(t)).then_some(v as i64)
757}
758
759pub fn wholes<F: Float>(t: impl Tolerant, x: &[F], limit: f64) -> Option<(Vec<i64>, bool)> {
761 const WHOLES: usize = 256;
762 let (limit, mut keys, mut exact) = (F::narrow(limit), Vec::with_capacity(x.len()), true);
763 for block in x.chunks(WHOLES) {
764 let (fits, whole) = unsafe {
765 crate::slice::appended(&mut keys, block.len(), |slots| {
766 const MAGIC: f64 = 6_755_399_441_055_744.0;
768 let (mut fits, mut whole, mut small) = (true, true, true);
769 for (slot, &y) in slots.iter_mut().zip(block) {
770 let n = nearest(y);
771 fits &= n.abs() < limit;
772 whole &= n == y;
773 small &= n.wide().abs() < 2_251_799_813_685_248.0;
774 slot.write(((n.wide() + MAGIC).to_bits() as i64).wrapping_sub(MAGIC.to_bits() as i64));
775 }
776 if !small { for (slot, &y) in slots.iter_mut().zip(block) { slot.write(nearest(y).wide() as i64); } }
777 (fits, whole)
778 })
779 };
780 if !fits || !whole && !block.iter().fold(true, |equal, &y| equal & t.nearest_whole(y).1) { return None; }
781 exact &= whole;
782 }
783 Some((keys, exact))
784}
785
786fn scan<F: Float>(t: impl Tolerant, x: &[F], v: F) -> usize {
788 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()) };
789 found.unwrap_or(x.len())
790}