cardano-serialization-lib 15.0.3

(De)serialization functions for the Cardano blockchain along with related utility functions
Documentation
use crate::{BigInt, BigNum, JsError, UnitInterval};

#[derive(Clone, Debug)]
pub(crate) struct Rational {
    numerator: BigInt,
    denominator: BigInt,
}

impl From<UnitInterval> for Rational {
    fn from(sc: UnitInterval) -> Self {
        Rational::new(BigInt::from(sc.numerator), BigInt::from(sc.denominator))
    }
}

impl From<&UnitInterval> for Rational {
    fn from(sc: &UnitInterval) -> Self {
        Rational::new(BigInt::from(sc.numerator), BigInt::from(sc.denominator))
    }
}

impl Rational {
    pub(crate) fn new(n: BigInt, d: BigInt) -> Self {
        Rational {
            numerator: n,
            denominator: d,
        }.reduce_minuses()
    }


    pub (crate) fn one() -> Self {
        Rational {
            numerator: BigInt::one(),
            denominator: BigInt::one(),
        }
    }

    pub(crate) fn zero() -> Self {
        Rational {
            numerator: BigInt::zero(),
            denominator: BigInt::one(),
        }
    }

    pub(crate) fn numerator(&self) -> &BigInt {
        &self.numerator
    }

    pub(crate) fn denominator(&self) -> &BigInt {
        &self.denominator
    }

    pub(crate) fn mul_bignum(&self, x: &BigNum) -> Result<Rational, JsError> {
        let m: BigInt = x.into();
        Ok(Rational::new(self.numerator.mul(&m), self.denominator.clone()))
    }

    pub(crate) fn mul_usize(&self, x: usize) -> Rational {
        Rational::new(self.numerator.mul(&BigInt::from(x)), self.denominator.clone())
    }

    pub(crate) fn mul_ratio(&self, x: &Rational) -> Rational {
        let a_num = &self.numerator;
        let a_denum = &self.denominator;
        let b_num = &x.numerator;
        let b_denum = &x.denominator;

        let a_num_fixed = a_num.mul(b_num);
        let a_denum_fixed = a_denum.mul(b_denum);
        Rational::new(a_num_fixed, a_denum_fixed)
    }

    pub(crate) fn div_ratio(&self, x: &Rational) -> Rational {
        let a_num = &self.numerator;
        let a_denum = &self.denominator;
        let b_num = &x.numerator;
        let b_denum = &x.denominator;

        let a_num_fixed = a_num.mul(b_denum);
        let a_denum_fixed = a_denum.mul(b_num);

        Rational::new(a_num_fixed, a_denum_fixed)
    }

    pub(crate) fn add(&self, x: &Rational) -> Rational {
        let a_num = &self.numerator;
        let a_denum = &self.denominator;
        let b_num = &x.numerator;
        let b_denum = &x.denominator;

        if a_num.is_zero() {
            return x.clone();
        }
        if b_num.is_zero() {
            return self.clone();
        }
        let a_num_fixed = &a_num.mul(b_denum);
        let b_num_fixed = &b_num.mul(a_denum);
        let a_b_num_sum = a_num_fixed.add(b_num_fixed);
        let common_denum = a_denum.mul(b_denum);
        Rational::new(a_b_num_sum, common_denum)
    }

    pub(crate) fn sub(&self, x: &Rational) -> Rational {
        let a_num = &self.numerator;
        let a_denum = &self.denominator;
        let b_num = &x.numerator;
        let b_denum = &x.denominator;

        if a_num.is_zero() {
            return x.clone();
        }
        if b_num.is_zero() {
            return self.clone();
        }
        let a_num_fixed = &a_num.mul(b_denum);
        let b_num_fixed = &b_num.mul(a_denum);
        let a_b_num_diff = a_num_fixed.sub(b_num_fixed);
        let common_denum = a_denum.mul(b_denum);
        Rational::new(a_b_num_diff, common_denum)
    }

    pub(crate) fn to_bignum_ceil(&self) -> Result<BigNum, JsError> {
        let num = self.numerator();
        let denum = self.denominator();
        if denum.is_zero() {
            return Err(JsError::from_str("Division by zero"));
        }
        let value = num.div_ceil(denum);
        match value.as_u64() {
            Some(coin) => Ok(coin),
            _ => Err(JsError::from_str(&format!(
                "Failed to calculate ceil from ratio {}/{}",
                num.to_str(),
                denum.to_str(),
            ))),
        }
    }

    pub(crate) fn pow(&self, exp: u32) -> Rational {
        let num = self.numerator.pow(exp);
        let denum = self.denominator.pow(exp);
        Rational::new(num, denum).reduce_minuses()
    }

    pub(crate) fn is_zero(&self) -> bool {
        self.numerator.is_zero()
    }

    pub(crate) fn is_negative(&self) -> bool {
        let is_num_negative = self.numerator.is_negative();
        let is_denum_negative = self.denominator.is_negative();
        is_num_negative ^ is_denum_negative
    }

    pub(crate) fn is_negative_or_zero(&self) -> bool {
        self.is_zero() || self.is_negative()
    }

    #[allow(dead_code)]
    pub(crate) fn to_bignum_floor(&self) -> Result<BigNum, JsError> {
        let num = self.numerator();
        let denum = self.denominator();
        if denum.is_zero() {
            return Err(JsError::from_str("Division by zero"));
        }
        let value = num.div_floor(denum);
        match value.as_u64() {
            Some(coin) => Ok(coin),
            _ => Err(JsError::from_str(&format!(
                "Failed to calculate floor from ratio {}/{}",
                num.to_str(),
                denum.to_str(),
            ))),
        }
    }

    fn reduce_minuses(mut self) -> Self{
        if self.numerator.is_negative() && self.denominator.is_negative() {
            self.numerator = self.numerator.abs();
            self.denominator = self.denominator.abs();
        }
        self
    }
}