Big refactoring, mul not yet working 100%

This commit is contained in:
Fabian Schmidt 2024-11-26 09:56:20 +01:00
parent e0fce27917
commit 23a96d5bee
7 changed files with 493 additions and 132 deletions

View File

@ -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)] #[derive(Debug, PartialEq, Eq)]
pub struct Fraction { pub struct Fraction {
pub numerator: Number, pub numerator: Number,
pub denominator: Number, pub denominator: Number,
pub sign: Sign,
} }
pub struct Rational {} pub struct Rational {
pub decimal: Number,
pub mantissa: Number,
pub recurring: Number,
}
impl Fraction { impl Fraction {
pub fn new(numerator: Number, denominator: Number) -> Result<Self, Box<dyn Error>> { pub fn new(numerator: Number, denominator: Number) -> Result<Self, Box<dyn Error>> {
if denominator == 0.into() { if denominator == 0.into() {
return Err(Box::from("Division by 0")); 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 { let mut f = Fraction {
numerator, numerator,
denominator, denominator,
sign,
}; };
f.reduce(); f.reduce();
Ok(f) Ok(f)
@ -31,10 +50,78 @@ impl Fraction {
self.numerator = self.numerator.clone() / gcd.clone(); self.numerator = self.numerator.clone() / gcd.clone();
self.denominator = self.denominator.clone() / gcd.clone(); self.denominator = self.denominator.clone() / gcd.clone();
} }
pub fn get_full_mantissa(fraction: &Fraction) -> Option<Number> {
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)] #[cfg(test)]
mod test { mod test {
use crate::number::Sign;
use super::Fraction; use super::Fraction;
#[test] #[test]
@ -44,7 +131,8 @@ mod test {
f, f,
Fraction { Fraction {
numerator: 4.into(), numerator: 4.into(),
denominator: 3.into() denominator: 3.into(),
sign: Sign::Positif
} }
); );
} }

View File

@ -5,10 +5,9 @@ mod mul;
mod ord; mod ord;
mod sub; mod sub;
use std::cmp::Ordering;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[derive(Clone, Debug, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Sign { pub enum Sign {
Positif, Positif,
Negatif, Negatif,
@ -32,85 +31,6 @@ impl Number {
(b as isize).wrapping_sub('0' as isize) (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 { pub fn pow(self, n: u32) -> Self {
let mut result = self.clone(); let mut result = self.clone();
if ((self.digits.len() * 8) as u32) < isize::BITS { if ((self.digits.len() * 8) as u32) < isize::BITS {
@ -158,6 +78,7 @@ impl Display for Number {
let number_string = self let number_string = self
.digits .digits
.iter() .iter()
.rev()
.map(|&digit| digit.to_string()) .map(|&digit| digit.to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(""); .join("");

View File

@ -18,21 +18,76 @@ impl Add for Number {
let difference = (self_len).abs_diff(rhs_len); let difference = (self_len).abs_diff(rhs_len);
let pad = vec![0isize; difference]; let pad = vec![0isize; difference];
if min(self_len, rhs_len) == self_len { if min(self_len, rhs_len) == self_len {
self_digits = [pad, self.digits].concat(); self_digits = [self.digits, pad].concat();
} else { } else {
rhs_digits = [pad, rhs.digits].concat(); rhs_digits = [rhs.digits, pad].concat();
} }
} }
let zipped = zip(self_digits.iter(), rhs_digits.iter()); let zipped = zip(self_digits.iter(), rhs_digits.iter());
let added = zipped let mut carry = 0;
.map(|(self_digit, rhs_digit)| self_digit + rhs_digit) let mut digits = Vec::new();
.collect(); let mut sign = match (self.sign, rhs.sign) {
let mut overflown_number = Self { (Sign::Positif, Sign::Positif) => Sign::Positif,
digits: added, (Sign::Negatif, Sign::Negatif) => Sign::Negatif,
sign: Sign::Positif, (Sign::Positif, Sign::Negatif) | (Sign::Negatif, Sign::Positif) => Sign::Positif,
}; };
overflown_number.handle_overflows(); for (a, b) in zipped {
overflown_number 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; 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);
}
}

View File

@ -44,7 +44,6 @@ impl Number {
digits: quotient, digits: quotient,
sign: Sign::Positif, sign: Sign::Positif,
}; };
res.handle_overflows();
for digit in res.clone().digits { for digit in res.clone().digits {
if digit != 0 || res.digits.len() == 1 { if digit != 0 || res.digits.len() == 1 {
break; break;

View File

@ -7,7 +7,7 @@ impl TryFrom<Number> for isize {
fn try_from(value: Number) -> Result<Self, Self::Error> { fn try_from(value: Number) -> Result<Self, Self::Error> {
let mut num = 0; 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)); let mul = digit.checked_mul(10isize.pow(pos as u32));
if mul.is_none() { if mul.is_none() {
return Err(Box::from("Cannot convert Number to isize. Too big.")); return Err(Box::from("Cannot convert Number to isize. Too big."));
@ -23,6 +23,7 @@ impl From<Number> for String {
let string_vec: Vec<String> = value let string_vec: Vec<String> = value
.digits .digits
.iter() .iter()
.rev()
.map(|&digit| digit.to_string()) .map(|&digit| digit.to_string())
.collect(); .collect();
string_vec.concat() string_vec.concat()
@ -37,7 +38,7 @@ impl From<&str> for Number {
_ => (Sign::Positif, 0), _ => (Sign::Positif, 0),
}; };
let mut digits = vec![]; 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); let digit = Self::byte_to_digit(byte);
digits.push(digit); digits.push(digit);
} }
@ -64,7 +65,7 @@ impl From<isize> for Number {
let digit = Self::get_digit(value, digit_idx); let digit = Self::get_digit(value, digit_idx);
digits.push(digit); digits.push(digit);
} }
let digits = digits.iter().rev().copied().collect(); let digits = digits.to_vec();
Self { digits, sign } Self { digits, sign }
} }
} }
@ -79,7 +80,7 @@ mod test_number_from {
assert_eq!( assert_eq!(
number, number,
Number { Number {
digits: vec![1, 2, 3, 4], digits: vec![4, 3, 2, 1],
sign: Sign::Negatif 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
}
);
}
} }

View File

@ -5,15 +5,44 @@ use super::{Number, Sign};
impl Mul for Number { impl Mul for Number {
type Output = Self; type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
fn mul(self, rhs: Self) -> Self::Output { fn mul(self, rhs: Self) -> Self::Output {
let multiplied = self.digits.iter().rev().enumerate().map(|(pos, &digit)| { println!("left {self:#?}");
let mut mult = digit * rhs.clone(); println!("right {rhs:#?}");
mult.digits = [mult.digits, vec![0; pos]].concat(); let mut mult_vecs = Vec::new();
mult let sign = match (self.sign, rhs.sign) {
}); (Sign::Positif, Sign::Positif) => Sign::Positif,
let mut overflown_number = multiplied.reduce(|acc, num| acc + num).unwrap(); (Sign::Positif, Sign::Negatif) => Sign::Negatif,
overflown_number.handle_overflows(); (Sign::Negatif, Sign::Positif) => Sign::Negatif,
overflown_number (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<isize> = 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<Number> for isize {
type Output = Number; type Output = Number;
fn mul(self, rhs: Number) -> Self::Output { fn mul(self, rhs: Number) -> Self::Output {
let multiplied = rhs.digits.iter().map(|digit| digit * self).collect(); Number::from(self) * rhs
let mut overflown_number = Number {
digits: multiplied,
sign: Sign::Positif,
};
overflown_number.handle_overflows();
overflown_number
} }
} }
@ -35,13 +58,7 @@ impl Mul<isize> for Number {
type Output = Self; type Output = Self;
fn mul(self, rhs: isize) -> Self::Output { fn mul(self, rhs: isize) -> Self::Output {
let multiplied = self.digits.iter().map(|digit| digit * rhs).collect(); self * Number::from(rhs)
let mut overflown_number = Self {
digits: multiplied,
sign: Sign::Positif,
};
overflown_number.handle_overflows();
overflown_number
} }
} }
@ -52,3 +69,64 @@ impl MulAssign for Number {
self.sign = new.sign; 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);
}
}

View File

@ -18,21 +18,75 @@ impl Sub for Number {
let difference = (self_len).abs_diff(rhs_len); let difference = (self_len).abs_diff(rhs_len);
let pad = vec![0isize; difference]; let pad = vec![0isize; difference];
if min(self_len, rhs_len) == self_len { if min(self_len, rhs_len) == self_len {
self_digits = [pad, self.digits].concat(); self_digits = [self.digits, pad].concat();
} else { } else {
rhs_digits = [pad, rhs.digits].concat(); rhs_digits = [rhs.digits, pad].concat();
} }
} }
let zipped = zip(self_digits.iter(), rhs_digits.iter()); let zipped = zip(self_digits.iter(), rhs_digits.iter());
let added = zipped let mut carry = 0;
.map(|(self_digit, rhs_digit)| self_digit - rhs_digit) let mut digits = Vec::new();
.collect(); let mut sign = match (self.sign, rhs.sign) {
let mut underflown_number = Self { (Sign::Positif, Sign::Negatif) => Sign::Positif,
digits: added, (Sign::Negatif, Sign::Positif) => Sign::Negatif,
sign: Sign::Positif, (Sign::Positif, Sign::Positif) | (Sign::Negatif, Sign::Negatif) => Sign::Positif,
}; };
underflown_number.handle_underflows(); for (a, b) in zipped {
underflown_number 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; 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);
}
}