#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u64)]
pub enum RootType {
Real = 0,
Complex = 1,
PosInf = 2,
NegInf = 3,
Nan = 4,
}
#[derive(Debug, Clone, Copy)]
pub struct Roots<const N: usize> {
pub(crate) values: [f64; N],
types: u64,
}
impl<const N: usize> Roots<N> {
#[inline]
pub fn get(&self, i: usize) -> f64 {
match self.values.get(i) {
Some(&v) => v,
None => f64::NAN,
}
}
#[inline]
pub fn root_type(&self, i: usize) -> RootType {
match (self.types >> (3 * i)) & 7 {
0 => RootType::Real,
1 => RootType::Complex,
2 => RootType::PosInf,
3 => RootType::NegInf,
_ => RootType::Nan,
}
}
#[inline]
pub fn set_type(&mut self, i: usize, t: RootType) {
let shift = 3 * i;
self.types = (self.types & !(7u64 << shift)) | ((t as u64) << shift);
}
}
impl<const N: usize> std::ops::Index<usize> for Roots<N> {
type Output = f64;
#[inline]
fn index(&self, i: usize) -> &f64 {
static OUT_OF_RANGE: f64 = f64::NAN;
match self.values.get(i) {
Some(v) => v,
None => &OUT_OF_RANGE,
}
}
}
impl Roots<1> {
#[inline]
pub fn new(t: RootType, x: f64) -> Self {
Self {
values: [x],
types: t as u64,
}
}
}
impl Roots<2> {
#[inline]
pub fn from_pair(a: Roots<1>, b: Roots<1>) -> Self {
let ([a0], [b0]) = (a.values, b.values);
Self {
values: [a0, b0],
types: a.types | (b.types << 3),
}
}
#[inline]
pub fn with_tail(head: Roots<1>, t: RootType, x: f64) -> Self {
let [h0] = head.values;
Self {
values: [h0, x],
types: head.types | ((t as u64) << 3),
}
}
#[inline]
pub fn both(r: Roots<1>) -> Self {
Self::from_pair(r, r)
}
}
impl Roots<3> {
#[inline]
pub fn uniform(t: RootType, x: f64) -> Self {
let ti = t as u64;
Self {
values: [x, x, x],
types: ti | (ti << 3) | (ti << 6),
}
}
#[inline]
pub fn concat_1_2(a: Roots<1>, b: Roots<2>) -> Self {
let ([a0], [b0, b1]) = (a.values, b.values);
Self {
values: [a0, b0, b1],
types: a.types | (b.types << 3),
}
}
#[inline]
pub fn concat_2_1(a: Roots<2>, b: Roots<1>) -> Self {
let ([a0, a1], [b0]) = (a.values, b.values);
Self {
values: [a0, a1, b0],
types: a.types | (b.types << 6),
}
}
#[inline]
pub fn with_tail(head: Roots<2>, t: RootType, x: f64) -> Self {
Self::concat_2_1(head, Roots::<1>::new(t, x))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encoding_round_trip() {
let r = Roots::<1>::new(RootType::Real, 3.14);
assert_eq!(r.root_type(0), RootType::Real);
assert_eq!(r.get(0), 3.14);
}
#[test]
fn concat_2() {
let a = Roots::<1>::new(RootType::Real, 1.0);
let b = Roots::<1>::new(RootType::Nan, 0.0);
let r = Roots::<2>::from_pair(a, b);
assert_eq!(r.root_type(0), RootType::Real);
assert_eq!(r.root_type(1), RootType::Nan);
assert_eq!(r[0], 1.0);
assert_eq!(r[1], 0.0);
}
#[test]
fn concat_3() {
let a = Roots::<1>::new(RootType::Real, 1.0);
let b = Roots::<2>::from_pair(
Roots::<1>::new(RootType::Real, 2.0),
Roots::<1>::new(RootType::Nan, 0.0),
);
let r = Roots::<3>::concat_1_2(a, b);
assert_eq!(r.root_type(0), RootType::Real);
assert_eq!(r.root_type(1), RootType::Real);
assert_eq!(r.root_type(2), RootType::Nan);
assert_eq!(r[0], 1.0);
assert_eq!(r[1], 2.0);
}
#[test]
fn uniform_3() {
let r = Roots::<3>::uniform(RootType::Complex, -5.0);
for i in 0..3 {
assert_eq!(r.root_type(i), RootType::Complex);
assert_eq!(r[i], -5.0);
}
}
}