refactor!(argus-core): remove unnecessary traits and Copy constraints

This commit is contained in:
Anand Balakrishnan 2023-08-29 18:16:10 -07:00
parent 86cef692dc
commit 28a79cb88c
No known key found for this signature in database
9 changed files with 255 additions and 271 deletions

View file

@ -87,6 +87,16 @@ pub enum Error {
}, },
} }
impl Error {
/// An [`InvalidCast`](Error::InvalidCast) error from `T` to `U`.
pub fn invalid_cast<T, U>() -> Self {
Self::InvalidCast {
from: std::any::type_name::<T>(),
to: std::any::type_name::<U>(),
}
}
}
/// Alias for [`Error`](enum@Error) /// Alias for [`Error`](enum@Error)
pub type ArgusError = Error; pub type ArgusError = Error;
/// Alias for [`Result<T, ArgusError>`] /// Alias for [`Result<T, ArgusError>`]

View file

@ -263,7 +263,7 @@ impl<T> Signal<T> {
/// Augment synchronization points with time points where signals intersect /// Augment synchronization points with time points where signals intersect
pub fn sync_with_intersection<Interp>(&self, other: &Signal<T>) -> Option<Vec<Duration>> pub fn sync_with_intersection<Interp>(&self, other: &Signal<T>) -> Option<Vec<Duration>>
where where
T: PartialOrd + Copy, T: PartialOrd + Clone,
Interp: FindIntersectionMethod<T>, Interp: FindIntersectionMethod<T>,
{ {
use core::cmp::Ordering::*; use core::cmp::Ordering::*;
@ -289,12 +289,12 @@ impl<T> Signal<T> {
if let (Less, Greater) | (Greater, Less) = (last, ord) { if let (Less, Greater) | (Greater, Less) = (last, ord) {
// Find the point of intersection between the points. // Find the point of intersection between the points.
let a = utils::Neighborhood { let a = utils::Neighborhood {
first: self.at(tm1).copied().map(|value| Sample { time: tm1, value }), first: self.at(tm1).cloned().map(|value| Sample { time: tm1, value }),
second: self.at(*t).copied().map(|value| Sample { time: *t, value }), second: self.at(*t).cloned().map(|value| Sample { time: *t, value }),
}; };
let b = utils::Neighborhood { let b = utils::Neighborhood {
first: other.at(tm1).copied().map(|value| Sample { time: tm1, value }), first: other.at(tm1).cloned().map(|value| Sample { time: tm1, value }),
second: other.at(*t).copied().map(|value| Sample { time: *t, value }), second: other.at(*t).cloned().map(|value| Sample { time: *t, value }),
}; };
let intersect = Interp::find_intersection(&a, &b); let intersect = Interp::find_intersection(&a, &b);
return_points.push(intersect.time); return_points.push(intersect.time);
@ -309,7 +309,7 @@ impl<T> Signal<T> {
} }
} }
impl<T: Copy> Signal<T> { impl<T: Clone> Signal<T> {
/// Interpolate the value of the signal at the given time point /// Interpolate the value of the signal at the given time point
/// ///
/// If there exists a sample at the given time point then `Some(value)` is returned /// If there exists a sample at the given time point then `Some(value)` is returned
@ -324,7 +324,7 @@ impl<T: Copy> Signal<T> {
{ {
match self { match self {
Signal::Empty => None, Signal::Empty => None,
Signal::Constant { value } => Some(*value), Signal::Constant { value } => Some(value.clone()),
Signal::Sampled { values, time_points } => { Signal::Sampled { values, time_points } => {
assert_eq!( assert_eq!(
time_points.len(), time_points.len(),
@ -339,7 +339,7 @@ impl<T: Copy> Signal<T> {
// We will use binary search to find the appropriate index // We will use binary search to find the appropriate index
let hint_idx = match time_points.binary_search(&time) { let hint_idx = match time_points.binary_search(&time) {
Ok(idx) => return values.get(idx).copied(), Ok(idx) => return values.get(idx).cloned(),
Err(idx) => idx, Err(idx) => idx,
}; };
@ -349,22 +349,22 @@ impl<T: Copy> Signal<T> {
// Sample appears before the start of the signal // Sample appears before the start of the signal
// So, let's return just the following sample, which is the first sample // So, let's return just the following sample, which is the first sample
// (since we know that the signal is non-empty). // (since we know that the signal is non-empty).
Some(values[hint_idx]) Some(values[hint_idx].clone())
} else if hint_idx == time_points.len() { } else if hint_idx == time_points.len() {
// Sample appears past the end of the signal // Sample appears past the end of the signal
// So, let's return just the preceding sample, which is the last sample // So, let's return just the preceding sample, which is the last sample
// (since we know the signal is non-empty) // (since we know the signal is non-empty)
Some(values[hint_idx - 1]) Some(values[hint_idx - 1].clone())
} else { } else {
// The sample should exist within the signal. // The sample should exist within the signal.
assert!(time_points.len() >= 2, "There should be at least 2 elements"); assert!(time_points.len() >= 2, "There should be at least 2 elements");
let first = Sample { let first = Sample {
time: time_points[hint_idx - 1], time: time_points[hint_idx - 1],
value: values[hint_idx - 1], value: values[hint_idx - 1].clone(),
}; };
let second = Sample { let second = Sample {
time: time_points[hint_idx], time: time_points[hint_idx],
value: values[hint_idx], value: values[hint_idx].clone(),
}; };
Interp::at(&first, &second, time) Interp::at(&first, &second, time)
} }

View file

@ -1,12 +1,29 @@
use super::interpolation::Linear; use super::interpolation::Linear;
use crate::signals::utils::{apply1, apply2};
use crate::signals::Signal; use crate::signals::Signal;
impl core::ops::Not for Signal<bool> {
type Output = Signal<bool>;
fn not(self) -> Self::Output {
use Signal::*;
match self {
Empty => self,
Constant { value } => Signal::constant(!value),
signal => signal.into_iter().map(|(&t, v)| (t, !v)).collect(),
}
}
}
impl core::ops::Not for &Signal<bool> { impl core::ops::Not for &Signal<bool> {
type Output = Signal<bool>; type Output = Signal<bool>;
fn not(self) -> Self::Output { fn not(self) -> Self::Output {
apply1(self, |v| !v) use Signal::*;
match self {
Empty => Empty,
Constant { value } => Signal::constant(!value),
signal => signal.into_iter().map(|(&t, &v)| (t, !v)).collect(),
}
} }
} }
@ -18,7 +35,7 @@ impl Signal<bool> {
/// ///
/// See [`Signal::sync_with_intersection`]. /// See [`Signal::sync_with_intersection`].
pub fn and(&self, other: &Self) -> Self { pub fn and(&self, other: &Self) -> Self {
apply2::<_, _, _, Linear>(self, other, |lhs, rhs| lhs && rhs) self.binary_op::<_, _, Linear>(other, |lhs, rhs| *lhs && *rhs)
} }
/// Apply logical disjunction for each sample across the two signals. /// Apply logical disjunction for each sample across the two signals.
@ -28,22 +45,38 @@ impl Signal<bool> {
/// ///
/// See [`Signal::sync_with_intersection`]. /// See [`Signal::sync_with_intersection`].
pub fn or(&self, other: &Self) -> Self { pub fn or(&self, other: &Self) -> Self {
apply2::<_, _, _, Linear>(self, other, |lhs, rhs| lhs || rhs) self.binary_op::<_, _, Linear>(other, |lhs, rhs| *lhs || *rhs)
} }
} }
impl core::ops::BitAnd<Self> for &Signal<bool> { impl core::ops::BitAnd<&Signal<bool>> for Signal<bool> {
type Output = Signal<bool>; type Output = Signal<bool>;
fn bitand(self, other: Self) -> Self::Output { fn bitand(self, other: &Signal<bool>) -> Self::Output {
self.and(other) self.and(other)
} }
} }
impl core::ops::BitOr<Self> for &Signal<bool> { impl core::ops::BitAnd<&Signal<bool>> for &Signal<bool> {
type Output = Signal<bool>; type Output = Signal<bool>;
fn bitor(self, other: Self) -> Self::Output { fn bitand(self, other: &Signal<bool>) -> Self::Output {
self.and(other)
}
}
impl core::ops::BitOr<&Signal<bool>> for Signal<bool> {
type Output = Signal<bool>;
fn bitor(self, other: &Signal<bool>) -> Self::Output {
self.or(other)
}
}
impl core::ops::BitOr<&Signal<bool>> for &Signal<bool> {
type Output = Signal<bool>;
fn bitor(self, other: &Signal<bool>) -> Self::Output {
self.or(other) self.or(other)
} }
} }

View file

@ -1,124 +1,28 @@
use core::iter::zip; use core::iter::zip;
use crate::signals::traits::{SignalNumCast, TrySignalCast};
use crate::signals::Signal; use crate::signals::Signal;
use crate::{ArgusError, ArgusResult}; use crate::{ArgusError, ArgusResult};
macro_rules! impl_bool_to_num { impl<T> Signal<T>
($to:ty) => { where
paste::paste! { T: num_traits::NumCast + Copy,
#[inline] {
fn [<to_ $to>](&self) -> Option<Signal<$to>> { /// Cast a numeric signal to another numeric signal
match self { pub fn num_cast<U>(&self) -> ArgusResult<Signal<U>>
where
U: num_traits::NumCast,
{
let ret: Option<_> = match self {
Signal::Empty => Some(Signal::Empty), Signal::Empty => Some(Signal::Empty),
Signal::Constant { value } => num_traits::cast::<_, $to>(*value as i64).map(Signal::constant), Signal::Constant { value } => num_traits::cast(*value).map(Signal::constant),
Signal::Sampled { values, time_points } => {
zip(time_points, values)
.map(|(&t, &v)| {
let val = num_traits::cast::<_, $to>(v as i64)?;
Some((t, val))
})
.collect()
}
}
}
}
};
}
impl SignalNumCast for Signal<bool> {
impl_bool_to_num!(i8);
impl_bool_to_num!(i16);
impl_bool_to_num!(i32);
impl_bool_to_num!(i64);
impl_bool_to_num!(u8);
impl_bool_to_num!(u16);
impl_bool_to_num!(u32);
impl_bool_to_num!(u64);
#[inline]
fn to_f32(&self) -> Option<Signal<f32>> {
match self {
Signal::Empty => Some(Signal::Empty),
Signal::Constant { value } => {
let value: f32 = if *value { f32::INFINITY } else { f32::NEG_INFINITY };
Some(Signal::Constant { value })
}
Signal::Sampled { values, time_points } => zip(time_points, values) Signal::Sampled { values, time_points } => zip(time_points, values)
.map(|(&t, &v)| { .map(|(&t, &v)| {
let val = num_traits::cast::<_, f32>(v as i64)?; let val: U = num_traits::cast(v)?;
Some((t, val)) Some((t, val))
}) })
.collect(), .collect(),
}
}
#[inline]
fn to_f64(&self) -> Option<Signal<f64>> {
match self {
Signal::Empty => Some(Signal::Empty),
Signal::Constant { value } => {
let value: f64 = if *value { f64::INFINITY } else { f64::NEG_INFINITY };
Some(Signal::Constant { value })
}
Signal::Sampled { values, time_points } => zip(time_points, values)
.map(|(&t, &v)| {
let val = num_traits::cast::<_, f64>(v as i64)?;
Some((t, val))
})
.collect(),
}
}
}
macro_rules! impl_cast {
($from:ty, [$( $to:ty ),*]) => {
paste::paste! {
impl SignalNumCast for Signal<$from> {
$(
#[inline]
fn [<to_ $to>](&self) -> Option<Signal<$to>> {
match self {
Signal::Empty => Some(Signal::Empty),
Signal::Constant { value } => num_traits::cast::<_, $to>(*value).map(Signal::constant),
Signal::Sampled { values, time_points } => {
zip(time_points, values)
.map(|(&t, &v)| {
let val = num_traits::cast::<_, $to>(v)?;
Some((t, val))
})
.collect()
}
}
}
)*
}
$(
impl TrySignalCast<Signal<$to>> for Signal<$from> {
fn try_cast(&self) -> ArgusResult<Signal<$to>> {
self.[<to_ $to>]().ok_or(ArgusError::InvalidCast {
from: std::any::type_name::<$from>(),
to: std::any::type_name::<$to>(),
})
}
}
)*
}
}; };
($from:ty) => { ret.ok_or(ArgusError::invalid_cast::<T, U>())
impl_cast!($from, [i8, i16, i32, i64, u8, u16, u32, u64, f32, f64]); }
};
} }
impl_cast!(i8);
impl_cast!(i16);
impl_cast!(i32);
impl_cast!(i64);
impl_cast!(u8);
impl_cast!(u16);
impl_cast!(u32);
impl_cast!(u64);
impl_cast!(f32);
impl_cast!(f64);

View file

@ -1,12 +1,12 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use super::interpolation::Linear; use super::interpolation::Linear;
use super::traits::{SignalMinMax, SignalPartialOrd}; use super::traits::SignalPartialOrd;
use super::{FindIntersectionMethod, InterpolationMethod, Signal}; use super::{FindIntersectionMethod, InterpolationMethod, Signal};
impl<T> SignalPartialOrd<Self> for Signal<T> impl<T> SignalPartialOrd for Signal<T>
where where
T: PartialOrd + Copy, T: PartialOrd + Clone,
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>, Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
{ {
fn signal_cmp<F>(&self, other: &Self, op: F) -> Option<Signal<bool>> fn signal_cmp<F>(&self, other: &Self, op: F) -> Option<Signal<bool>>
@ -32,14 +32,13 @@ where
} }
} }
impl<T> SignalMinMax<Self> for Signal<T> impl<T> Signal<T>
where where
T: PartialOrd + Copy, T: PartialOrd + Clone,
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>, Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
{ {
type Output = Signal<T>; /// Compute the time-wise min of two signals
pub fn min(&self, other: &Self) -> Self {
fn min(&self, other: &Self) -> Self::Output {
let time_points = self.sync_with_intersection::<Linear>(other).unwrap(); let time_points = self.sync_with_intersection::<Linear>(other).unwrap();
time_points time_points
.into_iter() .into_iter()
@ -55,7 +54,8 @@ where
.collect() .collect()
} }
fn max(&self, other: &Self) -> Self::Output { /// Compute the time-wise max of two signals
pub fn max(&self, other: &Self) -> Self {
let time_points = self.sync_with_intersection::<Linear>(other).unwrap(); let time_points = self.sync_with_intersection::<Linear>(other).unwrap();
time_points time_points
.into_iter() .into_iter()

View file

@ -44,10 +44,7 @@ impl<'a, T> IntoIterator for &'a Signal<T> {
} }
} }
impl<T> FromIterator<(Duration, T)> for Signal<T> impl<T> FromIterator<(Duration, T)> for Signal<T> {
where
T: Copy,
{
/// Takes a sequence of sample points and creates a signal. /// Takes a sequence of sample points and creates a signal.
/// ///
/// # Panics /// # Panics

View file

@ -1,104 +1,186 @@
use num_traits::Signed;
use super::interpolation::Linear; use super::interpolation::Linear;
use super::traits::SignalAbs; use super::{FindIntersectionMethod, InterpolationMethod, SignalAbs};
use super::{FindIntersectionMethod, InterpolationMethod};
use crate::signals::utils::{apply1, apply2};
use crate::signals::Signal; use crate::signals::Signal;
impl<T> core::ops::Neg for &Signal<T> impl<T> core::ops::Neg for Signal<T>
where where
T: Signed + Copy, T: core::ops::Neg<Output = T>,
{ {
type Output = Signal<T>; type Output = Signal<T>;
/// Negate the signal at each time point
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
apply1(self, |v| -v) use Signal::*;
match self {
Empty => Signal::Empty,
Constant { value } => Signal::constant(value.neg()),
Sampled { values, time_points } => time_points.into_iter().zip(values.into_iter().map(|v| -v)).collect(),
}
} }
} }
impl<T> core::ops::Add for &Signal<T> impl<T> core::ops::Neg for &Signal<T>
where where
T: core::ops::Add<T, Output = T> + Copy, for<'a> &'a T: core::ops::Neg<Output = T>,
{
type Output = Signal<T>;
fn neg(self) -> Self::Output {
use Signal::*;
match self {
Empty => Signal::Empty,
Constant { value } => Signal::constant(value.neg()),
Sampled { values, time_points } => time_points
.iter()
.copied()
.zip(values.iter().map(|v| v.neg()))
.collect(),
}
}
}
impl<T> core::ops::Add<&Signal<T>> for Signal<T>
where
T: Clone,
for<'a, 'b> &'a T: core::ops::Add<&'b T, Output = T>,
Linear: InterpolationMethod<T>, Linear: InterpolationMethod<T>,
{ {
type Output = Signal<T>; type Output = Signal<T>;
/// Add the given signal with another /// Add the given signal with another
fn add(self, rhs: Self) -> Self::Output { fn add(self, other: &Signal<T>) -> Signal<T> {
apply2::<_, _, _, Linear>(self, rhs, |lhs, rhs| lhs + rhs) self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs + rhs)
} }
} }
impl<T> core::ops::Mul for &Signal<T> impl<T> core::ops::Add<&Signal<T>> for &Signal<T>
where where
T: core::ops::Mul<T, Output = T> + Copy, T: Clone,
for<'a, 'b> &'a T: core::ops::Add<&'b T, Output = T>,
Linear: InterpolationMethod<T>,
{
type Output = Signal<T>;
/// Add the given signal with another
fn add(self, other: &Signal<T>) -> Signal<T> {
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs + rhs)
}
}
impl<T> core::ops::Mul<&Signal<T>> for Signal<T>
where
for<'a, 'b> &'a T: core::ops::Mul<&'b T, Output = T>,
T: Clone,
Linear: InterpolationMethod<T>, Linear: InterpolationMethod<T>,
{ {
type Output = Signal<T>; type Output = Signal<T>;
/// Multiply the given signal with another /// Multiply the given signal with another
fn mul(self, rhs: Self) -> Self::Output { fn mul(self, rhs: &Signal<T>) -> Signal<T> {
apply2::<_, _, _, Linear>(self, rhs, |lhs, rhs| lhs * rhs) self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs * rhs)
} }
} }
impl<T> core::ops::Sub for &Signal<T> impl<T> core::ops::Mul<&Signal<T>> for &Signal<T>
where where
T: core::ops::Sub<T, Output = T> + Copy + PartialOrd, for<'a, 'b> &'a T: core::ops::Mul<&'b T, Output = T>,
T: Clone,
Linear: InterpolationMethod<T>,
{
type Output = Signal<T>;
/// Multiply the given signal with another
fn mul(self, rhs: &Signal<T>) -> Signal<T> {
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs * rhs)
}
}
impl<T> core::ops::Sub<&Signal<T>> for &Signal<T>
where
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
T: Clone + PartialOrd,
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>, Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
{ {
type Output = Signal<T>; type Output = Signal<T>;
/// Subtract the given signal with another /// Subtract the given signal with another
fn sub(self, rhs: Self) -> Self::Output { fn sub(self, other: &Signal<T>) -> Signal<T> {
// This has to be manually implemented and cannot use the apply2 functions. // This has to be manually implemented and cannot use the binary_op functions.
// This is because if we have two signals that cross each other, then there is // This is because if we have two signals that cross each other, then there is
// an intermediate point where the two signals are equal. This point must be // an intermediate point where the two signals are equal. This point must be
// added to the signal appropriately. // added to the signal appropriately.
use Signal::*;
match (self, other) {
// If either of the signals are empty, we return an empty signal. // If either of the signals are empty, we return an empty signal.
if self.is_empty() || rhs.is_empty() { (Empty, _) | (_, Empty) => Signal::Empty,
return Signal::new(); (Constant { value: v1 }, Constant { value: v2 }) => Signal::constant(v1 - v2),
} (lhs, rhs) => {
// the union of the sample points in self and other // the union of the sample points in self and other
let sync_points = self.sync_with_intersection::<Linear>(rhs).unwrap(); let sync_points = lhs.sync_with_intersection::<Linear>(rhs).unwrap();
sync_points sync_points
.into_iter() .into_iter()
.map(|t| { .map(|t| {
let lhs = self.interpolate_at::<Linear>(t).unwrap(); let lhs = lhs.interpolate_at::<Linear>(t).unwrap();
let rhs = rhs.interpolate_at::<Linear>(t).unwrap(); let rhs = rhs.interpolate_at::<Linear>(t).unwrap();
(t, lhs - rhs) (t, &lhs - &rhs)
}) })
.collect() .collect()
} }
} }
}
}
impl<T> core::ops::Div for &Signal<T> impl<T> core::ops::Sub<&Signal<T>> for Signal<T>
where where
T: core::ops::Div<T, Output = T> + Copy, for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
T: Clone + PartialOrd,
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
{
type Output = Signal<T>;
/// Subtract the given signal with another
fn sub(self, other: &Signal<T>) -> Signal<T> {
<&Self as core::ops::Sub>::sub(&self, other)
}
}
impl<T> core::ops::Div<&Signal<T>> for Signal<T>
where
for<'a, 'b> &'a T: core::ops::Div<&'b T, Output = T>,
T: Clone,
Linear: InterpolationMethod<T>, Linear: InterpolationMethod<T>,
{ {
type Output = Signal<T>; type Output = Signal<T>;
/// Divide the given signal with another /// Divide the given signal with another
fn div(self, rhs: Self) -> Self::Output { fn div(self, rhs: &Signal<T>) -> Self {
apply2::<_, _, _, Linear>(self, rhs, |lhs, rhs| lhs / rhs) self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs / rhs)
} }
} }
impl<T> num_traits::Pow<Self> for &Signal<T> impl<T> core::ops::Div<&Signal<T>> for &Signal<T>
where where
T: num_traits::Pow<T, Output = T> + Copy, for<'a, 'b> &'a T: core::ops::Div<&'b T, Output = T>,
T: Clone,
Linear: InterpolationMethod<T>, Linear: InterpolationMethod<T>,
{ {
type Output = Signal<T>; type Output = Signal<T>;
/// Divide the given signal with another
fn div(self, rhs: &Signal<T>) -> Signal<T> {
self.binary_op::<_, _, Linear>(rhs, |lhs, rhs| lhs / rhs)
}
}
impl<T> Signal<T>
where
for<'a, 'b> &'a T: num_traits::Pow<&'b T, Output = T>,
T: Clone,
Linear: InterpolationMethod<T>,
{
/// Returns the values in `self` to the power of the values in `other` /// Returns the values in `self` to the power of the values in `other`
fn pow(self, other: Self) -> Self::Output { pub fn pow(&self, other: &Self) -> Self {
apply2::<_, _, _, Linear>(self, other, |lhs, rhs| lhs.pow(rhs)) use num_traits::Pow;
self.binary_op::<_, _, Linear>(other, |lhs, rhs| lhs.pow(rhs))
} }
} }
@ -107,8 +189,8 @@ macro_rules! signal_abs_impl {
$( $(
impl SignalAbs for Signal<$ty> { impl SignalAbs for Signal<$ty> {
/// Return the absolute value for each sample in the signal /// Return the absolute value for each sample in the signal
fn abs(&self) -> Signal<$ty> { fn abs(self) -> Signal<$ty> {
apply1(self, |v| v.abs()) self.unary_op(|v| v.abs())
} }
} }
)* )*
@ -119,7 +201,7 @@ signal_abs_impl!(i64, f32, f64);
impl SignalAbs for Signal<u64> { impl SignalAbs for Signal<u64> {
/// Return the absolute value for each sample in the signal /// Return the absolute value for each sample in the signal
fn abs(&self) -> Signal<u64> { fn abs(self) -> Signal<u64> {
apply1(self, |v| v) self.unary_op(|v| v)
} }
} }

View file

@ -8,7 +8,6 @@ use paste::paste;
use super::utils::Neighborhood; use super::utils::Neighborhood;
use super::{Sample, Signal}; use super::{Sample, Signal};
use crate::ArgusResult;
/// Trait implemented by interpolation strategies /// Trait implemented by interpolation strategies
pub trait InterpolationMethod<T> { pub trait InterpolationMethod<T> {
@ -85,41 +84,8 @@ pub trait SignalMinMax<Rhs = Self> {
fn max(&self, rhs: &Rhs) -> Self::Output; fn max(&self, rhs: &Rhs) -> Self::Output;
} }
/// Trait for converting between signal types
pub trait SignalNumCast {
/// Try to convert the signal values/samples to `i8`
fn to_i8(&self) -> Option<Signal<i8>>;
/// Try to convert the signal values/samples to `i16`
fn to_i16(&self) -> Option<Signal<i16>>;
/// Try to convert the signal values/samples to `i32`
fn to_i32(&self) -> Option<Signal<i32>>;
/// Try to convert the signal values/samples to `i64`
fn to_i64(&self) -> Option<Signal<i64>>;
/// Try to convert the signal values/samples to `u8`
fn to_u8(&self) -> Option<Signal<u8>>;
/// Try to convert the signal values/samples to `u16`
fn to_u16(&self) -> Option<Signal<u16>>;
/// Try to convert the signal values/samples to `u32`
fn to_u32(&self) -> Option<Signal<u32>>;
/// Try to convert the signal values/samples to `u64`
fn to_u64(&self) -> Option<Signal<u64>>;
/// Try to convert the signal values/samples to `f32`
fn to_f32(&self) -> Option<Signal<f32>>;
/// Try to convert the signal values/samples to `f64`
fn to_f64(&self) -> Option<Signal<f64>>;
}
/// Trait to cast signal onto some type
pub trait TrySignalCast<T>: Sized + SignalNumCast {
/// Try to cast the given signal to another numeric type.
///
/// This returns a [`ArgusError::InvalidCast`](crate::Error::InvalidCast) if
/// some value in the signal isn't castable to the destination type.
fn try_cast(&self) -> ArgusResult<T>;
}
/// Trait for computing the absolute value of the samples in a signal /// Trait for computing the absolute value of the samples in a signal
pub trait SignalAbs { pub trait SignalAbs {
/// Compute the absolute value of the given signal /// Compute the absolute value of the given signal
fn abs(&self) -> Self; fn abs(self) -> Self;
} }

View file

@ -24,40 +24,31 @@ pub struct Neighborhood<T> {
pub second: Option<Sample<T>>, pub second: Option<Sample<T>>,
} }
#[inline] impl<T> Signal<T> {
pub fn apply1<T, U, F>(signal: &Signal<T>, op: F) -> Signal<U> pub(crate) fn unary_op<U, F>(self, op: F) -> Signal<U>
where where
T: Copy,
F: Fn(T) -> U, F: Fn(T) -> U,
Signal<U>: std::iter::FromIterator<(Duration, U)>, Signal<U>: std::iter::FromIterator<(Duration, U)>,
{ {
match signal { use Signal::*;
Signal::Empty => Signal::Empty, match self {
Signal::Constant { value } => Signal::Constant { value: op(*value) }, Empty => Signal::Empty,
Signal::Sampled { values, time_points } => { Constant { value } => Signal::constant(op(value)),
zip(time_points.iter().copied(), values.iter().map(|v| op(*v))).collect() Sampled { values, time_points } => zip(time_points.into_iter(), values.into_iter().map(op)).collect(),
}
} }
} }
#[inline] pub(crate) fn binary_op<U, F, Interp>(&self, other: &Signal<T>, op: F) -> Signal<U>
pub fn apply2<'a, T, U, F, Interp>(lhs: &'a Signal<T>, rhs: &'a Signal<T>, op: F) -> Signal<U>
where where
T: Copy, T: Clone,
U: Copy, F: Fn(&T, &T) -> U,
F: Fn(T, T) -> U,
Interp: InterpolationMethod<T>, Interp: InterpolationMethod<T>,
{ {
use Signal::*; use Signal::*;
match (self, other) {
// If either of the signals are empty, we return an empty signal. // If either of the signals are empty, we return an empty signal.
if lhs.is_empty() || rhs.is_empty() { (Empty, _) | (_, Empty) => Signal::Empty,
// Intersection with empty signal should yield an empty signal (Constant { value: v1 }, Constant { value: v2 }) => Signal::constant(op(v1, v2)),
return Signal::<U>::new();
}
match (lhs, rhs) {
// If either of the signals are empty, we return an empty signal.
(Empty, _) | (_, Empty) => Signal::new(),
(Constant { value: v1 }, Constant { value: v2 }) => Signal::constant(op(*v1, *v2)),
(lhs, rhs) => { (lhs, rhs) => {
// We determine the range of the signal (as the output signal can only be // We determine the range of the signal (as the output signal can only be
// defined in the domain where both signals are defined). // defined in the domain where both signals are defined).
@ -69,12 +60,13 @@ where
.map(|t| { .map(|t| {
let v1 = lhs.interpolate_at::<Interp>(*t).unwrap(); let v1 = lhs.interpolate_at::<Interp>(*t).unwrap();
let v2 = rhs.interpolate_at::<Interp>(*t).unwrap(); let v2 = rhs.interpolate_at::<Interp>(*t).unwrap();
(*t, op(v1, v2)) (*t, op(&v1, &v2))
}) })
.collect() .collect()
} }
} }
} }
}
fn partial_min<T>(a: T, b: T) -> Option<T> fn partial_min<T>(a: T, b: T) -> Option<T>
where where