Compare commits

...

2 commits

Author SHA1 Message Date
61fca40b68 Added support for big numbers 2025-01-02 21:19:44 +01:00
4487ec9c86 Added rest to base algebra 2025-01-02 21:19:11 +01:00
10 changed files with 225 additions and 180 deletions

View file

@ -6,6 +6,6 @@ edition = "2021"
[dependencies]
[features]
default = []
default = ["algebra"]
algebra = []
big_numbers = []
big_numbers = ["algebra"]

View file

@ -2,4 +2,5 @@ pub mod addition;
pub mod subtraction;
pub mod multiplication;
pub mod division;
pub mod power;
pub mod power;
pub mod rest;

3
src/algebra/rest.rs Normal file
View file

@ -0,0 +1,3 @@
pub fn rest(x: f64, y: f64) -> f64 {
x % y
}

View file

@ -1,154 +1 @@
#[derive(Debug, Clone)]
pub struct BigDecimal {
integer_part: Vec<u8>,
fractional_part: Vec<u8>,
}
impl BigDecimal {
pub fn new(value: f64, precision: usize) -> Self {
let integer_part = (value.trunc() as u64)
.to_string()
.chars()
.map(|c| c.to_digit(10).unwrap() as u8)
.collect::<Vec<u8>>();
let mut fractional_part = Vec::new();
let mut fractional_value = value.fract();
for _ in 0..precision {
fractional_value *= 10.0;
let digit = fractional_value.trunc() as u8;
fractional_part.push(digit);
fractional_value -= fractional_value.trunc();
}
BigDecimal {
integer_part,
fractional_part,
}
}
pub fn from_str(s: &str, precision: usize) -> Result<Self, String> {
let mut parts = s.split('.');
let integer_part_str = parts.next().unwrap_or("");
let fractional_part_str = parts.next().unwrap_or("");
let integer_part = integer_part_str
.chars()
.map(|c| c.to_digit(10).ok_or("Non è un numero valido"))
.collect::<Result<Vec<u8>, &str>>()?;
let mut fractional_part = Vec::new();
for (i, c) in fractional_part_str.chars().enumerate() {
if i >= precision {
break;
}
let digit = c.to_digit(10).ok_or("Non è un numero valido")?;
fractional_part.push(digit as u8);
}
Ok(BigDecimal {
integer_part,
fractional_part,
})
}
pub fn to_string(&self) -> String {
let integer_str: String = self
.integer_part
.iter()
.map(|&d| (b'0' + d) as char)
.collect();
let fractional_str: String = self
.fractional_part
.iter()
.map(|&d| (b'0' + d) as char)
.collect();
if !self.fractional_part.is_empty() {
format!("{}.{}", integer_str, fractional_str)
} else {
integer_str
}
}
pub fn add(&self, other: &BigDecimal) -> BigDecimal {
let mut result_integer = Vec::new();
let mut result_fractional = Vec::new();
let mut carry = 0;
let max_fractional_len = self.fractional_part.len().max(other.fractional_part.len());
let mut self_frac = self.fractional_part.clone();
let mut other_frac = other.fractional_part.clone();
while self_frac.len() < max_fractional_len {
self_frac.push(0);
}
while other_frac.len() < max_fractional_len {
other_frac.push(0);
}
for i in (0..max_fractional_len).rev() {
let sum = self_frac[i] + other_frac[i] + carry;
result_fractional.push(sum % 10);
carry = sum / 10;
}
result_fractional.reverse();
let max_integer_len = self.integer_part.len().max(other.integer_part.len());
let mut self_int = self.integer_part.clone();
let mut other_int = other.integer_part.clone();
while self_int.len() < max_integer_len {
self_int.insert(0, 0);
}
while other_int.len() < max_integer_len {
other_int.insert(0, 0);
}
for i in (0..max_integer_len).rev() {
let sum = self_int[i] + other_int[i] + carry;
result_integer.push(sum % 10);
carry = sum / 10;
}
result_integer.reverse();
BigDecimal {
integer_part: result_integer,
fractional_part: result_fractional,
}
}
pub fn multiply(&self, other: &BigDecimal) -> BigDecimal {
let mut result_integer = vec![0u8; self.integer_part.len() + other.integer_part.len()];
let mut result_fractional = vec![0u8; self.fractional_part.len() + other.fractional_part.len()];
for i in (0..self.integer_part.len()).rev() {
let mut carry = 0;
for j in (0..other.integer_part.len()).rev() {
let mul = self.integer_part[i] * other.integer_part[j] + result_integer[i + j + 1] + carry;
result_integer[i + j + 1] = mul % 10;
carry = mul / 10;
}
result_integer[i] = carry;
}
for i in (0..self.fractional_part.len()).rev() {
let mut carry = 0;
for j in (0..other.fractional_part.len()).rev() {
let mul = self.fractional_part[i] * other.fractional_part[j] + result_fractional[i + j + 1] + carry;
result_fractional[i + j + 1] = mul % 10;
carry = mul / 10;
}
result_fractional[i] = carry;
}
BigDecimal {
integer_part: result_integer,
fractional_part: result_fractional,
}
}
}
pub mod big_decimal;

