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}