circomspect_program_structure/intermediate_representation/
value_meta.rs

1use num_bigint::BigInt;
2use std::collections::HashMap;
3use std::fmt;
4
5use crate::constants::UsefulConstants;
6
7use super::ir::VariableName;
8
9#[derive(Clone)]
10pub struct ValueEnvironment {
11    constants: UsefulConstants,
12    reduces_to: HashMap<VariableName, ValueReduction>,
13}
14
15impl ValueEnvironment {
16    pub fn new(constants: &UsefulConstants) -> ValueEnvironment {
17        ValueEnvironment { constants: constants.clone(), reduces_to: HashMap::new() }
18    }
19
20    /// Set the value of the given variable. Returns `true` on first update.
21    ///
22    /// # Panics
23    ///
24    /// This function panics if the caller attempts to set two different values
25    /// for the same variable.
26    pub fn add_variable(&mut self, name: &VariableName, value: &ValueReduction) -> bool {
27        if let Some(previous) = self.reduces_to.insert(name.clone(), value.clone()) {
28            assert_eq!(previous, *value);
29            false
30        } else {
31            true
32        }
33    }
34
35    #[must_use]
36    pub fn get_variable(&self, name: &VariableName) -> Option<&ValueReduction> {
37        self.reduces_to.get(name)
38    }
39
40    /// Returns the prime used.
41    pub fn prime(&self) -> &BigInt {
42        self.constants.prime()
43    }
44}
45
46pub trait ValueMeta {
47    /// Propagate variable values defined by the environment to each sub-node.
48    /// The method returns true if the node (or a sub-node) was updated.
49    fn propagate_values(&mut self, env: &mut ValueEnvironment) -> bool;
50
51    /// Returns true if the node reduces to a constant value.
52    #[must_use]
53    fn is_constant(&self) -> bool;
54
55    /// Returns true if the node reduces to a boolean value.
56    #[must_use]
57    fn is_boolean(&self) -> bool;
58
59    /// Returns true if the node reduces to a field element.
60    #[must_use]
61    fn is_field_element(&self) -> bool;
62
63    /// Returns the value if the node reduces to a constant, and None otherwise.
64    #[must_use]
65    fn value(&self) -> Option<&ValueReduction>;
66}
67
68#[derive(Debug, Clone, Hash, PartialEq, Eq)]
69pub enum ValueReduction {
70    Boolean { value: bool },
71    FieldElement { value: BigInt },
72}
73
74impl fmt::Display for ValueReduction {
75    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
76        use ValueReduction::*;
77        match self {
78            Boolean { value } => write!(f, "{value}"),
79            FieldElement { value } => write!(f, "{value}"),
80        }
81    }
82}
83
84#[derive(Default, Clone)]
85pub struct ValueKnowledge {
86    reduces_to: Option<ValueReduction>,
87}
88
89impl ValueKnowledge {
90    #[must_use]
91    pub fn new() -> ValueKnowledge {
92        ValueKnowledge::default()
93    }
94
95    /// Sets the value of the node. Returns `true` on the first update.
96    #[must_use]
97    pub fn set_reduces_to(&mut self, reduces_to: ValueReduction) -> bool {
98        let result = self.reduces_to.is_none();
99        self.reduces_to = Some(reduces_to);
100        result
101    }
102
103    /// Gets the value of the node. Returns `None` if the value is unknown.
104    #[must_use]
105    pub fn get_reduces_to(&self) -> Option<&ValueReduction> {
106        self.reduces_to.as_ref()
107    }
108
109    /// Returns `true` if the value of the node is known.
110    #[must_use]
111    pub fn is_constant(&self) -> bool {
112        self.reduces_to.is_some()
113    }
114
115    /// Returns `true` if the value of the node is a boolean.
116    #[must_use]
117    pub fn is_boolean(&self) -> bool {
118        use ValueReduction::*;
119        matches!(self.reduces_to, Some(Boolean { .. }))
120    }
121
122    /// Returns `true` if the value of the node is a field element.
123    #[must_use]
124    pub fn is_field_element(&self) -> bool {
125        use ValueReduction::*;
126        matches!(self.reduces_to, Some(FieldElement { .. }))
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use num_bigint::BigInt;
133
134    use crate::ir::value_meta::ValueReduction;
135
136    use super::ValueKnowledge;
137
138    #[test]
139    fn test_value_knowledge() {
140        let mut value = ValueKnowledge::new();
141        assert!(matches!(value.get_reduces_to(), None));
142
143        let number = ValueReduction::FieldElement { value: BigInt::from(1) };
144        assert!(value.set_reduces_to(number));
145        assert!(matches!(value.get_reduces_to(), Some(ValueReduction::FieldElement { .. })));
146        assert!(value.is_field_element());
147        assert!(!value.is_boolean());
148
149        let boolean = ValueReduction::Boolean { value: true };
150        assert!(!value.set_reduces_to(boolean));
151        assert!(matches!(value.get_reduces_to(), Some(ValueReduction::Boolean { .. })));
152        assert!(!value.is_field_element());
153        assert!(value.is_boolean());
154    }
155}