View file

@ -0,0 +1,52 @@
mod sum;
mod sanitize;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct BigDecimal {
pub integer_part: Vec<u8>,
pub fractional_part: Vec<u8>,
}
impl BigDecimal {
pub fn from_str(s: &str) -> Result<Self, String> {
let mut parts = s.split('.');
let integer_part_str = parts.next().unwrap_or("");
let fractional_part_str = parts.next().unwrap_or("");
let integer_part = integer_part_str
.chars()
.map(|c| c.to_digit(10).expect("That's not a valid number (integer part)") as u8)
.collect::<Vec<u8>>();
let mut fractional_part = Vec::new();
for (_i, c) in fractional_part_str.chars().enumerate() {
let digit = c.to_digit(10).ok_or("That's not a valid number (fractional part)")?;
fractional_part.push(digit as u8);
}
Ok(BigDecimal {
integer_part,
fractional_part,
})
}
pub fn to_string(&self) -> String {
let integer_str: String = self
.integer_part
.iter()
.map(|&d| (b'0' + d) as char)
.collect();
let fractional_str: String = self
.fractional_part
.iter()
.map(|&d| (b'0' + d) as char)
.collect();
if !self.fractional_part.is_empty() {
format!("{}.{}", integer_str, fractional_str)
} else {
integer_str
}
}
}

View file

@ -0,0 +1,29 @@
use crate::big_numbers::big_decimal::BigDecimal;
fn remove_zeros(arr: &mut Vec<u8>, from_start: bool) {
if from_start {
while arr.first() == Some(&0) {
arr.remove(0);
}
} else {
while arr.last() == Some(&0) {
arr.pop();
}
}
}
pub fn sanitize(mut x: BigDecimal) -> BigDecimal {
remove_zeros(&mut x.integer_part, true);
remove_zeros(&mut x.fractional_part, false);
BigDecimal {
integer_part: x.integer_part,
fractional_part: x.fractional_part,
}
}
impl BigDecimal {
pub fn sanitize(&self) -> Self {
sanitize(self.clone())
}
}

View file

@ -0,0 +1,59 @@
use crate::big_numbers::big_decimal::BigDecimal;
impl BigDecimal {
pub fn sum(&self, other: &BigDecimal) -> BigDecimal {
let mut integer_part: Vec<u8> = Vec::new();
let mut fractional_part: Vec<u8> = Vec::new();
let mut carry: u8 = 0;
self.sanitize();
other.sanitize();
let (longer, shorter) = if self.fractional_part.len() >= other.fractional_part.len() {
(
self.fractional_part.clone(),
other.fractional_part.clone(),
)
} else {
(
other.fractional_part.clone(),
self.fractional_part.clone(),
)
};
for i in (0..longer.len()).rev() {
let sum = longer.get(i).unwrap_or(&0) + shorter.get(i).unwrap_or(&0) + carry;
carry = sum / 10;
fractional_part.insert(0,sum % 10);
}
// TODO: Split here for multithreading
let (longer, shorter) = if self.integer_part.len() >= other.integer_part.len() {
(
self.integer_part.clone(),
other.integer_part.clone(),
)
} else {
(
other.integer_part.clone(),
self.integer_part.clone(),
)
};
for i in 1..(longer.len() + 1) {
let sum = longer.get(longer.len() - &i).unwrap_or(&0) + shorter.get(longer.len() - &i).unwrap_or(&0) + carry;
carry = sum / 10;
integer_part.insert(0, sum % 10);
}
if carry != 0 {
integer_part.insert(0, carry);
}
BigDecimal {
integer_part,
fractional_part,
}
}
}

View file

@ -2,4 +2,4 @@
pub mod algebra;
#[cfg(feature = "big_numbers")]
mod big_numbers;
pub mod big_numbers;

View file

