1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use num_bigint::BigInt;
use std::collections::HashMap;
use std::fmt;

use crate::constants::UsefulConstants;

use super::ir::VariableName;

#[derive(Clone)]
pub struct ValueEnvironment {
    constants: UsefulConstants,
    reduces_to: HashMap<VariableName, ValueReduction>,
}

impl ValueEnvironment {
    pub fn new(constants: &UsefulConstants) -> ValueEnvironment {
        ValueEnvironment { constants: constants.clone(), reduces_to: HashMap::new() }
    }

    /// Set the value of the given variable. Returns `true` on first update.
    ///
    /// # Panics
    ///
    /// This function panics if the caller attempts to set two different values
    /// for the same variable.
    pub fn add_variable(&mut self, name: &VariableName, value: &ValueReduction) -> bool {
        if let Some(previous) = self.reduces_to.insert(name.clone(), value.clone()) {
            assert_eq!(previous, *value);
            false
        } else {
            true
        }
    }

    #[must_use]
    pub fn get_variable(&self, name: &VariableName) -> Option<&ValueReduction> {
        self.reduces_to.get(name)
    }

    /// Returns the prime used.
    pub fn prime(&self) -> &BigInt {
        self.constants.prime()
    }
}

pub trait ValueMeta {
    /// Propagate variable values defined by the environment to each sub-node.
    /// The method returns true if the node (or a sub-node) was updated.
    fn propagate_values(&mut self, env: &mut ValueEnvironment) -> bool;

    /// Returns true if the node reduces to a constant value.
    #[must_use]
    fn is_constant(&self) -> bool;

    /// Returns true if the node reduces to a boolean value.
    #[must_use]
    fn is_boolean(&self) -> bool;

    /// Returns true if the node reduces to a field element.
    #[must_use]
    fn is_field_element(&self) -> bool;

    /// Returns the value if the node reduces to a constant, and None otherwise.
    #[must_use]
    fn value(&self) -> Option<&ValueReduction>;
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum ValueReduction {
    Boolean { value: bool },
    FieldElement { value: BigInt },
}

impl fmt::Display for ValueReduction {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use ValueReduction::*;
        match self {
            Boolean { value } => write!(f, "{value}"),
            FieldElement { value } => write!(f, "{value}"),
        }
    }
}

#[derive(Default, Clone)]
pub struct ValueKnowledge {
    reduces_to: Option<ValueReduction>,
}

impl ValueKnowledge {
    #[must_use]
    pub fn new() -> ValueKnowledge {
        ValueKnowledge::default()
    }

    /// Sets the value of the node. Returns `true` on the first update.
    #[must_use]
    pub fn set_reduces_to(&mut self, reduces_to: ValueReduction) -> bool {
        let result = self.reduces_to.is_none();
        self.reduces_to = Some(reduces_to);
        result
    }

    /// Gets the value of the node. Returns `None` if the value is unknown.
    #[must_use]
    pub fn get_reduces_to(&self) -> Option<&ValueReduction> {
        self.reduces_to.as_ref()
    }

    /// Returns `true` if the value of the node is known.
    #[must_use]
    pub fn is_constant(&self) -> bool {
        self.reduces_to.is_some()
    }

    /// Returns `true` if the value of the node is a boolean.
    #[must_use]
    pub fn is_boolean(&self) -> bool {
        use ValueReduction::*;
        matches!(self.reduces_to, Some(Boolean { .. }))
    }

    /// Returns `true` if the value of the node is a field element.
    #[must_use]
    pub fn is_field_element(&self) -> bool {
        use ValueReduction::*;
        matches!(self.reduces_to, Some(FieldElement { .. }))
    }
}