use std::ops::{Add, Div, Sub};
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Time {
Clock(ClockTime),
Beat(BeatTime),
}
impl PartialOrd for Time {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(Time::Clock(x), Time::Clock(y)) => x.0.partial_cmp(&y.0),
(Time::Beat(x), Time::Beat(y)) => x.0.partial_cmp(&y.0),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct SameUnitTime<'id>(Time, generativity::Id<'id>);
impl From<SameUnitTime<'_>> for Time {
fn from(value: SameUnitTime<'_>) -> Self {
value.0
}
}
impl Time {
pub fn prove_same_unit<'id>(
self,
other: Time,
guard: generativity::Guard<'id>,
) -> Option<(SameUnitTime<'id>, SameUnitTime<'id>)> {
let id = guard.into();
match (self, other) {
(Time::Clock(_), Time::Clock(_)) | (Time::Beat(_), Time::Beat(_)) => {
Some((SameUnitTime(self, id), SameUnitTime(other, id)))
}
_ => None,
}
}
}
impl<'id> SameUnitTime<'id> {
pub fn prove_same_unit(self, other: Time) -> Option<SameUnitTime<'id>> {
match (self.0, other) {
(Time::Clock(_), Time::Clock(_)) | (Time::Beat(_), Time::Beat(_)) => {
Some(SameUnitTime(other, self.1))
}
_ => None,
}
}
pub fn as_time(&self) -> Time {
self.0
}
}
impl<'id> Add for SameUnitTime<'id> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self.0, rhs.0) {
(Time::Clock(x), Time::Clock(y)) => {
SameUnitTime(Time::Clock(ClockTime(non_nan_add(x.0, y.0))), self.1)
}
(Time::Beat(x), Time::Beat(y)) => {
SameUnitTime(Time::Beat(BeatTime(non_nan_add(x.0, y.0))), self.1)
}
_ => unreachable!("Both are proven to have the same unit"),
}
}
}
impl<'id> Sub for SameUnitTime<'id> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
match (self.0, rhs.0) {
(Time::Clock(x), Time::Clock(y)) => {
SameUnitTime(Time::Clock(ClockTime(non_nan_sub(x.0, y.0))), self.1)
}
(Time::Beat(x), Time::Beat(y)) => {
SameUnitTime(Time::Beat(BeatTime(non_nan_sub(x.0, y.0))), self.1)
}
_ => unreachable!("Both are proven to have the same unit"),
}
}
}
impl<'id> Div for SameUnitTime<'id> {
type Output = f64;
fn div(self, rhs: Self) -> Self::Output {
match (self.0, rhs.0) {
(Time::Clock(x), Time::Clock(y)) => x.0 / y.0,
(Time::Beat(x), Time::Beat(y)) => x.0 / y.0,
_ => unreachable!("Both are proven to have the same unit"),
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ClockTime(f64);
impl Eq for ClockTime {}
impl PartialOrd for ClockTime {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ClockTime {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.partial_cmp(&other.0).expect("Time can not be NaN")
}
}
impl ClockTime {
pub const ZERO: Self = Self(0.0);
pub const ONE: Self = Self(1.0);
pub const INF: Self = Self(f64::INFINITY);
pub const NEG_INF: Self = Self(f64::NEG_INFINITY);
pub fn from_seconds(secs: f64) -> Option<Self> {
(!secs.is_nan()).then_some(Self(secs))
}
pub fn from_milis(milis: f64) -> Option<Self> {
(!milis.is_nan()).then_some(Self(milis / 1000.0))
}
pub const fn from_duration(dur: std::time::Duration) -> Self {
Self(dur.as_secs_f64())
}
pub const fn seconds(self) -> f64 {
self.0
}
pub fn duration(self) -> Result<std::time::Duration, std::time::TryFromFloatSecsError> {
std::time::Duration::try_from_secs_f64(self.0)
}
pub fn scale(self, factor: f64) -> Option<Self> {
checked_mul(self.0, factor).map(Self)
}
}
impl Add for ClockTime {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(non_nan_add(self.0, rhs.0))
}
}
impl Sub for ClockTime {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(non_nan_sub(self.0, rhs.0))
}
}
impl Div for ClockTime {
type Output = f64;
fn div(self, rhs: Self) -> Self::Output {
self.0 / rhs.0
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BeatTime(f64);
impl Eq for BeatTime {}
impl PartialOrd for BeatTime {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for BeatTime {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.partial_cmp(&other.0).expect("Time can not be NaN")
}
}
impl BeatTime {
pub const ZERO: Self = Self(0.0);
pub const ONE: Self = Self(1.0);
pub const INF: Self = Self(f64::INFINITY);
pub fn new(beats: f64) -> Option<Self> {
(!beats.is_nan()).then_some(Self(beats))
}
pub fn beats(self) -> f64 {
self.0
}
pub fn scale(self, factor: f64) -> Option<Self> {
checked_mul(self.0, factor).map(Self)
}
}
impl Add for BeatTime {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(non_nan_add(self.0, rhs.0))
}
}
impl Sub for BeatTime {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(non_nan_sub(self.0, rhs.0))
}
}
impl Div for BeatTime {
type Output = f64;
fn div(self, rhs: Self) -> Self::Output {
self.0 / rhs.0
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SameUnitTimeVec {
Clock(Vec<ClockTime>),
Beat(Vec<BeatTime>),
}
impl SameUnitTimeVec {
pub fn try_from_iter(times: impl ExactSizeIterator<Item = Time>) -> Option<Self> {
let len = times.len();
let mut peekable = times.peekable();
let Some(first) = peekable.peek().copied() else {
return Some(Self::Clock(vec![]));
};
match first {
Time::Clock(_) => {
let mut v = Vec::with_capacity(len);
for t in peekable {
let Time::Clock(t) = t else {
return None;
};
v.push(t);
}
Some(Self::Clock(v))
}
Time::Beat(_) => {
let mut v = Vec::with_capacity(len);
for t in peekable {
let Time::Beat(t) = t else {
return None;
};
v.push(t);
}
Some(Self::Beat(v))
}
}
}
pub fn from_proven_iter<'id>(times: impl ExactSizeIterator<Item = SameUnitTime<'id>>) -> Self {
Self::try_from_iter(times.map(|x| x.into())).expect("Proven to be same unit")
}
pub fn len(&self) -> usize {
match self {
Self::Clock(times) => times.len(),
Self::Beat(times) => times.len(),
}
}
pub fn is_empty(&self) -> bool {
match self {
Self::Clock(times) => times.is_empty(),
Self::Beat(times) => times.is_empty(),
}
}
pub fn index(&self, i: usize) -> Time {
match self {
Self::Clock(times) => Time::Clock(ClockTime(times[i].0)),
Self::Beat(times) => Time::Beat(BeatTime(times[i].0)),
}
}
pub fn unit_match(&self, t: Time) -> bool {
matches!(
(self, t),
(SameUnitTimeVec::Clock(_), Time::Clock(_)) | (SameUnitTimeVec::Beat(_), Time::Beat(_))
)
}
pub fn try_insert(&mut self, index: usize, time: Time) -> Result<(), SameUnitTimeVecEditError> {
match (self, time) {
(SameUnitTimeVec::Clock(list), Time::Clock(t)) => {
if index > list.len() {
return Err(SameUnitTimeVecEditError::OutOfBound);
}
list.insert(index, t);
Ok(())
}
(SameUnitTimeVec::Beat(list), Time::Beat(t)) => {
if index > list.len() {
return Err(SameUnitTimeVecEditError::OutOfBound);
}
list.insert(index, t);
Ok(())
}
_ => Err(SameUnitTimeVecEditError::UnitMismatch),
}
}
pub fn try_set(&mut self, index: usize, time: Time) -> Result<(), SameUnitTimeVecEditError> {
match (self, time) {
(SameUnitTimeVec::Clock(list), Time::Clock(t)) => {
if index >= list.len() {
return Err(SameUnitTimeVecEditError::OutOfBound);
}
list[index] = t;
Ok(())
}
(SameUnitTimeVec::Beat(list), Time::Beat(t)) => {
if index >= list.len() {
return Err(SameUnitTimeVecEditError::OutOfBound);
}
list[index] = t;
Ok(())
}
_ => Err(SameUnitTimeVecEditError::UnitMismatch),
}
}
pub fn remove(&mut self, index: usize) -> Time {
match self {
SameUnitTimeVec::Clock(list) => Time::Clock(list.remove(index)),
SameUnitTimeVec::Beat(list) => Time::Beat(list.remove(index)),
}
}
pub fn swap(&mut self, i: usize, j: usize) {
match self {
SameUnitTimeVec::Clock(list) => list.swap(i, j),
SameUnitTimeVec::Beat(list) => list.swap(i, j),
}
}
pub fn read_with_guard<'id>(
&'id self,
g: generativity::Guard<'id>,
) -> SameUnitTimeVecRead<'id> {
SameUnitTimeVecRead(self, g.into())
}
}
pub struct SameUnitTimeVecRead<'id>(&'id SameUnitTimeVec, generativity::Id<'id>);
impl<'id> SameUnitTimeVecRead<'id> {
pub fn index(&self, i: usize) -> SameUnitTime<'id> {
SameUnitTime(self.0.index(i), self.1)
}
pub fn prove_same_unit(self, other: Time) -> Option<SameUnitTime<'id>> {
match (self.0, other) {
(SameUnitTimeVec::Clock(_), Time::Clock(_))
| (SameUnitTimeVec::Beat(_), Time::Beat(_)) => Some(SameUnitTime(other, self.1)),
_ => None,
}
}
}
#[derive(Debug)]
pub enum SameUnitTimeVecEditError {
OutOfBound,
UnitMismatch,
}
impl std::fmt::Display for SameUnitTimeVecEditError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SameUnitTimeVecEditError::OutOfBound => {
write!(f, "Indexing a vec with an index that is out of bound")
}
SameUnitTimeVecEditError::UnitMismatch => {
write!(
f,
"Adding an Time to a SameUnitVec that does not have matching unit"
)
}
}
}
}
impl std::error::Error for SameUnitTimeVecEditError {}
impl Default for SameUnitTimeVec {
fn default() -> Self {
Self::Clock(vec![])
}
}
fn non_nan_add(a: f64, b: f64) -> f64 {
let r = a + b;
debug_assert!(!r.is_nan());
if r.is_nan() { f64::INFINITY } else { r }
}
fn non_nan_sub(a: f64, b: f64) -> f64 {
let r = a - b;
debug_assert!(!r.is_nan());
if r.is_nan() { f64::INFINITY } else { r }
}
fn checked_mul(a: f64, b: f64) -> Option<f64> {
let r = a * b;
(!r.is_nan()).then_some(r)
}