fix: floating point arithmetic is shaky
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

https://floating-point-gui.de/
This commit is contained in:
Aravinth Manivannan 2023-08-17 02:20:27 +05:30
parent b29e56bc5c
commit 7fff3aaaaf
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
3 changed files with 530 additions and 128 deletions

416
Cargo.lock generated
View file

@ -2,12 +2,143 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "borsh"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
dependencies = [
"borsh-derive",
"hashbrown 0.13.2",
]
[[package]]
name = "borsh-derive"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7"
dependencies = [
"borsh-derive-internal",
"borsh-schema-derive-internal",
"proc-macro-crate",
"proc-macro2",
"syn",
]
[[package]]
name = "borsh-derive-internal"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "borsh-schema-derive-internal"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bytecheck"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627"
dependencies = [
"bytecheck_derive",
"ptr_meta",
"simdutf8",
]
[[package]]
name = "bytecheck_derive"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.10" version = "0.2.10"
@ -19,25 +150,295 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash 0.7.6",
]
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash 0.8.3",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.147" version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "num-traits"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
dependencies = [
"toml",
]
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "ptr_meta"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "quote"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rend"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab"
dependencies = [
"bytecheck",
]
[[package]]
name = "rkyv"
version = "0.7.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58"
dependencies = [
"bitvec",
"bytecheck",
"hashbrown 0.12.3",
"ptr_meta",
"rend",
"rkyv_derive",
"seahash",
"tinyvec",
"uuid",
]
[[package]]
name = "rkyv_derive"
version = "0.7.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "rust_decimal"
version = "1.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a2ab0025103a60ecaaf3abf24db1db240a4e1c15837090d2c32f625ac98abea"
dependencies = [
"arrayvec",
"borsh",
"byteorder",
"bytes",
"num-traits",
"rand",
"rkyv",
"serde",
"serde_json",
]
[[package]]
name = "rust_decimal_macros"
version = "1.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9c3bcd00383608992fd3e960f0a6e93a3b1e98218451aa1ca05a44505e5d52a"
dependencies = [
"quote",
"rust_decimal",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.183" version = "1.0.183"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
[[package]]
name = "serde_json"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "simdutf8"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
[[package]] [[package]]
name = "split" name = "split"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"rust_decimal",
"rust_decimal_macros",
"uuid", "uuid",
] ]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.4.1" version = "1.4.1"
@ -48,8 +449,23 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]

View file

@ -12,4 +12,6 @@ edition = "2021"
#build = "build.rs" #build = "build.rs"
[dependencies] [dependencies]
rust_decimal = "1.31.0"
rust_decimal_macros = "1.31.0"
uuid = { version = "1.4.1", features = ["v4", "serde"] } uuid = { version = "1.4.1", features = ["v4", "serde"] }

View file

