malachite_base/iterators/comparison.rs
1// Copyright © 2025 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use core::cmp::Ordering::{self, *};
10
11/// An iterator that generates the [`Ordering`]s of adjacent elements of a given iterator.
12///
13/// This `struct` is created by [`delta_directions`]; see its documentation for more.
14#[derive(Clone, Debug)]
15pub struct DeltaDirections<I: Iterator>
16where
17 I::Item: Ord,
18{
19 previous: Option<I::Item>,
20 xs: I,
21}
22
23impl<I: Iterator> Iterator for DeltaDirections<I>
24where
25 I::Item: Ord,
26{
27 type Item = Ordering;
28
29 fn next(&mut self) -> Option<Ordering> {
30 if self.previous.is_none() {
31 if let Some(x) = self.xs.next() {
32 self.previous = Some(x);
33 } else {
34 return None;
35 }
36 }
37 self.xs.next().and_then(|x| {
38 let result = Some(x.cmp(self.previous.as_ref().unwrap()));
39 self.previous = Some(x);
40 result
41 })
42 }
43}
44
45/// Returns an iterator that generates the [`Ordering`]s of adjacent pairs of elements of a given
46/// iterator.
47///
48/// To put it another way (at least for types where subtraction is defined), the returned iterator
49/// produces the signs of the finite differences of the input iterator.
50///
51/// $f((x_k)_{k=0}^N) = (\\operatorname{cmp}(x_k, x\_{k-1}))\_{k=1}^N$, where $N$ may be $\infty$.
52///
53/// The output length is infinite if `xs` is infinite, or $\max(n - 1, 0)$ otherwise, where $n$ is
54/// `xs.count()`.
55///
56/// # Examples
57/// ```
58/// use itertools::Itertools;
59/// use malachite_base::iterators::comparison::delta_directions;
60/// use std::cmp::Ordering::*;
61///
62/// assert_eq!(
63/// delta_directions([3, 1, 4, 1, 5, 9].into_iter()).collect_vec(),
64/// &[Less, Greater, Less, Greater, Greater]
65/// )
66/// ```
67#[inline]
68pub const fn delta_directions<I: Iterator>(xs: I) -> DeltaDirections<I>
69where
70 I::Item: Ord,
71{
72 DeltaDirections { previous: None, xs }
73}
74
75/// Determines whether each element of an iterator is greater than the preceding one.
76///
77/// This function will hang if given an infinite strictly ascending iterator.
78///
79/// $$
80/// f((x_k)\_{k=0}^N) = \\bigwedge\_{k=1}^N{x\_k > x\_{k-1}},
81/// $$
82/// where $N$ may be $\infty$.
83///
84/// # Examples
85/// ```
86/// use malachite_base::iterators::comparison::is_strictly_ascending;
87///
88/// assert_eq!(is_strictly_ascending([1, 2, 3, 4].into_iter()), true);
89/// assert_eq!(is_strictly_ascending([1, 2, 2, 4].into_iter()), false);
90/// ```
91#[inline]
92pub fn is_strictly_ascending<I: Iterator>(xs: I) -> bool
93where
94 I::Item: Ord,
95{
96 delta_directions(xs).all(|x| x == Greater)
97}
98
99/// Determines whether each element of an iterator is less than the preceding one.
100///
101/// This function will hang if given an infinite strictly descending iterator.
102///
103/// $$
104/// f((x_k)\_{k=0}^N) = \\bigwedge\_{k=1}^N{x\_k < x\_{k-1}},
105/// $$
106/// where $N$ may be $\infty$.
107///
108/// # Examples
109/// ```
110/// use malachite_base::iterators::comparison::is_strictly_descending;
111///
112/// assert_eq!(is_strictly_descending([4, 3, 2, 1].into_iter()), true);
113/// assert_eq!(is_strictly_descending([4, 2, 2, 1].into_iter()), false);
114/// ```
115#[inline]
116pub fn is_strictly_descending<I: Iterator>(xs: I) -> bool
117where
118 I::Item: Ord,
119{
120 delta_directions(xs).all(|x| x == Less)
121}
122
123/// Determines whether each element of an iterator is greater than or equal to the preceding one.
124///
125/// This function will hang if given an infinite weakly ascending iterator.
126///
127/// $$
128/// f((x_k)\_{k=0}^N) = \\bigwedge\_{k=1}^N{x\_k \geq x\_{k-1}},
129/// $$
130/// where $N$ may be $\infty$.
131///
132/// # Examples
133/// ```
134/// use malachite_base::iterators::comparison::is_weakly_ascending;
135///
136/// assert_eq!(is_weakly_ascending([1, 2, 3, 4].into_iter()), true);
137/// assert_eq!(is_weakly_ascending([1, 2, 2, 4].into_iter()), true);
138/// assert_eq!(is_weakly_ascending([1, 3, 2, 4].into_iter()), false);
139/// ```
140#[inline]
141pub fn is_weakly_ascending<I: Iterator>(xs: I) -> bool
142where
143 I::Item: Ord,
144{
145 delta_directions(xs).all(|x| x != Less)
146}
147
148/// Determines whether each element of an iterator is less than or equal to the preceding one.
149///
150/// This function will hang if given an infinite weakly descending iterator.
151///
152/// $$
153/// f((x_k)\_{k=0}^N) = \\bigwedge\_{k=1}^N{x\_k \leq x\_{k-1}},
154/// $$
155/// where $N$ may be $\infty$.
156///
157/// # Examples
158/// ```
159/// use malachite_base::iterators::comparison::is_weakly_descending;
160///
161/// assert_eq!(is_weakly_descending([4, 3, 2, 1].into_iter()), true);
162/// assert_eq!(is_weakly_descending([4, 2, 2, 1].into_iter()), true);
163/// assert_eq!(is_weakly_descending([4, 2, 3, 1].into_iter()), false);
164/// ```
165#[inline]
166pub fn is_weakly_descending<I: Iterator>(xs: I) -> bool
167where
168 I::Item: Ord,
169{
170 delta_directions(xs).all(|x| x != Greater)
171}
172
173/// Determines whether the sequence strictly zigzags.
174///
175/// A strictly zigzagging sequence is one where every odd-indexed element is greater than its
176/// even-indexed neighbors, or one where every odd-indexed element is less than its even-indexed
177/// neighbors.
178///
179/// This function will hang if given an infinite strictly zigzagging iterator.
180///
181/// # Examples
182/// ```
183/// use malachite_base::iterators::comparison::is_strictly_zigzagging;
184///
185/// assert_eq!(is_strictly_zigzagging([1, 2, 3, 4].into_iter()), false);
186/// assert_eq!(is_strictly_zigzagging([4, 3, 2, 1].into_iter()), false);
187/// assert_eq!(is_strictly_zigzagging([1, 3, 2, 4].into_iter()), true);
188/// assert_eq!(is_strictly_zigzagging([1, 2, 2, 4].into_iter()), false);
189/// ```
190pub fn is_strictly_zigzagging<I: Iterator>(xs: I) -> bool
191where
192 I::Item: Ord,
193{
194 let mut previous = None;
195 for direction in delta_directions(xs) {
196 if direction == Equal {
197 return false;
198 }
199 if let Some(previous) = previous {
200 if direction == previous {
201 return false;
202 }
203 }
204 previous = Some(direction);
205 }
206 true
207}
208
209/// Determines whether the sequence weakly zigzags.
210///
211/// A weakly zigzagging sequence is one where every odd-indexed element is greater than or equal to
212/// its even-indexed neighbors, or one where every odd-indexed element is less than or equal to its
213/// even-indexed neighbors.
214///
215/// This function will hang if given an infinite strictly zigzagging iterator.
216///
217/// # Examples
218/// ```
219/// use malachite_base::iterators::comparison::is_weakly_zigzagging;
220///
221/// assert_eq!(is_weakly_zigzagging([1, 2, 3, 4].into_iter()), false);
222/// assert_eq!(is_weakly_zigzagging([4, 3, 2, 1].into_iter()), false);
223/// assert_eq!(is_weakly_zigzagging([1, 3, 2, 4].into_iter()), true);
224/// assert_eq!(is_weakly_zigzagging([1, 2, 2, 4].into_iter()), true);
225/// ```
226pub fn is_weakly_zigzagging<I: Iterator>(xs: I) -> bool
227where
228 I::Item: Ord,
229{
230 let mut previous = None;
231 for direction in delta_directions(xs) {
232 if let Some(previous) = &mut previous {
233 if direction == *previous {
234 return false;
235 }
236 *previous = previous.reverse();
237 } else if direction != Equal {
238 previous = Some(direction);
239 }
240 }
241 true
242}