use crate::big_number::BigNum;
use std::cmp::Ordering;
use std::mem::swap;
use std::{fmt, ops};
#[derive(PartialEq, Clone)]
pub struct Num {
up: BigNum,
down: BigNum,
}
impl Num {
pub fn new(up: isize, down: usize) -> Num {
let mut res = Num {
up: BigNum::new(up),
down: BigNum::new(down as isize),
};
res.optimize();
res
}
pub fn from_num(n: isize) -> Num {
Num {
up: BigNum::new(n),
down: BigNum::one(),
}
}
pub fn from_big_num(up: BigNum, down: BigNum) -> Num {
let mut res = Num { up, down };
res.optimize();
res
}
pub fn zero() -> Num {
Num {
up: BigNum::zero(),
down: BigNum::one(),
}
}
pub fn one() -> Num {
Num {
up: BigNum::one(),
down: BigNum::one(),
}
}
pub fn nan() -> Num {
Num {
up: BigNum::one(),
down: BigNum::zero(),
}
}
pub fn floor(&self) -> BigNum {
if self.down == BigNum::one() {
self.up.clone()
} else {
&self.up / &self.down
}
}
pub fn is_pos(&self) -> bool {
self.up.is_pos() && !self.is_nan()
}
pub fn is_nan(&self) -> bool {
self.down.is_zero()
}
pub fn from_string(mut s: String) -> Num {
if s == "너무 커엇...".to_string() {
Num::nan()
} else {
let neg = s.starts_with('-');
if neg {
s = s[1..].to_string();
}
let v = s.split('/').map(|x| x.to_string()).collect::<Vec<_>>();
let mut res = if v.len() == 1 {
Num::from_big_num(BigNum::from_string(v[0].clone()).unwrap(), BigNum::one())
} else {
Num::from_big_num(
BigNum::from_string(v[0].clone()).unwrap(),
BigNum::from_string(v[1].clone()).unwrap(),
)
};
if neg {
res.minus();
}
res
}
}
pub fn to_string(&self) -> String {
if self.is_nan() {
format!("너무 커엇...")
} else {
if self.down == BigNum::one() {
format!("{}", self.up.to_string())
} else {
format!("{}/{}", self.up.to_string(), self.down.to_string())
}
}
}
fn optimize(&mut self) {
let g = BigNum::gcd(&self.up, &self.down);
self.up /= &g;
self.down /= &g;
}
pub fn minus(&mut self) {
self.up.minus();
}
pub fn flip(&mut self) {
if !self.is_nan() {
swap(&mut self.up, &mut self.down);
if !self.down.is_pos() {
self.down.minus();
self.up.minus();
}
}
}
pub fn add(lhs: &Num, rhs: &Num) -> Num {
if lhs.is_nan() || rhs.is_nan() {
return Num::nan();
}
let mut res = Num {
up: &(&lhs.up * &rhs.down) + &(&lhs.down * &rhs.up),
down: &lhs.down * &rhs.down,
};
res.optimize();
res
}
pub fn mul(lhs: &Num, rhs: &Num) -> Num {
if lhs.is_nan() || rhs.is_nan() {
return Num::nan();
}
let mut res = Num {
up: &lhs.up * &rhs.up,
down: &lhs.down * &rhs.down,
};
res.optimize();
res
}
pub fn neg(v: &Num) -> Num {
Num {
up: (-&v.up).clone(),
down: (&v.down).clone(),
}
}
pub fn set_copy(&mut self, rhs: &Num) {
self.up.set_copy(&rhs.up);
self.down.set_copy(&rhs.down);
}
pub fn set_move(&mut self, rhs: Num) {
self.up.set_move(rhs.up);
self.down.set_move(rhs.down);
}
}
impl PartialOrd for Num {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.is_nan() || other.is_nan() {
Option::None
} else {
if self == other {
Option::Some(Ordering::Equal)
} else if &self.up * &other.down < &self.down * &other.down {
Option::Some(Ordering::Less)
} else {
Option::Some(Ordering::Greater)
}
}
}
}
impl fmt::Debug for Num {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl fmt::Display for Num {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl ops::Add<&Num> for &Num {
type Output = Num;
fn add(self, rhs: &Num) -> Self::Output {
Num::add(self, rhs)
}
}
impl ops::AddAssign<&Num> for Num {
fn add_assign(&mut self, rhs: &Num) {
self.set_move(&*self + rhs);
}
}
impl ops::Mul<&Num> for &Num {
type Output = Num;
fn mul(self, rhs: &Num) -> Self::Output {
Num::mul(self, rhs)
}
}
impl ops::MulAssign<&Num> for Num {
fn mul_assign(&mut self, rhs: &Num) {
self.set_move(&*self * rhs);
}
}
impl ops::Neg for &Num {
type Output = Num;
fn neg(self) -> Self::Output {
Num::neg(self)
}
}