Skip to main content

simplicity/types/
final_data.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Finalized (Complete) Type Data
4//!
5//! Once a type is complete (has no free variables), it can be represented as
6//! a much simpler data structure than [`super::Type`], which we call [`Final`].
7//! This contains a recursively-defined [`CompleteBound`] which specifies what
8//! the type is, as well as a cached Merkle root (the TMR) and bit-width.
9//!
10//! We refer to types as "finalized" when they are represented by this data
11//! structure, since this structure is immutable.
12//!
13
14use crate::dag::{Dag, DagLike, NoSharing};
15use crate::Tmr;
16
17use std::sync::Arc;
18use std::{cmp, fmt, hash};
19
20/// A finalized type bound, whose tree is accessible without any mutex locking
21#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
22pub enum CompleteBound {
23    /// The unit type
24    Unit,
25    /// A sum of two other types
26    Sum(Arc<Final>, Arc<Final>),
27    /// A product of two other types
28    Product(Arc<Final>, Arc<Final>),
29}
30
31/// Data related to a finalized type, which can be extracted from a [`super::Type`]
32/// if (and only if) it is finalized.
33#[derive(Clone)]
34pub struct Final {
35    /// Underlying type
36    bound: CompleteBound,
37    /// Width of the type, in bits, in the bit machine
38    bit_width: usize,
39    /// Whether the type's bit representation has any padding. If this is true,
40    /// then its "compact" witness-encoded bit-width may be lower than its "padded"
41    /// bit-machine bit-width.
42    has_padding: bool,
43    /// TMR of the type
44    tmr: Tmr,
45}
46
47impl PartialEq for Final {
48    fn eq(&self, other: &Self) -> bool {
49        self.tmr == other.tmr
50    }
51}
52impl Eq for Final {}
53
54impl PartialOrd for Final {
55    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
56        Some(self.cmp(other))
57    }
58}
59impl Ord for Final {
60    fn cmp(&self, other: &Self) -> cmp::Ordering {
61        self.tmr.cmp(&other.tmr)
62    }
63}
64impl hash::Hash for Final {
65    fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
66        self.tmr.hash(hasher)
67    }
68}
69
70impl fmt::Debug for Final {
71    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72        write!(
73            f,
74            "{{ tmr: {}, bit_width: {}, bound: {} }}",
75            self.tmr, self.bit_width, self
76        )
77    }
78}
79
80impl fmt::Display for Final {
81    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82        let mut skipping: Option<Tmr> = None;
83        for data in self.verbose_pre_order_iter::<NoSharing>(None) {
84            if let Some(skip) = skipping {
85                if data.is_complete && data.node.tmr == skip {
86                    skipping = None;
87                }
88                continue;
89            } else {
90                if data.node.tmr == Tmr::TWO_TWO_N[0] {
91                    f.write_str("2")?;
92                    skipping = Some(data.node.tmr);
93                }
94                for (n, tmr) in Tmr::TWO_TWO_N.iter().enumerate().skip(1) {
95                    if data.node.tmr == *tmr {
96                        write!(f, "2^{}", 1 << n)?;
97                        skipping = Some(data.node.tmr);
98                    }
99                }
100            }
101            if skipping.is_some() {
102                continue;
103            }
104
105            match (&data.node.bound, data.n_children_yielded) {
106                (CompleteBound::Unit, _) => {
107                    f.write_str("1")?;
108                }
109                // special-case 1 + A as A?
110                (CompleteBound::Sum(ref left, _), 0)
111                    if matches!(left.bound, CompleteBound::Unit) =>
112                {
113                    skipping = Some(Tmr::unit());
114                }
115                (CompleteBound::Sum(ref left, _), 1)
116                    if matches!(left.bound, CompleteBound::Unit) => {}
117                (CompleteBound::Sum(ref left, _), 2)
118                    if matches!(left.bound, CompleteBound::Unit) =>
119                {
120                    f.write_str("?")?;
121                }
122                // other sums and products
123                (CompleteBound::Sum(..), 0) | (CompleteBound::Product(..), 0) => {
124                    if data.index > 0 {
125                        f.write_str("(")?;
126                    }
127                }
128                (CompleteBound::Sum(..), 2) | (CompleteBound::Product(..), 2) => {
129                    if data.index > 0 {
130                        f.write_str(")")?;
131                    }
132                }
133                (CompleteBound::Sum(..), _) => f.write_str(" + ")?,
134                (CompleteBound::Product(..), _) => f.write_str(" × ")?,
135            }
136        }
137        Ok(())
138    }
139}
140
141impl DagLike for &'_ Final {
142    type Node = Final;
143    fn data(&self) -> &Final {
144        self
145    }
146    fn as_dag_node(&self) -> Dag<Self> {
147        match self.bound {
148            CompleteBound::Unit => Dag::Nullary,
149            CompleteBound::Sum(ref left, ref right)
150            | CompleteBound::Product(ref left, ref right) => Dag::Binary(left, right),
151        }
152    }
153}
154
155macro_rules! construct_final_two_two_n {
156    ($name: ident, $n: expr, $text: expr) => {
157        #[doc = "Create the type of"]
158        #[doc = $text]
159        #[doc = "words.\n\nThe type is precomputed and fast to access."]
160        #[inline]
161        pub fn $name() -> Arc<Self> {
162            super::precomputed::nth_power_of_2($n)
163        }
164    };
165}
166
167impl Final {
168    /// Create the unit type.
169    pub fn unit() -> Arc<Self> {
170        Arc::new(Final {
171            bound: CompleteBound::Unit,
172            bit_width: 0,
173            has_padding: false,
174            tmr: Tmr::unit(),
175        })
176    }
177
178    /// Computes the successor of the type.
179    ///
180    /// Given a type `X`, we define its successor `S X` as `1 + X`.
181    /// In Rust notation this would be `Option<X>`.
182    pub fn successor(self: Arc<Self>) -> Arc<Self> {
183        Self::sum(Self::unit(), self)
184    }
185
186    /// Create the type `2^(2^n)` for the given `n`.
187    ///
188    /// The type is precomputed and fast to access.
189    #[inline]
190    pub fn two_two_n(n: usize) -> Result<Arc<Self>, TypeTooLargeError> {
191        let maximum = Tmr::TWO_TWO_N.len();
192        if n < maximum {
193            Ok(super::precomputed::nth_power_of_2(n))
194        } else {
195            Err(TypeTooLargeError {
196                ty: "2^(2^n)",
197                n,
198                maximum,
199            })
200        }
201    }
202
203    /// Create the type `2^(2^N)` for the compile-time constant `N`.
204    ///
205    /// Will fail to compile if `N` exceeds 31.
206    #[inline]
207    pub fn two_two_n_fixed<const N: usize>() -> Arc<Self> {
208        // This crazy construction amounts to a compile-time assertion that N is less than the max.
209        struct Hack<const N: usize>;
210        impl<const N: usize> Hack<N> {
211            const IS_IN_RANGE: () = {
212                assert!(N < Tmr::TWO_TWO_N.len());
213            };
214        }
215        let () = Hack::<N>::IS_IN_RANGE;
216
217        super::precomputed::nth_power_of_2(N)
218    }
219
220    /// Create the type `(TWO^8)^<2^(n+1)` for the given `n`.
221    ///
222    /// Here
223    /// * The notation X^<2 is notation for the type (S X)
224    /// * The notation X^<(2*n) is notation for the type S (X^n) * X^<n
225    ///
226    /// And `S X` is the successor of `X`, i.e. `Option<X>`
227    ///
228    /// The type is precomputed and fast to access.
229    #[inline]
230    pub fn buffer8_two_n_plus_one(n: usize) -> Result<Arc<Self>, TypeTooLargeError> {
231        let maximum = Tmr::BUFFER8_TWO_N_PLUS_ONE.len();
232        if n < maximum {
233            Ok(super::precomputed::buffer8_two_n_plus_one(n))
234        } else {
235            // This is arguably a programming error and a panic would be justified, but it's
236            // hard to say how SimplicityHL will use this. I also think the current maximum
237            // may be too small and we could bump into this with real code, so better to let
238            // the caller decide how to handle that.
239            Err(TypeTooLargeError {
240                ty: "(TWO^8)^<2^(n+1)",
241                n,
242                maximum,
243            })
244        }
245    }
246
247    /// Create the `Ctx8` type used by the SHA256 jets.
248    ///
249    /// The type is precomputed and fast to access.
250    pub fn ctx8() -> Arc<Self> {
251        super::precomputed::ctx8()
252    }
253
254    construct_final_two_two_n!(u1, 0, "1-bit");
255    construct_final_two_two_n!(u2, 1, "2-bit");
256    construct_final_two_two_n!(u4, 2, "4-bit");
257    construct_final_two_two_n!(u8, 3, "8-bit");
258    construct_final_two_two_n!(u16, 4, "16-bit");
259    construct_final_two_two_n!(u32, 5, "32-bit");
260    construct_final_two_two_n!(u64, 6, "64-bit");
261    construct_final_two_two_n!(u128, 7, "128-bit");
262    construct_final_two_two_n!(u256, 8, "256-bit");
263    construct_final_two_two_n!(u512, 9, "512-bit");
264
265    /// Create the sum of the given `left` and `right` types.
266    pub fn sum(left: Arc<Self>, right: Arc<Self>) -> Arc<Self> {
267        // Use saturating_add for bitwidths. If the user has overflowed usize, even on a 32-bit
268        // system this means that they have a 4-gigabit type and their program should be rejected
269        // by a sanity check somewhere. However, if we panic here, the user cannot finalize their
270        // program and cannot even tell that this resource limit has been hit before panicking.
271        Arc::new(Final {
272            tmr: Tmr::sum(left.tmr, right.tmr),
273            bit_width: cmp::max(left.bit_width, right.bit_width).saturating_add(1),
274            has_padding: left.has_padding || right.has_padding || left.bit_width != right.bit_width,
275            bound: CompleteBound::Sum(left, right),
276        })
277    }
278
279    /// Create the product of the given `left` and `right` types.
280    pub fn product(left: Arc<Self>, right: Arc<Self>) -> Arc<Self> {
281        // See comment in `sum` about use of saturating add.
282        Arc::new(Final {
283            tmr: Tmr::product(left.tmr, right.tmr),
284            bit_width: left.bit_width.saturating_add(right.bit_width),
285            has_padding: left.has_padding || right.has_padding,
286            bound: CompleteBound::Product(left, right),
287        })
288    }
289
290    /// Accessor for the TMR
291    pub fn tmr(&self) -> Tmr {
292        self.tmr
293    }
294
295    /// Accessor for the Bit Machine bit-width of the type
296    pub fn bit_width(&self) -> usize {
297        self.bit_width
298    }
299
300    /// Whether the type's bit representation has any padding.
301    ///
302    /// If this is true, then its "compact" witness-encoded bit-width may be lower
303    /// than its "padded" bit-machine bit-width.
304    pub fn has_padding(&self) -> bool {
305        self.has_padding
306    }
307
308    /// Check if the type is a nested product of units.
309    /// In this case, values contain no information.
310    pub fn is_empty(&self) -> bool {
311        self.bit_width() == 0
312    }
313
314    /// Accessor for the type bound
315    pub fn bound(&self) -> &CompleteBound {
316        &self.bound
317    }
318
319    /// Check if the type is a unit.
320    pub fn is_unit(&self) -> bool {
321        self.bound == CompleteBound::Unit
322    }
323
324    /// Access the inner types of a sum type.
325    pub fn as_sum(&self) -> Option<(&Arc<Self>, &Arc<Self>)> {
326        match &self.bound {
327            CompleteBound::Sum(left, right) => Some((left, right)),
328            _ => None,
329        }
330    }
331
332    /// Access the inner types of a product type.
333    pub fn as_product(&self) -> Option<(&Arc<Self>, &Arc<Self>)> {
334        match &self.bound {
335            CompleteBound::Product(left, right) => Some((left, right)),
336            _ => None,
337        }
338    }
339
340    /// If the type is of the form `TWO^(2^n)`, then return `n`.
341    ///
342    /// ## Post condition
343    ///
344    /// 0 ≤ n < 32.
345    pub fn as_word(&self) -> Option<u8> {
346        (0u8..32).find(|&n| self.tmr == Tmr::TWO_TWO_N[usize::from(n)])
347    }
348
349    /// Compute the padding of left values of the sum type `Self + Other`.
350    pub fn pad_left(&self, other: &Self) -> usize {
351        cmp::max(self.bit_width, other.bit_width) - self.bit_width
352    }
353
354    /// Compute the padding of right values of the sum type `Self + Other`.
355    pub fn pad_right(&self, other: &Self) -> usize {
356        cmp::max(self.bit_width, other.bit_width) - other.bit_width
357    }
358}
359
360/// Attempted to produce a `(TWO^8)^(2^(n+1))` type exceeding the maximum size.
361#[derive(Clone, PartialEq, Eq, Debug)]
362pub struct TypeTooLargeError {
363    ty: &'static str,
364    n: usize,
365    maximum: usize,
366}
367
368impl fmt::Display for TypeTooLargeError {
369    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370        write!(
371            f,
372            "maximum n for {} is {}; got {}",
373            self.ty, self.maximum, self.n
374        )
375    }
376}
377
378impl std::error::Error for TypeTooLargeError {
379    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
380        let Self {
381            ty: _,
382            n: _,
383            maximum: _,
384        } = self;
385        None
386    }
387}
388
389#[cfg(test)]
390mod tests {
391    use super::*;
392
393    #[test]
394    fn two_two_n_consistent() {
395        // This test probably does not need to be exhaustive.
396        assert_eq!(Final::two_two_n_fixed::<0>(), Final::two_two_n(0).unwrap());
397        assert_eq!(Final::two_two_n_fixed::<1>(), Final::two_two_n(1).unwrap());
398        assert_eq!(Final::two_two_n_fixed::<2>(), Final::two_two_n(2).unwrap());
399        assert_eq!(Final::two_two_n_fixed::<3>(), Final::two_two_n(3).unwrap());
400        assert_eq!(Final::two_two_n_fixed::<4>(), Final::two_two_n(4).unwrap());
401        assert_eq!(Final::two_two_n_fixed::<5>(), Final::two_two_n(5).unwrap());
402        assert_eq!(Final::two_two_n_fixed::<6>(), Final::two_two_n(6).unwrap());
403        assert_eq!(Final::two_two_n_fixed::<7>(), Final::two_two_n(7).unwrap());
404    }
405
406    #[test]
407    fn final_stringify() {
408        let ty1 = Final::two_two_n_fixed::<10>();
409        assert_eq!(ty1.to_string(), "2^1024");
410
411        let sum = Final::sum(Final::two_two_n_fixed::<5>(), Final::two_two_n(10).unwrap());
412        assert_eq!(sum.to_string(), "2^32 + 2^1024");
413
414        let prod = Final::product(
415            Final::two_two_n_fixed::<5>(),
416            Final::two_two_n_fixed::<10>(),
417        );
418        assert_eq!(prod.to_string(), "2^32 × 2^1024");
419
420        let ty1 = Final::two_two_n_fixed::<0>();
421        assert_eq!(ty1.to_string(), "2");
422
423        let ty1 = Final::sum(Final::unit(), Final::two_two_n_fixed::<2>());
424        assert_eq!(ty1.to_string(), "2^4?");
425    }
426}