First pass

This commit is contained in:
Jack Jackson 2023-05-15 21:18:04 -07:00
commit 2aa53c41dc
4 changed files with 183 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "realNumbers"
version = "0.1.0"

8
Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "realNumbers"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

167
src/lib.rs Normal file
View File

@ -0,0 +1,167 @@
use std::collections::HashMap;
use std::ops::{Add, Sub};
use std::cmp::max;
#[derive(Debug)]
struct Real {
int_part: i32,
decimal_parts: HashMap<i32, i8>
}
impl Real {
pub fn new_int(int_part: i32) -> Real {
Real { int_part: int_part, decimal_parts: HashMap::new() }
}
pub fn new(int_part: i32, decimal_parts: HashMap<i32, i8>) -> Real {
Real { int_part: int_part, decimal_parts: decimal_parts }
}
pub fn from_string(s: &str) -> Real {
let first_period_index = s.find('.');
dbg!(first_period_index);
match first_period_index {
None => {
Real { int_part: s.parse::<i32>().unwrap(), decimal_parts: HashMap::new() }
},
Some(idx) => {
let int_part = s[0..idx].parse::<i32>().unwrap();
dbg!(int_part);
let decimal_string = &s[idx+1..];
dbg!(decimal_string);
match decimal_string.find('.') {
Some(_) => {
panic!("Found two periods in input string");
},
None => {
let length = decimal_string.len();
dbg!(length);
let mut decimal_parts = HashMap::new();
for idx in 0..length {
let c = decimal_string.as_bytes()[idx] as char;
let num = c.to_digit(10).unwrap();
if num != 0 {
decimal_parts.entry(idx as i32).or_insert(num as i8);
}
}
Real { int_part: int_part, decimal_parts: decimal_parts }
}
}
}
}
}
}
impl Add for Real {
type Output = Real;
fn add(self, other: Real) -> Real {
let mut int_part = self.int_part + other.int_part;
let mut decimal_part = HashMap::new();
let highest_index = max(
self.decimal_parts.keys().max().unwrap_or(&0),
other.decimal_parts.keys().max().unwrap_or(&0));
let mut carry = false;
for idx in (0..=*highest_index).rev() {
let borrowed = &idx;
let sum = self.decimal_parts.get(borrowed).unwrap_or(&0) + other.decimal_parts.get(borrowed).unwrap_or(&0) + if carry {1} else {0};
carry = sum > 9;
let units = sum % 10;
if units != 0 {
decimal_part.entry( *borrowed).or_insert(units);
}
}
if carry {
int_part += 1;
}
Real::new(int_part, decimal_part)
}
}
impl Sub for Real {
type Output = Real;
fn sub(self, other: Real) -> Real {
let int_part = self.int_part - other.int_part;
Real::new_int(int_part)
}
}
impl PartialEq for Real {
fn eq(&self, other: &Real) -> bool {
// And also compare decimals
self.int_part == other.int_part &&
self.decimal_parts == other.decimal_parts
}
}
#[cfg(test)]
mod tests {
use crate::Real;
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
#[test]
fn int_addition() {
let one = Real::new_int(1);
let two = Real::new_int(2);
assert_eq!(one + two, Real::new_int(3));
}
#[test]
fn real_addition() {
// Basic equality
assert_eq!(
Real::from_string("1.23") + Real::from_string("3.45"),
Real::from_string("4.68")
);
// Basic inequality
assert_ne!(
Real::from_string("1.23") + Real::from_string("3.45"),
Real::from_string("4.70")
);
// Carry
assert_eq!(
Real::from_string("0.09") + Real::from_string("0.01"),
Real::from_string("0.1")
);
// Multiple carry
assert_eq!(
Real::from_string("0.99") + Real::from_string("0.01"),
Real::from_string("1")
);
// Ignore trailing zeros
assert_eq!(
Real::from_string("1.00"),
Real::from_string("1")
);
assert_eq!(
Real::from_string("2.00000"),
Real::from_string("2.00")
);
assert_eq!(
Real::from_string("3.000"),
Real::new_int(3)
);
}
#[test]
fn subtraction() {
let three = Real::new_int(3);
let one = Real::new_int(1);
assert_eq!(three - one, Real::new_int(2));
}
}