@ -4,6 +4,7 @@ use tn_math::algebra::subtraction::subtract;
use tn_math::algebra::multiplication::multiplication;
use tn_math::algebra::division::divide;
use tn_math::algebra::power::{power, power_tower};
use tn_math::algebra::rest::rest;
#[test]
fn test_sum() {
@ -33,6 +34,34 @@ fn test_division() {
assert_eq!(divide(20.0, 4.0), 5.0);
}
#[test]
fn test_rest() {
assert_eq!(rest(10.0, 5.0), 0.0);
assert_eq!(rest(5.0, 3.0), 2.0);
assert_eq!(rest(20.0, 4.0), 0.0);
}
#[test]
fn test_power() {
assert_eq!(power(2.0, 3.0), 8.0);
assert_eq!(power(2.0, -3.0), 0.125);
assert_eq!(power(-2.0, 3.0), -8.0);
}
#[test]
fn test_power_tower() {
assert_eq!(power_tower(2.0, 3), 16.0);
assert_eq!(power_tower(2.0, 4), 65536.0);
assert_eq!(power_tower(3.0, 2), 27.0);
}
//#[test]
//fn test_power_tower_tower() {
// assert_eq!(power_tower_tower(2, 3), 65536);
// assert_eq!(power_tower_tower(3, 2), 7625597484987);
// assert_eq!(power_tower_tower(2, 2), 16);
// }
#[test]
fn test_sum_macro() {
assert_eq!(sum!(1.0, 2.0, -5.0, -7.0, 12.0), 3.0);
@ -61,30 +90,9 @@ fn test_division_macro() {
assert_eq!(division!(240.0, 4.0, 6.0), 10.0);
}
#[test]
fn test_power() {
assert_eq!(power(2.0, 3.0), 8.0);
assert_eq!(power(2.0, -3.0), 0.125);
assert_eq!(power(-2.0, 3.0), -8.0);
}
#[test]
fn test_power_macro() {
assert_eq!(power!(2.0, 3.0), 8.0);
assert_eq!(power!(2.0, 3.0, 2.0), 64.0);
assert_eq!(power!(2.0, 3.0, 2.0, 2.0), 4096.0);
}
#[test]
fn test_power_tower() {
assert_eq!(power_tower(2.0, 3), 16.0);
assert_eq!(power_tower(2.0, 4), 65536.0);
assert_eq!(power_tower(3.0, 2), 27.0);
}
//#[test]
//fn test_power_tower_tower() {
// assert_eq!(power_tower_tower(2, 3), 65536);
// assert_eq!(power_tower_tower(3, 2), 7625597484987);
// assert_eq!(power_tower_tower(2, 2), 16);
// }
}

46
tests/big_numbers.rs Normal file
View file

@ -0,0 +1,46 @@
use tn_math::big_numbers::big_decimal::BigDecimal;
#[test]
fn test_conversions() {
assert_eq!(
BigDecimal::from_str("123.456").unwrap(),
BigDecimal{integer_part: Vec::from([1, 2, 3]), fractional_part: Vec::from([4, 5, 6])}
);
assert_eq!(
BigDecimal{integer_part: Vec::from([1, 2, 3]), fractional_part: Vec::from([4, 5, 6])}.to_string(),
"123.456".to_string()
)
}
#[test]
fn test_sum() {
let number1 = BigDecimal::from_str("123.456").unwrap();
let number2 = BigDecimal::from_str("789.012").unwrap();
assert_eq!(
number1.sum(&number2),
BigDecimal{integer_part: Vec::from([9, 1, 2]), fractional_part: Vec::from([4, 6, 8])}
);
let number1 = BigDecimal::from_str("123456789").unwrap();
let number2 = BigDecimal::from_str("987654321").unwrap();
assert_eq!(
number1.sum(&number2),
BigDecimal{integer_part: Vec::from([1, 1, 1, 1, 1, 1, 1, 1, 1, 0]), fractional_part: Vec::new()}
);
let number1 = BigDecimal::from_str("11.9999").unwrap();
let number2 = BigDecimal::from_str("11.1111").unwrap();
assert_eq!(
number1.sum(&number2),
BigDecimal{integer_part: Vec::from([2, 3]), fractional_part: Vec::from([1, 1, 1, 0])}
);
}
#[test]
fn test_sanitize() {
let number1 = BigDecimal::from_str("001000.00110").unwrap();
assert_eq!(
number1.sanitize(),
BigDecimal{integer_part: Vec::from([1, 0, 0, 0]), fractional_part: Vec::from([0, 0, 1, 1])}
);
}