computation_types/
zip.rs

1use core::fmt;
2
3use paste::paste;
4
5use crate::{
6    impl_computation_fn_for_binary, impl_computation_fn_for_unary, impl_core_ops, Computation,
7    ComputationFn, NamedArgs, Names,
8};
9
10#[derive(Clone, Copy, Debug)]
11pub struct Zip<A, B>(pub A, pub B)
12where
13    Self: Computation;
14
15impl<A, B> Computation for Zip<A, B>
16where
17    A: Computation,
18    B: Computation,
19{
20    type Dim = (A::Dim, B::Dim);
21    type Item = (A::Item, B::Item);
22}
23
24impl_core_ops!(Zip<A, B>);
25
26impl_computation_fn_for_binary!(Zip);
27
28impl<A, B> fmt::Display for Zip<A, B>
29where
30    Self: Computation,
31    A: fmt::Display,
32    B: fmt::Display,
33{
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        write!(f, "({}, {})", self.0, self.1)
36    }
37}
38
39#[derive(Clone, Copy, Debug)]
40pub struct Fst<A>(pub A)
41where
42    Self: Computation;
43
44impl<A, DimA, DimB, ItemA, ItemB> Computation for Fst<A>
45where
46    A: Computation<Dim = (DimA, DimB), Item = (ItemA, ItemB)>,
47{
48    type Dim = DimA;
49    type Item = ItemA;
50}
51
52impl_core_ops!(Fst<A>);
53
54impl_computation_fn_for_unary!(Fst);
55
56impl<A> fmt::Display for Fst<A>
57where
58    Self: Computation,
59    A: fmt::Display,
60{
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        write!(f, "{}.0", self.0)
63    }
64}
65
66#[derive(Clone, Copy, Debug)]
67pub struct Snd<A>(pub A)
68where
69    Self: Computation;
70
71impl<A, DimA, DimB, ItemA, ItemB> Computation for Snd<A>
72where
73    A: Computation<Dim = (DimA, DimB), Item = (ItemA, ItemB)>,
74{
75    type Dim = DimB;
76    type Item = ItemB;
77}
78
79impl_computation_fn_for_unary!(Snd);
80
81impl_core_ops!(Snd<A>);
82
83impl<A> fmt::Display for Snd<A>
84where
85    Self: Computation,
86    A: fmt::Display,
87{
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        write!(f, "{}.1", self.0)
90    }
91}
92
93macro_rules! zip_n {
94    ( $n:expr, $i_first:expr, $( $i_rest:expr ),* ) => {
95        zip_n!(@combined $n, { $i_first, $( $i_rest ),* } { $i_first, $( $i_rest ),* });
96    };
97    ( @combined $n:expr, { $i_first:expr, $( $i_rest:expr ),* } { $( $i:expr ),* } ) => {
98        paste! {
99            #[derive(Clone, Copy, Debug)]
100            pub struct [<Zip $n>]< $( [<T $i>] ),* >( $( pub [<T $i>] ),* )
101            where
102                Self: Computation;
103
104            impl< $( [<T $i>] ),* > Computation for [<Zip $n>]< $( [<T $i>] ),* >
105            where
106                $( [<T $i>]: Computation ),*
107            {
108                type Dim = ( $( [<T $i>]::Dim ),* );
109                type Item = ( $( [<T $i>]::Item ),* );
110            }
111
112            impl< $( [<T $i>] ),* > ComputationFn for [<Zip $n>]< $( [<T $i>] ),* >
113            where
114                Self: Computation,
115                $( [<T $i>]: ComputationFn ),*,
116                [<Zip $n>]< $( [<T $i>]::Filled ),* >: Computation
117            {
118                type Filled = [<Zip $n>]< $( [<T $i>]::Filled ),* >;
119
120                fn fill(self, named_args: NamedArgs) -> Self::Filled {
121                    let ( $( [<args_ $i>] ),* ) = named_args
122                        .[<partition $n>]( $( &self.$i.arg_names() ),* )
123                        .unwrap_or_else(|e| panic!("{}", e,));
124                    [<Zip $n>]( $( self.$i.fill([<args_ $i>]) ),* )
125                }
126
127                fn arg_names(&self) -> Names {
128                    Names::union_many([ $( &self.$i.arg_names() ),* ])
129                }
130            }
131
132            impl_core_ops!([<Zip $n>]< $( [<T $i>] ),* >);
133
134            impl< $( [<T $i>] ),* > fmt::Display for [<Zip $n>]< $( [<T $i>] ),* >
135            where
136                Self: Computation,
137                $( [<T $i>]: fmt::Display ),*
138            {
139                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140                    "(".fmt(f)?;
141                    self.$i_first.fmt(f)?;
142                    $(
143                      ", ".fmt(f)?;
144                      self.$i_rest.fmt(f)?;
145                    )*
146                    ")".fmt(f)
147                }
148            }
149        }
150    };
151}
152
153zip_n!(3, 0, 1, 2);
154zip_n!(4, 0, 1, 2, 3);
155zip_n!(5, 0, 1, 2, 3, 4);
156zip_n!(6, 0, 1, 2, 3, 4, 5);
157zip_n!(7, 0, 1, 2, 3, 4, 5, 6);
158zip_n!(8, 0, 1, 2, 3, 4, 5, 6, 7);
159zip_n!(9, 0, 1, 2, 3, 4, 5, 6, 7, 8);
160zip_n!(10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
161zip_n!(11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
162zip_n!(12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
163zip_n!(13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
164zip_n!(14, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
165zip_n!(15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
166zip_n!(16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
167
168#[cfg(test)]
169mod tests {
170    use proptest::prelude::*;
171    use test_strategy::proptest;
172
173    use crate::{val, Computation};
174
175    use super::*;
176
177    #[proptest]
178    fn zip_should_display(x: usize, y: usize) {
179        prop_assert_eq!(
180            val!(x).zip(val!(y)).to_string(),
181            format!("({}, {})", val!(x), val!(y))
182        );
183    }
184
185    #[proptest]
186    fn fst_should_display(x: usize, y: usize) {
187        let inp = val!(x).zip(val!(y));
188        prop_assert_eq!(inp.fst().to_string(), format!("{}.0", inp));
189    }
190
191    #[proptest]
192    fn snd_should_display(x: usize, y: usize) {
193        let inp = val!(x).zip(val!(y));
194        prop_assert_eq!(inp.snd().to_string(), format!("{}.1", inp));
195    }
196
197    #[proptest]
198    fn zip3_should_display(x: usize, y: usize, z: usize) {
199        prop_assert_eq!(
200            Zip3(val!(x), val!(y), val!(z)).to_string(),
201            format!("({}, {}, {})", val!(x), val!(y), val!(z))
202        );
203    }
204}