1use crate::{BETWEEN_CHECKS, error::Error, field::{minus, times}, float::{FUSED, Float, sealed::Core}, interrupt::check};
3use num_complex::Complex;
4use num_traits::{NumAssignRef, Zero};
5
6pub fn trimmed<T: Zero>(c: &[T]) -> &[T] { &c[..c.iter().rposition(|k| !k.is_zero()).map_or(0, |i| i + 1)] }
8
9pub fn evaluate<F: Float>(c: &[F], x: &[F], out: &mut Vec<F>) {
11 let Some((&top, rest)) = trimmed(c).split_last() else { return out.resize(out.len() + x.len(), F::zero()) };
12 unsafe {
13 crate::slice::appended(out, x.len(), |out| {
14 if crate::slice::running(size_of::<F::Working>()) == 16 { blocks::<F, 16>(top, rest, x, out) } else { blocks::<F, 32>(top, rest, x, out) }
15 })
16 }
17}
18
19#[inline]
21fn blocks<F: Float, const L: usize>(top: F, rest: &[F], x: &[F], out: &mut [std::mem::MaybeUninit<F>]) {
22 let ((blocks, tail), (outs, last)) = (x.as_chunks::<L>(), out.as_chunks_mut::<L>());
23 for (xs, ys) in blocks.iter().zip(outs) { horner(top, rest, xs, ys) }
24 let mut padded = [F::zero(); L];
25 padded[..tail.len()].copy_from_slice(tail);
26 let mut values = [std::mem::MaybeUninit::uninit(); L];
27 horner(top, rest, &padded, &mut values);
28 last.copy_from_slice(&values[..tail.len()]);
29}
30
31#[inline]
33fn horner<F: Float, const L: usize>(top: F, rest: &[F], xs: &[F; L], out: &mut [std::mem::MaybeUninit<F>; L]) {
34 let mut y = [top.working(); L];
35 for &k in rest.iter().rev() { let k = k.working(); for i in 0..L { y[i] = y[i].mla::<FUSED>(xs[i].working(), k); } }
36 for i in 0..L { out[i].write(F::from_working(y[i])); }
37}
38
39pub fn roots<F: Float>(c: &[Complex<F>]) -> Option<Vec<Complex<F>>> {
41 let c: Vec<Complex<f64>> = trimmed(c).iter().map(|z| Complex::new(z.re.wide(), z.im.wide())).collect();
42 let n = c.len().saturating_sub(1);
43 if n == 0 { return Some(Vec::new()); }
44 let a: Vec<Complex<f64>> = c[..n].iter().map(|&k| -crate::complex::divide(k, c[n])).collect();
46 let companion: Vec<Complex<f64>> = (0..n * n).map(|k| if k % n == n - 1 { a[k / n] } else { Complex::new(f64::from(k / n == k % n + 1), 0.0) }).collect();
47 let mut values = if a.iter().all(|z| z.im == 0.0) { crate::linalg::eigenvalues(&companion.iter().map(|z| z.re).collect::<Vec<f64>>(), n)? }
48 else { crate::linalg::eigenvalues(&companion, n)? };
49 values.sort_by(|x, y| x.re.total_cmp(&y.re).then(x.im.total_cmp(&y.im)));
50 Some(values.into_iter().map(|z| Complex::new(F::narrow(z.re), F::narrow(z.im))).collect())
51}
52
53pub fn from_roots<T: Clone + NumAssignRef>(m: T, roots: &[T]) -> Result<Vec<T>, Error> {
55 let mut c = vec![m];
56 for (i, r) in roots.iter().enumerate() {
57 if i > 0 { check()?; }
58 let less = |a: &T, b: &T| minus(a, ×(b, r));
60 c.push(T::zero());
61 for i in (1..c.len()).rev() { c[i] = less(&c[i - 1], &c[i]); }
62 c[0] = less(&T::zero(), &c[0]);
63 }
64 Ok(c)
65}
66
67pub fn derivative<T: Clone + NumAssignRef>(c: &[T]) -> Result<Vec<T>, Error> {
69 let (mut n, mut d) = (T::zero(), Vec::with_capacity(c.len().saturating_sub(1)));
70 for (i, group) in c.get(1..).unwrap_or_default().chunks(BETWEEN_CHECKS).enumerate() {
71 if i > 0 { check()?; }
72 d.extend(group.iter().map(|k| { n += T::one(); times(&n, k) }));
73 }
74 Ok(d)
75}
76
77pub fn value<T: Clone + NumAssignRef>(c: &[T], x: &T) -> Result<T, Error> {
79 let Some((top, rest)) = trimmed(c).split_last() else { return Ok(T::zero()) };
80 let mut y = top.clone();
81 for (i, group) in rest.rchunks(BETWEEN_CHECKS).enumerate() {
82 if i > 0 { check()?; }
83 for k in group.iter().rev() {
84 y *= x;
85 y += k;
86 }
87 }
88 Ok(y)
89}
90
91pub fn factored<T: Clone + NumAssignRef>(m: &T, roots: &[T], x: &T) -> Result<T, Error> {
93 let mut y = m.clone();
94 for (i, group) in roots.chunks(BETWEEN_CHECKS).enumerate() { if i > 0 { check()?; } for r in group { y *= &minus(x, r); } }
95 Ok(y)
96}
97
98pub fn decode<T: Clone + NumAssignRef>(bases: &[T], digits: &[T]) -> Vec<T> {
100 digits.chunks(bases.len()).map(|d| {
101 d[1..].iter().zip(&bases[1..]).fold(d[0].clone(), |mut v, (k, b)| {
102 v *= b;
103 v += k;
104 v
105 })
106 }).collect()
107}
108
109pub fn encode<T: Clone + NumAssignRef>(bases: &[T], values: &[T], residue: impl Fn(&T, &T) -> T) -> Vec<T> {
111 encoded(bases, values, |b, mut v| {
112 let digit = residue(b, &v);
113 if b.is_zero() { v.set_zero() } else {
114 v -= &digit;
115 v /= b;
116 }
117 Some((v, digit))
118 }).expect("a step in a field gives a value")
119}
120
121pub(crate) fn encoded<B, T: Clone + Zero>(bases: &[B], values: &[T], div_mod: impl Fn(&B, T) -> Option<(T, T)>) -> Option<Vec<T>> {
123 let mut digits = vec![T::zero(); bases.len() * values.len()];
124 for (out, value) in digits.chunks_mut(bases.len().max(1)).zip(values) {
125 let mut value = value.clone();
126 for (digit, base) in out.iter_mut().zip(bases).rev() { (value, *digit) = div_mod(base, value)?; }
127 }
128 Some(digits)
129}
130
131pub fn fit<F: Float>(x: &[F], y: &[F], degree: usize) -> Vec<F> {
133 assert_eq!(x.len(), y.len(), "fit needs one y for each x");
134 let powers: Vec<f64> = x.iter().flat_map(|x| {
135 let x = x.wide();
136 std::iter::successors(Some(1.0), move |p| Some(p * x)).take(degree + 1)
137 }).collect();
138 let y: Vec<f64> = y.iter().map(|y| y.wide()).collect();
139 crate::linalg::lstsq(&powers, &y, [x.len(), degree + 1], 1).0.into_iter().map(F::narrow).collect()
140}