#[cfg(feature = "demux")]
pub mod demux;
pub mod filter;
pub mod filter_map;
pub mod flatten;
pub mod for_each;
pub mod inspect;
pub mod map;
pub mod null;
pub mod partition;
pub mod pivot;
pub mod switch;
pub mod tee;
pub mod unzip;
use std::marker::PhantomData;
use either::Either;
pub trait Pusherator: Sized {
    type Item;
    fn give(&mut self, item: Self::Item);
}
pub trait IteratorToPusherator: Iterator {
    fn pull_to_push(self) -> pivot::PivotBuild<Self>
    where
        Self: Sized,
    {
        pivot::PivotBuild::new(self)
    }
}
impl<I> IteratorToPusherator for I where I: Sized + Iterator {}
pub trait PusheratorBuild {
    type ItemOut;
    type Output<Next: Pusherator<Item = Self::ItemOut>>;
    fn push_to<Next>(self, input: Next) -> Self::Output<Next>
    where
        Next: Pusherator<Item = Self::ItemOut>;
    fn map<Func, Out>(self, func: Func) -> map::MapBuild<Self, Func>
    where
        Self: Sized,
        Func: FnMut(Self::ItemOut) -> Out,
    {
        map::MapBuild::new(self, func)
    }
    fn inspect<Func>(self, func: Func) -> inspect::InspectBuild<Self, Func>
    where
        Self: Sized,
        Func: FnMut(&Self::ItemOut),
    {
        inspect::InspectBuild::new(self, func)
    }
    fn filter<Func>(self, func: Func) -> filter::FilterBuild<Self, Func>
    where
        Self: Sized,
        Func: FnMut(&Self::ItemOut) -> bool,
    {
        filter::FilterBuild::new(self, func)
    }
    fn tee<Next1>(self, next1: Next1) -> tee::TeeBuild<Self, Next1>
    where
        Self: Sized,
        Self::ItemOut: Clone,
        Next1: Pusherator<Item = Self::ItemOut>,
    {
        tee::TeeBuild::new(self, next1)
    }
    fn unzip<Next1, Item2>(self, next1: Next1) -> unzip::UnzipBuild<Self, Next1>
    where
        Self: Sized,
        Self: PusheratorBuild<ItemOut = (Next1::Item, Item2)>,
        Next1: Pusherator,
    {
        unzip::UnzipBuild::new(self, next1)
    }
    fn switch<Next1, Item2>(self, next1: Next1) -> switch::SwitchBuild<Self, Next1>
    where
        Self: Sized,
        Self: PusheratorBuild<ItemOut = Either<Next1::Item, Item2>>,
        Next1: Pusherator,
    {
        switch::SwitchBuild::new(self, next1)
    }
    fn for_each<Func>(self, func: Func) -> Self::Output<for_each::ForEach<Func, Self::ItemOut>>
    where
        Self: Sized,
        Func: FnMut(Self::ItemOut),
    {
        self.push_to(for_each::ForEach::new(func))
    }
    #[cfg(feature = "demux")]
    fn demux<Func, Nexts>(
        self,
        func: Func,
        nexts: Nexts,
    ) -> Self::Output<demux::Demux<Func, Nexts, Self::ItemOut>>
    where
        Self: Sized,
        Func: FnMut(Self::ItemOut, &mut Nexts),
    {
        self.push_to(demux::Demux::new(func, nexts))
    }
}
pub struct InputBuild<T>(PhantomData<T>);
impl<T> Default for InputBuild<T> {
    fn default() -> Self {
        Self(PhantomData)
    }
}
impl<T> InputBuild<T> {
    pub fn new() -> Self {
        Default::default()
    }
}
impl<T> PusheratorBuild for InputBuild<T> {
    type ItemOut = T;
    type Output<O: Pusherator<Item = Self::ItemOut>> = O;
    fn push_to<O>(self, input: O) -> Self::Output<O>
    where
        O: Pusherator<Item = Self::ItemOut>,
    {
        input
    }
}
#[cfg(test)]
mod tests {
    use std::rc::Rc;
    use super::filter::Filter;
    use super::for_each::ForEach;
    use super::map::Map;
    use super::partition::Partition;
    use super::pivot::Pivot;
    use super::tee::Tee;
    use super::Pusherator;
    #[test]
    fn linear_chains() {
        let mut v = Vec::new();
        let mut pusher = Map::new(
            |x| x * 2,
            Filter::new(|x| *x > 5, ForEach::new(|x| v.push(x))),
        );
        for i in 0..5 {
            pusher.give(i);
        }
        assert_eq!(v, vec![6, 8]);
    }
    #[test]
    fn partition() {
        let mut evens = Vec::new();
        let mut odds = Vec::new();
        let mut pusher = Partition::new(
            |x| x % 2 == 0,
            ForEach::new(|x| evens.push(x)),
            ForEach::new(|x| odds.push(x)),
        );
        for i in 0..5 {
            pusher.give(i);
        }
        assert_eq!(evens, vec![0, 2, 4]);
        assert_eq!(odds, vec![1, 3]);
    }
    #[test]
    fn tee() {
        let mut left = Vec::new();
        let mut right = Vec::new();
        let mut pusher = Tee::new(
            ForEach::new(|x| left.push(x)),
            ForEach::new(|x| right.push(x)),
        );
        for i in 0..5 {
            pusher.give(i);
        }
        assert_eq!(left, vec![0, 1, 2, 3, 4]);
        assert_eq!(right, vec![0, 1, 2, 3, 4]);
    }
    #[test]
    fn tee_rcs() {
        let mut left = Vec::new();
        let mut right = Vec::new();
        let mut pusher = Map::new(
            Rc::new,
            Tee::new(
                ForEach::new(|x: Rc<i32>| left.push(*x)),
                ForEach::new(|x: Rc<i32>| right.push(*x)),
            ),
        );
        for i in 0..5 {
            pusher.give(i);
        }
        assert_eq!(left, vec![0, 1, 2, 3, 4]);
        assert_eq!(right, vec![0, 1, 2, 3, 4]);
    }
    #[test]
    fn pivot() {
        let a = 0..10;
        let b = 10..20;
        let mut left = Vec::new();
        let mut right = Vec::new();
        let pivot = Pivot::new(
            a.into_iter().chain(b),
            Partition::new(
                |x| x % 2 == 0,
                ForEach::new(|x| left.push(x)),
                ForEach::new(|x| right.push(x)),
            ),
        );
        pivot.run();
        assert_eq!(left, vec![0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
        assert_eq!(right, vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
    }
}
#[cfg(test)]
mod test_builder {
    use super::*;
    #[test]
    fn test_builder_constructed() {
        let pb = InputBuild::<usize>(PhantomData);
        let pb = filter::FilterBuild::new(pb, |&x| 0 == x % 2);
        let pb = map::MapBuild::new(pb, |x| x * x);
        let mut output = Vec::new();
        let mut pusherator = pb.push_to(for_each::ForEach::new(|x| output.push(x)));
        for x in 0..10 {
            pusherator.give(x);
        }
        assert_eq!(&[0, 4, 16, 36, 64], &*output);
    }
    #[test]
    fn test_builder() {
        let mut output = Vec::new();
        let mut pusherator = <InputBuild<usize>>::new()
            .filter(|&x| 0 == x % 2)
            .map(|x| x * x)
            .for_each(|x| output.push(x));
        for x in 0..10 {
            pusherator.give(x);
        }
        assert_eq!(&[0, 4, 16, 36, 64], &*output);
    }
    #[test]
    fn test_builder_tee() {
        let mut output_evn = Vec::new();
        let mut output_odd = Vec::new();
        let mut pusherator = <InputBuild<usize>>::new()
            .tee(
                <InputBuild<usize>>::new()
                    .filter(|&x| 0 == x % 2)
                    .for_each(|x| output_evn.push(x)),
            )
            .filter(|&x| 1 == x % 2)
            .for_each(|x| output_odd.push(x));
        for x in 0..10 {
            pusherator.give(x);
        }
        assert_eq!(&[0, 2, 4, 6, 8], &*output_evn);
        assert_eq!(&[1, 3, 5, 7, 9], &*output_odd);
    }
    #[test]
    fn test_built_subgraph() {
        let mut output_evn = Vec::new();
        let mut output_odd = Vec::new();
        let pivot = [1, 2, 3, 4, 5]
            .into_iter()
            .chain([3, 4, 5, 6, 7])
            .map(|x| x * 9)
            .pull_to_push()
            .map(|x| if 0 == x % 2 { x / 2 } else { 3 * x + 1 })
            .tee(
                <InputBuild<usize>>::new()
                    .filter(|&x| 0 == x % 2)
                    .for_each(|x| output_evn.push(x)),
            )
            .filter(|&x| 1 == x % 2)
            .for_each(|x| output_odd.push(x));
        pivot.run();
        assert_eq!(&[28, 82, 18, 136, 82, 18, 136, 190], &*output_evn);
        assert_eq!(&[9, 27], &*output_odd);
    }
}