snarkvm_circuit_environment/helpers/
counter.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::*;
17use snarkvm_fields::PrimeField;
18
19use std::{mem, sync::Arc};
20
21#[derive(Debug, Default, Hash)]
22pub(crate) struct Counter<F: PrimeField> {
23    scope: Scope,
24    constraints: Vec<Arc<Constraint<F>>>,
25    constants: u64,
26    public: u64,
27    private: u64,
28    nonzeros: (u64, u64, u64),
29    parents: Vec<(Scope, Vec<Arc<Constraint<F>>>, u64, u64, u64, (u64, u64, u64))>,
30}
31
32impl<F: PrimeField> Counter<F> {
33    /// Saves and switches from the current scope to a new scope.
34    pub(crate) fn push<S: Into<String>>(&mut self, name: S) -> Result<(), String> {
35        let name = name.into();
36        match name.contains('.') {
37            true => Err("Scope names cannot contain periods (\".\")".to_string()),
38            false => {
39                // Construct the scope name.
40                let scope = match self.scope.is_empty() {
41                    true => name,
42                    false => format!("{}.{}", self.scope, name),
43                };
44
45                // Save the current scope members.
46                self.parents.push((
47                    self.scope.clone(),
48                    mem::take(&mut self.constraints),
49                    self.constants,
50                    self.public,
51                    self.private,
52                    self.nonzeros,
53                ));
54
55                // Initialize the new scope members.
56                self.scope = scope;
57                self.constants = 0;
58                self.public = 0;
59                self.private = 0;
60                self.nonzeros = (0, 0, 0);
61
62                Ok(())
63            }
64        }
65    }
66
67    /// Discards the current scope, reverting to the previous scope.
68    pub(crate) fn pop<S: Into<String>>(&mut self, name: S) -> Result<(), String> {
69        // Pop the current scope from the full scope.
70        let (_previous_scope, current_scope) = match self.scope.rsplit_once('.') {
71            Some((previous_scope, current_scope)) => (previous_scope, current_scope),
72            None => ("", self.scope.as_str()),
73        };
74
75        // Ensure the current scope is the last pushed scope.
76        match current_scope == name.into() {
77            true => {
78                if let Some((scope, constraints, constants, public, private, nonzeros)) = self.parents.pop() {
79                    self.scope = scope;
80                    self.constraints = constraints;
81                    self.constants = constants;
82                    self.public = public;
83                    self.private = private;
84                    self.nonzeros = nonzeros;
85                }
86            }
87            false => {
88                return Err("Mismatching scope. Scopes must return in the reverse order they are created".to_string());
89            }
90        }
91
92        Ok(())
93    }
94
95    /// Increments the number of constraints by 1.
96    pub(crate) fn add_constraint(&mut self, constraint: Arc<Constraint<F>>) {
97        let (a_nonzeros, b_nonzeros, c_nonzeros) = constraint.num_nonzeros();
98        self.nonzeros.0 += a_nonzeros;
99        self.nonzeros.1 += b_nonzeros;
100        self.nonzeros.2 += c_nonzeros;
101
102        self.constraints.push(constraint);
103    }
104
105    /// Returns `true` if all constraints in the scope are satisfied.
106    pub(crate) fn is_satisfied_in_scope(&self) -> bool {
107        self.constraints.iter().all(|constraint| constraint.is_satisfied())
108    }
109
110    /// Returns the current scope.
111    pub(crate) fn scope(&self) -> Scope {
112        self.scope.clone()
113    }
114
115    /// Increments the number of constants by 1.
116    pub(crate) fn increment_constant(&mut self) {
117        self.constants += 1;
118    }
119
120    /// Increments the number of public variables by 1.
121    pub(crate) fn increment_public(&mut self) {
122        self.public += 1;
123    }
124
125    /// Increments the number of private variables by 1.
126    pub(crate) fn increment_private(&mut self) {
127        self.private += 1;
128    }
129
130    /// Returns the number of constants in scope in scope.
131    pub(crate) fn num_constants_in_scope(&self) -> u64 {
132        self.constants
133    }
134
135    /// Returns the number of public variables in scope.
136    pub(crate) fn num_public_in_scope(&self) -> u64 {
137        self.public
138    }
139
140    /// Returns the number of private variables in scope.
141    pub(crate) fn num_private_in_scope(&self) -> u64 {
142        self.private
143    }
144
145    /// Returns the number of constraints in scope.
146    pub(crate) fn num_constraints_in_scope(&self) -> u64 {
147        self.constraints.len() as u64
148    }
149
150    /// Returns the number of nonzeros in scope.
151    pub(crate) fn num_nonzeros_in_scope(&self) -> (u64, u64, u64) {
152        self.nonzeros
153    }
154}