Sat, 14 Dec 2024 09:31:27 -0500
Convex analysis basics
| 5 | 1 | /*! |
| 2 | Utilities for mapping over various container types. | |
| 3 | */ | |
| 0 | 4 | |
|
55
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
5 | #[cfg(feature = "nightly")] |
| 0 | 6 | use std::mem::MaybeUninit; |
| 7 | use itertools::izip; | |
| 8 | ||
| 5 | 9 | /// Trait for a fixed-length container type. |
| 10 | /// | |
| 11 | /// Implemented by [`Loc`][crate::loc::Loc] vectors, [`Cube`][crate::sets::Cube]s, | |
| 12 | /// and basic arrays. | |
| 0 | 13 | pub trait FixedLength<const N : usize> { |
| 5 | 14 | /// Type of elements of the container. |
| 0 | 15 | type Elem; |
| 5 | 16 | /// Type of iterators over the elements of the container. |
| 17 | type Iter : Iterator<Item = Self::Elem>; | |
| 18 | ||
| 19 | /// Returns an iteartor over the elements of the container. | |
| 0 | 20 | fn fl_iter(self) -> Self::Iter; |
| 21 | } | |
| 22 | ||
| 5 | 23 | /// Trait for a mutable fixed-length container type. |
| 0 | 24 | pub trait FixedLengthMut<const N : usize> : FixedLength<N> { |
| 5 | 25 | /// Type of iterators over references to mutable elements of the container. |
| 0 | 26 | type IterMut<'a> : Iterator<Item=&'a mut Self::Elem> where Self : 'a; |
| 5 | 27 | |
| 28 | /// Returns an iterator over mutable references to elements of the container. | |
| 0 | 29 | fn fl_iter_mut(&mut self) -> Self::IterMut<'_>; |
| 30 | } | |
| 31 | ||
| 32 | impl<A, const N : usize> FixedLength<N> for [A; N] { | |
| 33 | type Elem = A; | |
| 34 | type Iter = std::array::IntoIter<A, N>; | |
| 35 | #[inline] | |
| 36 | fn fl_iter(self) -> Self::Iter { | |
| 37 | self.into_iter() | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | impl<A, const N : usize> FixedLengthMut<N> for [A; N] { | |
| 42 | type IterMut<'a> = std::slice::IterMut<'a, A> where A : 'a; | |
| 43 | #[inline] | |
| 44 | fn fl_iter_mut(&mut self) -> Self::IterMut<'_> { | |
| 45 | self.iter_mut() | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | impl<'a, A, const N : usize> FixedLength<N> for &'a [A; N] { | |
| 50 | type Elem = &'a A; | |
| 51 | type Iter = std::slice::Iter<'a, A>; | |
| 52 | #[inline] | |
| 53 | fn fl_iter(self) -> Self::Iter { | |
| 54 | self.iter() | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | macro_rules! tuple_or_singleton { | |
| 59 | ($a:ident,) => { $a }; | |
| 60 | ($($a:ident),+) => { ($($a),+) } | |
| 61 | } | |
| 62 | ||
| 63 | macro_rules! make_mapmany { | |
| 64 | ($name:ident, $name_indexed:ident, $var0:ident $($var:ident)* ; | |
| 65 | $etype0:ident $($etype:ident)*, $ctype0:ident $($ctype:ident)*) => { | |
| 5 | 66 | /// Map over [`FixedLength`] container(s), returning an array. |
| 0 | 67 | #[inline] |
| 68 | pub fn $name< | |
| 69 | $etype0, | |
| 70 | $($etype,)* | |
| 71 | $ctype0 : FixedLength<N,Elem=$etype0>, | |
| 72 | $($ctype : FixedLength<N,Elem=$etype>,)* | |
| 73 | Res, | |
| 74 | const N : usize | |
| 75 | >( | |
| 76 | $var0 : $ctype0, | |
| 77 | $($var : $ctype,)* | |
| 78 | f : impl Fn($etype0, $($etype),*) -> Res | |
| 79 | ) -> [Res; N] { | |
| 80 | let zipit = izip!($var0.fl_iter(), $($var.fl_iter()),*); | |
| 81 | let map = zipit.map(|tuple_or_singleton!($var0, $($var),*)| f($var0, $($var),*)); | |
| 82 | collect_into_array_unchecked(map) | |
| 83 | } | |
| 84 | ||
| 5 | 85 | /// Map over [`FixedLength`] containers(s) and element indices, returning an array. |
| 0 | 86 | #[inline] |
| 87 | pub fn $name_indexed< | |
| 88 | $etype0, | |
| 89 | $($etype,)* | |
| 90 | $ctype0 : FixedLength<N,Elem=$etype0>, | |
| 91 | $($ctype : FixedLength<N,Elem=$etype>,)* | |
| 92 | Res, | |
| 93 | const N : usize | |
| 94 | >( | |
| 95 | $var0 : $ctype0, | |
| 96 | $($var : $ctype,)* | |
| 97 | f : impl Fn(usize, $etype0, $($etype),*) -> Res | |
| 98 | ) -> [Res; N] { | |
| 99 | let zipit = (0..N).zip(izip!($var0.fl_iter(), $($var.fl_iter()),*)); | |
| 100 | let map = zipit.map(|(i, tuple_or_singleton!($var0, $($var),*))| f(i, $var0, $($var),*)); | |
| 101 | collect_into_array_unchecked(map) | |
| 102 | } | |
| 103 | } | |
| 104 | } | |
| 105 | ||
| 106 | make_mapmany!(map1, map1_indexed, a; A, CA); | |
| 107 | make_mapmany!(map2, map2_indexed, a b; A B, CA CB); | |
| 108 | make_mapmany!(map3, map3_indexed, a b c; A B C, CA CB CC); | |
| 109 | make_mapmany!(map4, map4_indexed, a b c d; A B C D, CA CB CC CD); | |
| 110 | make_mapmany!(map5, map5_indexed, a b c d e; A B C D E, CA CB CC CD CE); | |
| 111 | make_mapmany!(map6, map6_indexed, a b c d e f; A B C D E F, CA CB CC CD CE CF); | |
| 112 | ||
| 113 | macro_rules! make_mapmany_mut{ | |
| 114 | ($name:ident, $name_indexed:ident, $var0:ident $($var:ident)* ; | |
| 115 | $etype0:ident $($etype:ident)*, $ctype0:ident $($ctype:ident)*) => { | |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
116 | /// Map over [`FixedLength`] container(s) with mutable references to the first container. |
| 0 | 117 | #[inline] |
| 118 | pub fn $name< | |
| 119 | $etype0, | |
| 120 | $($etype,)* | |
| 121 | $ctype0 : FixedLengthMut<N,Elem=$etype0>, | |
| 122 | $($ctype : FixedLength<N,Elem=$etype>,)* | |
| 123 | const N : usize | |
| 124 | > ( | |
| 125 | $var0 : &mut $ctype0, | |
| 126 | $($var : $ctype,)* | |
| 127 | f : impl Fn(&mut $etype0, $($etype),*) | |
| 128 | ) { | |
| 129 | let zipit = izip!($var0.fl_iter_mut(), $($var.fl_iter()),*); | |
| 130 | zipit.for_each(|tuple_or_singleton!($var0, $($var),*)| f($var0, $($var),*)); | |
| 131 | } | |
| 132 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
133 | /// Map over [`FixedLength`] container(s) and element indices |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
134 | /// with mutable references to the first container. |
| 0 | 135 | #[inline] |
| 136 | pub fn $name_indexed< | |
| 137 | $etype0, | |
| 138 | $($etype,)* | |
| 139 | $ctype0 : FixedLengthMut<N,Elem=$etype0>, | |
| 140 | $($ctype : FixedLength<N,Elem=$etype>,)* | |
| 141 | const N : usize | |
| 142 | > ( | |
| 143 | $var0 : &mut $ctype0, | |
| 144 | $($var : $ctype,)* | |
| 145 | f : impl Fn(usize, &mut $etype0, $($etype),*) | |
| 146 | ) { | |
| 147 | let zipit = (0..N).zip(izip!($var0.fl_iter_mut(), $($var.fl_iter()),*)); | |
| 148 | zipit.for_each(|(i, tuple_or_singleton!($var0, $($var),*))| f(i, $var0, $($var),*)); | |
| 149 | } | |
| 150 | } | |
| 151 | } | |
| 152 | ||
| 153 | make_mapmany_mut!(map1_mut, map1_indexed_mut, a; A, CA); | |
| 154 | make_mapmany_mut!(map2_mut, map2_indexed_mut, a b; A B, CA CB); | |
| 155 | make_mapmany_mut!(map3_mut, map3_indexed_mut, a b c; A B C, CA CB CC); | |
| 156 | make_mapmany_mut!(map4_mut, map4_indexed_mut, a b c d; A B C D, CA CB CC CD); | |
| 157 | make_mapmany_mut!(map5_mut, map5_indexed_mut, a b c d e; A B C D E, CA CB CC CD CE); | |
| 158 | make_mapmany_mut!(map6_mut, map6_indexed_mut, a b c d e f; A B C D E F, CA CB CC CD CE CF); | |
| 159 | ||
| 160 | ||
| 161 | /// Initialise an array of length `N` by calling `f` multiple times. | |
| 162 | #[inline] | |
| 163 | pub fn array_init<A, F : Fn() -> A, const N : usize>(f : F) -> [A; N] { | |
| 164 | //[(); N].map(|_| f()) | |
| 165 | core::array::from_fn(|_| f()) | |
| 166 | } | |
| 167 | ||
| 168 | // /// Initialise an array of length `N` by calling `f` with the index of each element. | |
| 169 | // #[inline] | |
| 170 | // pub fn array_gen<A, F : Fn(usize) -> A, const N : usize>(f : F) -> [A; N] { | |
| 171 | // //[(); N].indexmap(|i, _| f(i)) | |
| 172 | // core::array::from_fn(f) | |
| 173 | // } | |
| 174 | ||
| 175 | ||
| 5 | 176 | |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
177 | /// Iterator returned by [`foldmap`][FoldMappable::foldmap] applied to an iterator. |
| 5 | 178 | |
| 0 | 179 | pub struct FoldMap<I : Iterator<Item=A>, A, B, J : Copy, F : Fn(J, A) -> (J, B)> { |
| 180 | iter : I, | |
| 181 | f : F, | |
| 182 | j : J, | |
| 183 | } | |
| 184 | ||
| 185 | impl<A, B, I : Iterator<Item=A>, J : Copy, F : Fn(J, A) -> (J, B)> Iterator for FoldMap<I, A, B, J, F> { | |
| 186 | type Item = B; | |
| 187 | #[inline] | |
| 188 | fn next(&mut self) -> Option<B> { | |
| 189 | self.iter.next().map(|a| { | |
| 190 | let (jnew, b) = (self.f)(self.j, a); | |
| 191 | self.j = jnew; | |
| 192 | b | |
| 193 | }) | |
| 194 | } | |
| 195 | } | |
| 196 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
197 | /// Iterator returned by [`indexmap`][IndexMappable::indexmap] applied to an iterator. |
| 0 | 198 | pub struct IndexMap<I : Iterator<Item=A>, A, B, F : Fn(usize, A) -> B> { |
| 199 | iter : I, | |
| 200 | f : F, | |
| 201 | j : usize, | |
| 202 | } | |
| 203 | ||
| 204 | impl<A, B, I : Iterator<Item=A>, F : Fn(usize, A) -> B> Iterator for IndexMap<I, A, B, F> { | |
| 205 | type Item = B; | |
| 206 | #[inline] | |
| 207 | fn next(&mut self) -> Option<B> { | |
| 208 | self.iter.next().map(|a| { | |
| 209 | let b = (self.f)(self.j, a); | |
| 210 | self.j = self.j+1; | |
| 211 | b | |
| 212 | }) | |
| 213 | } | |
| 214 | } | |
| 215 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
216 | /// Trait for things that can be foldmapped. |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
217 | /// |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
218 | /// `A` is the type of elements of `Self`, and `J` the accumulator type for the folding. |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
219 | pub trait FoldMappable<A, J> : Sized { |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
220 | type Output<B, F> where F : Fn(J, A) -> (J, B); |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
221 | /// Fold and map over `self` with `f`. `j` is the initial accumulator for folding. |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
222 | /// |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
223 | /// The output type depends on the implementation, but will generally have elements of |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
224 | /// type `B`. |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
225 | fn foldmap<B, F : Fn(J, A) -> (J, B)>(self, j : J, f : F) -> Self::Output<B, F>; |
| 0 | 226 | } |
| 227 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
228 | /// Trait for things that can be indexmapped. |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
229 | /// |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
230 | /// `A` is the type of elements of `Self`. |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
231 | pub trait IndexMappable<A> : Sized { |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
232 | type Output<B, F> where F : Fn(usize, A) -> B; |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
233 | /// Map over element indices and elements of `self`. |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
234 | /// |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
235 | /// The output type depends on the implementation, but will generally have elements of |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
236 | /// type `B`. |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
237 | fn indexmap<B, F : Fn(usize, A) -> B>(self, f : F) -> Self::Output<B, F>; |
| 0 | 238 | } |
| 239 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
240 | impl<'a, A, J : Copy> FoldMappable<&'a A, J> |
| 0 | 241 | for std::slice::Iter<'a, A> { |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
242 | type Output<B, F> = FoldMap<Self, &'a A, B, J, F> where F : Fn(J, &'a A) -> (J, B); |
| 0 | 243 | #[inline] |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
244 | fn foldmap<B, F : Fn(J, &'a A) -> (J, B)>(self, j : J, f : F) -> Self::Output<B, F> { |
| 0 | 245 | FoldMap { iter : self, j, f } |
| 246 | } | |
| 247 | } | |
| 248 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
249 | impl<'a, A> IndexMappable<&'a A> |
| 0 | 250 | for std::slice::Iter<'a, A> { |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
251 | type Output<B, F> = IndexMap<Self, &'a A, B, F> where F : Fn(usize, &'a A) -> B; |
| 0 | 252 | #[inline] |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
253 | fn indexmap<B, F : Fn(usize, &'a A) -> B>(self, f : F) -> Self::Output<B, F> { |
| 0 | 254 | IndexMap { iter : self, j : 0, f } |
| 255 | } | |
| 256 | } | |
| 257 | ||
| 258 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
259 | impl<A, J : Copy, const N : usize> FoldMappable<A, J> |
| 0 | 260 | for std::array::IntoIter<A, N> { |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
261 | type Output<B, F> = FoldMap<Self, A, B, J, F> where F : Fn(J, A) -> (J, B); |
| 0 | 262 | #[inline] |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
263 | fn foldmap<B, F : Fn(J, A) -> (J, B)>(self, j : J, f : F) -> Self::Output<B, F> { |
| 0 | 264 | FoldMap { iter : self, j, f } |
| 265 | } | |
| 266 | } | |
| 267 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
268 | impl<'a, A, const N : usize> IndexMappable<A> |
| 0 | 269 | for std::array::IntoIter<A, N> { |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
270 | type Output<B, F> = IndexMap<Self, A, B, F> where F : Fn(usize, A) -> B; |
| 0 | 271 | #[inline] |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
272 | fn indexmap<B, F : Fn(usize, A) -> B>(self, f : F) -> Self::Output<B, F> { |
| 0 | 273 | IndexMap { iter : self, j : 0, f } |
| 274 | } | |
| 275 | } | |
| 276 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
277 | impl<A, J : Copy, const N : usize> FoldMappable<A, J> for [A; N] { |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
278 | type Output<B, F> = [B; N] where F : Fn(J, A) -> (J, B); |
| 0 | 279 | #[inline] |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
280 | fn foldmap<B, F : Fn(J, A) -> (J, B)>(self, j : J, f : F) -> [B; N] { |
| 0 | 281 | // //let mut res : [MaybeUninit<B>; N] = unsafe { MaybeUninit::uninit().assume_init() }; |
| 282 | // let mut res = MaybeUninit::uninit_array::<N>(); | |
| 283 | // for (a, i) in self.into_iter().zip(0..N) { | |
| 284 | // let (jnew, b) = f(j, a); | |
| 285 | // unsafe { *(res.get_unchecked_mut(i)) = MaybeUninit::new(b) }; | |
| 286 | // j = jnew; | |
| 287 | // } | |
| 288 | // //unsafe { res.as_mut_ptr().cast::<[B; N]>().read() } | |
| 289 | // unsafe { MaybeUninit::array_assume_init(res) } | |
| 290 | let it = self.into_iter().foldmap(j, f); | |
| 291 | collect_into_array_unchecked(it) | |
| 292 | } | |
| 293 | } | |
| 294 | ||
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
295 | impl<A, const N : usize> IndexMappable<A> for [A; N] { |
|
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
296 | type Output<B, F> = [B; N] where F : Fn(usize, A) -> B; |
| 0 | 297 | #[inline] |
|
2
ac84e995e119
Convert iteration utilities to GATs
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
298 | fn indexmap<B, F : Fn(usize, A) -> B>(self, f : F) -> [B; N] { |
| 0 | 299 | // //let mut res : [MaybeUninit<B>; N] = unsafe { MaybeUninit::uninit().assume_init() }; |
| 300 | // let mut res = MaybeUninit::uninit_array::<N>(); | |
| 301 | // for (a, i) in self.into_iter().zip(0..N) { | |
| 302 | // let b = f(i, a); | |
| 303 | // unsafe { *(res.get_unchecked_mut(i)) = MaybeUninit::new(b) }; | |
| 304 | // } | |
| 305 | // //unsafe { res.as_mut_ptr().cast::<[B; N]>().read() } | |
| 306 | // unsafe { MaybeUninit::array_assume_init(res) } | |
| 307 | let it = self.into_iter().indexmap(f); | |
| 308 | collect_into_array_unchecked(it) | |
| 309 | } | |
| 310 | } | |
| 311 | ||
| 312 | /// This is taken and simplified from core::array to not involve `ControlFlow`, | |
| 313 | /// `Try` etc. (Pulling everything including `NeverShortCircuit` turned out | |
| 314 | /// too much to maintain here.) | |
| 315 | /// | |
| 316 | /// Pulls `N` items from `iter` and returns them as an array. If the iterator | |
| 317 | /// yields fewer than `N` items, `None` is returned and all already yielded | |
| 318 | /// items are dropped. | |
| 319 | /// | |
| 320 | /// Since the iterator is passed as a mutable reference and this function calls | |
| 321 | /// `next` at most `N` times, the iterator can still be used afterwards to | |
| 322 | /// retrieve the remaining items. | |
| 323 | /// | |
| 324 | /// If `iter.next()` panicks, all items already yielded by the iterator are | |
| 325 | /// dropped. | |
|
55
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
326 | #[cfg(feature = "nightly")] |
| 0 | 327 | #[inline] |
|
55
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
328 | pub(crate) fn collect_into_array_unchecked< |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
329 | T, |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
330 | I : Iterator<Item=T>, |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
331 | const N: usize |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
332 | >(mut iter: I) -> [T; N] |
| 0 | 333 | { |
| 334 | if N == 0 { | |
| 335 | // SAFETY: An empty array is always inhabited and has no validity invariants. | |
| 336 | return unsafe { core::mem::zeroed() }; | |
| 337 | } | |
| 338 | ||
| 339 | struct Guard<'a, T, const N: usize> { | |
| 340 | array_mut: &'a mut [MaybeUninit<T>; N], | |
| 341 | initialized: usize, | |
| 342 | } | |
| 343 | ||
| 344 | impl<T, const N: usize> Drop for Guard<'_, T, N> { | |
| 345 | #[inline] | |
| 346 | fn drop(&mut self) { | |
| 347 | debug_assert!(self.initialized <= N); | |
| 348 | ||
| 349 | // SAFETY: this slice will contain only initialized objects. | |
| 350 | unsafe { | |
| 351 | core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( | |
| 352 | &mut self.array_mut.get_unchecked_mut(..self.initialized), | |
| 353 | )); | |
| 354 | } | |
| 355 | } | |
| 356 | } | |
| 357 | ||
| 358 | let mut array = MaybeUninit::uninit_array::<N>(); | |
| 359 | let mut guard = Guard { array_mut: &mut array, initialized: 0 }; | |
| 360 | ||
| 361 | while let Some(item) = iter.next() { | |
| 362 | // SAFETY: `guard.initialized` starts at 0, is increased by one in the | |
| 363 | // loop and the loop is aborted once it reaches N (which is | |
| 364 | // `array.len()`). | |
| 365 | unsafe { | |
| 366 | guard.array_mut.get_unchecked_mut(guard.initialized).write(item); | |
| 367 | } | |
| 368 | guard.initialized += 1; | |
| 369 | ||
| 370 | // Check if the whole array was initialized. | |
| 371 | if guard.initialized == N { | |
| 372 | core::mem::forget(guard); | |
| 373 | ||
| 374 | // SAFETY: the condition above asserts that all elements are | |
| 375 | // initialized. | |
| 376 | let out = unsafe { MaybeUninit::array_assume_init(array) }; | |
| 377 | return out; | |
| 378 | } | |
| 379 | } | |
| 380 | ||
| 381 | unreachable!("Something went wrong with iterator length") | |
| 382 | } | |
| 383 | ||
|
55
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
384 | #[cfg(not(feature = "nightly"))] |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
385 | #[inline] |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
386 | pub(crate) fn collect_into_array_unchecked< |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
387 | T, |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
388 | I : Iterator<Item=T>, |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
389 | const N: usize |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
390 | >(iter: I) -> [T; N] |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
391 | { |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
392 | match iter.collect::<Vec<T>>().try_into() { |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
393 | Ok(a) => a, |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
394 | Err(_) => panic!("collect_into_array failure: should not happen"), |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
395 | } |
|
7b2ee3e84c5f
Add "nightly" feature and provide alternative low-performance implementations of several things when not available.
Tuomo Valkonen <tuomov@iki.fi>
parents:
5
diff
changeset
|
396 | } |
| 0 | 397 | |
| 398 | #[cfg(test)] | |
| 399 | mod tests { | |
| 400 | use super::*; | |
| 401 | ||
| 402 | #[test] | |
| 403 | fn mapx_test() { | |
| 404 | let a = [0,1,2]; | |
| 405 | let mut b = [2,1,0]; | |
| 406 | assert_eq!(map1(a, |x| x+1), [1,2,3]); | |
| 407 | assert_eq!(map2(a, b, |x, y| x+y), [2,2,2]); | |
| 408 | assert_eq!(map1_indexed(a, |i, y| y-i), [0,0,0]); | |
| 409 | map1_indexed_mut(&mut b, |i, y| *y=i); | |
| 410 | assert_eq!(b, a); | |
| 411 | } | |
| 412 | } |