frontend/components/
digits.rs

1use shared_types::Field;
2
3use crate::{
4    abstract_expr::AbstractExpression,
5    layouter::builder::{CircuitBuilder, NodeRef},
6};
7
8/// Digits-related circuit components
9pub struct DigitComponents;
10
11impl DigitComponents {
12    /// Digital recomposition of an unsigned integer, i.e. deriving the number from its digits.
13    /// Each of the Nodes in `mles` specifies the digits for a different
14    /// "decimal place".  Most significant digit comes first.
15    pub fn unsigned_recomposition<F: Field>(
16        builder_ref: &mut CircuitBuilder<F>,
17        mles: &[&NodeRef<F>],
18        base: u64,
19    ) -> NodeRef<F> {
20        let num_digits = mles.len();
21
22        let b_s_initial_acc = AbstractExpression::<F>::constant(F::ZERO);
23
24        let sector_expr = mles.iter().enumerate().fold(
25            b_s_initial_acc,
26            |acc_expr, (bit_idx, bin_decomp_node)| {
27                let power = F::from(base.pow((num_digits - (bit_idx + 1)) as u32));
28                acc_expr + *bin_decomp_node * power
29            },
30        );
31        builder_ref.add_sector(sector_expr)
32    }
33
34    /// Checks that the complementary decomposition of a signed
35    /// integer. To be used in conjunction with [Self::unsigned_recomposition]. See
36    /// [crate::digits::complementary_decomposition] and
37    /// [Notion](https://www.notion.so/Constraining-for-the-response-zero-case-using-the-complementary-representation-d77ddfe258a74a9ab949385cc6f7eda4).
38    /// Add self.sector to the circuit as an output layer to enforce this constraint.
39    /// Create a new ComplementaryDecompChecker. `values` are the original
40    /// values. `bits` are the bits of the complementary decomposition.
41    /// `unsigned_recomps` are the unsigned recompositions.
42    pub fn complementary_recomp_check<F: Field>(
43        builder_ref: &mut CircuitBuilder<F>,
44        values: &NodeRef<F>,
45        bits: &NodeRef<F>,
46        unsigned_recomps: &NodeRef<F>,
47        base: u64,
48        num_digits: usize,
49    ) -> NodeRef<F> {
50        let mut pow = F::from(1_u64);
51        for _ in 0..num_digits {
52            pow *= F::from(base);
53        }
54
55        builder_ref.add_sector(bits * pow - unsigned_recomps - values)
56    }
57
58    /// Ensures that each bit is either 0 or 1. Add self.sector to the circuit as an
59    /// output layer to enforce this constraint.
60    pub fn bits_are_binary<F: Field>(
61        builder_ref: &mut CircuitBuilder<F>,
62        values_node: &NodeRef<F>,
63    ) -> NodeRef<F> {
64        builder_ref.add_sector(values_node * values_node - values_node)
65    }
66
67    /// A component that concatenates all the separate digit MLEs (there is one for
68    /// each digital place) into a single MLE using a selector tree. (Necessary to
69    /// interact with logup).
70    pub fn digits_concatenator<F: Field>(
71        builder_ref: &mut CircuitBuilder<F>,
72        mles: &[&NodeRef<F>],
73    ) -> NodeRef<F> {
74        builder_ref.add_sector(AbstractExpression::binary_tree_selector(mles.to_vec()))
75    }
76}