feat!(core): Use new AST structure

Derive Expr methods using a derive proc-macro. These macros are present in
the `argus-derive` crate, but the traits are defined in `argus-core`
This commit is contained in:
Anand Balakrishnan 2023-06-06 10:44:45 -04:00
parent 70c5a50d22
commit 1c79847a77
No known key found for this signature in database
22 changed files with 958 additions and 702 deletions

View file

@ -1,7 +1,7 @@
use core::iter::zip;
use core::time::Duration;
use itertools::{enumerate, Itertools};
use itertools::Itertools;
use super::traits::LinearInterpolatable;
use super::{InterpolationMethod, Signal};
@ -23,7 +23,7 @@ where
// Find the first index that satisfies `t >= delta` while also checking
// if we need to interpolate
let Some((idx, first_t)) = time_points.into_iter().find_position(|&t| t >= &delta)
let Some((idx, first_t)) = time_points.iter().find_position(|&t| t >= &delta)
else {
// Return an empty signal (we exhauseted all samples).
return Signal::Empty;

View file

@ -6,6 +6,7 @@ use std::time::Duration;
use paste::paste;
use super::utils::Neighborhood;
use super::{Sample, Signal};
use crate::ArgusResult;
@ -18,6 +19,12 @@ pub trait LinearInterpolatable {
fn interpolate_at(a: &Sample<Self>, b: &Sample<Self>, time: Duration) -> Self
where
Self: Sized;
/// Given two signals with two sample points each, find the intersection of the two
/// lines.
fn find_intersection(a: &Neighborhood<Self>, b: &Neighborhood<Self>) -> Sample<Self>
where
Self: Sized;
}
impl LinearInterpolatable for bool {
@ -29,6 +36,45 @@ impl LinearInterpolatable for bool {
// We can't linear interpolate a boolean, so we return the previous.
a.value
}
fn find_intersection(a: &Neighborhood<Self>, b: &Neighborhood<Self>) -> Sample<Self>
where
Self: Sized,
{
let Sample { time: ta1, value: ya1 } = a.first.unwrap();
let Sample { time: ta2, value: ya2 } = a.second.unwrap();
let Sample { time: tb1, value: yb1 } = b.first.unwrap();
let Sample { time: tb2, value: yb2 } = b.second.unwrap();
let left_cmp = ya1.cmp(&yb1);
let right_cmp = ya2.cmp(&yb2);
if left_cmp.is_eq() {
// They already intersect, so we return the inner time-point
if ta1 < tb1 {
Sample { time: tb1, value: yb1 }
} else {
Sample { time: ta1, value: ya1 }
}
} else if right_cmp.is_eq() {
// They intersect at the end, so we return the outer time-point, as that is
// when they become equal.
if ta2 < tb2 {
Sample { time: tb2, value: yb2 }
} else {
Sample { time: ta2, value: ya2 }
}
} else {
// The switched, so the one that switched earlier will intersect with the
// other.
// So, we find the one that has a lower time point, i.e., the inner one.
if ta2 < tb2 {
Sample { time: ta2, value: ya2 }
} else {
Sample { time: tb2, value: yb2 }
}
}
}
}
macro_rules! interpolate_for_num {
@ -66,6 +112,38 @@ macro_rules! interpolate_for_num {
cast(val).unwrap()
}
fn find_intersection(a: &Neighborhood<Self>, b: &Neighborhood<Self>) -> Sample<Self>
where
Self: Sized,
{
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
use num_traits::cast;
let Sample { time: t1, value: y1 } = a.first.unwrap();
let Sample { time: t2, value: y2 } = a.second.unwrap();
let Sample { time: t3, value: y3 } = b.first.unwrap();
let Sample { time: t4, value: y4 } = b.second.unwrap();
let t1 = t1.as_secs_f64();
let t2 = t2.as_secs_f64();
let t3 = t3.as_secs_f64();
let t4 = t4.as_secs_f64();
let y1: f64 = cast(y1).unwrap();
let y2: f64 = cast(y2).unwrap();
let y3: f64 = cast(y3).unwrap();
let y4: f64 = cast(y4).unwrap();
let denom = ((t1 - t2) * (y3 - y4)) - ((y1 - y2) * (t3 - t4));
let t_top = (((t1 * y2) - (y1 * t2)) * (t3 - t4)) - ((t1 - t2) * (t3 * y4 - y3 * t4));
let y_top = (((t1 * y2) - (y1 * t2)) * (y3 - y4)) - ((y1 - y2) * (t3 * y4 - y3 * t4));
let t = Duration::from_secs_f64(t_top / denom);
let y: Self = cast(y_top / denom).unwrap();
Sample { time: t, value: y }
}
}
};
}

View file

@ -8,8 +8,6 @@ use core::ops::{Bound, RangeBounds};
use core::time::Duration;
use std::iter::zip;
use num_traits::NumCast;
use super::traits::LinearInterpolatable;
use super::{InterpolationMethod, Sample, Signal};
@ -22,45 +20,11 @@ use super::{InterpolationMethod, Sample, Signal};
/// This can be used to interpolate the value at the given `at` time using strategies
/// like constant previous, constant following, and linear interpolation.
#[derive(Copy, Clone, Debug)]
pub struct Neighborhood<T: ?Sized + Copy> {
pub struct Neighborhood<T> {
pub first: Option<Sample<T>>,
pub second: Option<Sample<T>>,
}
/// Given two signals with two sample points each, find the intersection of the two
/// lines.
pub fn find_intersection<T>(a: &Neighborhood<T>, b: &Neighborhood<T>) -> Sample<T>
where
T: Copy + NumCast,
{
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
use num_traits::cast;
let Sample { time: t1, value: y1 } = a.first.unwrap();
let Sample { time: t2, value: y2 } = a.second.unwrap();
let Sample { time: t3, value: y3 } = b.first.unwrap();
let Sample { time: t4, value: y4 } = b.second.unwrap();
let t1 = t1.as_secs_f64();
let t2 = t2.as_secs_f64();
let t3 = t3.as_secs_f64();
let t4 = t4.as_secs_f64();
let y1: f64 = cast(y1).unwrap();
let y2: f64 = cast(y2).unwrap();
let y3: f64 = cast(y3).unwrap();
let y4: f64 = cast(y4).unwrap();
let denom = ((t1 - t2) * (y3 - y4)) - ((y1 - y2) * (t3 - t4));
let t_top = (((t1 * y2) - (y1 * t2)) * (t3 - t4)) - ((t1 - t2) * (t3 * y4 - y3 * t4));
let y_top = (((t1 * y2) - (y1 * t2)) * (y3 - y4)) - ((y1 - y2) * (t3 * y4 - y3 * t4));
let t = Duration::from_secs_f64(t_top / denom);
let y: T = cast(y_top / denom).unwrap();
Sample { time: t, value: y }
}
#[inline]
pub fn apply1<T, U, F>(signal: &Signal<T>, op: F) -> Signal<U>
where