refactor!(argus-core): remove unnecessary traits and Copy constraints
This commit is contained in:
parent
86cef692dc
commit
28a79cb88c
9 changed files with 255 additions and 271 deletions
|
|
@ -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>`]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
|
||||||
Signal::Empty => Signal::Empty,
|
|
||||||
Signal::Constant { value } => Signal::Constant { value: op(*value) },
|
|
||||||
Signal::Sampled { values, time_points } => {
|
|
||||||
zip(time_points.iter().copied(), values.iter().map(|v| op(*v))).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn apply2<'a, T, U, F, Interp>(lhs: &'a Signal<T>, rhs: &'a Signal<T>, op: F) -> Signal<U>
|
|
||||||
where
|
|
||||||
T: Copy,
|
|
||||||
U: Copy,
|
|
||||||
F: Fn(T, T) -> U,
|
|
||||||
Interp: InterpolationMethod<T>,
|
|
||||||
{
|
|
||||||
use Signal::*;
|
use Signal::*;
|
||||||
// If either of the signals are empty, we return an empty signal.
|
match self {
|
||||||
if lhs.is_empty() || rhs.is_empty() {
|
Empty => Signal::Empty,
|
||||||
// Intersection with empty signal should yield an empty signal
|
Constant { value } => Signal::constant(op(value)),
|
||||||
return Signal::<U>::new();
|
Sampled { values, time_points } => zip(time_points.into_iter(), values.into_iter().map(op)).collect(),
|
||||||
}
|
}
|
||||||
match (lhs, rhs) {
|
}
|
||||||
|
|
||||||
|
pub(crate) fn binary_op<U, F, Interp>(&self, other: &Signal<T>, op: F) -> Signal<U>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
F: Fn(&T, &T) -> U,
|
||||||
|
Interp: InterpolationMethod<T>,
|
||||||
|
{
|
||||||
|
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.
|
||||||
(Empty, _) | (_, Empty) => Signal::new(),
|
(Empty, _) | (_, Empty) => Signal::Empty,
|
||||||
(Constant { value: v1 }, Constant { value: v2 }) => Signal::constant(op(*v1, *v2)),
|
(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,11 +60,12 @@ 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>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue