bullet 0.1.2

Supersonic Math
use std::ops::{MulAssign, AddAssign, DivAssign, Mul, Div};
use std::cmp::{Ordering, Ord, PartialOrd};
use std::fmt;


fn gcd(mut ab: (i64, i64)) -> i64 {
    loop {
        match ab {
            (a, 0) => break a,
            (a, b) => ab = (b, a % b)
        }
    }
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Rational {
    num: i64,
    denom: i64
}
impl AddAssign<i64> for Rational {
    fn add_assign(&mut self, i: i64) {
        self.num += i * self.denom;
        self.normalize();
    }
}
impl AddAssign for Rational {
    fn add_assign(&mut self, rhs: Rational) {
        let denom = self.denom * rhs.denom;
        self.num = self.num * rhs.denom + rhs.num * self.denom;
        self.denom = denom;
        self.normalize();
    }
}
impl MulAssign<i64> for Rational {
    fn mul_assign(&mut self, n: i64) {
        self.num *= n;
        self.normalize();
    }
}
impl MulAssign for Rational {
    fn mul_assign(&mut self, rhs: Rational) {
        self.num *= rhs.num;
        self.denom *= rhs.denom;
        self.normalize();
    }
}
impl Mul for Rational {
    type Output = Rational;
    fn mul(self, rhs: Rational) -> Rational {
        Rational::new(self.num * rhs.num, self.denom * rhs.denom)
    }
}
impl DivAssign for Rational {
    fn div_assign(&mut self, rhs: Rational) {
        self.num *= rhs.denom;
        self.denom *= rhs.num;
        self.normalize();
    }
}
impl DivAssign<i64> for Rational {
    fn div_assign(&mut self, n: i64) {
        self.denom *= n;
        self.normalize();
    }
}
impl Div<Rational> for Rational {
    type Output = Rational;
    fn div(mut self, rhs: Rational) -> Rational {
        self /= rhs;
        self
    }
}
impl Rational {
    pub fn new(num: i64, denom: i64) -> Rational {
        let mut r = Rational { num, denom };
        r.normalize();
        r
    }
    fn normalize(&mut self) {
        let (n, d) = match (self.num, self.denom) {
            (0, 0) => panic!("undefined rational"),
            (_, 0) => panic!("infinite rational"),
            (0, _) => (0, 1),
            (n, 1) => (n, 1),
            (1, -1) => (1, 1),
            (mut n, mut d) => {
                if d < 0 {
                    n = -n;
                    d = -d;
                }
                let c = gcd((n, d));
                (n/c, d/c)
            }
        };

        self.num = n;
        self.denom = d;
    }
    pub fn frac(&self) -> (i64, i64) {
        (self.num, self.denom)
    }
    pub fn as_int(&self) -> Option<i64> {
        match self.frac() {
            (i, 1) => Some(i),
            _ => None
        }
    }
    pub fn is_zero(&self) -> bool {
        self.num == 0
    }

    pub fn is_negative(&self) -> bool {
        (self.num < 0) ^ (self.denom < 0)
    }
    pub fn pow(&self, i: i32) -> Rational {
        match i.cmp(&0) {
            Ordering::Greater => Rational {
                num: self.num.pow(i as u32),
                denom: self.denom.pow(i as u32)
            },
            Ordering::Equal => Rational::from(1),
            Ordering::Less => Rational {
                num: self.denom.pow(-i as u32),
                denom: self.num.pow(-i as u32)
            }
        }
    }
    pub fn to_f64(&self) -> f64 {
        self.num as f64 / self.denom as f64
    }
}
impl Ord for Rational {
    fn cmp(&self, rhs: &Rational) -> Ordering {
        let a = self.num * rhs.denom;
        let b = rhs.num * self.denom;
        a.cmp(&b)
    }
}
impl PartialOrd for Rational {
    fn partial_cmp(&self, rhs: &Rational) -> Option<Ordering> {
        Some(self.cmp(rhs))
    }
}
impl From<i64> for Rational {
    fn from(i: i64) -> Rational {
        Rational {
            num: i,
            denom: 1
        }
    }
}
impl fmt::Display for Rational {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.frac() {
            (n, 1) => write!(f, "{}", n),
            (n, d) => write!(f, "{}/{}", n, d),
        }
    }
}