use crate::number_theory;
use crate::scalar::{ One, Zero, Latex, Similar, Abs, Neg, Rec, Con, Pow };
use std::fmt::{ Formatter, Display, Result };
use std::cmp::{ PartialEq, Eq, PartialOrd, Ord, Ordering };
use std::ops::{ Add, Sub, Mul, Div };
use std::convert::{ From, Into };
use std::iter::{ Iterator, Sum };
#[derive(Debug, Clone, Copy)]
pub struct Fraction {
num: i128,
den: i128,
}
impl Fraction {
pub fn new(num: i128, den: i128) -> Self {
if (num == 0) && (den == 0) { panic!("0/0 isn't a valid fraction!"); }
let (mut num, mut den) = (num, den);
if den < 0 {
num *= -1;
den *= -1;
}
let gcd = number_theory::greatest_common_divisor(num.abs() as u128, den.abs() as u128) as i128;
Fraction {
num: num / gcd,
den: den / gcd,
}
}
pub fn get_num(&self) -> i128 {
self.num
}
pub fn get_den(&self) -> i128 {
self.den
}
}
#[macro_export]
macro_rules! frac {
($num:expr, $den:expr) => {
Fraction::new($num, $den)
};
($num:expr) => {
Fraction::new($num, 1i128)
}
}
impl Display for Fraction {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}/{}", self.num, self.den)
}
}
impl Latex for Fraction {
fn latex(&self) -> String {
if self.den == 1 {
format!("{}", self.num)
} else {
format!("\\frac{{{}}}{{{}}}", self.num, self.den)
}
}
}
impl One for Fraction {
fn get_one(&self) -> Self {
Fraction::new(1, 1)
}
fn eq_one(&self) -> bool {
*self == Fraction::new(1, 1)
}
fn similar_one(&self, precision: f64) -> bool {
let num: f64 = (*self).into();
(num - 1.0).abs() < precision.abs()
}
}
impl Zero for Fraction {
fn get_zero(&self) -> Self {
Fraction::new(0, 1)
}
fn eq_zero(&self) -> bool {
*self == Fraction::new(0, 1)
}
fn similar_zero(&self, precision: f64) -> bool {
let num: f64 = (*self).into();
num.abs() < precision.abs()
}
}
impl Abs for Fraction {
type Output = Self;
fn abs(&self) -> Self {
Fraction::new(self.num.abs(), self.den.abs())
}
}
impl Neg for Fraction {
type Output = Self;
fn neg(&self) -> Self {
Fraction::new(-self.num, self.den)
}
}
impl Rec for Fraction {
type Output = Self;
fn rec(&self) -> Self {
Fraction::new(self.den, self.num)
}
}
impl Con for Fraction {
type Output = Self;
fn con(&self) -> Self {
*self
}
}
impl Pow for Fraction {
type Output = Self;
fn pow(&self, power: u32) -> Self {
Fraction::new(self.num.pow(power), self.den.pow(power))
}
}
impl PartialEq for Fraction {
fn eq(&self, other: &Self) -> bool {
self.num * other.den == self.den * other.num
}
}
impl Eq for Fraction {}
impl PartialOrd for Fraction {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Fraction {
fn cmp(&self, other: &Self) -> Ordering {
(self.num * other.den).cmp(&(other.num * self.den))
}
}
impl Similar for Fraction {
fn similar(&self, other: &Self, precision: f64) -> bool {
let self_num: f64 = (*self).into();
let other_num: f64 = (*other).into();
(self_num - other_num).abs() < precision.abs()
}
}
impl Add for Fraction {
type Output = Self;
fn add(self, other: Self) -> Self {
Fraction::new(self.num * other.den + self.den * other.num, self.den * other.den)
}
}
impl Sub for Fraction {
type Output = Self;
fn sub(self, other: Self) -> Self {
Fraction::new(self.num * other.den - self.den * other.num, self.den * other.den)
}
}
impl Mul for Fraction {
type Output = Self;
fn mul(self, other: Self) -> Self {
Fraction::new(self.num * other.num, self.den * other.den)
}
}
impl Div for Fraction {
type Output = Self;
fn div(self, other: Self) -> Self {
Fraction::new(self.num * other.den, self.den * other.num)
}
}
impl Sum for Fraction {
fn sum<T>(iter: T) -> Self where T: Iterator<Item = Self> {
let mut result = Fraction::new(0, 1);
for item in iter { result = result + item; }
return result;
}
}
macro_rules! impl_mul_div_with_frac_for {
($($ty:ty)*) => {$(
impl Mul<$ty> for Fraction {
type Output = Self;
fn mul(self, other: $ty) -> Self::Output {
Fraction::new(self.num * other as i128, self.den)
}
}
impl Mul<Fraction> for $ty {
type Output = Fraction;
fn mul(self, other: Fraction) -> Self::Output {
Fraction::new(self as i128 * other.num, other.den)
}
}
impl Div<$ty> for Fraction {
type Output = Self;
fn div(self, other: $ty) -> Self::Output {
Fraction::new(self.num, self.den * other as i128)
}
}
impl Div<Fraction> for $ty {
type Output = Fraction;
fn div(self, other: Fraction) -> Self::Output {
Fraction::new(self as i128 * other.den, other.num)
}
}
)*}
}
impl_mul_div_with_frac_for!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
impl From<i128> for Fraction {
fn from(item: i128) -> Self {
Fraction::new(item, 1)
}
}
impl Into<f64> for Fraction {
fn into(self) -> f64 {
(self.num as f64) / (self.den as f64)
}
}