@ -17,18 +17,16 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use rust_decimal::prelude::*;
use rust_decimal_macros::dec;
use uuid::Uuid; use uuid::Uuid;
pub trait Purchasable { pub trait Purchasable {
fn cost(&self) -> f64; fn cost(&self) -> Decimal;
fn name(&self) -> &str; fn name(&self) -> &str;
fn paid_by(&self) -> Rc<dyn Actor>; fn paid_by(&self) -> Rc<dyn Actor>;
} }
pub fn money_owned(splits: &[Split]) {
for split in splits.iter() {}
}
#[derive(Clone)] #[derive(Clone)]
pub struct Split { pub struct Split {
purchase: Rc<dyn Purchasable>, purchase: Rc<dyn Purchasable>,
@ -36,68 +34,78 @@ pub struct Split {
parties: Vec<Rc<dyn Actor>>, parties: Vec<Rc<dyn Actor>>,
} }
//#[derive(Clone)]
//pub struct Split {
// shares: ShareHashMap,
// paid_by: Rc<dyn Actor>,
//}
impl Split { impl Split {
pub fn new(purchase: Rc<dyn Purchasable>, split_strategy: SplitStrategy, parties: Vec<Rc<dyn Actor>>) -> Self { pub fn new(
purchase: Rc<dyn Purchasable>,
split_strategy: SplitStrategy,
parties: Vec<Rc<dyn Actor>>,
) -> Self {
Self { Self {
split_strategy, parties, purchase split_strategy,
parties,
purchase,
} }
} }
// pub fn iter(&self) -> SplitIter { pub fn money_owed(&self) -> ShareHashMap {
// let iter = self.shares.iter(); match &self.split_strategy {
// let paid_by = self.paid_by; SplitStrategy::Unequal(shares) => {
// SplitIter { iter, paid_by} let mut s = shares.clone();
// } let payer = self.purchase.paid_by();
// if let Some((_, money)) = s.get_mut(payer.name()) {
// pub fn insert(&mut self, actor: Rc<dyn Actor>, amount: f64) { *money = dec!(0.0);
// let name = actor.name().to_owned(); }
// self.shares.insert(name, (actor, amount)); s
// } }
SplitStrategy::Equal => {
let mut shares = ShareHashMap::default();
let cost = self.purchase.cost();
let total_parties = Decimal::new(self.parties.len() as i64, 0);
for actor in self.parties.iter() {
if *actor == self.purchase.paid_by() {
shares.insert(actor.name().to_owned(), (actor.clone(), dec!(0.00)));
} else {
shares.insert(
actor.name().to_owned(),
(actor.clone(), cost / total_parties),
);
}
}
pub fn get(&mut self, actor: Rc<dyn Actor>) -> Option<f64> { shares
}
SplitStrategy::Shares(c) => {
let mut shares = ShareHashMap::default();
let cost = self.purchase.cost();
for (actor_name, (actor, share)) in c.iter() {
//if actor.name() == split.purchase.paid_by().name() {
if *actor == self.purchase.paid_by() {
shares.insert(actor_name.clone(), (actor.clone(), dec!(0.00)));
} else {
shares.insert(actor_name.clone(), (actor.clone(), cost * share));
}
}
shares
}
}
}
pub fn get(&mut self, actor: Rc<dyn Actor>) -> Option<Decimal> {
unimplemented!(); unimplemented!();
// if let Some((_actor, cost)) = self.shares.get(actor.name()) { // if let Some((_actor, cost)) = self.shares.get(actor.name()) {
// Some(*cost) // Some(*cost)
// } else { // } else {
// None // None
// } // }
} }
}
//pub struct SplitIter<'a> {
// iter: std::collections::hash_map::Iter<'a, String, (Rc<dyn Actor>, f64)>,
// paid_by: Rc<dyn Actor>,
//}
//
//impl<'a> Iterator for SplitIter<'a> {
// type Item = (Rc<dyn Actor>, f64);
//
// fn next(&mut self) -> Option<Self::Item> {
// if let Some((_, (actor, cost))) = self.iter.next() {
// Some((actor.clone(), *cost))
// } else {
// None
// }
// }
//}
impl Split {
pub fn validate(&self) -> bool { pub fn validate(&self) -> bool {
self.split_strategy.validate(&self) self.split_strategy.validate(self.purchase.as_ref())
} }
} }
// key is a string for getting Eq and PartialEq w object safety // key is a string for getting Eq and PartialEq w object safety
pub type ShareHashMap = HashMap<String, (Rc<dyn Actor>, f64)>; pub type ShareHashMap = HashMap<String, (Rc<dyn Actor>, Decimal)>;
/* /*
* SPLITTING STRATEGIES: * SPLITTING STRATEGIES:
@ -113,74 +121,35 @@ pub enum SplitStrategy {
} }
impl SplitStrategy { impl SplitStrategy {
pub fn validate(&self, split: &Split) -> bool { fn validate(&self, purchase: &dyn Purchasable) -> bool {
match self { match self {
SplitStrategy::Unequal(shares) => { SplitStrategy::Unequal(shares) => {
let mut total = 0.00; let mut total = dec!(0.00);
for (_actor_str, (_actor_obj, share)) in shares.iter() { for (_actor_str, (_actor_obj, share)) in shares.iter() {
total += share; total += share;
} }
let cost = split.purchase.cost(); let cost = purchase.cost();
println!("Unequal sharing: expected {cost} got {total}"); println!("Unequal sharing: expected {cost} got {total}");
total == cost total == cost
} }
SplitStrategy::Shares(shares) => { SplitStrategy::Shares(shares) => {
let mut total = 0.00; let mut total = dec!(0.00);
let cost = split.purchase.cost(); let cost = purchase.cost();
let mut total_shares = 0.00; let mut total_shares = dec!(0.00);
for (_actor_str, (_actor_obj, share)) in shares.iter() { for (_actor_str, (_actor_obj, share)) in shares.iter() {
total_shares += share; total_shares += share;
total += cost * share; total += cost * share;
println!("Shares: {:?}, total: {total_shares}", share);
} }
println!("Unequal sharing: expected shares 1 got {total_shares}"); println!("Unequal sharing: expected shares 1 got {total_shares}");
println!("Unequal sharing: expected {cost} got {total}"); println!("Unequal sharing: expected {cost} got {total}");
total_shares == 1.00 && total == cost total_shares == dec!(1.00) && total == cost
} }
SplitStrategy::Equal => true, SplitStrategy::Equal => true,
} }
} }
pub fn money_owed(&self, split: &Split) -> ShareHashMap {
match self {
SplitStrategy::Unequal(shares) => {
let mut s = shares.clone();
let payer = split.purchase.paid_by();
if let Some((_,money)) = s.get_mut(payer.name()) {
*money = 0.0;
}
s
}
SplitStrategy::Equal => {
let mut shares = ShareHashMap::default();
let cost = split.purchase.cost();
for actor in split.parties.iter() {
if *actor == split.purchase.paid_by() {
shares.insert(actor.name().to_owned(), (actor.clone(), 0.00));
} else {
shares.insert(actor.name().to_owned(), (actor.clone(), cost / split.parties.len() as f64));
}
}
shares
}
SplitStrategy::Shares(c) => {
let mut shares = ShareHashMap::default();
let cost = split.purchase.cost();
for (actor_name,(actor, share)) in c.iter() {
//if actor.name() == split.purchase.paid_by().name() {
if *actor == split.purchase.paid_by() {
shares.insert(actor_name.clone(), (actor.clone(), 0.00));
} else {
shares.insert(actor_name.clone(), (actor.clone(), cost * share));
}
}
shares
}
}
}
} }
pub trait Actor { pub trait Actor {
@ -209,7 +178,12 @@ impl Person {
let owes = Vec::default(); let owes = Vec::default();
let spent = Vec::default(); let spent = Vec::default();
Self { name, spent, owes, id} Self {
name,
spent,
owes,
id,
}
} }
} }
@ -234,23 +208,22 @@ impl Actor for Person {
fn id(&self) -> &Uuid { fn id(&self) -> &Uuid {
&self.id &self.id
} }
} }
pub struct Item { pub struct Item {
cost: f64, cost: Decimal,
name: String, name: String,
payer: Rc<dyn Actor>, payer: Rc<dyn Actor>,
} }
impl Item { impl Item {
pub fn new(name: String, cost: f64, payer: Rc<dyn Actor>) -> Self { pub fn new(name: String, cost: Decimal, payer: Rc<dyn Actor>) -> Self {
Self { cost, name, payer } Self { cost, name, payer }
} }
} }
impl Purchasable for Item { impl Purchasable for Item {
fn cost(&self) -> f64 { fn cost(&self) -> Decimal {
self.cost self.cost
} }
fn name(&self) -> &str { fn name(&self) -> &str {
@ -278,7 +251,7 @@ mod tests {
let person3: Rc<dyn Actor> = Rc::new(Person::new("person3".into(), Uuid::new_v4())); let person3: Rc<dyn Actor> = Rc::new(Person::new("person3".into(), Uuid::new_v4()));
let person4: Rc<dyn Actor> = Rc::new(Person::new("person4".into(), Uuid::new_v4())); let person4: Rc<dyn Actor> = Rc::new(Person::new("person4".into(), Uuid::new_v4()));
let item = Item::new("test item".into(), 240.00, person1.clone()); let item = Item::new("test item".into(), dec!(240.00), person1.clone());
let actors = vec![ let actors = vec![
person1.clone(), person1.clone(),
person2.clone(), person2.clone(),
@ -286,37 +259,39 @@ mod tests {
person4.clone(), person4.clone(),
]; ];
// equal split // equal split
let strategy = SplitStrategy::Equal; let strategy = SplitStrategy::Equal;
let mut split = Split::new(Rc::new(item), strategy, actors); let mut split = Split::new(Rc::new(item), strategy, actors);
assert!(split.split_strategy.validate(&split)); assert!(split.validate());
let dues = split.split_strategy.money_owed(&split); let dues = split.money_owed();
for (_actor_name,(actor, due)) in dues.iter() { for (_actor_name, (actor, due)) in dues.iter() {
if split.purchase.paid_by().name() == actor.name() { if split.purchase.paid_by().name() == actor.name() {
assert_eq!(*due, 0.00); assert_eq!(*due, dec!(0.00));
} else { } else {
assert_eq!(*due, split.purchase.cost() / split.parties.len() as f64); assert_eq!(
*due,
split.purchase.cost() / Decimal::new(split.parties.len() as i64, 0)
);
} }
} }
// share split // share split
let mut shares = ShareHashMap::default(); let mut shares = ShareHashMap::default();
shares.insert(person1.name().to_owned(), (person1.clone(), 0.3)); shares.insert(person1.name().to_owned(), (person1.clone(), dec!(0.3)));
shares.insert(person2.name().to_owned(), (person2.clone(), 0.4)); shares.insert(person2.name().to_owned(), (person2.clone(), dec!(0.4)));
shares.insert(person3.name().to_owned(), (person3.clone(), 0.2)); shares.insert(person3.name().to_owned(), (person3.clone(), dec!(0.2)));
shares.insert(person4.name().to_owned(), (person4.clone(), 0.1)); shares.insert(person4.name().to_owned(), (person4.clone(), dec!(0.1)));
let strategy = SplitStrategy::Shares(shares.clone()); let strategy = SplitStrategy::Shares(shares.clone());
split.split_strategy = strategy; split.split_strategy = strategy;
assert!(split.split_strategy.validate(&split)); assert!(split.validate());
let dues = split.split_strategy.money_owed(&split); let dues = split.money_owed();
for (actor_name, (actor, due)) in dues.iter() { for (actor_name, (actor, due)) in dues.iter() {
println!("Testing for actor: {actor_name} with due: {due}"); println!("Testing for actor: {actor_name} with due: {due}");
if split.purchase.paid_by().name() == actor.name() { if split.purchase.paid_by().name() == actor.name() {
assert_eq!(*due, 0.00); assert_eq!(*due, dec!(0.00));
} else { } else {
let (_, share) = shares.get(actor_name).unwrap(); let (_, share) = shares.get(actor_name).unwrap();
assert_eq!(*due, split.purchase.cost() * *share); assert_eq!(*due, split.purchase.cost() * *share);
@ -325,15 +300,24 @@ mod tests {
// Unequal split // Unequal split
let mut shares = ShareHashMap::default(); let mut shares = ShareHashMap::default();
shares.insert(person1.name().to_owned(), (person1.clone(), 0.3 * split.purchase.cost())); shares.insert(
shares.insert(person2.name().to_owned(), (person2.clone(), 0.4 * split.purchase.cost())); person1.name().to_owned(),
shares.insert(person3.name().to_owned(), (person3.clone(), 0.2 * split.purchase.cost())); (person1.clone(), dec!(0.3) * split.purchase.cost()),
shares.insert(person4.name().to_owned(), (person4.clone(), 0.1 * split.purchase.cost())); );
shares.insert(
person2.name().to_owned(),
(person2.clone(), dec!(0.4) * split.purchase.cost()),
);
shares.insert(
person3.name().to_owned(),
(person3.clone(), dec!(0.2) * split.purchase.cost()),
);
shares.insert(
person4.name().to_owned(),
(person4.clone(), dec!(0.1) * split.purchase.cost()),
);
let strategy = SplitStrategy::Unequal(shares); let strategy = SplitStrategy::Unequal(shares);
split.split_strategy = strategy; split.split_strategy = strategy;
assert!(split.split_strategy.validate(&split)); assert!(split.validate());
} }
} }