From f4b975cfc674fcc01997388f4081da6101f3282e Mon Sep 17 00:00:00 2001 From: fruzitent Date: Wed, 26 Feb 2025 16:05:19 +0200 Subject: [PATCH 1/6] feat(s4_prob/p01): initial commit --- s4_prob/p01/Cargo.lock | 7 +++++++ s4_prob/p01/Cargo.toml | 4 ++++ s4_prob/p01/src/main.rs | 17 +++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 s4_prob/p01/Cargo.lock create mode 100644 s4_prob/p01/Cargo.toml create mode 100644 s4_prob/p01/src/main.rs diff --git a/s4_prob/p01/Cargo.lock b/s4_prob/p01/Cargo.lock new file mode 100644 index 0000000..5d1bf36 --- /dev/null +++ b/s4_prob/p01/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "p01" +version = "0.1.0" diff --git a/s4_prob/p01/Cargo.toml b/s4_prob/p01/Cargo.toml new file mode 100644 index 0000000..897a95d --- /dev/null +++ b/s4_prob/p01/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "p01" +version = "0.1.0" +edition = "2024" diff --git a/s4_prob/p01/src/main.rs b/s4_prob/p01/src/main.rs new file mode 100644 index 0000000..1fefc4d --- /dev/null +++ b/s4_prob/p01/src/main.rs @@ -0,0 +1,17 @@ +#[rustfmt::skip] +const ITEMS: &[usize] = &[ + 75, 85, 84, 81, 84, 80, 82, 76, 75, 77, + 80, 82, 81, 84, 85, 77, 76, 84, 83, 87, + 78, 77, 88, 86, 87, 79, 80, 79, 78, 87, + 76, 81, 83, 85, 78, 76, 83, 81, 84, 88, +]; + +fn main() { + let mut map = std::collections::BTreeMap::new(); + for item in ITEMS.iter() { + *map.entry(item).or_insert(0) += 1; + } + for (key, value) in map.iter() { + println!("{key}: {value}") + } +} From e0bacb406bf329031a083496e8d080f9113dc57a Mon Sep 17 00:00:00 2001 From: fruzitent Date: Wed, 26 Feb 2025 19:14:10 +0200 Subject: [PATCH 2/6] feat(s4_prob/p01): print variation table --- s4_prob/p01/Cargo.lock | 118 ++++++++++++++++++++++++++++++++++++++++ s4_prob/p01/Cargo.toml | 3 + s4_prob/p01/src/main.rs | 10 +++- 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/s4_prob/p01/Cargo.lock b/s4_prob/p01/Cargo.lock index 5d1bf36..36af7b3 100644 --- a/s4_prob/p01/Cargo.lock +++ b/s4_prob/p01/Cargo.lock @@ -2,6 +2,124 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "p01" version = "0.1.0" +dependencies = [ + "tabled", +] + +[[package]] +name = "papergrid" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b915f831b85d984193fdc3d3611505871dc139b2534530fa01c1a6a6707b6723" +dependencies = [ + "bytecount", + "fnv", + "unicode-width", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tabled" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121d8171ee5687a4978d1b244f7d99c43e7385a272185a2f1e1fa4dc0979d444" +dependencies = [ + "papergrid", + "tabled_derive", +] + +[[package]] +name = "tabled_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d9946811baad81710ec921809e2af67ad77719418673b2a3794932d57b7538" +dependencies = [ + "heck", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" diff --git a/s4_prob/p01/Cargo.toml b/s4_prob/p01/Cargo.toml index 897a95d..91b0b84 100644 --- a/s4_prob/p01/Cargo.toml +++ b/s4_prob/p01/Cargo.toml @@ -2,3 +2,6 @@ name = "p01" version = "0.1.0" edition = "2024" + +[dependencies] +tabled = "0.18.0" diff --git a/s4_prob/p01/src/main.rs b/s4_prob/p01/src/main.rs index 1fefc4d..df326bc 100644 --- a/s4_prob/p01/src/main.rs +++ b/s4_prob/p01/src/main.rs @@ -11,7 +11,13 @@ fn main() { for item in ITEMS.iter() { *map.entry(item).or_insert(0) += 1; } - for (key, value) in map.iter() { - println!("{key}: {value}") + + let mut builder = tabled::builder::Builder::new(); + for (i, (key, value)) in map.iter().enumerate() { + builder.push_column([format!("x_{i}"), key.to_string(), value.to_string()]); } + + let mut table = builder.build(); + table.with(tabled::settings::Style::modern()); + println!("{table}"); } From df448deece0085cb0d1a4031ec549634cb14b332 Mon Sep 17 00:00:00 2001 From: fruzitent Date: Thu, 27 Feb 2025 02:02:44 +0200 Subject: [PATCH 3/6] feat(s4_prob/p01): support floats --- s4_prob/p01/Cargo.lock | 25 ++++++++++++++ s4_prob/p01/Cargo.toml | 1 + s4_prob/p01/src/main.rs | 74 ++++++++++++++++++++++++++++++++++------- 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/s4_prob/p01/Cargo.lock b/s4_prob/p01/Cargo.lock index 36af7b3..b775583 100644 --- a/s4_prob/p01/Cargo.lock +++ b/s4_prob/p01/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + [[package]] name = "bytecount" version = "0.6.8" @@ -20,10 +26,29 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ordered-float" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01" +dependencies = [ + "num-traits", +] + [[package]] name = "p01" version = "0.1.0" dependencies = [ + "ordered-float", "tabled", ] diff --git a/s4_prob/p01/Cargo.toml b/s4_prob/p01/Cargo.toml index 91b0b84..a407916 100644 --- a/s4_prob/p01/Cargo.toml +++ b/s4_prob/p01/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +ordered-float = "5.0.0" tabled = "0.18.0" diff --git a/s4_prob/p01/src/main.rs b/s4_prob/p01/src/main.rs index df326bc..c159ea8 100644 --- a/s4_prob/p01/src/main.rs +++ b/s4_prob/p01/src/main.rs @@ -1,19 +1,38 @@ -#[rustfmt::skip] -const ITEMS: &[usize] = &[ - 75, 85, 84, 81, 84, 80, 82, 76, 75, 77, - 80, 82, 81, 84, 85, 77, 76, 84, 83, 87, - 78, 77, 88, 86, 87, 79, 80, 79, 78, 87, - 76, 81, 83, 85, 78, 76, 83, 81, 84, 88, -]; +pub struct Variation(std::collections::BTreeMap); -fn main() { - let mut map = std::collections::BTreeMap::new(); - for item in ITEMS.iter() { - *map.entry(item).or_insert(0) += 1; +impl Variation { + fn new(items: &[T]) -> Self { + Self(items.iter().fold(Default::default(), |mut map, &item| { + *map.entry(item).or_insert(0) += 1; + map + })) + } +} + +impl Variation> { + pub fn new_f32(items: &[f32]) -> Self { + Self::new( + &items + .iter() + .copied() + .map(ordered_float::OrderedFloat) + .collect::>(), + ) } +} + +fn main() { + #[rustfmt::skip] + const ITEMS: &[i32] = &[ + 75, 85, 84, 81, 84, 80, 82, 76, 75, 77, + 80, 82, 81, 84, 85, 77, 76, 84, 83, 87, + 78, 77, 88, 86, 87, 79, 80, 79, 78, 87, + 76, 81, 83, 85, 78, 76, 83, 81, 84, 88, + ]; + let series = Variation::new(ITEMS); let mut builder = tabled::builder::Builder::new(); - for (i, (key, value)) in map.iter().enumerate() { + for (i, (key, value)) in series.0.iter().enumerate() { builder.push_column([format!("x_{i}"), key.to_string(), value.to_string()]); } @@ -21,3 +40,34 @@ fn main() { table.with(tabled::settings::Style::modern()); println!("{table}"); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + #[rustfmt::skip] + const ITEMS: &[f32] = &[ + 0.87, 0.94, 0.99, 0.90, 0.90, 0.87, 0.85, 0.87, + 0.90, 0.94, 0.87, 0.87, 0.82, 0.90, 0.94, 0.90, + 0.85, 0.85, 0.87, 0.94, 0.81, 0.82, 0.87, 0.97, + 0.90, 0.94, 0.85, 0.81, 0.87, 0.85, 0.90, 0.82, + 0.99, 0.90, 0.94, 0.82, 0.97, 0.81, 0.85, 0.87, + ]; + let series = Variation::new_f32(ITEMS); + const EXPECTED: &[(f32, usize)] = &[ + (0.81, 3), + (0.82, 4), + (0.85, 6), + (0.87, 9), + (0.90, 8), + (0.94, 6), + (0.97, 2), + (0.99, 2), + ]; + for (value, count) in EXPECTED.iter() { + assert_eq!(*series.0.get(value.into()).unwrap(), *count); + } + } +} From 5feb5458de438e2fb26fead67825da6f9a7d8d81 Mon Sep 17 00:00:00 2001 From: fruzitent Date: Thu, 27 Feb 2025 02:10:57 +0200 Subject: [PATCH 4/6] refactor(s4_prob/p01): dispatch input type --- s4_prob/p01/src/main.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/s4_prob/p01/src/main.rs b/s4_prob/p01/src/main.rs index c159ea8..6352b18 100644 --- a/s4_prob/p01/src/main.rs +++ b/s4_prob/p01/src/main.rs @@ -1,26 +1,30 @@ +pub trait IntoOrd { + fn into_ord(self) -> T; +} + +impl IntoOrd for i32 { + fn into_ord(self) -> i32 { + self + } +} + +impl IntoOrd> for f32 { + fn into_ord(self) -> ordered_float::OrderedFloat { + self.into() + } +} + pub struct Variation(std::collections::BTreeMap); impl Variation { - fn new(items: &[T]) -> Self { + fn new>(items: &[U]) -> Self { Self(items.iter().fold(Default::default(), |mut map, &item| { - *map.entry(item).or_insert(0) += 1; + *map.entry(item.into_ord()).or_insert(0) += 1; map })) } } -impl Variation> { - pub fn new_f32(items: &[f32]) -> Self { - Self::new( - &items - .iter() - .copied() - .map(ordered_float::OrderedFloat) - .collect::>(), - ) - } -} - fn main() { #[rustfmt::skip] const ITEMS: &[i32] = &[ @@ -55,7 +59,7 @@ mod tests { 0.90, 0.94, 0.85, 0.81, 0.87, 0.85, 0.90, 0.82, 0.99, 0.90, 0.94, 0.82, 0.97, 0.81, 0.85, 0.87, ]; - let series = Variation::new_f32(ITEMS); + let series = Variation::new(ITEMS); const EXPECTED: &[(f32, usize)] = &[ (0.81, 3), (0.82, 4), From 79acd2fdba5819c08dc6c602439431f107bae2e4 Mon Sep 17 00:00:00 2001 From: fruzitent Date: Thu, 27 Feb 2025 21:02:34 +0200 Subject: [PATCH 5/6] feat(s4_prob/p01): compute params --- s4_prob/p01/Cargo.lock | 25 ++++++++++ s4_prob/p01/Cargo.toml | 4 ++ s4_prob/p01/src/main.rs | 102 +++++++++++++++++++++++++++++++++++----- 3 files changed, 119 insertions(+), 12 deletions(-) diff --git a/s4_prob/p01/Cargo.lock b/s4_prob/p01/Cargo.lock index b775583..9c13181 100644 --- a/s4_prob/p01/Cargo.lock +++ b/s4_prob/p01/Cargo.lock @@ -14,6 +14,15 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +[[package]] +name = "fern" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" +dependencies = [ + "log", +] + [[package]] name = "fnv" version = "1.0.7" @@ -26,6 +35,18 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + [[package]] name = "num-traits" version = "0.2.19" @@ -48,6 +69,10 @@ dependencies = [ name = "p01" version = "0.1.0" dependencies = [ + "fern", + "humantime", + "log", + "num-traits", "ordered-float", "tabled", ] diff --git a/s4_prob/p01/Cargo.toml b/s4_prob/p01/Cargo.toml index a407916..030f301 100644 --- a/s4_prob/p01/Cargo.toml +++ b/s4_prob/p01/Cargo.toml @@ -4,5 +4,9 @@ version = "0.1.0" edition = "2024" [dependencies] +fern = "0.7.1" +humantime = "2.1.0" +log = "0.4.26" +num-traits = "0.2.19" ordered-float = "5.0.0" tabled = "0.18.0" diff --git a/s4_prob/p01/src/main.rs b/s4_prob/p01/src/main.rs index 6352b18..3189370 100644 --- a/s4_prob/p01/src/main.rs +++ b/s4_prob/p01/src/main.rs @@ -1,3 +1,21 @@ +fn setup_logger() -> Result<(), fern::InitError> { + fern::Dispatch::new() + .format(|out, message, record| { + out.finish(format_args!( + "[{} {} {}:{}] {}", + humantime::format_rfc3339_seconds(std::time::SystemTime::now()), + record.level(), + record.file().unwrap(), + record.line().unwrap(), + message + )) + }) + .level(log::LevelFilter::Debug) + .chain(std::io::stdout()) + .apply()?; + Ok(()) +} + pub trait IntoOrd { fn into_ord(self) -> T; } @@ -17,7 +35,44 @@ impl IntoOrd> for f32 { pub struct Variation(std::collections::BTreeMap); impl Variation { - fn new>(items: &[U]) -> Self { + // TODO: https://en.wikipedia.org/wiki/Histogram#Number_of_bins_and_width + fn get_count(&self, total: usize) -> usize { + let count = total.isqrt(); + log::debug!("n = {count}"); + count + } + + fn get_first_key(&self) -> Option { + self.0.first_key_value().map(|(key, _)| *key) + } + + fn get_last_key(&self) -> Option { + self.0.last_key_value().map(|(key, _)| *key) + } + + fn get_step(&self, count: usize) -> T + where + T: num_traits::cast::FromPrimitive + + std::fmt::Display + + std::ops::Div + + std::ops::Sub, + { + let x_1 = self.get_first_key().unwrap(); + let x_k = self.get_last_key().unwrap(); + let step = (x_k - x_1) / T::from_usize(count).unwrap(); + log::debug!("h = (x_k - x_1) / m = ({x_k} - {x_1}) / {count} = {step}"); + step + } + + fn get_total(&self) -> usize { + let total = self.0.values().sum(); + log::debug!("n = {total}"); + total + } +} + +impl> From<&[U]> for Variation { + fn from(items: &[U]) -> Self { Self(items.iter().fold(Default::default(), |mut map, &item| { *map.entry(item.into_ord()).or_insert(0) += 1; map @@ -25,7 +80,21 @@ impl Variation { } } +impl std::fmt::Display for Variation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut builder = tabled::builder::Builder::new(); + for (i, (key, value)) in self.0.iter().enumerate() { + builder.push_column([format!("x_{i}"), key.to_string(), value.to_string()]); + } + let mut table = builder.build(); + table.with(tabled::settings::Style::modern()); + write!(f, "{table}") + } +} + fn main() { + setup_logger().unwrap(); + #[rustfmt::skip] const ITEMS: &[i32] = &[ 75, 85, 84, 81, 84, 80, 82, 76, 75, 77, @@ -33,22 +102,25 @@ fn main() { 78, 77, 88, 86, 87, 79, 80, 79, 78, 87, 76, 81, 83, 85, 78, 76, 83, 81, 84, 88, ]; - let series = Variation::new(ITEMS); - - let mut builder = tabled::builder::Builder::new(); - for (i, (key, value)) in series.0.iter().enumerate() { - builder.push_column([format!("x_{i}"), key.to_string(), value.to_string()]); - } - - let mut table = builder.build(); - table.with(tabled::settings::Style::modern()); - println!("{table}"); + let series = Variation::from(ITEMS); + let total = series.get_total(); + let count = series.get_count(total); + let step = series.get_step(count); + println!("{series}"); } #[cfg(test)] mod tests { use super::*; + macro_rules! assert_delta { + ($x:expr, $y:expr, $d:expr) => { + if !($x - $y < $d || $y - $x < $d) { + panic!(); + } + }; + } + #[test] fn it_works() { #[rustfmt::skip] @@ -59,7 +131,7 @@ mod tests { 0.90, 0.94, 0.85, 0.81, 0.87, 0.85, 0.90, 0.82, 0.99, 0.90, 0.94, 0.82, 0.97, 0.81, 0.85, 0.87, ]; - let series = Variation::new(ITEMS); + let series = Variation::from(ITEMS); const EXPECTED: &[(f32, usize)] = &[ (0.81, 3), (0.82, 4), @@ -73,5 +145,11 @@ mod tests { for (value, count) in EXPECTED.iter() { assert_eq!(*series.0.get(value.into()).unwrap(), *count); } + let total = series.get_total(); + assert_eq!(total, 40); + let count = series.get_count(total); + assert_eq!(count, 6); + let step = series.get_step(count); + assert_delta!(step.0, 0.03, 1e-3); } } From bbad32125f8ccc0382382fe97dd717fab5b10a42 Mon Sep 17 00:00:00 2001 From: fruzitent Date: Thu, 27 Feb 2025 22:59:08 +0200 Subject: [PATCH 6/6] refactor(s4_prob/p01): abstract use of ordered_float --- s4_prob/p01/src/main.rs | 73 +++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/s4_prob/p01/src/main.rs b/s4_prob/p01/src/main.rs index 3189370..80ec31c 100644 --- a/s4_prob/p01/src/main.rs +++ b/s4_prob/p01/src/main.rs @@ -16,63 +16,75 @@ fn setup_logger() -> Result<(), fern::InitError> { Ok(()) } -pub trait IntoOrd { - fn into_ord(self) -> T; +pub trait IntoOrd { + type Output: Ord; + fn into_ord(self) -> Self::Output; } -impl IntoOrd for i32 { - fn into_ord(self) -> i32 { +impl IntoOrd for i32 { + type Output = i32; + fn into_ord(self) -> Self::Output { self } } -impl IntoOrd> for f32 { - fn into_ord(self) -> ordered_float::OrderedFloat { +impl IntoOrd for f32 { + type Output = ordered_float::OrderedFloat; + fn into_ord(self) -> Self::Output { self.into() } } -pub struct Variation(std::collections::BTreeMap); +pub struct Variation(std::collections::BTreeMap); + +impl Variation { + pub fn get(&self, value: T) -> Option<&usize> { + self.0.get(&value.into_ord()) + } -impl Variation { // TODO: https://en.wikipedia.org/wiki/Histogram#Number_of_bins_and_width - fn get_count(&self, total: usize) -> usize { + pub fn get_count(&self, total: usize) -> usize { let count = total.isqrt(); log::debug!("n = {count}"); count } - fn get_first_key(&self) -> Option { + pub fn get_total(&self) -> usize { + let total = self.0.values().sum(); + log::debug!("n = {total}"); + total + } +} + +impl Variation +where + T::Output: Copy, +{ + pub fn get_first_key(&self) -> Option { self.0.first_key_value().map(|(key, _)| *key) } - fn get_last_key(&self) -> Option { + pub fn get_last_key(&self) -> Option { self.0.last_key_value().map(|(key, _)| *key) } - fn get_step(&self, count: usize) -> T + pub fn get_step(&self, count: usize) -> T::Output where - T: num_traits::cast::FromPrimitive + T::Output: num_traits::cast::FromPrimitive + std::fmt::Display - + std::ops::Div - + std::ops::Sub, + + std::ops::Div + + std::ops::Sub, { - let x_1 = self.get_first_key().unwrap(); - let x_k = self.get_last_key().unwrap(); - let step = (x_k - x_1) / T::from_usize(count).unwrap(); - log::debug!("h = (x_k - x_1) / m = ({x_k} - {x_1}) / {count} = {step}"); + let first = self.get_first_key().unwrap(); + let last = self.get_last_key().unwrap(); + let step = (last - first) / num_traits::FromPrimitive::from_usize(count).unwrap(); + log::debug!("h = (x_k - x_1) / m = ({last} - {first}) / {count} = {step}"); step } - - fn get_total(&self) -> usize { - let total = self.0.values().sum(); - log::debug!("n = {total}"); - total - } } -impl> From<&[U]> for Variation { - fn from(items: &[U]) -> Self { +impl From<&[T]> for Variation { + fn from(items: &[T]) -> Self { Self(items.iter().fold(Default::default(), |mut map, &item| { *map.entry(item.into_ord()).or_insert(0) += 1; map @@ -80,7 +92,10 @@ impl> From<&[U]> for Variation { } } -impl std::fmt::Display for Variation { +impl std::fmt::Display for Variation +where + T::Output: std::fmt::Display, +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut builder = tabled::builder::Builder::new(); for (i, (key, value)) in self.0.iter().enumerate() { @@ -143,7 +158,7 @@ mod tests { (0.99, 2), ]; for (value, count) in EXPECTED.iter() { - assert_eq!(*series.0.get(value.into()).unwrap(), *count); + assert_eq!(*series.get(*value).unwrap(), *count); } let total = series.get_total(); assert_eq!(total, 40);