# hmath 0.1.8

Big Integers and Rational Numbers
Documentation
``````use crate::{BigInt, gcd_bi};

mod arith;
mod comp;
mod convert;
pub mod e;
pub mod funcs;
pub mod ln2;
pub mod pi;

// denom is always a positive integer
// when numer is 0, denom is 1
// denom and numer are always coprime
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Ratio {
denom: BigInt,
numer: BigInt
}

impl Ratio {

/// This function DOES NOT check whether `denom` and `numer` are coprime.
/// Avoid using this function except when converting the result of `Ratio::into_raw` to `Ratio`.
/// In most cases, it's safer to use `Ratio::from_denom_and_numer`.
pub fn from_raw(denom: Vec<u32>, denom_neg: bool, numer: Vec<u32>, numer_neg: bool) -> Self {
Ratio { denom: BigInt::from_raw(denom, denom_neg), numer: BigInt::from_raw(numer, numer_neg) }
}

pub fn into_raw(self) -> (Vec<u32>, bool, Vec<u32>, bool) {
let (denom, numer) = (self.denom, self.numer);
let (denom, denom_neg) = denom.into_raw();
let (numer, numer_neg) = numer.into_raw();

(denom, denom_neg, numer, numer_neg)
}

pub fn zero() -> Self {
Ratio {
denom: BigInt::one(),
numer: BigInt::zero()
}
}

pub fn one() -> Self {
Ratio {
denom: BigInt::one(),
numer: BigInt::one()
}
}

pub fn is_neg(&self) -> bool {
self.numer.is_neg()
}

pub fn is_one(&self) -> bool {
self.denom.is_one() && self.numer.is_one()
}

pub fn is_zero(&self) -> bool {
self.denom.is_one() && self.numer.is_zero()
}

pub fn is_integer(&self) -> bool {
self.denom.is_one()
}

#[cfg(test)]
pub fn is_valid(&self) -> bool {
self.denom.is_valid() && self.numer.is_valid() && !self.denom.is_neg() && (!self.numer.is_zero() || self.denom.is_one()) && gcd_bi(&self.denom, &self.numer).is_one()
}

// TODO: better name
fn fit(&mut self) {

if self.denom.is_neg() {
self.denom.neg_mut();
self.numer.neg_mut();
}

let r = gcd_bi(&self.denom, &self.numer);

if !r.is_one() {
self.denom.div_bi_mut(&r);
self.numer.div_bi_mut(&r);
}

#[cfg(test)] assert!(self.is_valid());
}

// TODO: test this function
/// It shrinks the size of `self.numer` and `self.denom` until they're less than or equal to `2^(limit * 32)`. It may lose accuracy.
/// If `denom` and `numer` are already small enough, it returns `Ok(0)`.
/// If it successfully shrinks, it returns `Ok(n)` where `n` is how much numbers it removed.
/// Sometimes, the shrinked result doesn't satisfy the limit. It returns `Err(n)` in those cases where `n` is how much numbers it removed.
pub fn shrink(&mut self, limit: usize) -> Result<usize, usize> {

if limit < 3 {
return Err(0);
}

let numer_shrink = self.numer.len().max(limit) - limit;
let denom_shrink = self.denom.len().max(limit) - limit;
let shrink = numer_shrink.max(denom_shrink);

if shrink == 0 {
Ok(0)
}

else if shrink + 2 < self.numer.len().min(self.denom.len()) {
self.numer.shift_right_mut(shrink);
self.denom.shift_right_mut(shrink);
self.fit();

Ok(shrink)
}

else if self.numer.len() + 4 < self.denom.len() {
let shrink = self.numer.len();
self.denom.div_bi_mut(&self.numer);

if self.denom.is_neg() {
self.denom.abs_mut();
self.numer = BigInt::one().neg();
}

else {
self.numer = BigInt::one();
}

if self.denom.len() > limit {
Err(shrink - 1)
}

else {
Ok(shrink - 1)
}

}

else if self.denom.len() + 4 < self.numer.len() {
let shrink = self.denom.len();
self.numer.div_bi_mut(&self.denom);
self.denom = BigInt::one();

if self.numer.len() > limit {
Err(shrink - 1)
}

else {
Ok(shrink - 1)
}

}

else {
Err(0)
}

}

}

impl Default for Ratio {
fn default() -> Self { Ratio::zero() }
}``````