use core::cmp::Ordering;
use core::fmt;
use crate::{Decimal, ErrorKind, Fraction, Number};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Exact<const DIGITS: u32 = 14, const MAX_EXPONENT: i32 = 99>(Form<DIGITS, MAX_EXPONENT>);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
enum Form<const DIGITS: u32, const MAX_EXPONENT: i32> {
Fraction(Fraction),
Decimal(Decimal<DIGITS, MAX_EXPONENT>),
}
impl<const DIGITS: u32, const MAX_EXPONENT: i32> Exact<DIGITS, MAX_EXPONENT> {
const VALID: () = assert!(
MAX_EXPONENT >= 19,
"Exact needs a maximum exponent of at least 19, to hold every fraction as a decimal"
);
pub const fn is_exact(self) -> bool {
matches!(self.0, Form::Fraction(_))
}
pub const fn as_fraction(self) -> Option<Fraction> {
match self.0 {
Form::Fraction(fraction) => Some(fraction),
Form::Decimal(_) => None,
}
}
pub const fn as_decimal(self) -> Option<Decimal<DIGITS, MAX_EXPONENT>> {
match self.0 {
Form::Fraction(_) => None,
Form::Decimal(decimal) => Some(decimal),
}
}
pub fn to_decimal(self) -> Decimal<DIGITS, MAX_EXPONENT> {
let () = Self::VALID;
match self.0 {
Form::Fraction(fraction) => Decimal::try_from(fraction).unwrap_or(Decimal::ZERO),
Form::Decimal(decimal) => decimal,
}
}
fn apply(
self,
rhs: Self,
exact: fn(Fraction, Fraction) -> Result<Fraction, ErrorKind>,
decimal: fn(
Decimal<DIGITS, MAX_EXPONENT>,
Decimal<DIGITS, MAX_EXPONENT>,
) -> Result<Decimal<DIGITS, MAX_EXPONENT>, ErrorKind>,
) -> Result<Self, ErrorKind> {
if let (Form::Fraction(left), Form::Fraction(right)) = (self.0, rhs.0) {
match exact(left, right) {
Err(ErrorKind::Overflow | ErrorKind::Inexact) => {}
result => return result.map(Self::from),
}
}
decimal(self.to_decimal(), rhs.to_decimal()).map(Self::from)
}
fn power_parts(
base: Fraction,
exponent: Decimal<DIGITS, MAX_EXPONENT>,
) -> Result<Decimal<DIGITS, MAX_EXPONENT>, ErrorKind> {
let part = |value: u64| {
Decimal::<DIGITS, MAX_EXPONENT>::try_from(Fraction::from(value))
.and_then(|part| part.power(exponent))
};
let magnitude = part(base.numerator())?.divide(part(base.denominator())?)?;
if base.is_negative() {
let sign = Decimal::<DIGITS, MAX_EXPONENT>::try_from(Fraction::from(-1_i64))?;
magnitude.multiply(sign.power(exponent)?)
} else {
Ok(magnitude)
}
}
pub(crate) fn cmp_value(self, rhs: Self) -> Ordering {
match (self.0, rhs.0) {
(Form::Fraction(left), Form::Fraction(right)) => left.cmp(&right),
(Form::Decimal(left), Form::Decimal(right)) => left.cmp(&right),
(Form::Decimal(left), Form::Fraction(right)) => left.cmp_fraction(right),
(Form::Fraction(left), Form::Decimal(right)) => right.cmp_fraction(left).reverse(),
}
}
}
impl<const DIGITS: u32, const MAX_EXPONENT: i32> PartialOrd for Exact<DIGITS, MAX_EXPONENT> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let ordering = self.cmp_value(*other);
(ordering != Ordering::Equal || self == other).then_some(ordering)
}
}
impl<const DIGITS: u32, const MAX_EXPONENT: i32> From<Fraction> for Exact<DIGITS, MAX_EXPONENT> {
fn from(value: Fraction) -> Self {
Self(Form::Fraction(value))
}
}
impl<const DIGITS: u32, const MAX_EXPONENT: i32> From<Decimal<DIGITS, MAX_EXPONENT>>
for Exact<DIGITS, MAX_EXPONENT>
{
fn from(value: Decimal<DIGITS, MAX_EXPONENT>) -> Self {
Self(Form::Decimal(value))
}
}
impl<const DIGITS: u32, const MAX_EXPONENT: i32> Default for Exact<DIGITS, MAX_EXPONENT> {
fn default() -> Self {
Self::from(Fraction::ZERO)
}
}
impl<const DIGITS: u32, const MAX_EXPONENT: i32> Number for Exact<DIGITS, MAX_EXPONENT> {
fn parse(literal: &str) -> Option<Self> {
let () = Self::VALID;
Fraction::parse(literal)
.map(Self::from)
.or_else(|| Decimal::parse(literal).map(Self::from))
}
fn add(self, rhs: Self) -> Result<Self, ErrorKind> {
self.apply(rhs, Number::add, Number::add)
}
fn subtract(self, rhs: Self) -> Result<Self, ErrorKind> {
self.apply(rhs, Number::subtract, Number::subtract)
}
fn multiply(self, rhs: Self) -> Result<Self, ErrorKind> {
self.apply(rhs, Number::multiply, Number::multiply)
}
fn divide(self, rhs: Self) -> Result<Self, ErrorKind> {
self.apply(rhs, Number::divide, Number::divide)
}
fn remainder(self, rhs: Self) -> Result<Self, ErrorKind> {
self.apply(rhs, Number::remainder, Number::remainder)
}
fn power(self, rhs: Self) -> Result<Self, ErrorKind> {
if let (Form::Fraction(base), Form::Fraction(exponent)) = (self.0, rhs.0) {
match base.power(exponent) {
Err(ErrorKind::Overflow | ErrorKind::Inexact) => {}
result => return result.map(Self::from),
}
}
let exponent = rhs.to_decimal();
if let Form::Fraction(base) = self.0 {
if let Ok(value) = Self::power_parts(base, exponent) {
return Ok(Self::from(value));
}
}
self.to_decimal().power(exponent).map(Self::from)
}
fn negate(self) -> Result<Self, ErrorKind> {
Ok(match self.0 {
Form::Fraction(fraction) => Self::from(fraction.negate()?),
Form::Decimal(decimal) => Self::from(decimal.negate()?),
})
}
fn compare(self, rhs: Self) -> Result<Ordering, ErrorKind> {
Ok(self.cmp_value(rhs))
}
fn is_zero(self) -> Result<bool, ErrorKind> {
Ok(match self.0 {
Form::Fraction(fraction) => fraction.is_zero(),
Form::Decimal(decimal) => decimal.is_zero(),
})
}
fn from_bool(value: bool) -> Result<Self, ErrorKind> {
Fraction::from_bool(value).map(Self::from)
}
}
impl<const DIGITS: u32, const MAX_EXPONENT: i32> fmt::Display for Exact<DIGITS, MAX_EXPONENT> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Form::Fraction(fraction) => fmt::Display::fmt(&fraction, formatter),
Form::Decimal(decimal) => fmt::Display::fmt(&decimal, formatter),
}
}
}
impl<const DIGITS: u32, const MAX_EXPONENT: i32> fmt::Debug for Exact<DIGITS, MAX_EXPONENT> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Form::Fraction(fraction) => write!(formatter, "Fraction({fraction})"),
Form::Decimal(decimal) => write!(formatter, "Decimal({decimal})"),
}
}
}