use std::fmt::Display;
use std::str::FromStr;
use crate::f64::consts::SECONDS_PER_ATTOSECOND;
use crate::i64::consts::{
ATTOSECONDS_IN_FEMTOSECOND, ATTOSECONDS_IN_MICROSECOND, ATTOSECONDS_IN_MILLISECOND,
ATTOSECONDS_IN_NANOSECOND, ATTOSECONDS_IN_PICOSECOND, ATTOSECONDS_IN_SECOND,
};
use thiserror::Error;
const FACTORS: [i64; 6] = [
ATTOSECONDS_IN_MILLISECOND,
ATTOSECONDS_IN_MICROSECOND,
ATTOSECONDS_IN_NANOSECOND,
ATTOSECONDS_IN_PICOSECOND,
ATTOSECONDS_IN_FEMTOSECOND,
1,
];
#[derive(Debug, Default, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Subsecond([u32; 6]);
impl Subsecond {
pub const ZERO: Self = Self::new();
pub const fn new() -> Self {
Self([0; 6])
}
pub const fn from_attoseconds(attoseconds: i64) -> Self {
let attoseconds_normalized = if attoseconds < 0 {
ATTOSECONDS_IN_SECOND + attoseconds
} else {
attoseconds
} as i128;
let mut this = Self::new();
this.0[0] = ((attoseconds_normalized / ATTOSECONDS_IN_MILLISECOND as i128) % 1000) as u32;
this.0[1] = ((attoseconds_normalized / ATTOSECONDS_IN_MICROSECOND as i128) % 1000) as u32;
this.0[2] = ((attoseconds_normalized / ATTOSECONDS_IN_NANOSECOND as i128) % 1000) as u32;
this.0[3] = ((attoseconds_normalized / ATTOSECONDS_IN_PICOSECOND as i128) % 1000) as u32;
this.0[4] = ((attoseconds_normalized / ATTOSECONDS_IN_FEMTOSECOND as i128) % 1000) as u32;
this.0[5] = (attoseconds_normalized % 1000) as u32;
this
}
pub const fn from_f64(value: f64) -> Option<Self> {
if !value.is_finite() {
return None;
}
let rem = value % 1.0;
let rem = if rem < 0.0 { rem + 1.0 } else { rem };
let attoseconds = (rem / crate::f64::consts::SECONDS_PER_ATTOSECOND).round() as i64;
Some(Self::from_attoseconds(attoseconds))
}
pub const fn set_milliseconds(mut self, milliseconds: u32) -> Self {
debug_assert!(milliseconds < 1000);
self.0[0] = milliseconds % 1000;
self
}
pub const fn set_microseconds(mut self, microseconds: u32) -> Self {
debug_assert!(microseconds < 1000);
self.0[1] = microseconds % 1000;
self
}
pub const fn set_nanoseconds(mut self, nanoseconds: u32) -> Self {
debug_assert!(nanoseconds < 1000);
self.0[2] = nanoseconds % 1000;
self
}
pub const fn set_picoseconds(mut self, picoseconds: u32) -> Self {
debug_assert!(picoseconds < 1000);
self.0[3] = picoseconds % 1000;
self
}
pub const fn set_femtoseconds(mut self, femtoseconds: u32) -> Self {
debug_assert!(femtoseconds < 1000);
self.0[4] = femtoseconds % 1000;
self
}
pub const fn set_attoseconds(mut self, attoseconds: u32) -> Self {
debug_assert!(attoseconds < 1000);
self.0[5] = attoseconds % 1000;
self
}
pub const fn as_attoseconds(&self) -> i64 {
self.0[0] as i64 * FACTORS[0]
+ self.0[1] as i64 * FACTORS[1]
+ self.0[2] as i64 * FACTORS[2]
+ self.0[3] as i64 * FACTORS[3]
+ self.0[4] as i64 * FACTORS[4]
+ self.0[5] as i64 * FACTORS[5]
}
pub const fn as_seconds_f64(&self) -> f64 {
self.as_attoseconds() as f64 * SECONDS_PER_ATTOSECOND
}
pub const fn milliseconds(&self) -> u32 {
self.0[0]
}
pub const fn microseconds(&self) -> u32 {
self.0[1]
}
pub const fn nanoseconds(&self) -> u32 {
self.0[2]
}
pub const fn picoseconds(&self) -> u32 {
self.0[3]
}
pub const fn femtoseconds(&self) -> u32 {
self.0[4]
}
pub const fn attoseconds(&self) -> u32 {
self.0[5]
}
}
impl Ord for Subsecond {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_attoseconds().cmp(&other.as_attoseconds())
}
}
impl PartialOrd for Subsecond {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Subsecond {
fn eq(&self, other: &Self) -> bool {
self.as_attoseconds() == other.as_attoseconds()
}
}
impl Eq for Subsecond {}
const DIGITS: usize = 18;
impl Display for Subsecond {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
"0.".fmt(f)?;
let mut s = self.as_attoseconds().to_string();
if s.len() < DIGITS {
s = format!("{:0>width$}", s, width = DIGITS);
}
let p = f.precision().unwrap_or(3).clamp(0, DIGITS);
s[0..p].fmt(f)
}
}
#[derive(Debug, Error)]
#[error("could not parse subsecond from {0}")]
pub struct SubsecondParseError(String);
impl FromStr for Subsecond {
type Err = SubsecondParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut this = Self::default();
if s.is_empty() {
return Ok(this);
}
if s.chars().any(|c| !c.is_numeric()) {
return Err(SubsecondParseError(s.to_owned()));
}
let n = s.len();
if n > DIGITS {
return Err(SubsecondParseError(s.to_owned()));
}
let rem = n % 3;
let s = if rem != 0 {
let width = n + 3 - rem;
format!("{:0<width$}", s)
} else {
s.to_owned()
};
for i in (0..s.len()).step_by(3) {
this.0[i / 3] = s[i..i + 3].parse().unwrap();
}
Ok(this)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_subsecond() {
let s = Subsecond::new()
.set_milliseconds(123)
.set_microseconds(456)
.set_nanoseconds(789)
.set_picoseconds(123)
.set_femtoseconds(456)
.set_attoseconds(789);
assert_eq!(s.as_attoseconds(), 123456789123456789);
assert_eq!(s.as_seconds_f64(), 0.1234567891234568);
assert_eq!(s.milliseconds(), 123);
assert_eq!(s.microseconds(), 456);
assert_eq!(s.nanoseconds(), 789);
assert_eq!(s.picoseconds(), 123);
assert_eq!(s.femtoseconds(), 456);
assert_eq!(s.attoseconds(), 789);
}
#[test]
fn test_subsecond_from_attoseconds() {
let s = Subsecond::from_attoseconds(123456789123456789);
assert_eq!(s.as_attoseconds(), 123456789123456789);
assert_eq!(s.as_seconds_f64(), 0.1234567891234568);
assert_eq!(s.milliseconds(), 123);
assert_eq!(s.microseconds(), 456);
assert_eq!(s.nanoseconds(), 789);
assert_eq!(s.picoseconds(), 123);
assert_eq!(s.femtoseconds(), 456);
assert_eq!(s.attoseconds(), 789);
}
#[test]
fn test_subsecond_display() {
let s = Subsecond::new()
.set_milliseconds(123)
.set_microseconds(456)
.set_nanoseconds(789)
.set_picoseconds(123)
.set_femtoseconds(456)
.set_attoseconds(789);
assert_eq!(format!("{}", s), "0.123");
assert_eq!(format!("{:.6}", s), "0.123456");
assert_eq!(format!("{:.18}", s), "0.123456789123456789");
}
#[test]
fn test_subsecond_parse() {
let exp = Subsecond::new().set_milliseconds(123).set_microseconds(400);
let act: Subsecond = "1234".parse().unwrap();
assert_eq!(act, exp);
let exp = Subsecond::new()
.set_milliseconds(123)
.set_microseconds(456)
.set_nanoseconds(789)
.set_picoseconds(123)
.set_femtoseconds(456)
.set_attoseconds(789);
let act: Subsecond = "123456789123456789".parse().unwrap();
assert_eq!(act, exp);
}
#[test]
#[should_panic]
fn test_subsecond_parse_error() {
"123foo".parse::<Subsecond>().unwrap();
}
#[test]
fn test_subsecond_from_attoseconds_negative() {
let s = Subsecond::from_attoseconds(-1);
assert_eq!(s.as_attoseconds(), 999999999999999999);
assert_eq!(s.milliseconds(), 999);
assert_eq!(s.microseconds(), 999);
assert_eq!(s.nanoseconds(), 999);
assert_eq!(s.picoseconds(), 999);
assert_eq!(s.femtoseconds(), 999);
assert_eq!(s.attoseconds(), 999);
}
#[test]
fn test_subsecond_from_attoseconds_zero() {
let s = Subsecond::from_attoseconds(0);
assert_eq!(s.as_attoseconds(), 0);
assert_eq!(s, Subsecond::ZERO);
}
#[test]
fn test_subsecond_from_attoseconds_max() {
let max = ATTOSECONDS_IN_SECOND - 1;
let s = Subsecond::from_attoseconds(max);
assert_eq!(s.as_attoseconds(), max);
assert_eq!(s.milliseconds(), 999);
assert_eq!(s.microseconds(), 999);
assert_eq!(s.nanoseconds(), 999);
assert_eq!(s.picoseconds(), 999);
assert_eq!(s.femtoseconds(), 999);
assert_eq!(s.attoseconds(), 999);
}
#[test]
fn test_subsecond_from_attoseconds_overflow() {
let s = Subsecond::from_attoseconds(ATTOSECONDS_IN_SECOND);
assert_eq!(s.as_attoseconds(), 0);
let s = Subsecond::from_attoseconds(ATTOSECONDS_IN_SECOND + 123);
assert_eq!(s.as_attoseconds(), 123);
}
#[test]
fn test_subsecond_set_methods_max_value() {
let s = Subsecond::new().set_milliseconds(999);
assert_eq!(s.milliseconds(), 999);
let s = Subsecond::new().set_microseconds(999);
assert_eq!(s.microseconds(), 999);
let s = Subsecond::new().set_nanoseconds(999);
assert_eq!(s.nanoseconds(), 999);
}
#[test]
fn test_subsecond_display_edge_cases() {
assert_eq!(format!("{}", Subsecond::ZERO), "0.000");
let s = Subsecond::new().set_milliseconds(123);
assert_eq!(format!("{:.0}", s), "");
assert_eq!(format!("{:.25}", s), "0.123000000000000000");
}
#[test]
fn test_subsecond_parse_edge_cases() {
let s: Subsecond = "".parse().unwrap();
assert_eq!(s, Subsecond::ZERO);
let s: Subsecond = "1".parse().unwrap();
assert_eq!(s.milliseconds(), 100);
let s: Subsecond = "12".parse().unwrap();
assert_eq!(s.milliseconds(), 120);
let s: Subsecond = "999999999999999999".parse().unwrap();
assert_eq!(s.as_attoseconds(), 999999999999999999);
}
#[test]
fn test_subsecond_parse_too_long() {
let result = "1234567890123456789".parse::<Subsecond>();
assert!(result.is_err());
}
#[test]
fn test_subsecond_ordering() {
let a = Subsecond::from_attoseconds(100);
let b = Subsecond::from_attoseconds(200);
let c = Subsecond::from_attoseconds(200);
assert!(a < b);
assert!(b > a);
assert_eq!(b, c);
assert!(b <= c);
assert!(b >= c);
}
#[test]
fn test_subsecond_equality() {
let a = Subsecond::new().set_milliseconds(123);
let b = Subsecond::from_attoseconds(123000000000000000);
assert_eq!(a, b);
assert_eq!(a.as_attoseconds(), b.as_attoseconds());
}
}