#![no_std]
#![doc = include_str!("../README.md")]
#![deny(
missing_docs,
clippy::missing_safety_doc,
clippy::undocumented_unsafe_blocks,
clippy::must_use_candidate,
clippy::perf,
clippy::complexity,
clippy::suspicious
)]
use core::ops::{Add, Div, Mul, Neg};
use num_traits::{MulAdd, One, Zero};
pub trait PolyNum:
Sized
+ Copy
+ Zero
+ Add<Self, Output = Self>
+ Mul<Self, Output = Self>
+ MulAdd<Self, Self, Output = Self>
{
}
pub trait PolyRational: PolyNum + One + Div<Self, Output = Self> + PartialOrd {}
impl<T> PolyNum for T where
T: Sized
+ Copy
+ Zero
+ Add<Self, Output = Self>
+ Mul<Self, Output = Self>
+ MulAdd<Self, Self, Output = Self>
{
}
impl<T> PolyRational for T where
T: PolyNum + One + Neg<Output = Self> + Div<Self, Output = Self> + PartialOrd
{
}
pub mod polynomials;
#[inline(always)]
pub fn poly_array<F: PolyNum, const N: usize>(x: F, coeffs: &[F; N]) -> F {
poly_f_n::<F, _, N>(x, |i| unsafe { *coeffs.get_unchecked(i) })
}
#[inline(always)]
pub fn rational_array<F: PolyRational, const P: usize, const Q: usize>(
x: F,
numerator: &[F; P],
denominator: &[F; Q],
) -> F {
rational_f_n::<F, _, _, P, Q>(
x,
|i| unsafe { *numerator.get_unchecked(i) },
|i| unsafe { *denominator.get_unchecked(i) },
)
}
#[inline(always)]
pub fn poly_array_t<F: PolyNum, T, const N: usize>(x: F, coeffs: &[T; N]) -> F
where
T: Clone + Into<F>,
{
poly_f_n::<F, _, N>(x, |i| unsafe { coeffs.get_unchecked(i).clone().into() })
}
#[inline(always)]
pub fn rational_array_t<F: PolyRational, T, const P: usize, const Q: usize>(
x: F,
numerator: &[T; P],
denominator: &[T; Q],
) -> F
where
T: Clone + Into<F>,
{
rational_f_n::<F, _, _, P, Q>(
x,
|i| unsafe { numerator.get_unchecked(i).clone().into() },
|i| unsafe { denominator.get_unchecked(i).clone().into() },
)
}
pub fn poly<F: PolyNum>(x: F, coeffs: &[F]) -> F {
poly_f_internal::<F, _, 0>(x, coeffs.len(), |i| unsafe { *coeffs.get_unchecked(i) })
}
pub fn poly_t<F: PolyNum, T>(x: F, coeffs: &[T]) -> F
where
T: Clone + Into<F>,
{
poly_f_internal::<F, _, 0>(x, coeffs.len(), |i| unsafe {
coeffs.get_unchecked(i).clone().into()
})
}
pub fn rational<F: PolyRational>(x: F, numerator: &[F], denominator: &[F]) -> F {
rational_f_internal::<F, _, _, 0, 0>(
x,
numerator.len(),
denominator.len(),
|i| unsafe { *numerator.get_unchecked(i) },
|i| unsafe { *denominator.get_unchecked(i) },
)
}
#[inline]
pub fn poly_f<F: PolyNum, G>(x: F, n: usize, g: G) -> F
where
G: FnMut(usize) -> F,
{
poly_f_internal::<F, _, 0>(x, n, g)
}
#[inline]
pub fn rational_f<F: PolyRational, N, D>(
x: F,
p: usize,
q: usize,
numerator: N,
denominator: D,
) -> F
where
N: FnMut(usize) -> F,
D: FnMut(usize) -> F,
{
rational_f_internal::<F, _, _, 0, 0>(x, p, q, numerator, denominator)
}
#[inline]
pub fn poly_f_n<F: PolyNum, G, const N: usize>(x: F, g: G) -> F
where
G: FnMut(usize) -> F,
{
poly_f_internal::<F, _, N>(x, N, g)
}
#[inline]
pub fn rational_f_n<F: PolyRational, N, D, const P: usize, const Q: usize>(
x: F,
numerator: N,
denominator: D,
) -> F
where
N: FnMut(usize) -> F,
D: FnMut(usize) -> F,
{
rational_f_internal::<F, _, _, P, Q>(x, P, Q, numerator, denominator)
}
#[rustfmt::skip]
#[inline(always)]
fn rational_f_internal<F: PolyRational, N, D, const P: usize, const Q: usize>(
x: F,
p: usize,
q: usize,
mut numerator: N,
mut denominator: D,
) -> F
where
N: FnMut(usize) -> F,
D: FnMut(usize) -> F,
{
let one = F::one();
let high_degree = (P > 2 || Q > 2) || (P == 0 && Q == 0 && (p > 2 || q > 2));
if high_degree && (x * x) > one {
if P > 0 { unsafe { core::hint::assert_unchecked(p == P) } }
if Q > 0 { unsafe { core::hint::assert_unchecked(q == Q) } }
let z = one / x;
let n = poly_f_internal::<_, _, P>(z, p, |i| numerator(p - i - 1));
let d = poly_f_internal::<_, _, Q>(z, q, |i| denominator(q - i - 1));
let mut res = n / d;
if P == Q && (P > 0 || likely(p == q)) {
return res;
}
let (mut u, mut e) = if p < q { (z, q - p) } else { (x, p - q) };
if P > 0 && Q > 0 {
loop {
if e & 1 != 0 {
res = res * u;
}
e >>= 1;
if e == 0 {
return res;
}
u = u * u;
}
} else {
loop {
if e & 1 != 0 {
res = res * u;
if e == 1 {
return res;
}
}
e >>= 1;
u = u * u;
}
}
}
poly_f_internal::<_, _, P>(x, p, numerator) / poly_f_internal::<_, _, Q>(x, q, denominator)
}
#[inline(always)]
#[rustfmt::skip]
fn poly_f_internal<F: PolyNum, G, const LENGTH: usize>(x: F, n: usize, mut g: G) -> F
where
G: FnMut(usize) -> F,
{
use polynomials::*;
if LENGTH > 0 {
unsafe { core::hint::assert_unchecked(n == LENGTH) };
}
macro_rules! poly {
($name:ident($($pows:expr),*; { $j:expr } + $c:ident[$($coeff:expr),*])) => {{
$name($($pows,)* $($c($j + $coeff)),*)
}};
}
match n {
0 => return F::zero(),
1 => return g(0),
2 => return poly!(poly_1(x; {0} + g[0, 1])),
3 => return poly!(poly_2(x, x * x; {0} + g[0, 1, 2])),
4 => return poly!(poly_3(x, x * x; {0} + g[0, 1, 2, 3])),
_ => {}
}
let x2 = x * x;
let x4 = x2 * x2;
let x8 = x4 * x4;
match n {
5 => return poly!(poly_4 (x, x2, x4; {0} + g[0, 1, 2, 3, 4])),
6 => return poly!(poly_5 (x, x2, x4; {0} + g[0, 1, 2, 3, 4, 5])),
7 => return poly!(poly_6 (x, x2, x4; {0} + g[0, 1, 2, 3, 4, 5, 6])),
8 => return poly!(poly_7 (x, x2, x4; {0} + g[0, 1, 2, 3, 4, 5, 6, 7])),
9 => return poly!(poly_8 (x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8])),
10 => return poly!(poly_9 (x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])),
11 => return poly!(poly_10(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])),
12 => return poly!(poly_11(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])),
13 => return poly!(poly_12(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])),
14 => return poly!(poly_13(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])),
15 => return poly!(poly_14(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])),
16 => return poly!(poly_15(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])),
_ => {}
}
let x16 = x8 * x8;
let mut sum = F::zero();
let mut j = n;
while j >= 16 {
j -= 16;
sum = sum.mul_add(x16, poly!(poly_15(x, x2, x4, x8; { j } + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])));
}
let (rmx, res) = match j {
0 => return sum,
1 => (x, g(0)),
2 => (x2, poly!(poly_1 (x; {0} + g[0, 1]))),
3 => (x2*x, poly!(poly_2 (x, x2; {0} + g[0, 1, 2]))),
4 => (x4, poly!(poly_3 (x, x2; {0} + g[0, 1, 2, 3]))),
5 => (x4*x, poly!(poly_4 (x, x2, x4; {0} + g[0, 1, 2, 3, 4]))),
6 => (x4*x2, poly!(poly_5 (x, x2, x4; {0} + g[0, 1, 2, 3, 4, 5]))),
7 => (x4*x2*x, poly!(poly_6 (x, x2, x4; {0} + g[0, 1, 2, 3, 4, 5, 6]))),
8 => (x8, poly!(poly_7 (x, x2, x4; {0} + g[0, 1, 2, 3, 4, 5, 6, 7]))),
9 => (x8*x, poly!(poly_8 (x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8]))),
10 => (x8*x2, poly!(poly_9 (x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))),
11 => (x8*x2*x, poly!(poly_10(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))),
12 => (x8*x4, poly!(poly_11(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]))),
13 => (x8*x4*x, poly!(poly_12(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]))),
14 => (x8*x4*x2, poly!(poly_13(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]))),
15 => ((x8*x4)*(x2*x), poly!(poly_14(x, x2, x4, x8; {0} + g[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]))),
_ => unsafe { core::hint::unreachable_unchecked() }
};
sum.mul_add(rmx, res)
}
#[inline(always)]
#[cold]
fn cold() {}
#[inline(always)]
#[rustfmt::skip]
fn likely(b: bool) -> bool {
if !b { cold() } b
}