ebi_arithmetic 0.3.13

Exact arithmetic for Ebi - a stochastic process mining utility
Documentation
use anyhow::{Result, anyhow};
use malachite::{Integer, Natural, rational::Rational};

use crate::{
    exact::MaybeExact,
    fraction::{
        fraction_enum::FractionEnum, fraction_exact::FractionExact, fraction_f64::FractionF64,
    },
    is_exact_globally,
};

impl MaybeExact for FractionF64 {
    type Approximate = f64;
    type Exact = Rational;

    fn is_exact(&self) -> bool {
        false
    }

    fn approx_ref(&self) -> Result<&f64> {
        Ok(&self.0)
    }

    fn exact_ref(&self) -> Result<&Self::Exact> {
        Err(anyhow!("cannot extract a fraction from a float"))
    }

    fn approx(self) -> Result<f64> {
        Ok(self.0)
    }

    fn exact(self) -> Result<Self::Exact> {
        Err(anyhow!("cannot extract a fraction from a float"))
    }

    fn try_to_exact(_: Self::Exact) -> Result<Self> {
        Err(anyhow!("cannot put fraction in a float"))
    }

    fn try_to_approx(approx: Self::Approximate) -> Result<Self> {
        Ok(FractionF64(approx))
    }
}

impl MaybeExact for FractionExact {
    type Approximate = f64;
    type Exact = Rational;

    fn is_exact(&self) -> bool {
        true
    }

    fn approx_ref(&self) -> Result<&f64> {
        Err(anyhow!("cannot extract a float from a fraction"))
    }

    fn exact_ref(&self) -> Result<&Rational> {
        Ok(&self.0)
    }

    fn approx(self) -> Result<f64> {
        Err(anyhow!("cannot extract a float from a fraction"))
    }

    fn exact(self) -> Result<Rational> {
        Ok(self.0)
    }

    fn try_to_exact(exact: Self::Exact) -> Result<Self> {
        Ok(FractionExact(exact))
    }

    fn try_to_approx(_: Self::Approximate) -> Result<Self> {
        Err(anyhow!("cannot put float in a fraction"))
    }
}

impl MaybeExact for FractionEnum {
    type Approximate = f64;
    type Exact = Rational;

    fn is_exact(&self) -> bool {
        match self {
            FractionEnum::Exact(_) => true,
            FractionEnum::Approx(_) => false,
            FractionEnum::CannotCombineExactAndApprox => false,
        }
    }

    fn approx_ref(&self) -> Result<&f64> {
        match self {
            FractionEnum::Exact(_) => Err(anyhow!("cannot extract a float from a fraction")),
            FractionEnum::Approx(f) => Ok(f),
            FractionEnum::CannotCombineExactAndApprox => {
                Err(anyhow!("cannot combine exact and approximate arithmetic"))
            }
        }
    }

    fn exact_ref(&self) -> Result<&Rational> {
        match self {
            FractionEnum::Exact(f) => Ok(f),
            FractionEnum::Approx(_) => Err(anyhow!("cannot extract a fraction from a float")),
            FractionEnum::CannotCombineExactAndApprox => {
                Err(anyhow!("cannot combine exact and approximate arithmetic"))
            }
        }
    }

    fn approx(self) -> Result<f64> {
        match self {
            FractionEnum::Exact(_) => Err(anyhow!("cannot extract a float from a fraction")),
            FractionEnum::Approx(f) => Ok(f),
            FractionEnum::CannotCombineExactAndApprox => {
                Err(anyhow!("cannot combine exact and approximate arithmetic"))
            }
        }
    }

    fn exact(self) -> Result<Rational> {
        match self {
            FractionEnum::Exact(f) => Ok(f),
            FractionEnum::Approx(_) => Err(anyhow!("cannot extract a fraction from a float")),
            FractionEnum::CannotCombineExactAndApprox => {
                Err(anyhow!("cannot combine exact and approximate arithmetic"))
            }
        }
    }

    fn try_to_exact(exact: <FractionEnum as MaybeExact>::Exact) -> Result<Self> {
        if is_exact_globally() {
            Ok(FractionEnum::Exact(exact))
        } else {
            Err(anyhow!("cannot put float in a fraction"))
        }
    }

    fn try_to_approx(approx: Self::Approximate) -> Result<Self> {
        if !is_exact_globally() {
            Ok(FractionEnum::Approx(approx))
        } else {
            Err(anyhow!("cannot put fraction in a float"))
        }
    }
}

impl MaybeExact for Rational {
    type Approximate = f64;
    type Exact = Rational;

