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