1use crate::{Arg, Binom, Pow, convert::{self, WIDE_LIMIT}, float::sealed::Core, tolerant::{Gcd, Lcm, Residue}};
5use strength_reduce::StrengthReducedU64;
6
7pub fn gcd(x: i64, y: i64) -> Option<i64> { i64::try_from(unsigned_gcd(x.unsigned_abs(), y.unsigned_abs())).ok() }
9pub(crate) fn gcd_wrapped(x: i64, y: i64) -> i64 { unsigned_gcd(x.unsigned_abs(), y.unsigned_abs()) as i64 }
11
12fn unsigned_gcd(mut a: u64, mut b: u64) -> u64 {
14 if a == 0 || b == 0 { return a | b; }
15 let shift = (a | b).trailing_zeros();
16 a >>= a.trailing_zeros();
17 loop {
18 b >>= b.trailing_zeros();
19 if a > b { std::mem::swap(&mut a, &mut b) }
20 b -= a;
21 if b == 0 { return a << shift; }
22 }
23}
24
25pub fn lcm(x: i64, y: i64) -> Option<i64> { if x == 0 || y == 0 { return Some(0); } (x / gcd(x, y)?).checked_mul(y) }
27pub(crate) fn lcm_wrapped(x: i64, y: i64) -> i64 { if x == 0 || y == 0 { 0 } else { (x / gcd_wrapped(x, y)).wrapping_mul(y) } }
29
30pub(crate) fn overflowed(variant: crate::domain::Variant, exact: Option<i64>, wrapped: impl FnOnce() -> i64) -> i64 {
32 exact.unwrap_or_else(|| {
33 if variant.fails_on_overflow() { (crate::error::Kind::Overflow.reporter())(); }
34 wrapped()
35 })
36}
37
38pub fn binom(n: i64, k: i64) -> Option<i64> { binomial(n, k, false) }
40
41pub(crate) fn binom_wrapped(n: i64, k: i64) -> Option<i64> { binomial(n, k, true) }
42
43fn binomial(n: i64, k: i64, wrapping: bool) -> Option<i64> {
44 if k < 0 {
45 if n >= 0 || k > n { return Some(0); }
46 let count = n - k;
47 return binomial(-(k + 1), count, wrapping).map(|c| if count % 2 == 0 { c } else { c.wrapping_neg() });
48 }
49 let k = k as u64;
50 let (top, negative) = if n >= 0 { (n as u64, false) } else { (k + (n.unsigned_abs() - 1), k % 2 == 1) };
52 if top < k { return Some(0); }
53 let k = k.min(top - k);
54 let c = if wrapping { choose_wrapped(top, k) } else { choose(top, k, if negative { 1 << 63 } else { i64::MAX as u64 })? };
55 Some(if negative { (c as i64).wrapping_neg() } else { c as i64 })
56}
57
58fn choose_wrapped(n: u64, k: u64) -> u64 {
60 let shift = k.count_ones() + (n - k).count_ones() - n.count_ones();
61 let (mut numerator, mut denominator) = (1u64, 1u64);
62 for i in 0..k {
63 if i % crate::BETWEEN_CHECKS as u64 == 0 && let Err(error) = crate::interrupt::check() { crate::error::record(error); return 0; }
64 let (a, b) = (n - i, i + 1);
65 numerator = numerator.wrapping_mul(a >> a.trailing_zeros());
66 denominator = denominator.wrapping_mul(b >> b.trailing_zeros());
67 }
68 let mut inverse = denominator;
70 for _ in 0..5 { inverse = inverse.wrapping_mul(2u64.wrapping_sub(denominator.wrapping_mul(inverse))); }
71 numerator.wrapping_mul(inverse).checked_shl(shift).unwrap_or(0)
72}
73
74fn choose(n: u64, k: u64, limit: u64) -> Option<u64> {
76 (0..k).try_fold(1u64, |c, i| {
77 let c = c as u128 * (n - i) as u128 / (i + 1) as u128;
78 (c <= limit as u128).then_some(c as u64)
79 })
80}
81
82pub fn power(x: i64, y: i64) -> Option<i64> { let (p, exact) = wrapped_power(x, y, i64::MAX); exact.then_some(p) }
84
85pub(crate) fn wrapped_power(x: i64, y: i64, most: i64) -> (i64, bool) {
87 match x {
88 1 => (1, true),
89 -1 => (if y % 2 == 0 { 1 } else { -1 }, true),
90 0 if y < 0 => (most, true),
91 _ if y < 0 => (if x < 0 && y % 2 != 0 { -1 } else { 0 }, true),
92 0 => (i64::from(y == 0), true),
93 _ => {
94 let (mut base, mut e, mut p, mut exact, mut base_exact) = (x, y as u64, 1i64, true, true);
96 loop {
97 if e & 1 == 1 {
98 let (q, over) = p.overflowing_mul(base);
99 (p, exact) = (q, exact & base_exact & !over);
100 }
101 e >>= 1;
102 if e == 0 { break (p, exact); }
103 let (b, over) = base.overflowing_mul(base);
104 (base, base_exact) = (b, base_exact & !over);
105 }
106 }
107 }
108}
109
110#[inline]
112pub fn residue(x: i64, y: i64) -> i64 { if x == 0 { y } else { floored(y.wrapping_rem(x), x) } }
113
114#[inline]
116pub fn mask(x: i64) -> Option<i64> { (x > 0 && x.count_ones() == 1).then(|| x - 1) }
117
118#[inline]
120fn floored<T: num_traits::PrimInt + num_traits::Signed>(r: T, x: T) -> T { if !r.is_zero() && r.is_negative() != x.is_negative() { r + x } else { r } }
121
122#[derive(Clone, Copy, Debug)]
124pub struct Divisor { x: i64, magnitude: StrengthReducedU64 }
125
126impl Divisor {
127 pub fn new(x: i64) -> Option<Self> { (x != 0).then(|| Self { x, magnitude: StrengthReducedU64::new(x.unsigned_abs()) }) }
129
130 #[inline]
132 pub fn residue(self, y: i64) -> i64 { let r = (y.unsigned_abs() % self.magnitude) as i64; floored(if y < 0 { -r } else { r }, self.x) }
133
134 #[inline]
136 pub fn div_mod(self, y: i64) -> Option<(i64, i64)> {
137 let (q, r) = StrengthReducedU64::div_rem(y.unsigned_abs(), self.magnitude);
138 let q = if (y < 0) != (self.x < 0) { -(q as i128) } else { q as i128 };
139 let r0 = if y < 0 { -(r as i64) } else { r as i64 };
140 let r = floored(r0, self.x);
141 Some((i64::try_from(q - i128::from(r != r0)).ok()?, r))
142 }
143}
144
145#[derive(Clone, Copy)]
147struct Short { x: i16, magnitude: u16, multiplier: u16, shift: u32 }
148
149impl Short {
150 fn new(x: i16) -> Option<Self> {
152 let d = x.unsigned_abs();
153 (d >= 2).then(|| {
154 let l = u16::BITS - (d - 1).leading_zeros();
155 Self { x, magnitude: d, multiplier: (((1 << (15 + l)) - 1) / u32::from(d) + 1) as u16, shift: l - 1 }
156 })
157 }
158
159 #[inline]
161 fn residue(self, y: i16) -> i16 {
162 let a = y.unsigned_abs();
163 let q = ((u32::from(a) * u32::from(self.multiplier)) >> 16) as u16 >> self.shift;
164 let r = (a - q * self.magnitude) as i16;
165 floored(if y < 0 { -r } else { r }, self.x)
166 }
167}
168
169#[derive(Clone, Copy)]
171struct FloatDivisor { x: f64, reciprocal: f64 }
172
173impl FloatDivisor {
174 #[inline]
176 fn new(x: i64) -> Option<Self> { (x != 0 && x.unsigned_abs() <= WIDE_LIMIT as u64).then(|| Self { x: x as f64, reciprocal: 1.0 / x as f64 }) }
177
178 #[inline(always)]
180 fn residue(self, y: i64) -> i64 {
181 let y = convert::unshifted(y);
182 let q = (y * self.reciprocal + f64::SHIFT) - f64::SHIFT;
183 let r = y - q * self.x;
184 convert::shifted(if r * self.x < 0.0 { r + self.x } else { r })
185 }
186}
187
188fn fit(y: Arg<'_, i64>, n: usize) -> bool {
190 let fits = |v: i64| v.unsigned_abs() <= WIDE_LIMIT as u64;
191 match y { Arg::One(v) => fits(v), Arg::Many(v) => { v[..n].is_empty() || { let (lo, hi) = crate::reduce::extremes(&v[..n]); fits(lo) && fits(hi) } } }
192}
193
194pub fn encode(bases: &[i64], values: &[i64]) -> Option<Vec<i64>> {
196 let divisors: Vec<_> = bases.iter().map(|&b| Divisor::new(b)).collect();
197 crate::polynomial::encoded(&divisors, values, |d, v| match d { Some(d) => d.div_mod(v), None => Some((0, v)) }).or_else(|| {
198 let wide = |v: &[i64]| v.iter().map(|&n| i128::from(n)).collect::<Vec<_>>();
199 let digits = crate::polynomial::encode(&wide(bases), &wide(values), |&b, &v| if b == 0 { v } else { floored(v % b, b) });
200 digits.into_iter().map(|n| i64::try_from(n).ok()).collect()
201 })
202}
203
204pub fn decode(bases: &[i64], digits: &[i64]) -> Vec<i64> {
206 decoded(bases, digits, |v, b, d| Some(v.wrapping_mul(b).wrapping_add(d))).expect("a wrapped step always fits")
207}
208
209fn decoded(bases: &[i64], digits: &[i64], step: impl Fn(i64, i64, i64) -> Option<i64>) -> Option<Vec<i64>> {
211 digits.chunks(bases.len()).map(|d| (1..d.len()).try_fold(d[0], |v, k| step(v, bases[k], d[k]))).collect()
212}
213
214forms!(#[inline] Gcd at i64: |_, [x, y]| gcd(x, y));
215forms!(#[inline] Lcm at i64: |_, [x, y]| lcm(x, y));
216forms!(#[inline] Gcd at bool: |_, [x, y]| x | y);
218forms!(#[inline] Lcm at bool: |_, [x, y]| x & y);
219forms!(#[inline] Binom at i64: |_, [n, k]| binom(n, k));
220forms!(#[inline] Pow at i64: |_, [x, y]| wrapped_power(x, y, i64::MAX));
221forms!(
222 #[inline] Residue at i64: |_, [x, y]| residue(x, y), many |_, [x, y], n, out| if let Arg::One(d) = x && let Some(m) = mask(d) {
223 each!([y], n, out, |[y]| y & m)
224 } else if let Arg::One(d) = x && let Some(r) = FloatDivisor::new(d) && fit(y, n) {
225 each!([y], n, out, |[y]| r.residue(y))
226 } else if let Arg::One(d) = x && let Some(d) = Divisor::new(d) {
227 each!([y], n, out, |[y]| d.residue(y))
228 } else {
229 each!([x, y], n, out, |[x, y]| residue(x, y))
230 }
231);
232
233macro_rules! narrower {
235 ($($t:ty),+) => {$(
236 forms!(#[inline] Gcd at $t: |_, [x, y]| gcd(x.into(), y.into()).and_then(|r| <$t>::try_from(r).ok()));
237 forms!(#[inline] Lcm at $t: |_, [x, y]| lcm(x.into(), y.into()).and_then(|r| <$t>::try_from(r).ok()));
238 forms!(#[inline] Binom at $t: |_, [n, k]| binom(n.into(), k.into()).and_then(|r| <$t>::try_from(r).ok()));
239 forms!(#[inline] Pow at $t: |_, [x, y]| { let (p, exact) = wrapped_power(x.into(), y.into(), <$t>::MAX.into()); (p as $t, exact && <$t>::try_from(p).is_ok()) });
240 forms!(
241 #[inline] Residue at $t: |_, [x, y]| residue(x.into(), y.into()) as $t,
242 many |_, [x, y], n, out| if let Arg::One(d) = x && let Some(m) = mask(d.into()) {
243 each!([y], n, out, |[y]| y & m as $t)
244 } else if let Arg::One(d) = x && <$t>::BITS <= 16 && let Some(s) = Short::new(d as i16) {
245 each!([y], n, out, |[y]| s.residue(y as i16) as $t)
246 } else if let Arg::One(d) = x && let Some(r) = FloatDivisor::new(d.into()) {
247 each!([y], n, out, |[y]| r.residue(y.into()) as $t)
248 } else {
249 each!([x, y], n, out, |[x, y]| residue(x.into(), y.into()) as $t)
250 }
251 );
252 )+};
253}
254narrower!(u8, i16, i32);
255
256pub(crate) fn native_power(variant: crate::domain::Variant, x: i64, y: i64) -> i64 {
258 let most = match variant.dtype {
259 crate::buffer::DType::Integer(width) if !variant.fails_on_overflow() => with_width!(width, T => T::MAX as i64), _ => i64::MAX
260 };
261 let (value, exact) = wrapped_power(x, y, most);
262 let fits = exact && match variant.dtype { crate::buffer::DType::Integer(width) => width.holds(value), _ => true };
263 if variant.fails_on_overflow() && !fits { (crate::error::Kind::Overflow.reporter())(); }
264 value
265}