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