use std::ops::{Add, Div, Mul, Neg, Sub};
use super::{FixedMatrix, FixedVector};
use super::kernels;
use crate::fixed_point::fixed_num::{Compute, Fixed, FixedNum};
use crate::fixed_point::width::{Storage, WideInt};
use crate::fixed_point::core_types::errors::OverflowDetected;
#[inline]
fn st_add<S: Storage, const F: u32>(a: S, b: S) -> Result<S, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
a.checked_add_wide(b).ok_or(OverflowDetected::TierOverflow)
}
#[inline]
fn st_sub<S: Storage, const F: u32>(a: S, b: S) -> Result<S, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
a.checked_sub_wide(b).ok_or(OverflowDetected::TierOverflow)
}
#[inline]
fn st_neg<S: Storage, const F: u32>(a: S) -> Result<S, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
a.checked_neg_wide().ok_or(OverflowDetected::TierOverflow)
}
#[inline]
fn compute_zero<S: Storage, const F: u32>() -> Compute<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
Fixed::<S, F>::upscale_to_compute(Fixed::ZERO.raw())
}
#[inline]
fn directed_divide<S: Storage, const F: u32>(a: S, b: S) -> Result<(S, S), OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
if b == S::ZERO { return Err(OverflowDetected::DivisionByZero); }
let num = a.widen().shl_bits(F);
let den = b.widen();
let (q, rem) = num.divmod(den);
let one = <Compute<Fixed<S, F>> as WideInt>::one();
let (f, c) = if rem == <Compute<Fixed<S, F>> as WideInt>::ZERO {
(q, q)
} else if num.is_negative() == den.is_negative() {
(q, q + one)
} else {
(q - one, q)
};
match (S::narrow(f), S::narrow(c)) {
(Some(f), Some(c)) => Ok((f, c)),
_ => Err(OverflowDetected::TierOverflow),
}
}
#[inline]
fn certified_sqrt<S: Storage, const F: u32>(x: S) -> Result<(S, S), OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let n = Fixed::<S, F>::upscale_to_compute(x);
let mut k = compute_narrow_integer(isqrt_compute(n))?;
let unit = S::one();
let mut steps = 0u32;
while S::mul_wide(k, k) > n {
k = st_sub(k, unit)?;
steps += 1;
assert!(steps <= 2, "certified_sqrt: candidate failed the certificate downward");
}
loop {
let next = st_add(k, unit)?;
if S::mul_wide(next, next) <= n {
k = next;
steps += 1;
assert!(steps <= 2, "certified_sqrt: candidate failed the certificate upward");
} else {
break;
}
}
let floor = k;
let ceil = if S::mul_wide(k, k) == n { k } else { st_add(k, unit)? };
Ok((floor, ceil))
}
#[inline]
fn isqrt_compute<S: Storage, const F: u32>(n: Compute<Fixed<S, F>>) -> Compute<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
debug_assert!(!n.is_negative());
n.isqrt()
}
#[inline]
fn compute_narrow_integer<S: Storage, const F: u32>(v: Compute<Fixed<S, F>>) -> Result<S, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
S::narrow(v).ok_or(OverflowDetected::TierOverflow)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Interval<T> {
lo: T,
hi: T,
}
impl<S: Storage, const F: u32> Interval<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
#[inline]
pub fn point(x: Fixed<S, F>) -> Self {
Interval::<Fixed<S, F>> { lo: x, hi: x }
}
#[inline]
pub fn new(lo: Fixed<S, F>, hi: Fixed<S, F>) -> Self {
Self::try_new(lo, hi).expect("Interval::new: lo > hi")
}
#[inline]
pub fn try_new(lo: Fixed<S, F>, hi: Fixed<S, F>) -> Result<Self, OverflowDetected> {
if lo > hi {
return Err(OverflowDetected::InvalidInput);
}
Ok(Interval::<Fixed<S, F>> { lo, hi })
}
#[inline]
fn from_raw(lo: S, hi: S) -> Self {
let iv = Interval::<Fixed<S, F>> { lo: Fixed::from_raw(lo), hi: Fixed::from_raw(hi) };
debug_assert!(iv.lo <= iv.hi, "Interval<Fixed<S, F>> invariant violated: lo > hi");
iv
}
#[inline]
pub fn lo(self) -> Fixed<S, F> { self.lo }
#[inline]
pub fn hi(self) -> Fixed<S, F> { self.hi }
#[inline]
pub fn width(self) -> Fixed<S, F> {
Fixed::from_raw(st_sub(self.hi.raw(), self.lo.raw()).expect("Interval::width: overflow"))
}
#[inline]
pub fn is_point(self) -> bool { self.lo == self.hi }
#[inline]
pub fn contains(self, x: Fixed<S, F>) -> bool { self.lo <= x && x <= self.hi }
#[inline]
pub fn contains_zero(self) -> bool { self.contains(Fixed::ZERO) }
#[inline]
pub fn is_certainly_positive(self) -> bool { self.lo > Fixed::ZERO }
#[inline]
pub fn is_certainly_negative(self) -> bool { self.hi < Fixed::ZERO }
pub fn try_add(self, rhs: Self) -> Result<Self, OverflowDetected> {
Ok(Self::from_raw(st_add(self.lo.raw(), rhs.lo.raw())?, st_add(self.hi.raw(), rhs.hi.raw())?))
}
pub fn try_sub(self, rhs: Self) -> Result<Self, OverflowDetected> {
Ok(Self::from_raw(st_sub(self.lo.raw(), rhs.hi.raw())?, st_sub(self.hi.raw(), rhs.lo.raw())?))
}
pub fn try_neg(self) -> Result<Self, OverflowDetected> {
Ok(Self::from_raw(st_neg(self.hi.raw())?, st_neg(self.lo.raw())?))
}
pub fn try_mul(self, rhs: Self) -> Result<Self, OverflowDetected> {
let p = [
S::mul_wide(self.lo.raw(), rhs.lo.raw()),
S::mul_wide(self.lo.raw(), rhs.hi.raw()),
S::mul_wide(self.hi.raw(), rhs.lo.raw()),
S::mul_wide(self.hi.raw(), rhs.hi.raw()),
];
let mut mn = p[0];
let mut mx = p[0];
for q in &p[1..] {
if *q < mn { mn = *q; }
if *q > mx { mx = *q; }
}
Ok(Self::from_raw(Fixed::<S, F>::downscale_to_storage_floor(mn)?, Fixed::<S, F>::downscale_to_storage_ceil(mx)?))
}
pub fn try_div(self, rhs: Self) -> Result<Self, OverflowDetected> {
if rhs.contains_zero() {
return Err(OverflowDetected::DivisionByZero);
}
let corners = [
directed_divide(self.lo.raw(), rhs.lo.raw())?,
directed_divide(self.lo.raw(), rhs.hi.raw())?,
directed_divide(self.hi.raw(), rhs.lo.raw())?,
directed_divide(self.hi.raw(), rhs.hi.raw())?,
];
let mut lo = corners[0].0;
let mut hi = corners[0].1;
for (f, c) in &corners[1..] {
if *f < lo { lo = *f; }
if *c > hi { hi = *c; }
}
Ok(Self::from_raw(lo, hi))
}
pub fn try_sqrt(self) -> Result<Self, OverflowDetected> {
if self.lo < Fixed::ZERO {
return Err(OverflowDetected::DomainError);
}
let (lo, _) = certified_sqrt(self.lo.raw())?;
let (_, hi) = certified_sqrt(self.hi.raw())?;
Ok(Self::from_raw(lo, hi))
}
pub fn try_dot(a: &[Fixed<S, F>], b: &[Fixed<S, F>]) -> Result<Self, OverflowDetected> {
assert_eq!(a.len(), b.len(), "Interval::dot: length mismatch");
let mut acc = compute_zero::<S, F>();
for i in 0..a.len() {
acc = Fixed::<S, F>::compute_checked_add(acc, S::mul_wide(a[i].raw(), b[i].raw()))?;
}
Ok(Self::from_raw(Fixed::<S, F>::downscale_to_storage_floor(acc)?, Fixed::<S, F>::downscale_to_storage_ceil(acc)?))
}
pub fn try_dot_intervals(a: &[Interval<Fixed<S, F>>], b: &[Interval<Fixed<S, F>>]) -> Result<Self, OverflowDetected> {
assert_eq!(a.len(), b.len(), "Interval::dot_intervals: length mismatch");
let mut lo_acc = compute_zero::<S, F>();
let mut hi_acc = compute_zero::<S, F>();
for i in 0..a.len() {
let p = [
S::mul_wide(a[i].lo.raw(), b[i].lo.raw()),
S::mul_wide(a[i].lo.raw(), b[i].hi.raw()),
S::mul_wide(a[i].hi.raw(), b[i].lo.raw()),
S::mul_wide(a[i].hi.raw(), b[i].hi.raw()),
];
let mut mn = p[0];
let mut mx = p[0];
for q in &p[1..] {
if *q < mn { mn = *q; }
if *q > mx { mx = *q; }
}
lo_acc = Fixed::<S, F>::compute_checked_add(lo_acc, mn)?;
hi_acc = Fixed::<S, F>::compute_checked_add(hi_acc, mx)?;
}
Ok(Self::from_raw(Fixed::<S, F>::downscale_to_storage_floor(lo_acc)?, Fixed::<S, F>::downscale_to_storage_ceil(hi_acc)?))
}
pub fn try_quadratic_form(v: &FixedVector<Fixed<S, F>>, m: &FixedMatrix<Fixed<S, F>>) -> Result<Self, OverflowDetected> {
let acc = kernels::quadratic_form_exact(Fixed::raw_slice(v.as_slice()), m)?;
Ok(Self::from_raw(kernels::narrow_triple_floor::<Fixed<S, F>>(acc)?, kernels::narrow_triple_ceil::<Fixed<S, F>>(acc)?))
}
pub fn sqrt(self) -> Self {
self.try_sqrt().expect("Interval::sqrt: domain error or overflow")
}
pub fn dot(a: &[Fixed<S, F>], b: &[Fixed<S, F>]) -> Self {
Self::try_dot(a, b).expect("Interval::dot: overflow")
}
pub fn quadratic_form(v: &FixedVector<Fixed<S, F>>, m: &FixedMatrix<Fixed<S, F>>) -> Self {
Self::try_quadratic_form(v, m).expect("Interval::quadratic_form: overflow")
}
pub fn dot_intervals(a: &[Interval<Fixed<S, F>>], b: &[Interval<Fixed<S, F>>]) -> Self {
Self::try_dot_intervals(a, b).expect("Interval::dot_intervals: overflow")
}
}
impl<S: Storage, const F: u32> Add for Interval<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self { self.try_add(rhs).expect("Interval<Fixed<S, F>>: addition overflow") }
}
impl<S: Storage, const F: u32> Sub for Interval<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self { self.try_sub(rhs).expect("Interval<Fixed<S, F>>: subtraction overflow") }
}
impl<S: Storage, const F: u32> Mul for Interval<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self { self.try_mul(rhs).expect("Interval<Fixed<S, F>>: multiplication overflow") }
}
impl<S: Storage, const F: u32> Div for Interval<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self { self.try_div(rhs).expect("Interval<Fixed<S, F>>: division by an interval containing zero, or overflow") }
}
impl<S: Storage, const F: u32> Neg for Interval<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
type Output = Self;
#[inline]
fn neg(self) -> Self { self.try_neg().expect("Interval<Fixed<S, F>>: negation overflow") }
}
impl<S: Storage, const F: u32> From<Fixed<S, F>> for Interval<Fixed<S, F>>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
#[inline]
fn from(x: Fixed<S, F>) -> Self { Interval::<Fixed<S, F>>::point(x) }
}