Skip to main content

implicit_predicates_core/scratch/
mod.rs

1//! A bump arena for expansion arithmetic.
2//!
3//! A [`Scratch`] arena hands out the memory for the expansions that a
4//! computation builds step by step. Its memory is a buffer the caller lends
5//! it ([`Scratch::borrowed`]), so nothing allocates. With the `alloc` feature
6//! an arena can instead own its memory and grow on the heap
7//! (`Scratch::growable`).
8//!
9//! # Model
10//!
11//! - **Bump allocation.** The arena keeps a *top*: the buffer below it is in
12//!   use, the rest is free. Each operation writes its result just above the
13//!   top and raises the top by the length of that result.
14//! - **Spans are indices.** Operations take and return [`Span`]s, positions
15//!   in the arena rather than references to it, so a computation can hold
16//!   many results while it keeps calling `&mut self` operations. Read the
17//!   components of a span with [`Scratch::get`]; write an expansion computed
18//!   elsewhere with [`Scratch::write`].
19//! - **Marks and reset.** [`Scratch::mark`] records the current top and
20//!   [`Scratch::reset`] lowers the top back to a mark, releasing at once
21//!   every result built since. Spans created before the mark stay valid.
22//! - **Exhaustion.** Before it writes, an operation reserves all the space
23//!   it may need above the top: the longest result it can produce (the
24//!   capacity documented by the slice primitive in [`expansion`]), plus, for
25//!   [`Scratch::product`], room for its intermediate expansions. If that does
26//!   not fit, the operation returns [`ScratchExhausted`] and leaves the arena
27//!   unchanged. [`Scratch::high_water`] tells how much of the buffer a
28//!   computation needed, which is how to size the buffer.
29//!
30//! An arena uses at most `u32::MAX` components of its buffer. An arena that
31//! grows fails with [`ScratchExhausted`] only when an operation needs more
32//! than that.
33//!
34//! A span means something only to the arena that created it, and only while
35//! the top stays above it. Using a span after a [`reset`](Scratch::reset)
36//! past its start, or with another arena, is a logic error: debug builds
37//! detect it when the span reaches past the top, and the result is otherwise
38//! unspecified (a panic or unrelated components), but never undefined
39//! behavior.
40//!
41//! # Examples
42//!
43//! ```
44//! use implicit_predicates_core::{Scratch, Sign};
45//!
46//! let mut buf = [0.0; 16];
47//! let mut scratch = Scratch::borrowed(&mut buf);
48//! let a = scratch.push(&[1e-20, 1.0])?;
49//! let b = scratch.push(&[3.0])?;
50//!
51//! let mark = scratch.mark();
52//! let sum = scratch.sum(a, b)?; // 1 + 3 + 1e-20
53//! let scaled = scratch.scale(sum, -2.0)?;
54//! assert_eq!(scratch.get(scaled), &[-2e-20, -8.0]);
55//! assert_eq!(scratch.sign(scaled), Sign::Negative);
56//!
57//! // Release `sum` and `scaled`; `a` and `b` stay valid.
58//! scratch.reset(mark);
59//! assert_eq!(scratch.used(), 3);
60//! assert_eq!(scratch.get(a), &[1e-20, 1.0]);
61//! # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
62//! ```
63
64mod error;
65#[cfg(feature = "alloc")]
66mod growable;
67mod product;
68mod reservation;
69mod span;
70mod storage;
71mod write;
72
73pub use error::ScratchExhausted;
74pub use span::{Mark, Span};
75
76use core::fmt;
77
78use crate::{Sign, expansion};
79use span::to_index;
80use storage::Storage;
81
82/// A bump arena of `f64` components for expansion arithmetic. See the
83/// [module documentation](self) for the model.
84pub struct Scratch<'a> {
85    storage: Storage<'a>,
86    /// The top: components `..used` are in use.
87    used: u32,
88    /// The highest the top has been, counting worst-case reservations.
89    high_water: u32,
90}
91
92impl<'a> Scratch<'a> {
93    /// Creates an empty arena over `buf`, of which it uses at most
94    /// `u32::MAX` components. The initial contents of `buf` do not matter.
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// use implicit_predicates_core::Scratch;
100    ///
101    /// let mut buf = [0.0; 64];
102    /// let scratch = Scratch::borrowed(&mut buf);
103    /// assert_eq!(scratch.capacity(), 64);
104    /// assert_eq!(scratch.used(), 0);
105    /// ```
106    #[must_use]
107    pub fn borrowed(buf: &'a mut [f64]) -> Scratch<'a> {
108        Scratch {
109            storage: Storage::Borrowed(buf),
110            used: 0,
111            high_water: 0,
112        }
113    }
114
115    /// Returns a mark of the current top, to return to with
116    /// [`reset`](Self::reset).
117    ///
118    /// # Examples
119    ///
120    /// ```
121    /// use implicit_predicates_core::Scratch;
122    ///
123    /// let mut buf = [0.0; 4];
124    /// let mut scratch = Scratch::borrowed(&mut buf);
125    /// scratch.push(&[1.0])?;
126    /// let mark = scratch.mark();
127    /// scratch.push(&[2.0])?;
128    /// scratch.reset(mark);
129    /// assert_eq!(scratch.used(), 1);
130    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
131    /// ```
132    #[must_use]
133    pub fn mark(&self) -> Mark {
134        Mark(self.used)
135    }
136
137    /// Lowers the top to the mark `m`, releasing every span created since
138    /// `m` was taken. [`high_water`](Self::high_water) is unchanged.
139    ///
140    /// `m` must not be above the current top, which happens when a reset
141    /// to an earlier mark came in between. That is a logic error: debug
142    /// builds panic, release builds leave the top unchanged.
143    ///
144    /// # Examples
145    ///
146    /// ```
147    /// use implicit_predicates_core::Scratch;
148    ///
149    /// let mut buf = [0.0; 4];
150    /// let mut scratch = Scratch::borrowed(&mut buf);
151    /// let mark = scratch.mark();
152    /// scratch.push(&[1e-20, 1.0])?;
153    /// scratch.reset(mark);
154    /// assert_eq!(scratch.used(), 0);
155    /// assert_eq!(scratch.high_water(), 2);
156    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
157    /// ```
158    pub fn reset(&mut self, m: Mark) {
159        debug_assert!(
160            m.0 <= self.used,
161            "reset to a mark at {} above the top at {}",
162            m.0,
163            self.used
164        );
165        self.used = self.used.min(m.0);
166    }
167
168    /// Returns the components of the expansion at `s`.
169    ///
170    /// Debug builds panic if `s` reaches past the top; see the
171    /// [module documentation](self) for spans that are no longer valid.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// use implicit_predicates_core::Scratch;
177    ///
178    /// let mut buf = [0.0; 4];
179    /// let mut scratch = Scratch::borrowed(&mut buf);
180    /// let s = scratch.push(&[1e-20, 1.0])?;
181    /// assert_eq!(scratch.get(s), &[1e-20, 1.0]);
182    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
183    /// ```
184    #[must_use]
185    pub fn get(&self, s: Span) -> &[f64] {
186        self.debug_check(s);
187        &self.storage.as_slice()[..self.used()][s.range()]
188    }
189
190    /// Returns the number of components in use, below the top.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// use implicit_predicates_core::Scratch;
196    ///
197    /// let mut buf = [0.0; 4];
198    /// let mut scratch = Scratch::borrowed(&mut buf);
199    /// scratch.push(&[1e-20, 1.0])?;
200    /// assert_eq!(scratch.used(), 2);
201    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
202    /// ```
203    #[must_use]
204    pub fn used(&self) -> usize {
205        to_index(self.used)
206    }
207
208    /// Returns the number of components the arena can hold: the length of
209    /// its buffer, or `u32::MAX` if the buffer is longer.
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// use implicit_predicates_core::Scratch;
215    ///
216    /// let mut buf = [0.0; 8];
217    /// assert_eq!(Scratch::borrowed(&mut buf).capacity(), 8);
218    /// ```
219    #[must_use]
220    pub fn capacity(&self) -> usize {
221        to_index(self.storage.capacity())
222    }
223
224    /// Returns the highest [`used`](Self::used) ever reached, counting the
225    /// space each successful operation reserves while it works, even when
226    /// its result turns out shorter. A buffer this long is enough
227    /// to repeat the same computation. Failed operations reserve nothing.
228    ///
229    /// # Examples
230    ///
231    /// ```
232    /// use implicit_predicates_core::Scratch;
233    ///
234    /// let mut buf = [0.0; 8];
235    /// let mut scratch = Scratch::borrowed(&mut buf);
236    /// let a = scratch.push(&[1e-20, 1.0])?;
237    /// let zero = scratch.diff(a, a)?; // reserves 4 components, keeps none
238    /// assert!(zero.is_empty());
239    /// assert_eq!(scratch.used(), 2);
240    /// assert_eq!(scratch.high_water(), 6);
241    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
242    /// ```
243    #[must_use]
244    pub fn high_water(&self) -> usize {
245        to_index(self.high_water)
246    }
247
248    /// Copies the expansion `e` into the arena and returns its span.
249    ///
250    /// Needs `e.len()` free components; an empty `e` always succeeds, giving
251    /// an empty span. Requirement: `e` is a valid expansion (see
252    /// [`expansion`]); it is copied as is, and debug builds panic if it is
253    /// not valid.
254    ///
255    /// # Errors
256    ///
257    /// [`ScratchExhausted`] if `e` does not fit; the arena is unchanged.
258    ///
259    /// # Examples
260    ///
261    /// ```
262    /// use implicit_predicates_core::Scratch;
263    ///
264    /// let mut buf = [0.0; 2];
265    /// let mut scratch = Scratch::borrowed(&mut buf);
266    /// let s = scratch.push(&[1e-20, 1.0])?;
267    /// assert_eq!(scratch.get(s), &[1e-20, 1.0]);
268    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
269    /// ```
270    pub fn push(&mut self, e: &[f64]) -> Result<Span, ScratchExhausted> {
271        debug_assert!(
272            expansion::is_valid(e),
273            "Scratch::push of {e:?}, which is not a valid expansion"
274        );
275        let worst_case = u64::try_from(e.len()).unwrap_or(u64::MAX);
276        self.write_result([], worst_case, |[], out| {
277            out.copy_from_slice(e);
278            e.len()
279        })
280    }
281
282    /// Writes the expansion of `a + b` and returns its span, as
283    /// [`expansion::sum`] does.
284    ///
285    /// Needs `a.len() + b.len()` free components while it works.
286    ///
287    /// # Errors
288    ///
289    /// [`ScratchExhausted`] if that space is not free; the arena is
290    /// unchanged.
291    ///
292    /// # Examples
293    ///
294    /// ```
295    /// use implicit_predicates_core::Scratch;
296    ///
297    /// let mut buf = [0.0; 8];
298    /// let mut scratch = Scratch::borrowed(&mut buf);
299    /// let a = scratch.push(&[1e-20, 1.0])?;
300    /// let b = scratch.push(&[-1e-20, 2.0])?;
301    /// let s = scratch.sum(a, b)?;
302    /// assert_eq!(scratch.get(s), &[3.0]);
303    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
304    /// ```
305    pub fn sum(&mut self, a: Span, b: Span) -> Result<Span, ScratchExhausted> {
306        let worst_case = u64::from(a.len) + u64::from(b.len);
307        self.write_result([a, b], worst_case, |[a, b], out| expansion::sum(a, b, out))
308    }
309
310    /// Writes the expansion of `a - b` and returns its span, as
311    /// [`expansion::diff`] does.
312    ///
313    /// Needs `a.len() + b.len()` free components while it works.
314    ///
315    /// # Errors
316    ///
317    /// [`ScratchExhausted`] if that space is not free; the arena is
318    /// unchanged.
319    ///
320    /// # Examples
321    ///
322    /// ```
323    /// use implicit_predicates_core::Scratch;
324    ///
325    /// let mut buf = [0.0; 8];
326    /// let mut scratch = Scratch::borrowed(&mut buf);
327    /// let a = scratch.push(&[1e-20, 1.0])?;
328    /// let b = scratch.push(&[1e-20, 2.0])?;
329    /// let d = scratch.diff(a, b)?;
330    /// assert_eq!(scratch.get(d), &[-1.0]);
331    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
332    /// ```
333    pub fn diff(&mut self, a: Span, b: Span) -> Result<Span, ScratchExhausted> {
334        let worst_case = u64::from(a.len) + u64::from(b.len);
335        self.write_result([a, b], worst_case, |[a, b], out| expansion::diff(a, b, out))
336    }
337
338    /// Writes the expansion of `a * b` and returns its span, as
339    /// [`expansion::scale`] does, with the same requirements on the
340    /// magnitudes.
341    ///
342    /// Needs `2 * a.len()` free components while it works.
343    ///
344    /// # Errors
345    ///
346    /// [`ScratchExhausted`] if that space is not free; the arena is
347    /// unchanged.
348    ///
349    /// # Examples
350    ///
351    /// ```
352    /// use implicit_predicates_core::Scratch;
353    ///
354    /// let mut buf = [0.0; 8];
355    /// let mut scratch = Scratch::borrowed(&mut buf);
356    /// let a = scratch.push(&[0.25, 4.0])?;
357    /// let s = scratch.scale(a, 3.0)?;
358    /// assert_eq!(scratch.get(s), &[12.75]);
359    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
360    /// ```
361    pub fn scale(&mut self, a: Span, b: f64) -> Result<Span, ScratchExhausted> {
362        let worst_case = 2 * u64::from(a.len);
363        self.write_result([a], worst_case, |[a], out| expansion::scale(a, b, out))
364    }
365
366    /// Returns the exact sign of the expansion at `a`, as
367    /// [`expansion::sign`] does.
368    ///
369    /// # Examples
370    ///
371    /// ```
372    /// use implicit_predicates_core::{Scratch, Sign};
373    ///
374    /// let mut buf = [0.0; 2];
375    /// let mut scratch = Scratch::borrowed(&mut buf);
376    /// let a = scratch.push(&[1e-20, -1.0])?;
377    /// assert_eq!(scratch.sign(a), Sign::Negative);
378    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
379    /// ```
380    #[must_use]
381    pub fn sign(&self, a: Span) -> Sign {
382        expansion::sign(self.get(a))
383    }
384}
385
386/// Shows the accounting of the arena, not its contents.
387impl fmt::Debug for Scratch<'_> {
388    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389        f.debug_struct("Scratch")
390            .field("used", &self.used)
391            .field("capacity", &self.storage.capacity())
392            .field("high_water", &self.high_water)
393            .finish()
394    }
395}