From 23a96d5bee18a4555f98cebb2abd10a6074d0f20 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Tue, 26 Nov 2024 09:56:20 +0100 Subject: [PATCH] Big refactoring, mul not yet working 100% --- src/fraction.rs | 96 +++++++++++++++++++++++++-- src/number.rs | 83 +---------------------- src/number/add.rs | 160 ++++++++++++++++++++++++++++++++++++++++++--- src/number/div.rs | 1 - src/number/from.rs | 20 ++++-- src/number/mul.rs | 122 +++++++++++++++++++++++++++------- src/number/sub.rs | 143 +++++++++++++++++++++++++++++++++++++--- 7 files changed, 493 insertions(+), 132 deletions(-) diff --git a/src/fraction.rs b/src/fraction.rs index a5fc803..b3ed0d8 100644 --- a/src/fraction.rs +++ b/src/fraction.rs @@ -1,23 +1,42 @@ -use std::error::Error; +use std::{ + collections::{HashMap, HashSet}, + error::Error, +}; -use crate::number::Number; +use crate::number::{Number, Sign}; #[derive(Debug, PartialEq, Eq)] pub struct Fraction { pub numerator: Number, pub denominator: Number, + pub sign: Sign, } -pub struct Rational {} +pub struct Rational { + pub decimal: Number, + pub mantissa: Number, + pub recurring: Number, +} impl Fraction { pub fn new(numerator: Number, denominator: Number) -> Result> { if denominator == 0.into() { return Err(Box::from("Division by 0")); } + let sign = match numerator.sign { + Sign::Positif => match denominator.sign { + Sign::Positif => Sign::Positif, + Sign::Negatif => Sign::Negatif, + }, + Sign::Negatif => match denominator.sign { + Sign::Positif => Sign::Negatif, + Sign::Negatif => Sign::Positif, + }, + }; let mut f = Fraction { numerator, denominator, + sign, }; f.reduce(); Ok(f) @@ -31,10 +50,78 @@ impl Fraction { self.numerator = self.numerator.clone() / gcd.clone(); self.denominator = self.denominator.clone() / gcd.clone(); } + + pub fn get_full_mantissa(fraction: &Fraction) -> Option { + let mut div_memory = HashMap::new(); + let mut rem_memory = HashSet::new(); + rem_memory.insert(1.into()); + let mut div_with = fraction.numerator.clone(); + let mut rational = Vec::new(); + if div_with < fraction.denominator { + div_with = div_with * 10; + } + loop { + while div_with < fraction.denominator { + rational.push(0); + div_memory + .entry(0) + .and_modify(|value| *value += 1) + .or_insert(1); + div_with = div_with * 10; + rem_memory.insert(div_with.clone()); + } + let (div, rem) = + Number::div_with_rem(div_with.clone(), fraction.denominator.clone()).unwrap(); + if rem == 0.into() { + return None; + } + let next_digit = div.digits[0]; + if div_memory.contains_key(&next_digit) && rem_memory.contains(&rem) && next_digit != 0 + { + let mut digits = vec![]; + if div_memory.values().min() != div_memory.values().max() { + rational.push(div.digits[0]); + digits = rational; + let (l, r) = digits.split_at(digits.len() / 2); + if l == r { + digits = l.to_vec(); + } + } else { + let idx = rational + .iter() + .position(|digit| digit == &div.digits[0]) + .unwrap(); + if idx == rational.len() - 1 { + digits.push(div.digits[0]); + } else { + for digit in &rational[idx..] { + if digits.contains(digit) { + break; + } + digits.push(*digit); + } + } + } + return Some(Number { + digits, + sign: Sign::Positif, + }); + } + div_memory + .entry(next_digit) + .and_modify(|count| *count += 1) + .or_insert(1); + rem_memory.insert(rem.clone()); + rational.push(div.digits[0]); + div_with = rem * 10; + } + } } #[cfg(test)] mod test { + use crate::number::Sign; + use super::Fraction; #[test] @@ -44,7 +131,8 @@ mod test { f, Fraction { numerator: 4.into(), - denominator: 3.into() + denominator: 3.into(), + sign: Sign::Positif } ); } diff --git a/src/number.rs b/src/number.rs index 07dd308..21fc589 100644 --- a/src/number.rs +++ b/src/number.rs @@ -5,10 +5,9 @@ mod mul; mod ord; mod sub; -use std::cmp::Ordering; use std::fmt::{Display, Formatter}; -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub enum Sign { Positif, Negatif, @@ -32,85 +31,6 @@ impl Number { (b as isize).wrapping_sub('0' as isize) } - fn handle_overflows(&mut self) { - let new_digits = &mut self.digits; - let digits_len = new_digits.len(); - let mut digits_idx = digits_len - 1; - loop { - let digit_or_num = new_digits[digits_idx]; - let digit_len = if digit_or_num != 0 { - (digit_or_num.abs() as f64 + 1.0).log10().ceil() as usize - } else { - 1 - }; - for i in 0..digit_len { - let new_digit = Self::get_digit(digit_or_num, i); - let (digit_idx, is_overflow) = digits_idx.overflowing_sub(i); - if is_overflow { - new_digits.insert(0, new_digit); - digits_idx += 1; - } else { - let digit = new_digits.get_mut(digit_idx).unwrap(); - if i == 0 { - *digit = new_digit; - } else { - *digit += new_digit; - } - } - } - if digits_idx == 0 { - break; - } - digits_idx -= 1; - } - } - - fn handle_underflows(&mut self) { - let new_digits = &mut self.digits; - let mut digits_len = new_digits.len(); - for digit in new_digits.clone() { - match digit.cmp(&0) { - Ordering::Equal => { - if digits_len == 1 { - return; - } - digits_len -= 1; - new_digits.remove(0); - } - Ordering::Less => { - self.sign = Sign::Negatif; - break; - } - _ => break, - }; - } - let mut digits_idx = digits_len - 1; - loop { - let digit = new_digits[digits_idx]; - if self.sign == Sign::Positif && digit < 0 && digits_idx > 0 { - let mut_digit = new_digits.get_mut(digits_idx).unwrap(); - *mut_digit = 10 - digit.abs(); - let mut_digit = new_digits.get_mut(digits_idx - 1).unwrap(); - *mut_digit -= 1; - } else { - let mut_digit = new_digits.get_mut(digits_idx).unwrap(); - *mut_digit = digit.abs(); - } - if digits_idx == 0 { - break; - } - digits_idx -= 1; - } - for digit in new_digits.clone() { - match digit.cmp(&0) { - Ordering::Equal => { - new_digits.remove(0); - } - _ => break, - }; - } - } - pub fn pow(self, n: u32) -> Self { let mut result = self.clone(); if ((self.digits.len() * 8) as u32) < isize::BITS { @@ -158,6 +78,7 @@ impl Display for Number { let number_string = self .digits .iter() + .rev() .map(|&digit| digit.to_string()) .collect::>() .join(""); diff --git a/src/number/add.rs b/src/number/add.rs index 311b1c3..0f4d0ea 100644 --- a/src/number/add.rs +++ b/src/number/add.rs @@ -18,21 +18,76 @@ impl Add for Number { let difference = (self_len).abs_diff(rhs_len); let pad = vec![0isize; difference]; if min(self_len, rhs_len) == self_len { - self_digits = [pad, self.digits].concat(); + self_digits = [self.digits, pad].concat(); } else { - rhs_digits = [pad, rhs.digits].concat(); + rhs_digits = [rhs.digits, pad].concat(); } } let zipped = zip(self_digits.iter(), rhs_digits.iter()); - let added = zipped - .map(|(self_digit, rhs_digit)| self_digit + rhs_digit) - .collect(); - let mut overflown_number = Self { - digits: added, - sign: Sign::Positif, + let mut carry = 0; + let mut digits = Vec::new(); + let mut sign = match (self.sign, rhs.sign) { + (Sign::Positif, Sign::Positif) => Sign::Positif, + (Sign::Negatif, Sign::Negatif) => Sign::Negatif, + (Sign::Positif, Sign::Negatif) | (Sign::Negatif, Sign::Positif) => Sign::Positif, }; - overflown_number.handle_overflows(); - overflown_number + for (a, b) in zipped { + let comb = match (self.sign, rhs.sign) { + (Sign::Positif, Sign::Positif) => { + let comb = a + b + carry; + if comb > 9 { + carry = 1; + } else { + carry = 0; + } + comb % 10 + } + (Sign::Positif, Sign::Negatif) => { + let comb = a - b - carry; + if comb > 0 && b > a { + carry = 1; + } else { + carry = 0; + } + if b > a { + sign = Sign::Negatif; + } else { + sign = Sign::Positif; + } + (comb % 10).abs() + } + (Sign::Negatif, Sign::Positif) => { + let comb = -a + b - carry; + if comb > 0 && a > b { + carry = 1; + } else { + carry = 0; + } + if a > b { + sign = Sign::Negatif; + } else { + sign = Sign::Positif; + } + (comb % 10).abs() + } + (Sign::Negatif, Sign::Negatif) => { + let comb = a + b + carry; + if comb > 9 { + carry = 1; + } else { + carry = 0; + } + comb % 10 + } + }; + digits.push(comb); + } + + if carry != 0 { + digits.push(carry); + } + + Self { digits, sign } } } @@ -43,3 +98,88 @@ impl AddAssign for Number { self.sign = new.sign; } } + +#[cfg(test)] +mod test_number_add { + use crate::number::Number; + + #[test] + fn add_positif() { + let a = Number::from(1); + let b = 1.into(); + let res = Number::from(2); + assert_eq!(res, a + b); + } + + #[test] + fn add_large_positif() { + let a = Number::from(9); + let b = 9.into(); + let res = Number::from(18); + assert_eq!(res, a + b); + } + + #[test] + fn add_negatif() { + let a = Number::from(-1); + let b = (-1).into(); + let res = Number::from(-2); + assert_eq!(res, a + b); + } + + #[test] + fn add_large_negatif() { + let a = Number::from(-9); + let b = (-9).into(); + let res = Number::from(-18); + assert_eq!(res, a + b); + } + + #[test] + fn add_negatif_to_positif() { + let a = Number::from(1); + let b = (-1).into(); + let res = Number::from(0); + assert_eq!(res, a + b); + } + + #[test] + fn add_positif_to_negatif() { + let a = Number::from(-1); + let b = (1).into(); + let res = Number::from(0); + assert_eq!(res, a + b); + } + + #[test] + fn add_larger_positif_to_negatif() { + let a = Number::from(-1); + let b = (11).into(); + let res = Number::from(10); + assert_eq!(res, a + b); + } + + #[test] + fn add_larger_negatif_to_positif() { + let a = Number::from(1); + let b = (-11).into(); + let res = Number::from(-10); + assert_eq!(res, a + b); + } + + #[test] + fn add_positif_to_larger_negatif() { + let a = Number::from(-99); + let b = (11).into(); + let res = Number::from(-88); + assert_eq!(res, a + b); + } + + #[test] + fn add_negatif_to_larger_positif() { + let a = Number::from(99); + let b = (-11).into(); + let res = Number::from(88); + assert_eq!(res, a + b); + } +} diff --git a/src/number/div.rs b/src/number/div.rs index d6d6c31..b74086b 100644 --- a/src/number/div.rs +++ b/src/number/div.rs @@ -44,7 +44,6 @@ impl Number { digits: quotient, sign: Sign::Positif, }; - res.handle_overflows(); for digit in res.clone().digits { if digit != 0 || res.digits.len() == 1 { break; diff --git a/src/number/from.rs b/src/number/from.rs index db95ef7..39ac26a 100644 --- a/src/number/from.rs +++ b/src/number/from.rs @@ -7,7 +7,7 @@ impl TryFrom for isize { fn try_from(value: Number) -> Result { let mut num = 0; - for (pos, &digit) in value.digits.iter().rev().enumerate() { + for (pos, &digit) in value.digits.iter().enumerate() { let mul = digit.checked_mul(10isize.pow(pos as u32)); if mul.is_none() { return Err(Box::from("Cannot convert Number to isize. Too big.")); @@ -23,6 +23,7 @@ impl From for String { let string_vec: Vec = value .digits .iter() + .rev() .map(|&digit| digit.to_string()) .collect(); string_vec.concat() @@ -37,7 +38,7 @@ impl From<&str> for Number { _ => (Sign::Positif, 0), }; let mut digits = vec![]; - for &byte in &bytes[idx_start..] { + for &byte in bytes[idx_start..].iter().rev() { let digit = Self::byte_to_digit(byte); digits.push(digit); } @@ -64,7 +65,7 @@ impl From for Number { let digit = Self::get_digit(value, digit_idx); digits.push(digit); } - let digits = digits.iter().rev().copied().collect(); + let digits = digits.to_vec(); Self { digits, sign } } } @@ -79,7 +80,7 @@ mod test_number_from { assert_eq!( number, Number { - digits: vec![1, 2, 3, 4], + digits: vec![4, 3, 2, 1], sign: Sign::Negatif } ); @@ -96,4 +97,15 @@ mod test_number_from { } ); } + #[test] + fn test_from_str() { + let number = Number::from("-1234"); + assert_eq!( + number, + Number { + digits: vec![4, 3, 2, 1], + sign: Sign::Negatif + } + ); + } } diff --git a/src/number/mul.rs b/src/number/mul.rs index 08bb400..45661db 100644 --- a/src/number/mul.rs +++ b/src/number/mul.rs @@ -5,15 +5,44 @@ use super::{Number, Sign}; impl Mul for Number { type Output = Self; + #[allow(clippy::suspicious_arithmetic_impl)] fn mul(self, rhs: Self) -> Self::Output { - let multiplied = self.digits.iter().rev().enumerate().map(|(pos, &digit)| { - let mut mult = digit * rhs.clone(); - mult.digits = [mult.digits, vec![0; pos]].concat(); - mult - }); - let mut overflown_number = multiplied.reduce(|acc, num| acc + num).unwrap(); - overflown_number.handle_overflows(); - overflown_number + println!("left {self:#?}"); + println!("right {rhs:#?}"); + let mut mult_vecs = Vec::new(); + let sign = match (self.sign, rhs.sign) { + (Sign::Positif, Sign::Positif) => Sign::Positif, + (Sign::Positif, Sign::Negatif) => Sign::Negatif, + (Sign::Negatif, Sign::Positif) => Sign::Negatif, + (Sign::Negatif, Sign::Negatif) => Sign::Positif, + }; + for (idx, rdigit) in rhs.digits.iter().enumerate() { + let rdigit = rdigit * 10_isize.pow(idx as u32); + let mult_vec: Vec = self.digits.iter().map(|ldigit| ldigit * rdigit).collect(); + let mut normalized_mult_vec = Vec::new(); + let mut carry = 0; + let mut add_zero = true; + mult_vec.into_iter().for_each(|digit| { + let digit = digit + carry; + if digit > 9 { + carry = digit % 10; + normalized_mult_vec.insert(0, digit / 10); + } else { + normalized_mult_vec.insert(0, digit); + carry = 0; + add_zero = false; + } + }); + if carry != 0 || add_zero { + normalized_mult_vec.insert(0, carry); + } + mult_vecs.push(Number { + digits: normalized_mult_vec, + sign, + }); + } + + mult_vecs.into_iter().reduce(|acc, num| acc + num).unwrap() } } @@ -21,13 +50,7 @@ impl Mul for isize { type Output = Number; fn mul(self, rhs: Number) -> Self::Output { - let multiplied = rhs.digits.iter().map(|digit| digit * self).collect(); - let mut overflown_number = Number { - digits: multiplied, - sign: Sign::Positif, - }; - overflown_number.handle_overflows(); - overflown_number + Number::from(self) * rhs } } @@ -35,13 +58,7 @@ impl Mul for Number { type Output = Self; fn mul(self, rhs: isize) -> Self::Output { - let multiplied = self.digits.iter().map(|digit| digit * rhs).collect(); - let mut overflown_number = Self { - digits: multiplied, - sign: Sign::Positif, - }; - overflown_number.handle_overflows(); - overflown_number + self * Number::from(rhs) } } @@ -52,3 +69,64 @@ impl MulAssign for Number { self.sign = new.sign; } } + +#[cfg(test)] +mod test_number_mul { + use crate::number::Number; + + #[test] + fn mul_positif() { + let a = Number::from(9); + let b = Number::from(9); + let res = Number::from(81); + assert_eq!(res, a * b); + } + + #[test] + fn mul_negatif() { + let a = Number::from(-9); + let b = Number::from(-9); + let res = Number::from(81); + assert_eq!(res, a * b); + } + + #[test] + fn mul_positif_with_negatif() { + let a = Number::from(9); + let b = Number::from(-9); + let res = Number::from(-81); + assert_eq!(res, a * b); + } + + #[test] + fn mul_negatif_with_positif() { + let a = Number::from(-9); + let b = Number::from(9); + let res = Number::from(-81); + assert_eq!(res, a * b); + } + + #[test] + fn mul_eleven_x_ten() { + let a = Number::from(11); + let b = Number::from(10); + let res = Number::from(110); + assert_eq!(res, a * b); + } + + #[test] + fn mul_eleven_x_eleven() { + let a = Number::from(11); + let b = Number::from(11); + let res = Number::from(121); + assert_eq!(res, a * b); + } + + #[test] + fn mul_big() { + let a = Number::from("123456789"); + let b = Number::from("987654321"); + let res = Number::from("121932631112635269"); + assert_eq!(res, a * b); + } +} diff --git a/src/number/sub.rs b/src/number/sub.rs index 970e7ca..86e02de 100644 --- a/src/number/sub.rs +++ b/src/number/sub.rs @@ -18,21 +18,75 @@ impl Sub for Number { let difference = (self_len).abs_diff(rhs_len); let pad = vec![0isize; difference]; if min(self_len, rhs_len) == self_len { - self_digits = [pad, self.digits].concat(); + self_digits = [self.digits, pad].concat(); } else { - rhs_digits = [pad, rhs.digits].concat(); + rhs_digits = [rhs.digits, pad].concat(); } } let zipped = zip(self_digits.iter(), rhs_digits.iter()); - let added = zipped - .map(|(self_digit, rhs_digit)| self_digit - rhs_digit) - .collect(); - let mut underflown_number = Self { - digits: added, - sign: Sign::Positif, + let mut carry = 0; + let mut digits = Vec::new(); + let mut sign = match (self.sign, rhs.sign) { + (Sign::Positif, Sign::Negatif) => Sign::Positif, + (Sign::Negatif, Sign::Positif) => Sign::Negatif, + (Sign::Positif, Sign::Positif) | (Sign::Negatif, Sign::Negatif) => Sign::Positif, }; - underflown_number.handle_underflows(); - underflown_number + for (a, b) in zipped { + let comb = match (self.sign, rhs.sign) { + (Sign::Positif, Sign::Positif) => { + let comb = a - b - carry; + if comb < 0 { + carry = 1; + } else { + carry = 0; + } + if b > a { + sign = Sign::Negatif; + } else { + sign = Sign::Positif; + } + (comb % 10).abs() + } + (Sign::Positif, Sign::Negatif) => { + let comb = a + b + carry; + if comb > 9 { + carry = 1; + } else { + carry = 0; + } + comb % 10 + } + (Sign::Negatif, Sign::Positif) => { + let comb = -a - b - carry; + if comb < -9 { + carry = 1; + } else { + carry = 0; + } + (comb % 10).abs() + } + (Sign::Negatif, Sign::Negatif) => { + let comb = -a + b + carry; + if comb > 10 { + carry = 1; + } else { + carry = 0; + } + if a > b { + sign = Sign::Negatif; + } else { + sign = Sign::Positif; + } + comb % 10 + } + }; + digits.push(comb); + } + if carry != 0 { + digits.push(carry); + } + + Self { digits, sign } } } @@ -43,3 +97,72 @@ impl SubAssign for Number { self.sign = new.sign; } } + +#[cfg(test)] +mod test_number_sub { + use crate::number::Number; + + #[test] + fn sub_positif() { + let a = Number::from(1); + let b = 1.into(); + let res = Number::from(0); + assert_eq!(res, a - b); + } + + #[test] + fn sub_negatif() { + let a = Number::from(-1); + let b = (-1).into(); + let res = Number::from(0); + assert_eq!(res, a - b); + } + + #[test] + fn sub_negatif_from_positif() { + let a = Number::from(1); + let b = (-1).into(); + let res = Number::from(2); + assert_eq!(res, a - b); + } + + #[test] + fn sub_positif_from_negatif() { + let a = Number::from(-1); + let b = (1).into(); + let res = Number::from(-2); + assert_eq!(res, a - b); + } + + #[test] + fn sub_larger_positif_from_negatif() { + let a = Number::from(-1); + let b = (11).into(); + let res = Number::from(-12); + assert_eq!(res, a - b); + } + + #[test] + fn sub_larger_negatif_from_positif() { + let a = Number::from(1); + let b = (-11).into(); + let res = Number::from(12); + assert_eq!(res, a - b); + } + + #[test] + fn sub_positif_from_larger_negatif() { + let a = Number::from(-99); + let b = (11).into(); + let res = Number::from(-110); + assert_eq!(res, a - b); + } + + #[test] + fn sub_negatif_from_larger_positif() { + let a = Number::from(99); + let b = (-11).into(); + let res = Number::from(110); + assert_eq!(res, a - b); + } +}