Compare commits
2 commits
b31f676047
...
61fca40b68
Author | SHA1 | Date | |
---|---|---|---|
61fca40b68 | |||
4487ec9c86 |
10 changed files with 225 additions and 180 deletions
|
@ -6,6 +6,6 @@ edition = "2021"
|
|||
[dependencies]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["algebra"]
|
||||
algebra = []
|
||||
big_numbers = []
|
||||
big_numbers = ["algebra"]
|
|
@ -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
3
src/algebra/rest.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub fn rest(x: f64, y: f64) -> f64 {
|
||||
x % y
|
||||
}
|
|
@ -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;
|
52
src/big_numbers/big_decimal.rs
Normal file
52
src/big_numbers/big_decimal.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
29
src/big_numbers/big_decimal/sanitize.rs
Normal file
29
src/big_numbers/big_decimal/sanitize.rs
Normal 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())
|
||||
}
|
||||
}
|
59
src/big_numbers/big_decimal/sum.rs
Normal file
59
src/big_numbers/big_decimal/sum.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,4 +2,4 @@
|
|||
pub mod algebra;
|
||||
|
||||
#[cfg(feature = "big_numbers")]
|
||||
mod big_numbers;
|
||||
pub mod big_numbers;
|
|
@ -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
46
tests/big_numbers.rs
Normal 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])}
|
||||
);
|
||||
}
|
Loading…
Add table
Reference in a new issue