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)
|
||||
pub type ArgusError = Error;
|
||||
/// Alias for [`Result<T, ArgusError>`]
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ impl<T> Signal<T> {
|
|||
/// Augment synchronization points with time points where signals intersect
|
||||
pub fn sync_with_intersection<Interp>(&self, other: &Signal<T>) -> Option<Vec<Duration>>
|
||||
where
|
||||
T: PartialOrd + Copy,
|
||||
T: PartialOrd + Clone,
|
||||
Interp: FindIntersectionMethod<T>,
|
||||
{
|
||||
use core::cmp::Ordering::*;
|
||||
|
|
@ -289,12 +289,12 @@ impl<T> Signal<T> {
|
|||
if let (Less, Greater) | (Greater, Less) = (last, ord) {
|
||||
// Find the point of intersection between the points.
|
||||
let a = utils::Neighborhood {
|
||||
first: self.at(tm1).copied().map(|value| Sample { time: tm1, value }),
|
||||
second: self.at(*t).copied().map(|value| Sample { time: *t, value }),
|
||||
first: self.at(tm1).cloned().map(|value| Sample { time: tm1, value }),
|
||||
second: self.at(*t).cloned().map(|value| Sample { time: *t, value }),
|
||||
};
|
||||
let b = utils::Neighborhood {
|
||||
first: other.at(tm1).copied().map(|value| Sample { time: tm1, value }),
|
||||
second: other.at(*t).copied().map(|value| Sample { time: *t, value }),
|
||||
first: other.at(tm1).cloned().map(|value| Sample { time: tm1, value }),
|
||||
second: other.at(*t).cloned().map(|value| Sample { time: *t, value }),
|
||||
};
|
||||
let intersect = Interp::find_intersection(&a, &b);
|
||||
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
|
||||
///
|
||||
/// 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 {
|
||||
Signal::Empty => None,
|
||||
Signal::Constant { value } => Some(*value),
|
||||
Signal::Constant { value } => Some(value.clone()),
|
||||
Signal::Sampled { values, time_points } => {
|
||||
assert_eq!(
|
||||
time_points.len(),
|
||||
|
|
@ -339,7 +339,7 @@ impl<T: Copy> Signal<T> {
|
|||
|
||||
// We will use binary search to find the appropriate index
|
||||
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,
|
||||
};
|
||||
|
||||
|
|
@ -349,22 +349,22 @@ impl<T: Copy> Signal<T> {
|
|||
// Sample appears before the start of the signal
|
||||
// So, let's return just the following sample, which is the first sample
|
||||
// (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() {
|
||||
// Sample appears past the end of the signal
|
||||
// So, let's return just the preceding sample, which is the last sample
|
||||
// (since we know the signal is non-empty)
|
||||
Some(values[hint_idx - 1])
|
||||
Some(values[hint_idx - 1].clone())
|
||||
} else {
|
||||
// The sample should exist within the signal.
|
||||
assert!(time_points.len() >= 2, "There should be at least 2 elements");
|
||||
let first = Sample {
|
||||
time: time_points[hint_idx - 1],
|
||||
value: values[hint_idx - 1],
|
||||
value: values[hint_idx - 1].clone(),
|
||||
};
|
||||
let second = Sample {
|
||||
time: time_points[hint_idx],
|
||||
value: values[hint_idx],
|
||||
value: values[hint_idx].clone(),
|
||||
};
|
||||
Interp::at(&first, &second, time)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,29 @@
|
|||
use super::interpolation::Linear;
|
||||
use crate::signals::utils::{apply1, apply2};
|
||||
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> {
|
||||
type Output = Signal<bool>;
|
||||
|
||||
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`].
|
||||
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.
|
||||
|
|
@ -28,22 +45,38 @@ impl Signal<bool> {
|
|||
///
|
||||
/// See [`Signal::sync_with_intersection`].
|
||||
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>;
|
||||
|
||||
fn bitand(self, other: Self) -> Self::Output {
|
||||
fn bitand(self, other: &Signal<bool>) -> Self::Output {
|
||||
self.and(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::BitOr<Self> for &Signal<bool> {
|
||||
impl core::ops::BitAnd<&Signal<bool>> for &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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,124 +1,28 @@
|
|||
use core::iter::zip;
|
||||
|
||||
use crate::signals::traits::{SignalNumCast, TrySignalCast};
|
||||
use crate::signals::Signal;
|
||||
use crate::{ArgusError, ArgusResult};
|
||||
|
||||
macro_rules! impl_bool_to_num {
|
||||
($to:ty) => {
|
||||
paste::paste! {
|
||||
#[inline]
|
||||
fn [<to_ $to>](&self) -> Option<Signal<$to>> {
|
||||
match self {
|
||||
Signal::Empty => Some(Signal::Empty),
|
||||
Signal::Constant { value } => num_traits::cast::<_, $to>(*value as i64).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 {
|
||||
impl<T> Signal<T>
|
||||
where
|
||||
T: num_traits::NumCast + Copy,
|
||||
{
|
||||
/// Cast a numeric signal to another numeric signal
|
||||
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::Constant { value } => {
|
||||
let value: f32 = if *value { f32::INFINITY } else { f32::NEG_INFINITY };
|
||||
Some(Signal::Constant { value })
|
||||
}
|
||||
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::<_, f32>(v as i64)?;
|
||||
let val: U = num_traits::cast(v)?;
|
||||
Some((t, val))
|
||||
})
|
||||
.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(),
|
||||
}
|
||||
ret.ok_or(ArgusError::invalid_cast::<T, U>())
|
||||
}
|
||||
}
|
||||
|
||||
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) => {
|
||||
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 super::interpolation::Linear;
|
||||
use super::traits::{SignalMinMax, SignalPartialOrd};
|
||||
use super::traits::SignalPartialOrd;
|
||||
use super::{FindIntersectionMethod, InterpolationMethod, Signal};
|
||||
|
||||
impl<T> SignalPartialOrd<Self> for Signal<T>
|
||||
impl<T> SignalPartialOrd for Signal<T>
|
||||
where
|
||||
T: PartialOrd + Copy,
|
||||
T: PartialOrd + Clone,
|
||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
||||
{
|
||||
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
|
||||
T: PartialOrd + Copy,
|
||||
T: PartialOrd + Clone,
|
||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
fn min(&self, other: &Self) -> Self::Output {
|
||||
/// Compute the time-wise min of two signals
|
||||
pub fn min(&self, other: &Self) -> Self {
|
||||
let time_points = self.sync_with_intersection::<Linear>(other).unwrap();
|
||||
time_points
|
||||
.into_iter()
|
||||
|
|
@ -55,7 +54,8 @@ where
|
|||
.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();
|
||||
time_points
|
||||
.into_iter()
|
||||
|
|
|
|||
|
|
@ -44,10 +44,7 @@ impl<'a, T> IntoIterator for &'a Signal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> FromIterator<(Duration, T)> for Signal<T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
impl<T> FromIterator<(Duration, T)> for Signal<T> {
|
||||
/// Takes a sequence of sample points and creates a signal.
|
||||
///
|
||||
/// # Panics
|
||||
|
|
|
|||
|
|
@ -1,104 +1,186 @@
|
|||
use num_traits::Signed;
|
||||
|
||||
use super::interpolation::Linear;
|
||||
use super::traits::SignalAbs;
|
||||
use super::{FindIntersectionMethod, InterpolationMethod};
|
||||
use crate::signals::utils::{apply1, apply2};
|
||||
use super::{FindIntersectionMethod, InterpolationMethod, SignalAbs};
|
||||
use crate::signals::Signal;
|
||||
|
||||
impl<T> core::ops::Neg for &Signal<T>
|
||||
impl<T> core::ops::Neg for Signal<T>
|
||||
where
|
||||
T: Signed + Copy,
|
||||
T: core::ops::Neg<Output = T>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
/// Negate the signal at each time point
|
||||
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
|
||||
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>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
/// Add the given signal with another
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
apply2::<_, _, _, Linear>(self, rhs, |lhs, rhs| lhs + rhs)
|
||||
fn add(self, other: &Signal<T>) -> Signal<T> {
|
||||
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
|
||||
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>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
/// Multiply the given signal with another
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
apply2::<_, _, _, Linear>(self, rhs, |lhs, rhs| lhs * rhs)
|
||||
fn mul(self, rhs: &Signal<T>) -> Signal<T> {
|
||||
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
|
||||
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>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
/// Subtract the given signal with another
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
// This has to be manually implemented and cannot use the apply2 functions.
|
||||
fn sub(self, other: &Signal<T>) -> Signal<T> {
|
||||
// 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
|
||||
// an intermediate point where the two signals are equal. This point must be
|
||||
// added to the signal appropriately.
|
||||
|
||||
// If either of the signals are empty, we return an empty signal.
|
||||
if self.is_empty() || rhs.is_empty() {
|
||||
return Signal::new();
|
||||
use Signal::*;
|
||||
match (self, other) {
|
||||
// If either of the signals are empty, we return an empty signal.
|
||||
(Empty, _) | (_, Empty) => Signal::Empty,
|
||||
(Constant { value: v1 }, Constant { value: v2 }) => Signal::constant(v1 - v2),
|
||||
(lhs, rhs) => {
|
||||
// the union of the sample points in self and other
|
||||
let sync_points = lhs.sync_with_intersection::<Linear>(rhs).unwrap();
|
||||
sync_points
|
||||
.into_iter()
|
||||
.map(|t| {
|
||||
let lhs = lhs.interpolate_at::<Linear>(t).unwrap();
|
||||
let rhs = rhs.interpolate_at::<Linear>(t).unwrap();
|
||||
(t, &lhs - &rhs)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
// the union of the sample points in self and other
|
||||
let sync_points = self.sync_with_intersection::<Linear>(rhs).unwrap();
|
||||
sync_points
|
||||
.into_iter()
|
||||
.map(|t| {
|
||||
let lhs = self.interpolate_at::<Linear>(t).unwrap();
|
||||
let rhs = rhs.interpolate_at::<Linear>(t).unwrap();
|
||||
(t, lhs - rhs)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::Div for &Signal<T>
|
||||
impl<T> core::ops::Sub<&Signal<T>> for Signal<T>
|
||||
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>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
/// Divide the given signal with another
|
||||
fn div(self, rhs: Self) -> Self::Output {
|
||||
apply2::<_, _, _, Linear>(self, rhs, |lhs, rhs| lhs / rhs)
|
||||
fn div(self, rhs: &Signal<T>) -> Self {
|
||||
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
|
||||
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>,
|
||||
{
|
||||
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`
|
||||
fn pow(self, other: Self) -> Self::Output {
|
||||
apply2::<_, _, _, Linear>(self, other, |lhs, rhs| lhs.pow(rhs))
|
||||
pub fn pow(&self, other: &Self) -> Self {
|
||||
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> {
|
||||
/// Return the absolute value for each sample in the signal
|
||||
fn abs(&self) -> Signal<$ty> {
|
||||
apply1(self, |v| v.abs())
|
||||
fn abs(self) -> Signal<$ty> {
|
||||
self.unary_op(|v| v.abs())
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
|
@ -119,7 +201,7 @@ signal_abs_impl!(i64, f32, f64);
|
|||
|
||||
impl SignalAbs for Signal<u64> {
|
||||
/// Return the absolute value for each sample in the signal
|
||||
fn abs(&self) -> Signal<u64> {
|
||||
apply1(self, |v| v)
|
||||
fn abs(self) -> Signal<u64> {
|
||||
self.unary_op(|v| v)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use paste::paste;
|
|||
|
||||
use super::utils::Neighborhood;
|
||||
use super::{Sample, Signal};
|
||||
use crate::ArgusResult;
|
||||
|
||||
/// Trait implemented by interpolation strategies
|
||||
pub trait InterpolationMethod<T> {
|
||||
|
|
@ -85,41 +84,8 @@ pub trait SignalMinMax<Rhs = Self> {
|
|||
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
|
||||
pub trait SignalAbs {
|
||||
/// Compute the absolute value of the given signal
|
||||
fn abs(&self) -> Self;
|
||||
fn abs(self) -> Self;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,54 +24,46 @@ pub struct Neighborhood<T> {
|
|||
pub second: Option<Sample<T>>,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn apply1<T, U, F>(signal: &Signal<T>, op: F) -> Signal<U>
|
||||
where
|
||||
T: Copy,
|
||||
F: Fn(T) -> 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()
|
||||
impl<T> Signal<T> {
|
||||
pub(crate) fn unary_op<U, F>(self, op: F) -> Signal<U>
|
||||
where
|
||||
F: Fn(T) -> U,
|
||||
Signal<U>: std::iter::FromIterator<(Duration, U)>,
|
||||
{
|
||||
use Signal::*;
|
||||
match self {
|
||||
Empty => Signal::Empty,
|
||||
Constant { value } => Signal::constant(op(value)),
|
||||
Sampled { values, time_points } => zip(time_points.into_iter(), values.into_iter().map(op)).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::*;
|
||||
// If either of the signals are empty, we return an empty signal.
|
||||
if lhs.is_empty() || rhs.is_empty() {
|
||||
// Intersection with empty signal should yield an empty signal
|
||||
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) => {
|
||||
// We determine the range of the signal (as the output signal can only be
|
||||
// defined in the domain where both signals are defined).
|
||||
let time_points = lhs.sync_points(rhs).unwrap();
|
||||
// Now, at each of the merged time points, we sample each signal and operate on
|
||||
// them
|
||||
time_points
|
||||
.into_iter()
|
||||
.map(|t| {
|
||||
let v1 = lhs.interpolate_at::<Interp>(*t).unwrap();
|
||||
let v2 = rhs.interpolate_at::<Interp>(*t).unwrap();
|
||||
(*t, op(v1, v2))
|
||||
})
|
||||
.collect()
|
||||
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.
|
||||
(Empty, _) | (_, Empty) => Signal::Empty,
|
||||
(Constant { value: v1 }, Constant { value: v2 }) => Signal::constant(op(v1, v2)),
|
||||
(lhs, rhs) => {
|
||||
// We determine the range of the signal (as the output signal can only be
|
||||
// defined in the domain where both signals are defined).
|
||||
let time_points = lhs.sync_points(rhs).unwrap();
|
||||
// Now, at each of the merged time points, we sample each signal and operate on
|
||||
// them
|
||||
time_points
|
||||
.into_iter()
|
||||
.map(|t| {
|
||||
let v1 = lhs.interpolate_at::<Interp>(*t).unwrap();
|
||||
let v2 = rhs.interpolate_at::<Interp>(*t).unwrap();
|
||||
(*t, op(&v1, &v2))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue