malachite_base/iterators/iterator_cache.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 alloc::vec::Vec;
10
11/// Remembers values produced by an iterator.
12///
13/// After wrapping an iterator with an `IteratorCache`, you can retrieve a reference to the $n$th
14/// element of an iterator, and then retrieve a reference to the $m$th element in constant time for
15/// any $m \leq n$ (not counting the time it took to first get the $n$th element).
16#[derive(Clone, Debug)]
17pub struct IteratorCache<I: Iterator> {
18 xs: I,
19 cache: Vec<I::Item>,
20 done: bool,
21}
22
23impl<I: Iterator> IteratorCache<I> {
24 /// Creates a new `IteratorCache`.
25 ///
26 /// This function does not allocate any memory or advance the iterator.
27 ///
28 /// # Worst-case complexity
29 /// Constant time and additional memory.
30 ///
31 /// # Examples
32 /// ```
33 /// use malachite_base::iterators::iterator_cache::IteratorCache;
34 ///
35 /// IteratorCache::new([1, 2, 3].iter());
36 /// ```
37 pub const fn new(xs: I) -> Self {
38 Self {
39 xs,
40 cache: Vec::new(),
41 done: false,
42 }
43 }
44
45 /// Retrieves the $n$th element of an iterator. Indexing starts at 0.
46 ///
47 /// If the index is higher than any other previously-requested index, the iterator is advanced
48 /// to that index, or until it runs out. If the iterator has previously been advanced past the
49 /// index, the requested element is returned from the cache in constant time. If the iterator is
50 /// too short to have an element at the index, `None` is returned.
51 ///
52 /// If you know that the element is present, and want to only take an immutable reference to
53 /// `self`, consider using [`assert_get`](Self::assert_get) instead.
54 ///
55 /// # Worst-case complexity
56 /// $T(n) = O(n)$
57 ///
58 /// $M(n) = O(n)$
59 ///
60 /// where $T$ is time, $M$ is additional memory, and $n$ is 1 if `get` has previously been
61 /// called with an index at least this large, or `index` otherwise.
62 ///
63 /// # Examples
64 /// ```
65 /// use malachite_base::iterators::iterator_cache::IteratorCache;
66 ///
67 /// let mut xs = IteratorCache::new([1, 2, 3].iter().cloned());
68 /// assert_eq!(xs.get(1), Some(&2));
69 /// assert_eq!(xs.get(0), Some(&1));
70 /// assert_eq!(xs.get(3), None);
71 /// assert_eq!(xs.get(2), Some(&3));
72 /// ```
73 pub fn get(&mut self, index: usize) -> Option<&I::Item> {
74 if !self.done && index >= self.cache.len() {
75 self.cache
76 .extend((&mut self.xs).take(index - self.cache.len() + 1));
77 if index >= self.cache.len() {
78 self.done = true;
79 return None;
80 }
81 }
82 self.cache.get(index)
83 }
84
85 /// Retrieves the $n$th element of an iterator, while asserting that the iterator has already
86 /// reached that element. Indexing starts at 0.
87 ///
88 /// If the iterator has not advanced that far, or if it has fewer than $n + 1$ elements, this
89 /// function panics.
90 ///
91 /// The purpose of this function is to allow the caller to get an element immutably, assuming
92 /// that the caller knows that the element is present. The [`get`](Self::get) function has to
93 /// take a mutable reference.
94 ///
95 /// # Worst-case complexity
96 /// Constant time and additional memory.
97 ///
98 /// # Examples
99 /// ```
100 /// use malachite_base::iterators::iterator_cache::IteratorCache;
101 ///
102 /// let mut xs = IteratorCache::new([1, 2, 3].iter().cloned());
103 /// // Force the iterator to iterate to completion
104 /// xs.get(3);
105 /// assert_eq!(xs.assert_get(1), &2);
106 /// assert_eq!(xs.assert_get(0), &1);
107 /// assert_eq!(xs.assert_get(2), &3);
108 /// ```
109 pub fn assert_get(&self, index: usize) -> &I::Item {
110 self.cache.get(index).unwrap()
111 }
112
113 /// Returns the total number of elements in the iterator, if the iterator has been completely
114 /// consumed.
115 ///
116 /// If the iterator has not been completely consumed yet, returns `None`.
117 ///
118 /// # Worst-case complexity
119 /// Constant time and additional memory.
120 ///
121 /// # Examples
122 /// ```
123 /// use malachite_base::iterators::iterator_cache::IteratorCache;
124 ///
125 /// let mut xs = IteratorCache::new([1, 2, 3].iter().cloned());
126 /// assert_eq!(xs.known_len(), None);
127 /// assert_eq!(xs.get(1), Some(&2));
128 /// assert_eq!(xs.known_len(), None);
129 /// assert_eq!(xs.get(0), Some(&1));
130 /// assert_eq!(xs.get(3), None);
131 /// assert_eq!(xs.known_len(), Some(3));
132 /// assert_eq!(xs.get(2), Some(&3));
133 /// ```
134 pub const fn known_len(&self) -> Option<usize> {
135 if self.done {
136 Some(self.cache.len())
137 } else {
138 None
139 }
140 }
141}