diff --git a/rust/clock/src/lib.rs b/rust/clock/src/lib.rs index c625f73..d3f0573 100644 --- a/rust/clock/src/lib.rs +++ b/rust/clock/src/lib.rs @@ -1,11 +1,120 @@ -pub struct Clock; +use std::fmt::{Display, Formatter, self}; + +const HOURS_PER_DAY: i32 = 24; +const MINUTES_PER_HOUR: i32 = 60; +type MinutesSinceMidnight = RingI32<{ HOURS_PER_DAY * MINUTES_PER_HOUR }>; + +const HOUR_SINCE_MIDNIGHT: MinutesSinceMidnight = MinutesSinceMidnight::new(MINUTES_PER_HOUR); + +#[derive(Debug, PartialEq)] +pub struct Clock { + since_midnight: MinutesSinceMidnight, +} impl Clock { pub fn new(hours: i32, minutes: i32) -> Self { - todo!("Construct a new Clock from {hours} hours and {minutes} minutes"); + let since_midnight = MinutesSinceMidnight::new(minutes); + let since_midnight = HOUR_SINCE_MIDNIGHT * hours + since_midnight; + Self { + since_midnight, + } } pub fn add_minutes(&self, minutes: i32) -> Self { - todo!("Add {minutes} minutes to existing Clock time"); + let since_midnight = self.since_midnight + minutes; + Self { + since_midnight, + } + } +} + +impl Display for Clock { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let hours = self.since_midnight / HOUR_SINCE_MIDNIGHT; + let minutes = self.since_midnight % HOUR_SINCE_MIDNIGHT; + write!(f, "{hours:02}:{minutes:02}") + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +struct RingI32(i32); + +impl RingI32 { + const fn new(v: i32) -> Self { + Self(v.rem_euclid(N)) + } + + const fn val(&self) -> i32 { + self.0 + } +} + +impl Display for RingI32 { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl std::ops::Add for RingI32 { + type Output = Self; + fn add(self, rhs: i32) -> Self { + let lhs = self.val(); + let rhs = rhs.rem_euclid(N); + Self((lhs + rhs).rem_euclid(N)) + } +} + +impl std::ops::Mul for RingI32 { + type Output = Self; + fn mul(self, rhs: i32) -> Self { + let lhs = self.val(); + let rhs = rhs.rem_euclid(N); + Self((lhs * rhs).rem_euclid(N)) + } +} + +impl std::ops::Div for RingI32 { + type Output = Self; + fn div(self, rhs: i32) -> Self { + let lhs = self.val(); + let rhs = rhs.rem_euclid(N); + Self((lhs / rhs).rem_euclid(N)) + } +} + +impl std::ops::Rem for RingI32 { + type Output = Self; + fn rem(self, rhs: i32) -> Self { + let lhs = self.val(); + let rhs = rhs.rem_euclid(N); + Self((lhs % rhs).rem_euclid(N)) + } +} + +impl std::ops::Add for RingI32 { + type Output = Self; + fn add(self, rhs: Self) -> Self { + self + rhs.val() + } +} + +impl std::ops::Mul for RingI32 { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + self * rhs.val() + } +} + +impl std::ops::Div for RingI32 { + type Output = Self; + fn div(self, rhs: Self) -> Self { + self / rhs.val() + } +} + +impl std::ops::Rem for RingI32 { + type Output = Self; + fn rem(self, rhs: Self) -> Self { + self % rhs.val() } } diff --git a/rust/clock/tests/clock.rs b/rust/clock/tests/clock.rs index 416a6f9..9e70400 100644 --- a/rust/clock/tests/clock.rs +++ b/rust/clock/tests/clock.rs @@ -10,127 +10,106 @@ fn on_the_hour() { } #[test] -#[ignore] fn past_the_hour() { assert_eq!(Clock::new(11, 9).to_string(), "11:09"); } #[test] -#[ignore] fn midnight_is_zero_hours() { assert_eq!(Clock::new(24, 0).to_string(), "00:00"); } #[test] -#[ignore] fn hour_rolls_over() { assert_eq!(Clock::new(25, 0).to_string(), "01:00"); } #[test] -#[ignore] fn hour_rolls_over_continuously() { assert_eq!(Clock::new(100, 0).to_string(), "04:00"); } #[test] -#[ignore] fn sixty_minutes_is_next_hour() { assert_eq!(Clock::new(1, 60).to_string(), "02:00"); } #[test] -#[ignore] fn minutes_roll_over() { assert_eq!(Clock::new(0, 160).to_string(), "02:40"); } #[test] -#[ignore] fn minutes_roll_over_continuously() { assert_eq!(Clock::new(0, 1723).to_string(), "04:43"); } #[test] -#[ignore] fn hours_and_minutes_roll_over() { assert_eq!(Clock::new(25, 160).to_string(), "03:40"); } #[test] -#[ignore] fn hours_and_minutes_roll_over_continuously() { assert_eq!(Clock::new(201, 3001).to_string(), "11:01"); } #[test] -#[ignore] fn hours_and_minutes_roll_over_to_exactly_midnight() { assert_eq!(Clock::new(72, 8640).to_string(), "00:00"); } #[test] -#[ignore] fn negative_hour() { assert_eq!(Clock::new(-1, 15).to_string(), "23:15"); } #[test] -#[ignore] fn negative_hour_roll_over() { assert_eq!(Clock::new(-25, 00).to_string(), "23:00"); } #[test] -#[ignore] fn negative_hour_roll_over_continuously() { assert_eq!(Clock::new(-91, 00).to_string(), "05:00"); } #[test] -#[ignore] fn negative_minutes() { assert_eq!(Clock::new(1, -40).to_string(), "00:20"); } #[test] -#[ignore] fn negative_minutes_roll_over() { assert_eq!(Clock::new(1, -160).to_string(), "22:20"); } #[test] -#[ignore] fn negative_minutes_roll_over_continuously() { assert_eq!(Clock::new(1, -4820).to_string(), "16:40"); } #[test] -#[ignore] fn negative_sixty_minutes_is_prev_hour() { assert_eq!(Clock::new(2, -60).to_string(), "01:00"); } #[test] -#[ignore] fn negative_one_twenty_minutes_is_two_prev_hours() { assert_eq!(Clock::new(1, -120).to_string(), "23:00"); } #[test] -#[ignore] fn negative_hour_and_minutes_both_roll_over() { assert_eq!(Clock::new(-25, -160).to_string(), "20:20"); } #[test] -#[ignore] fn negative_hour_and_minutes_both_roll_over_continuously() { assert_eq!(Clock::new(-121, -5810).to_string(), "22:10"); } #[test] -#[ignore] fn zero_hour_and_negative_minutes() { assert_eq!(Clock::new(0, -22).to_string(), "23:38"); } @@ -140,112 +119,96 @@ fn zero_hour_and_negative_minutes() { // #[test] -#[ignore] fn add_minutes() { let clock = Clock::new(10, 0).add_minutes(3); assert_eq!(clock.to_string(), "10:03"); } #[test] -#[ignore] fn add_no_minutes() { let clock = Clock::new(6, 41).add_minutes(0); assert_eq!(clock.to_string(), "06:41"); } #[test] -#[ignore] fn add_to_next_hour() { let clock = Clock::new(0, 45).add_minutes(40); assert_eq!(clock.to_string(), "01:25"); } #[test] -#[ignore] fn add_more_than_one_hour() { let clock = Clock::new(10, 0).add_minutes(61); assert_eq!(clock.to_string(), "11:01"); } #[test] -#[ignore] fn add_more_than_two_hours_with_carry() { let clock = Clock::new(0, 45).add_minutes(160); assert_eq!(clock.to_string(), "03:25"); } #[test] -#[ignore] fn add_across_midnight() { let clock = Clock::new(23, 59).add_minutes(2); assert_eq!(clock.to_string(), "00:01"); } #[test] -#[ignore] fn add_more_than_one_day() { let clock = Clock::new(5, 32).add_minutes(1500); assert_eq!(clock.to_string(), "06:32"); } #[test] -#[ignore] fn add_more_than_two_days() { let clock = Clock::new(1, 1).add_minutes(3500); assert_eq!(clock.to_string(), "11:21"); } #[test] -#[ignore] fn subtract_minutes() { let clock = Clock::new(10, 3).add_minutes(-3); assert_eq!(clock.to_string(), "10:00"); } #[test] -#[ignore] fn subtract_to_previous_hour() { let clock = Clock::new(10, 3).add_minutes(-30); assert_eq!(clock.to_string(), "09:33"); } #[test] -#[ignore] fn subtract_more_than_an_hour() { let clock = Clock::new(10, 3).add_minutes(-70); assert_eq!(clock.to_string(), "08:53"); } #[test] -#[ignore] fn subtract_across_midnight() { let clock = Clock::new(0, 3).add_minutes(-4); assert_eq!(clock.to_string(), "23:59"); } #[test] -#[ignore] fn subtract_more_than_two_hours() { let clock = Clock::new(0, 0).add_minutes(-160); assert_eq!(clock.to_string(), "21:20"); } #[test] -#[ignore] fn subtract_more_than_two_hours_with_borrow() { let clock = Clock::new(6, 15).add_minutes(-160); assert_eq!(clock.to_string(), "03:35"); } #[test] -#[ignore] fn subtract_more_than_one_day() { let clock = Clock::new(5, 32).add_minutes(-1500); assert_eq!(clock.to_string(), "04:32"); } #[test] -#[ignore] fn subtract_more_than_two_days() { let clock = Clock::new(2, 20).add_minutes(-3000); assert_eq!(clock.to_string(), "00:20"); @@ -256,97 +219,81 @@ fn subtract_more_than_two_days() { // #[test] -#[ignore] fn compare_clocks_for_equality() { assert_eq!(Clock::new(15, 37), Clock::new(15, 37)); } #[test] -#[ignore] fn compare_clocks_a_minute_apart() { assert_ne!(Clock::new(15, 36), Clock::new(15, 37)); } #[test] -#[ignore] fn compare_clocks_an_hour_apart() { assert_ne!(Clock::new(14, 37), Clock::new(15, 37)); } #[test] -#[ignore] fn compare_clocks_with_hour_overflow() { assert_eq!(Clock::new(10, 37), Clock::new(34, 37)); } #[test] -#[ignore] fn compare_clocks_with_hour_overflow_by_several_days() { assert_eq!(Clock::new(99, 11), Clock::new(3, 11)); } #[test] -#[ignore] fn compare_clocks_with_negative_hour() { assert_eq!(Clock::new(-2, 40), Clock::new(22, 40)); } #[test] -#[ignore] fn compare_clocks_with_negative_hour_that_wraps() { assert_eq!(Clock::new(-31, 3), Clock::new(17, 3)); } #[test] -#[ignore] fn compare_clocks_with_negative_hour_that_wraps_multiple_times() { assert_eq!(Clock::new(-83, 49), Clock::new(13, 49)); } #[test] -#[ignore] fn compare_clocks_with_minutes_overflow() { assert_eq!(Clock::new(0, 1441), Clock::new(0, 1)); } #[test] -#[ignore] fn compare_clocks_with_minutes_overflow_by_several_days() { assert_eq!(Clock::new(2, 4322), Clock::new(2, 2)); } #[test] -#[ignore] fn compare_clocks_with_negative_minute() { assert_eq!(Clock::new(3, -20), Clock::new(2, 40)); } #[test] -#[ignore] fn compare_clocks_with_negative_minute_that_wraps() { assert_eq!(Clock::new(5, -1490), Clock::new(4, 10)); } #[test] -#[ignore] fn compare_clocks_with_negative_minute_that_wraps_multiple() { assert_eq!(Clock::new(6, -4305), Clock::new(6, 15)); } #[test] -#[ignore] fn compare_clocks_with_negative_hours_and_minutes() { assert_eq!(Clock::new(-12, -268), Clock::new(7, 32)); } #[test] -#[ignore] fn compare_clocks_with_negative_hours_and_minutes_that_wrap() { assert_eq!(Clock::new(-54, -11_513), Clock::new(18, 7)); } #[test] -#[ignore] fn compare_full_clock_and_zeroed_clock() { assert_eq!(Clock::new(24, 0), Clock::new(0, 0)); }