diff --git a/codes/rust_lang/part08/Cargo.toml b/codes/rust_lang/part08/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..b4fb6388d1fffc2604ffc2367b80b36ab5fc0fc2 --- /dev/null +++ b/codes/rust_lang/part08/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "part08" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/codes/rust_lang/part08/src/custom_int.rs b/codes/rust_lang/part08/src/custom_int.rs new file mode 100644 index 0000000000000000000000000000000000000000..fa151502b3231e4212b659332e02f3b9e826d2f0 --- /dev/null +++ b/codes/rust_lang/part08/src/custom_int.rs @@ -0,0 +1,117 @@ +use crate::minimum::Minimum; + +/// Larger ints based on a [Vec] of [u8] to repensent arbitrary lengthy numbers. +/// The number has a sign as well. +pub struct CustomInt { + /// The data contains the unsigned integers that are read from right to left + /// The number 1337 is stored as vec![7, 3, 3, 1]. Each number must be in the range [0,9] + /// and no trailing 0s are allowed. + data: Vec<u8>, + /// Contains the sign of the number +/-1; + sign: i8, +} + +impl CustomInt { + /// Tries to create a new [CustomInt]. If the number is valid it returns + /// an Ok(CustomInt) an Error otherwise. + /// + /// # Examples + /// + /// ``` + /// use part08::custom_int::CustomInt; + /// let num = CustomInt::try_new(vec![1, 2, 3, 4], 1); + /// assert!(num.is_ok()); + /// let num = CustomInt::try_new(vec![1, 2, 3, 4], -1); + /// assert!(num.is_ok()); + /// let num = CustomInt::try_new(vec![1, 2, 3, 4], 10); + /// assert!(num.is_err()); + /// let num = CustomInt::try_new(vec![1, 2, 3, 4], -10); + /// assert!(num.is_err()); + /// ``` + /// + pub fn try_new(data: Vec<u8>, sign: i8) -> Result<Self, String> { + // EXERCISE: + // We don't check for trailing 0s but maybe this could be an exercise. + // Also we should check numbers are between 0 and 9. + if sign == 1 || sign == -1 { + Ok(CustomInt { data, sign }) + } else { + Err(String::from("Invalid sign.")) + } + } +} + +impl Clone for CustomInt { + fn clone(&self) -> Self { + CustomInt { + data: self.data.clone(), + sign: self.sign, + } + } +} + +impl Minimum for CustomInt { + // EXERCISE: Correct this function by using clippy and let it guide you. + // Get inspiration from Display to compute the Minimum + fn min(self, rhs: Self) -> Self { + if self.sign < rhs.sign { + return self; + } else if self.sign > rhs.sign { + return rhs; + } + if self.data.len() < rhs.data.len() { + return self; + } else if self.data.len() > rhs.data.len() { + return rhs; + } + for (l, r) in self.data.iter().rev().zip(rhs.data.iter().rev()) { + let ls = (*l as i8) * self.sign; + let rs = (*r as i8) * self.sign; + if ls < rs { + return self; + } else if ls > rs { + return rhs; + } + } + self + } +} + +impl PartialEq for CustomInt { + fn eq(&self, other: &Self) -> bool { + if self.sign == other.sign && self.data.len() == other.data.len() { + self.data + .iter() + .zip(other.data.iter()) + .try_fold(true, |_, (l, r)| if l == r { Some(true) } else { None }) + .is_some() + } else { + false + } + } +} + +impl std::fmt::Display for CustomInt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // This could be replaced by an `?` + if self.sign == -1 { + if let Err(e) = write!(f, "-") { + return Err(e); + } + } + + // This could be replaced by an `?` + let res = self + .data + .iter() + .rev() + .try_fold((), |_, t| write!(f, "{}", t)); + res + } +} + +// EXERCISE: write tests +// EXERCISE: Modify Minimum trait to take references? + +#[cfg(test)] +mod tests {} diff --git a/codes/rust_lang/part08/src/io.rs b/codes/rust_lang/part08/src/io.rs new file mode 100644 index 0000000000000000000000000000000000000000..496c227a0a46ba1cbecd6488b2eede64a61b3e3c --- /dev/null +++ b/codes/rust_lang/part08/src/io.rs @@ -0,0 +1,57 @@ +use std::io::BufRead; + +use crate::custom_int::CustomInt; + +/// Reads i32 from the command line and returns a [Vec] containing +/// these numbers. Returns errors when the parsing fails. +pub fn read_command_line() -> Result<Vec<i32>, String> { + let mut v = Vec::new(); + let stdin = std::io::stdin(); + println!("Enter a list of numbers, one per line. End with Ctrl-D (Linux) or Ctrl-Z (Windows)."); + + for line in stdin.lock().lines() { + let line = match line { + Ok(l) => l, + Err(_) => { + return Err(String::from("Could not read line")); + } + }; + + match line.trim().parse::<i32>() { + Ok(num) => v.push(num), + Err(_) => { + return Err(String::from("Could not parse integer")); + } + } + } + Ok(v) +} + +// EXERCISE: Rewrite using ? +pub fn read_command_line_custom_int() -> Result<Vec<CustomInt>, String> { + let v = vec![ + CustomInt::try_new(vec![1, 3, 6, 9], 1).unwrap(), + CustomInt::try_new(vec![2, 4, 2, 1], -1).unwrap(), + CustomInt::try_new(vec![7, 4, 5, 3], 1).unwrap(), + CustomInt::try_new(vec![4, 1, 1, 1], -1).unwrap(), + ]; + Ok(v) +} + +/// Prints all the elements of the `tab`. +/// Tab is borrowed here +pub fn print_tab(tab: &Vec<i32>) { + for t in tab { + print!("{} ", t); + } + println!(); +} + +/// Prints all the elements of the `tab`. +/// Tab is borrowed here +pub fn print_tab_CustomInt(tab: &Vec<CustomInt>) { + for i in tab { + println!("{i} "); + } + println!(); +} diff --git a/codes/rust_lang/part08/src/lib.rs b/codes/rust_lang/part08/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..ac338352b68eb4d8969338dd79c3da95fa16d62c --- /dev/null +++ b/codes/rust_lang/part08/src/lib.rs @@ -0,0 +1,67 @@ +/*! +part08 illustrates the use of [Vec] and the Error Handling with [Option] and [Result]. +It also showcases struct enums. +*/ + +pub mod custom_int; +pub mod io; +mod minimum; +pub mod something_or_nothing; + +#[cfg(test)] +mod tests { + use crate::minimum::Minimum; + use crate::something_or_nothing::{find_min, SomethingOrNothing}; + + #[test] + fn test_creation() { + let n1: SomethingOrNothing<i32> = SomethingOrNothing::default(); + assert!(n1 == SomethingOrNothing::default()); + let n2: SomethingOrNothing<i32> = SomethingOrNothing::new(1); + assert!(n2 == SomethingOrNothing::new(1)); + } + + #[test] + #[should_panic] + fn test_failure_creation() { + let n2: SomethingOrNothing<i32> = SomethingOrNothing::new(1); + assert!(n2 == SomethingOrNothing::default()); + assert!(n2 == SomethingOrNothing::new(2)); + } + + #[test] + fn test_min() { + let a = vec![1, 5, -1, 2, 0, 10, 11, 0, 3]; + let min = find_min(&a); + assert!(min == SomethingOrNothing::new(-1)); + } + + #[test] + fn test_min_empty() { + let a: Vec<i32> = vec![]; + let min = find_min(&a); + assert!(min == SomethingOrNothing::default()); + } + + #[test] + fn test_min_i32() { + let x = 5; + let y = 10; + assert_eq!(Minimum::min(x, y), x); + assert_eq!(Minimum::min(y, x), x); + assert_eq!(Minimum::min(x, x), x); + assert_eq!(Minimum::min(y, y), y); + } + + #[test] + fn test_min_something_or_nothing() { + let x = SomethingOrNothing::new(5i32); + let y = SomethingOrNothing::new(10i32); + let z = SomethingOrNothing::default(); + assert!(x.min(y) == x); + assert!(y.min(x) == x); + assert!(z.min(y) == y); + assert!(y.min(z) == y); + assert!(z.min(z) == z); + } +} diff --git a/codes/rust_lang/part08/src/main.rs b/codes/rust_lang/part08/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..4a028c00099da36cb7f45343536e6c24076e08ee --- /dev/null +++ b/codes/rust_lang/part08/src/main.rs @@ -0,0 +1,16 @@ +use part08::io; +use part08::something_or_nothing::find_min; + +fn main() -> Result<(), String> { + let tab = match io::read_command_line_custom_int() { + Ok(tab) => tab, + Err(s) => return Err(s), + }; + println!("Among the Big Ints in the list:"); + io::print_tab_CustomInt(&tab); + // There are alternatives to access fields of tuples + let min = find_min(&tab); + // The first field is not used therefore we can replace it with "_" + min.print(); + Ok(()) +} diff --git a/codes/rust_lang/part08/src/minimum.rs b/codes/rust_lang/part08/src/minimum.rs new file mode 100644 index 0000000000000000000000000000000000000000..fd524a1b6d36c0f09f39d78854f93ba69cfc730b --- /dev/null +++ b/codes/rust_lang/part08/src/minimum.rs @@ -0,0 +1,15 @@ +// If we remove Copy, we have a problem with the t in tab +// in the computation of the minimum. +pub trait Minimum: Clone { + fn min(self, rhs: Self) -> Self; +} + +impl Minimum for i32 { + fn min(self, rhs: Self) -> Self { + if self < rhs { + self + } else { + rhs + } + } +} diff --git a/codes/rust_lang/part08/src/something_or_nothing.rs b/codes/rust_lang/part08/src/something_or_nothing.rs new file mode 100644 index 0000000000000000000000000000000000000000..ce9af04719e495fb0a2341c929cfe3ba3becd22b --- /dev/null +++ b/codes/rust_lang/part08/src/something_or_nothing.rs @@ -0,0 +1,78 @@ +use std::fmt::Display; + +use crate::minimum::Minimum; + +/// An generic enumerated type that encapsulates and Option<T>. +#[derive(Clone, Copy)] +pub struct SomethingOrNothing<T>(Option<T>); + +impl<T: Minimum + Display> SomethingOrNothing<T> { + pub fn new(val: T) -> Self { + SomethingOrNothing(Some(val)) + } + /// A static function that prints the content of a SomethingOrNothing. + pub fn print(&self) { + match &self.0 { + None => println!("Nothing."), + Some(val) => println!("Something is: {}", val), + } + } +} + +impl<T> Default for SomethingOrNothing<T> { + /// By Default a [SomethingOrNothing] is a nothing. + fn default() -> Self { + SomethingOrNothing(None) + } +} + +impl<T: PartialEq + Minimum> PartialEq for SomethingOrNothing<T> { + fn eq(&self, other: &Self) -> bool { + match (&self.0, &other.0) { + (None, None) => true, + (Some(lhs), Some(rhs)) => lhs == rhs, + _ => false, + } + } +} + +impl<T: Minimum + Display> Minimum for SomethingOrNothing<T> { + fn min(self, rhs: Self) -> Self { + match (self.0, rhs.0) { + (None, None) => SomethingOrNothing(None), + (Some(lhs), Some(rhs)) => SomethingOrNothing::new(lhs.min(rhs)), + (None, Some(rhs)) => SomethingOrNothing::new(rhs), + (Some(lhs), None) => SomethingOrNothing::new(lhs), + } + } +} + +/// Computes the minimum of an Array of a type T which implements the [Minimum] trait. +/// Returns a [Some] containing the the minimum value +/// or [None] if no minimum value was found. +/// +/// # Examples +/// +/// ``` +/// # use part08::something_or_nothing::{SomethingOrNothing, find_min}; +/// # fn main() { +/// let tab = vec![10, 32, 12, 43, 52, 53, 83, 2, 9]; +/// let min = find_min(&tab); +/// assert!(min == SomethingOrNothing::new(2)); +/// # } +/// ``` +/// +/// ``` +/// # use part08::something_or_nothing::{SomethingOrNothing, find_min}; +/// # fn main() { +/// let tab: Vec<i32> = vec![]; +/// let min = find_min(&tab); +/// assert!(min == SomethingOrNothing::default()); +/// # } +/// ``` +pub fn find_min<T: Minimum + Display>(tab: &Vec<T>) -> SomethingOrNothing<T> { + // A very elegant fold applied on an iterator + tab.iter().fold(SomethingOrNothing::default(), |res, x| { + res.min(SomethingOrNothing::new(x.clone())) + }) +}