Created
October 3, 2024 14:43
-
-
Save jaoleal/d3e1ec7d95416a769c27fdc6a6e40677 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /// Module to group all median-time-past related functions. | |
| pub mod median_time_past { | |
| extern crate alloc; | |
| use alloc::vec; | |
| /// New type to hold the Median-Time-Past value. | |
| pub struct Mtp(u32); | |
| impl Mtp { | |
| /// Gets the MTP value. | |
| pub const fn get_mtp(&self) -> u32 { | |
| self.0 | |
| } | |
| pub const fn new_from_value(value: u32) -> Self { | |
| Self(value) | |
| } | |
| } | |
| pub struct MtpContext { | |
| /// The array of timestamps. | |
| pub array: [u32; 11], | |
| /// if the inner array is sorted. | |
| /// | |
| /// See [MtpContext::sort()] for more info. | |
| pub is_sorted: bool, | |
| } | |
| impl MtpContext { | |
| pub const fn to_mtp(mut self) -> Mtp { | |
| self.sort(); | |
| self.get_mtp().unwrap() | |
| } | |
| /// Creates a [`MtpContext`] from a unique timestamp. | |
| pub const fn new_from_unique(timestamp: u32) -> Self { | |
| Self { | |
| array: [timestamp, 0u32, 0u32, 0u32, 0u32, 0u32, 0u32, 0u32, 0u32, 0u32, 0u32], | |
| is_sorted: true, | |
| } | |
| } | |
| /// Creates a [`MtpContext`] from an unordered array of timestamps. | |
| pub const fn new_from_array(array: [u32; 11]) -> Self { | |
| Self { | |
| array, | |
| is_sorted: false, | |
| } | |
| } | |
| /// Creates a [`MtpContext`] from an unordered iterator of timestamps. | |
| /// | |
| /// Basically fills the gap between the sized array and a unsized. | |
| /// The input is anything iterable over u32. | |
| /// | |
| /// # Errors | |
| /// Fails if the iterator has more than 11 elements. | |
| /// | |
| /// # Examples | |
| /// ``` | |
| /// use median_time_past::MtpContext; | |
| /// use rand::Rng; | |
| /// | |
| /// pub struct RandomTimestamps(u32); | |
| /// impl RandomTimestamps{ | |
| /// pub fn new()-> Self{ | |
| /// Self(rand::threadrng().gen::<u32>()) | |
| /// } | |
| /// } | |
| /// | |
| /// type RandomTimestamps = RTS; | |
| /// | |
| /// let array = [RTS::new(),RTS::new(),RTS::new()] | |
| /// | |
| /// let array_in_11_elements = [array[0],array[1],array[2], 0u32, 0u32, 0u32, 0u32, 0u32, 0u32, 0u32, 0u32]; | |
| /// | |
| /// assert_eq!(MtpContext::auto_new_from_array(array.clone()).unwrap().get_array(), array_in_11_elements ); | |
| /// | |
| /// ``` | |
| pub fn auto_new_from_array<A: core::iter::IntoIterator<Item = u32>>(array: A) -> Result<Self, MtpError> { | |
| let mut array = array.into_iter().collect::<vec::Vec<u32>>(); | |
| if array.len() > 11 { | |
| return Err(MtpError::ArrayOverflow) | |
| } | |
| array.resize(11, 0); | |
| // I dont see a way to get this done without using try_into.unwrap() | |
| // | |
| // The if statement above ensures the array will always have less than 11 elements. | |
| Ok(Self::new_from_array(array.try_into().unwrap())) | |
| } | |
| /// Creates a [`MtpContext`] from an ordered array of timestamps. | |
| pub const fn new_from_sorted_array(array: [u32; 11]) -> Self { | |
| Self { | |
| array, | |
| is_sorted: true, | |
| } | |
| } | |
| /// Count how much of the timestamps the inner array have. | |
| pub const fn count(&self) -> usize { | |
| self.array.iter().filter(|&x| *x != 0).count() | |
| } | |
| /// Adds a timestamp to the inner array. | |
| pub const fn add(&mut self, timestamp: u32) { | |
| if self.count() == 11 { | |
| self.array[0] = timestamp; | |
| self.is_sorted = false; | |
| } else { | |
| self.array[self.count()] = timestamp; | |
| self.is_sorted = false; | |
| } | |
| } | |
| /// Sorts the inner array. | |
| /// | |
| /// The sorts needs to invert so we can use the return of [MtpContext::count()] as a index. | |
| pub const fn sort(&mut self) { | |
| self.array.sort_unstable(); | |
| self.array.reverse(); | |
| self.is_sorted = true; | |
| } | |
| /// Gets the median-time-past value from the [`MtpContext`] instance. | |
| pub const fn get_mtp(&self) -> Result<Mtp, MtpError> { | |
| if self.is_sorted { | |
| if self.array.len() == 11 { | |
| return Ok(Mtp::new_from_value(self.array[6])) | |
| } else { | |
| return Ok(Mtp::new_from_value(self.array[self.count() / 2])) | |
| } | |
| } | |
| Err(MtpError::UnsortedArray) | |
| } | |
| /// Returns the inner array. | |
| pub const fn get_array(&self) -> [u32; 11] { | |
| self.array | |
| } | |
| } | |
| #[derive(Debug, Clone, PartialEq, Eq)] | |
| pub enum MtpError { | |
| UnsortedArray, | |
| ArrayOverflow | |
| } | |
| } | |
| impl fmt::Display for MtpError { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| match self { | |
| MtpError::UnsortedArray => write!(f, "The array is not sorted"), | |
| MtpError::ArrayOverflow => write!(f, "The array has more than 11 elements"), | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment