1use crate::arith::{raise_by, Floor};
2use patterns_macros::because;
3
4pub fn map<T, U>(items: &[T], each: fn(&T) -> U) -> Vec<U> {
5 let mut out = Vec::new();
6 for item in items {
7 out.push(each(item));
8 }
9 out
10}
11
12pub fn keep<T: Clone>(items: &[T], when: fn(&T) -> bool) -> Vec<T> {
13 let mut out = Vec::new();
14 for item in items {
15 if when(item) {
16 out.push(item.clone());
17 }
18 }
19 out
20}
21
22pub fn fold<T, A>(items: &[T], start: A, step: fn(A, &T) -> A) -> A {
23 let mut acc = start;
24 for item in items {
25 acc = step(acc, item);
26 }
27 acc
28}
29
30pub fn count<T>(items: &[T], when: fn(&T) -> bool) -> usize {
31 let mut total = 0usize;
32 for item in items {
33 if when(item) {
34 total += 1;
35 }
36 }
37 total
38}
39
40pub fn any<T>(items: &[T], when: fn(&T) -> bool) -> bool {
41 for item in items {
42 if when(item) {
43 return true;
44 }
45 }
46 false
47}
48
49pub fn all<T>(items: &[T], when: fn(&T) -> bool) -> bool {
50 for item in items {
51 if !when(item) {
52 return false;
53 }
54 }
55 true
56}
57
58pub fn first<T: Clone>(items: &[T], when: fn(&T) -> bool) -> Option<T> {
59 for item in items {
60 if when(item) {
61 return Some(item.clone());
62 }
63 }
64 None
65}
66
67pub trait Keyed {
68 type Key: PartialEq;
69
70 fn key(&self) -> Self::Key;
71}
72
73pub trait Advance {
74 type By: Copy;
75
76 fn advanced(self, by: Self::By) -> Self;
77}
78
79pub trait Shift<M> {
80 type By: Copy;
81
82 fn shifted(self, by: Self::By) -> Self;
83}
84
85
86pub fn is_empty<T>(items: &[T]) -> bool {
87 items.is_empty()
88}
89
90pub fn size<T>(items: &[T]) -> usize {
91 items.len()
92}
93
94pub fn is_full<T>(items: &[T], capacity: usize) -> bool {
95 items.len() >= capacity
96}
97
98pub fn holds<T: Keyed>(items: &[T], key: T::Key) -> bool {
99 for item in items {
100 if item.key() == key {
101 return true;
102 }
103 }
104 false
105}
106
107pub fn found<T: Keyed + Clone>(items: &[T], key: T::Key) -> Option<T> {
108 for item in items {
109 if item.key() == key {
110 return Some(item.clone());
111 }
112 }
113 None
114}
115
116pub fn with<T: Clone>(items: &[T], item: T) -> Vec<T> {
117 let mut out = items.to_vec();
118 out.push(item);
119 out
120}
121
122pub fn without<T: Keyed + Clone>(items: &[T], key: T::Key) -> Vec<T> {
123 let mut out = Vec::new();
124 for item in items {
125 if item.key() != key {
126 out.push(item.clone());
127 }
128 }
129 out
130}
131
132pub fn advance_all<T: Advance + Clone>(items: &[T], by: T::By) -> Vec<T> {
133 let mut out = Vec::new();
134 for item in items {
135 out.push(item.clone().advanced(by));
136 }
137 out
138}
139
140pub fn shift_all<M, T: Shift<M> + Clone>(items: &[T], by: T::By) -> Vec<T> {
141 let mut out = Vec::new();
142 for item in items {
143 out.push(item.clone().shifted(by));
144 }
145 out
146}
147
148pub trait Ranked {
149 type Rank: PartialOrd;
150
151 fn rank(&self) -> Self::Rank;
152}
153
154pub fn count_above<T: Ranked>(items: &[T], bound: T::Rank) -> usize {
155 let mut seen = 0usize;
156 for item in items {
157 if item.rank() > bound {
158 seen += 1;
159 }
160 }
161 seen
162}
163
164pub fn keep_above<T: Ranked + Clone>(items: &[T], bound: T::Rank) -> Vec<T> {
165 let mut out = Vec::new();
166 for item in items {
167 if item.rank() > bound {
168 out.push(item.clone());
169 }
170 }
171 out
172}
173
174pub fn rank_of<T: Ranked>(value: &T) -> T::Rank {
175 value.rank()
176}
177
178pub trait Facet<F> {
179 type Value: PartialEq;
180
181 fn facet(&self) -> Self::Value;
182}
183
184pub fn count_facet<F, T: Facet<F>>(items: &[T], value: T::Value) -> usize {
185 let mut seen = 0usize;
186 for item in items {
187 if item.facet() == value {
188 seen += 1;
189 }
190 }
191 seen
192}
193
194pub fn any_facet<F, T: Facet<F>>(items: &[T], value: T::Value) -> bool {
195 for item in items {
196 if item.facet() == value {
197 return true;
198 }
199 }
200 false
201}
202
203pub fn first_facet<F, T: Facet<F> + Clone>(items: &[T], value: T::Value) -> Option<T> {
204 for item in items {
205 if item.facet() == value {
206 return Some(item.clone());
207 }
208 }
209 None
210}
211because!(map, "the replacement for a for loop that builds a list, taking a fn pointer rather than a closure so nothing can capture");
212because!(keep, "filtering by a predicate, separate from map because a filter changes the length and a map does not");
213because!(fold, "the general accumulation, kept because map and keep cannot express a running total");
214because!(count, "folding to a count, named separately because counting is common enough that spelling it as a fold obscures the intent");
215because!(any, "existence over a predicate, which short circuits and so is not count compared against zero");
216because!(all, "universality over a predicate, the mirror of any and separate because the empty case answers differently");
217because!(first, "the first element satisfying a predicate, returning an Option so the empty case is a value rather than a panic");
218because!(is_empty, "emptiness as a call, so a length comparison never appears in spec or app");
219because!(size, "length as a call, so a length comparison never appears in spec or app");
220because!(is_full, "a collection held against its capacity, so a capacity is a usize in exactly one place and a fullness test is a call rather than a comparison");
221because!(holds, "membership by key, so a caller asks whether the collection already has this thing without writing the comparison");
222because!(found, "retrieval by key, separate from holds because the caller that wants the item should not have to search twice");
223because!(with, "adding an element and returning the new collection, so a binding never mutates");
224because!(without, "removing by key and returning the new collection, the mirror of with");
225because!(advance_all, "advancing every element by its own rule, so a caller never loops to apply a step to a collection");
226because!(count_above, "counting elements ranked over a bound, which is count composed with a comparison the caller would otherwise write as a closure");
227because!(keep_above, "the filtering form of count_above, kept separate because a caller usually wants either the number or the survivors and not both");
228because!(rank_of, "the rank a single element projects to, exposed so a caller can order by the same projection the collection functions use");
229because!(count_facet, "counting elements whose facet matches, the equality form of count_above and the route for a predicate over a named field");
230because!(any_facet, "existence over a facet, short circuiting, so a caller asks whether any element matches without counting them all");
231because!(first_facet, "the first element whose facet matches, returning an Option so the empty case is a value rather than a panic");
232because!(Shift, "the marker parameterised form of Advance, because coherence allows one Advance impl per type and a value that needs two different mutations cannot have them");
233because!(shift_all, "advancing every element by one named mutation, so a type with several ways to move keeps them apart instead of merging them into one enum");
234
235pub fn count_below<T: Ranked>(items: &[T], bound: T::Rank) -> usize {
236 let mut seen = 0usize;
237 for item in items {
238 if item.rank() < bound {
239 seen += 1;
240 }
241 }
242 seen
243}
244because!(count_below, "counting elements ranked under a bound, the mirror of count_above and separate because a caller asking how many are short of a limit should not have to invert the question");
245
246pub fn count_from<T: Ranked>(items: &[T], bound: T::Rank) -> usize {
247 let mut seen = 0usize;
248 for item in items {
249 if item.rank() >= bound {
250 seen += 1;
251 }
252 }
253 seen
254}
255because!(count_from, "counting elements ranked at or over a bound, separate from count_above because the boundary case is exactly what a limit turns on and inverting a strict comparison is a common error");
256
257pub trait Valued<M> {
258 type Value: Copy;
259
260 fn value(&self) -> Self::Value;
261}
262because!(Valued, "a lookup from a vocabulary variant to the figure it stands for, kept apart from Ranked because a cap, a rate or an allowance is read and never ordered, and the marker lets one variant carry several such figures");
263
264pub fn value_of<M, T: Valued<M>>(item: &T) -> T::Value {
265 item.value()
266}
267because!(value_of, "the figure a variant stands for, as a call, so the table a vocabulary carries is read from spec and app without a method call");
268
269pub fn keep_facet<F, T: Facet<F> + Clone>(items: &[T], value: T::Value) -> Vec<T> {
270 let mut out = Vec::new();
271 for item in items {
272 if item.facet() == value {
273 out.push(item.clone());
274 }
275 }
276 out
277}
278because!(keep_facet, "the elements whose facet matches, the equality form of keep_above and the route for keeping every match on a named field without a closure");
279
280pub fn without_facet<F, T: Facet<F> + Clone>(items: &[T], value: T::Value) -> Vec<T> {
281 let mut out = Vec::new();
282 for item in items {
283 if item.facet() != value {
284 out.push(item.clone());
285 }
286 }
287 out
288}
289because!(without_facet, "removing every element whose facet matches, the mirror of keep_facet, and separate from without because a facet can be a tuple over several fields where a key is one identity");
290
291pub fn last<T: Clone>(items: &[T], when: fn(&T) -> bool) -> Option<T> {
292 let mut out = None;
293 for item in items {
294 if when(item) {
295 out = Some(item.clone());
296 }
297 }
298 out
299}
300because!(last, "the final element satisfying a predicate, separate from first because a log is read from its end as often as its start and reversing the list to ask copies it");
301
302pub fn total<M, T: Valued<M>>(items: &[T], start: T::Value) -> T::Value
303where
304 T::Value: Floor,
305{
306 let mut sum = start;
307 for item in items {
308 sum = raise_by(sum, item.value());
309 }
310 sum
311}
312because!(total, "the sum of every value a marker reads from the elements, seeded by the caller and saturating, so a total of money or days stops at the type ceiling rather than wrapping and the pattern never conjures a zero; it reads Valued and not Ranked because a rank orders and a value is added");
313
314pub fn found_ref<T: Keyed>(items: &[T], key: T::Key) -> Option<&T> {
315 items.iter().find(|item| item.key() == key)
316}
317because!(found_ref, "retrieval by key that borrows the element, beside found because a query over a large state should not copy the element it reads while a reducer that keeps the element wants its own copy");
318
319pub fn first_ref<T>(items: &[T], when: fn(&T) -> bool) -> Option<&T> {
320 items.iter().find(|item| when(item))
321}
322because!(first_ref, "the first element satisfying a predicate, borrowed, beside first because a caller that only reads the element should not pay for a clone and a type that is not Clone still needs the search");
323
324pub fn keep_ref<T>(items: &[T], when: fn(&T) -> bool) -> Vec<&T> {
325 let mut out = Vec::new();
326 for item in items {
327 if when(item) {
328 out.push(item);
329 }
330 }
331 out
332}
333because!(keep_ref, "filtering that borrows the survivors, beside keep because a query that only counts or reads what passes should not copy the elements it passes over");
334
335pub fn first_facet_ref<F, T: Facet<F>>(items: &[T], value: T::Value) -> Option<&T> {
336 items.iter().find(|item| item.facet() == value)
337}
338because!(first_facet_ref, "the first element whose facet matches, borrowed, beside first_facet because a binding that reads one field of the match should not clone the whole element to get it");
339
340pub fn at<T: Clone>(items: &[T], index: usize) -> Option<T> {
341 items.get(index).cloned()
342}
343because!(at, "the item in a position if the position is there, so a binding reaches into a sequence without the square brackets it may not write and without the chance of ending the program on a position that is not");
344
345pub fn firsts<A: Clone, B>(pairs: &[(A, B)]) -> Vec<A> {
346 pairs.iter().map(|(a, _)| a.clone()).collect()
347}
348because!(firsts, "the left of every pair, the twin of seconds and separate from map because a pair has no facet to name, so what is wanted is said by which side is taken rather than by a function that reaches in");
349
350pub fn seconds<A, B: Clone>(pairs: &[(A, B)]) -> Vec<B> {
351 pairs.iter().map(|(_, b)| b.clone()).collect()
352}
353because!(seconds, "the right of every pair, the twin of firsts, and separate from map because a pair carries no facet a function could name; the two sides are kept apart because a side taken by its number reads as nothing at the call");
354
355pub fn pushed<T>(items: Vec<T>, item: T) -> Vec<T> {
356 let mut out = items;
357 out.push(item);
358 out
359}
360because!(pushed, "a list with one more element, taking the list by value so a reducer that owns its state appends without copying what it already holds, where with copies for a caller that only borrows");
361
362pub fn without_trailing_facet<F, T: Facet<F> + Clone>(items: &[T]) -> Vec<T> {
363 let last = match items.last() {
364 Some(item) => item.facet(),
365 None => return vec![],
366 };
367 let mut out = Vec::with_capacity(items.len());
368 for item in items {
369 if item.facet() != last {
370 out.push(item.clone());
371 }
372 }
373 out
374}
375because!(without_trailing_facet, "a list with every element of its last group removed, the group being whatever the projection says, so a capped fetch keeps only groups it is sure it saw whole");
376
377pub fn listed<T: Clone>(items: &[T]) -> Vec<T> {
378 items.to_vec()
379}
380because!(listed, "a borrowed run of elements as an owned list, for a reducer that must hand a copy into an event it builds and may not call a method to do it");