    fn is_exact(&self) -> bool {
        true
    }

    fn approx_ref(&self) -> Result<&Self::Approximate> {
        Err(anyhow!("cannot extract a float from a fraction"))
    }

    fn exact_ref(&self) -> Result<&Self::Exact> {
        Ok(self)
    }

    fn approx(self) -> Result<Self::Approximate> {
        Err(anyhow!(
            "cannot extract an approximate float from an fraction"
        ))
    }

    fn exact(self) -> Result<Self::Exact> {
        Ok(self)
    }

    fn try_to_exact(exact: Self::Exact) -> Result<Self> {
        Ok(exact)
    }

    fn try_to_approx(_: Self::Approximate) -> Result<Self> {
        Err(anyhow!("cannot put float in a fraction"))
    }
}

impl MaybeExact for Integer {
    type Approximate = f64;
    type Exact = Integer;

    fn is_exact(&self) -> bool {
        true
    }

    fn approx_ref(&self) -> Result<&Self::Approximate> {
        Err(anyhow!(
            "cannot extract an approximate integer from an integer"
        ))
    }

    fn exact_ref(&self) -> Result<&Self::Exact> {
        Ok(self)
    }

    fn approx(self) -> Result<Self::Approximate> {
        Err(anyhow!(
            "cannot extract an approximate integer from an integer"
        ))
    }

    fn exact(self) -> Result<Self::Exact> {
        Ok(self)
    }

    fn try_to_exact(exact: Self::Exact) -> Result<Self> {
        Ok(exact)
    }

    fn try_to_approx(_: Self::Approximate) -> Result<Self> {
        Err(anyhow!("cannot put float in an integer"))
    }
}

impl MaybeExact for Natural {
    type Approximate = f64;
    type Exact = Natural;

    fn is_exact(&self) -> bool {
        true
    }

    fn approx_ref(&self) -> Result<&Self::Approximate> {
        Err(anyhow!(
            "cannot extract an approximate integer from an integer"
        ))
    }

    fn exact_ref(&self) -> Result<&Self::Exact> {
        Ok(self)
    }

    fn approx(self) -> Result<Self::Approximate> {
        Err(anyhow!(
            "cannot extract an approximate integer from an integer"
        ))
    }

    fn exact(self) -> Result<Self::Exact> {
        Ok(self)
    }

    fn try_to_exact(exact: Self::Exact) -> Result<Self> {
        Ok(exact)
    }

    fn try_to_approx(_: Self::Approximate) -> Result<Self> {
        Err(anyhow!("cannot put float in an integer"))
    }
}

macro_rules! approx {
    ($t: ident) => {
        impl MaybeExact for $t {
            type Approximate = $t;
            type Exact = ();

            fn is_exact(&self) -> bool {
                false
            }

            fn approx_ref(&self) -> Result<&Self::Approximate> {
                Ok(self)
            }

            fn exact_ref(&self) -> Result<&Self::Exact> {
                Err(anyhow!("cannot extract exact value"))
            }

            fn approx(self) -> Result<Self::Approximate> {
                Ok(self)
            }

            fn exact(self) -> Result<Self::Exact> {
                Err(anyhow!("cannot extract exact value"))
            }

            fn try_to_exact(_: Self::Exact) -> Result<Self> {
                Err(anyhow!("cannot put fraction in a float"))
            }

            fn try_to_approx(approx: Self::Approximate) -> Result<Self> {
                Ok(approx)
            }
        }
    };
}

macro_rules! exact {
    ($t: ident) => {
        impl MaybeExact for $t {
            type Approximate = ();
            type Exact = $t;

            fn is_exact(&self) -> bool {
                true
            }

            fn approx_ref(&self) -> Result<&Self::Approximate> {
                Err(anyhow!("cannot extract approximate value"))
            }

            fn exact_ref(&self) -> Result<&Self::Exact> {
                Ok(self)
            }

            fn approx(self) -> Result<Self::Approximate> {
                Err(anyhow!("cannot extract approximate value"))
            }

            fn exact(self) -> Result<Self::Exact> {
                Ok(self)
            }

            fn try_to_exact(exact: Self::Exact) -> Result<Self> {
                Ok(exact)
            }

            fn try_to_approx(_: Self::Approximate) -> Result<Self> {
                Err(anyhow!("cannot put float in an integer"))
            }
        }
    };
}

exact!(i128);
exact!(i64);
exact!(i32);
exact!(i16);
exact!(u128);
exact!(u64);
exact!(u32);
exact!(u16);
exact!(u8);
approx!(f64);
approx!(f32);