Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@typetetris
Created November 25, 2021 13:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save typetetris/095e18bd16de31ec57a89c51c7c818e4 to your computer and use it in GitHub Desktop.
Save typetetris/095e18bd16de31ec57a89c51c7c818e4 to your computer and use it in GitHub Desktop.
pub trait Filter {
type Input: Copy;
type Output;
fn process(&mut self, sample: Self::Input) -> Self::Output;
}
#[derive(Copy, Clone)]
pub struct Nil<X>(pub X);
#[derive(Copy, Clone)]
pub struct Cons<X, Xs: NonEmpty>(pub X, pub Xs);
pub trait NonEmpty {}
impl<X> NonEmpty for Nil<X> {}
impl<X, Xs: NonEmpty> NonEmpty for Cons<X, Xs> {}
pub trait NonEmptyConcatSplit {
type Output: NonEmpty;
fn concat(self) -> Self::Output;
fn split(to_be_split: Self::Output) -> Self;
}
impl<X, L2: NonEmpty> NonEmptyConcatSplit for (Nil<X>, L2) {
type Output = Cons<X, L2>;
fn concat(self) -> Self::Output {
let (Nil(x), l2) = self;
Cons(x, l2)
}
fn split(Cons(x, l2): Self::Output) -> Self {
(Nil(x), l2)
}
}
impl<X, Xs, L2> NonEmptyConcatSplit for (Cons<X, Xs>, L2)
where
Xs: NonEmpty,
(Xs, L2): NonEmptyConcatSplit,
{
type Output = Cons<X, <(Xs, L2) as NonEmptyConcatSplit>::Output>;
fn concat(self) -> Self::Output {
let (Cons(x, xs), l2) = self;
Cons(x, (xs, l2).concat())
}
fn split(Cons(x, rest): Self::Output) -> Self {
let (xs, l2) = <(Xs, L2) as NonEmptyConcatSplit>::split(rest);
(Cons(x, xs), l2)
}
}
pub struct LiftedFilter<I, O, F>(F, std::marker::PhantomData<I>, std::marker::PhantomData<O>);
impl<I, O, F> LiftedFilter<I, O, F>
where
F: Filter<Input = I, Output = O>,
{
pub fn new(filter: F) -> Self {
LiftedFilter(
filter,
std::marker::PhantomData {},
std::marker::PhantomData {},
)
}
}
impl<I, O, F> Filter for LiftedFilter<I, O, F>
where
I: Copy,
F: Filter<Input = I, Output = O>,
{
type Input = Nil<I>;
type Output = Nil<O>;
fn process(&mut self, Nil(sample): Nil<I>) -> Nil<O> {
Nil(self.0.process(sample))
}
}
pub struct FilterStack<I1, O1, F1, I2, O2, F2>(
pub F1,
pub F2,
std::marker::PhantomData<I1>,
std::marker::PhantomData<O1>,
std::marker::PhantomData<I2>,
std::marker::PhantomData<O2>,
);
impl<I1, I2, O1, O2, F1, F2> FilterStack<I1, O1, F1, I2, O2, F2>
where
F1: Filter<Input = I1, Output = O1>,
F2: Filter<Input = I2, Output = O2>,
{
pub fn new(filter1: F1, filter2: F2) -> Self {
FilterStack(
filter1,
filter2,
std::marker::PhantomData {},
std::marker::PhantomData {},
std::marker::PhantomData {},
std::marker::PhantomData {},
)
}
}
impl<I1, I2, O1, O2, F1, F2> Filter for FilterStack<I1, O1, F1, I2, O2, F2>
where
I1: NonEmpty + Copy,
I2: NonEmpty + Copy,
(I1, I2): NonEmptyConcatSplit,
<(I1, I2) as NonEmptyConcatSplit>::Output: Copy,
O1: NonEmpty,
O2: NonEmpty,
(O1, O2): NonEmptyConcatSplit,
F1: Filter<Input = I1, Output = O1>,
F2: Filter<Input = I2, Output = O2>,
{
type Input = <(I1, I2) as NonEmptyConcatSplit>::Output;
type Output = <(O1, O2) as NonEmptyConcatSplit>::Output;
fn process(&mut self, sample: Self::Input) -> Self::Output {
let (i1, i2) = <(I1, I2) as NonEmptyConcatSplit>::split(sample);
let (o1, o2) = (self.0.process(i1), self.1.process(i2));
(o1, o2).concat()
}
}
pub struct Dup<I, X>(
pub std::marker::PhantomData<I>,
pub std::marker::PhantomData<X>,
);
impl<I, X> Default for Dup<I, X> {
fn default() -> Self {
Self::new()
}
}
impl<I, X> Dup<I, X> {
pub fn new() -> Self {
Dup(std::marker::PhantomData {}, std::marker::PhantomData {})
}
}
impl<I> Filter for Dup<I, Nil<I>>
where
I: Copy,
{
type Input = I;
type Output = Nil<I>;
fn process(&mut self, sample: I) -> Nil<I> {
Nil(sample)
}
}
impl<I, Is> Filter for Dup<I, Cons<I, Is>>
where
I: Copy,
Is: NonEmpty,
Dup<I, Is>: Filter<Input = I, Output = Is>,
{
type Input = I;
type Output = Cons<I, Is>;
fn process(&mut self, sample: I) -> Cons<I, Is> {
let mut rest_f: Dup<I, Is> = Dup(std::marker::PhantomData {}, std::marker::PhantomData {});
let rest = rest_f.process(sample);
Cons(sample, rest)
}
}
pub struct Attach<I1, O1, F1, O2, F2>(
F1,
F2,
std::marker::PhantomData<I1>,
std::marker::PhantomData<O1>,
std::marker::PhantomData<O2>,
);
impl<I1, O1, F1, O2, F2> Attach<I1, O1, F1, O2, F2>
where
I1: Copy,
O1: Copy,
F1: Filter<Input = I1, Output = O1>,
F2: Filter<Input = O1, Output = O2>,
{
pub fn new(filter1: F1, filter2: F2) -> Self {
Attach(
filter1,
filter2,
std::marker::PhantomData {},
std::marker::PhantomData {},
std::marker::PhantomData {},
)
}
}
impl<I1, O1, F1, O2, F2> Filter for Attach<I1, O1, F1, O2, F2>
where
I1: Copy,
O1: Copy,
F1: Filter<Input = I1, Output = O1>,
F2: Filter<Input = O1, Output = O2>,
{
type Input = I1;
type Output = O2;
fn process(&mut self, sample: I1) -> O2 {
let first = self.0.process(sample);
self.1.process(first)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Silence;
impl Filter for Silence {
type Input = f64;
type Output = f64;
fn process(&mut self, _sample: f64) -> f64 {
0f64
}
}
#[test]
fn dup() {
let stacked_silence =
FilterStack::new(LiftedFilter::new(Silence {}), LiftedFilter::new(Silence {}));
let dup: Dup<f64, Cons<f64,Nil<f64>>> = Dup::new();
let _result = Attach::new(dup, stacked_silence);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment