Skip to content
Snippets Groups Projects
Commit d9d9751f authored by Michaël El Kharroubi's avatar Michaël El Kharroubi :satellite:
Browse files

Merge branch 'part08' into 'main'

Adds part08 illustrates Vec and iterators

See merge request orestis.malaspin/rust-101!13
parents 300f0df7 eb9668bd
No related branches found
No related tags found
No related merge requests found
[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]
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 {}
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!();
}
/*!
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);
}
}
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(())
}
// 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
}
}
}
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()))
})
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment