snarkvm_circuit_environment/macros/
metrics.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
16#[macro_export]
17macro_rules! count {
18    ($type_:ty, $operation:path, $case:expr) => {
19        <$type_ as Metrics<dyn $operation>>::count($case)
20    };
21}
22
23#[macro_export]
24macro_rules! output_mode {
25    ($type_:ty, $operation:path, $case:expr) => {
26        <$type_ as OutputMode<dyn $operation>>::output_mode($case)
27    };
28}
29
30/// Asserts the count for a given operation and case, and ensure the circuit is satisfied.
31///
32/// ## Example
33/// ```ignore
34/// assert_count!(Add(Field, Field) => Field, &(mode_a, mode_b))
35/// ```
36#[macro_export]
37macro_rules! assert_count {
38    ($type_:ty, $operation:path, $case:expr) => {{
39        $crate::print_scope!();
40
41        let Count(num_constants, num_public, num_private, num_constraints) = count!($type_, $operation, $case);
42        assert!(num_constants.matches(Circuit::num_constants_in_scope()), "(num_constants)");
43        assert!(num_public.matches(Circuit::num_public_in_scope()), "(num_public)");
44        assert!(num_private.matches(Circuit::num_private_in_scope()), "(num_private)");
45        assert!(num_constraints.matches(Circuit::num_constraints_in_scope()), "(num_constraints)");
46        assert!(Circuit::is_satisfied_in_scope(), "(is_satisfied_in_scope)");
47    }};
48
49    //////////////
50    // Standard //
51    //////////////
52
53    // Special case: FromBoolean trait deviates from the normal convention.
54    (FromBoolean($input:ident) => $output:ident, $case:expr) => {{
55        assert_count!($output<Circuit>, FromBoolean<Boolean = $input<Circuit>>, $case)
56    }};
57    // Empty case (special): Sets a Boolean associated type.
58    ($operation:tt<$boolean:ident>() => $output:ident, $case:expr) => {{
59        assert_count!($output<Circuit>, $operation<Boolean = $boolean<Circuit>>, $case)
60    }};
61    // Unary case
62    ($operation:tt($input:ident) => $output:ident, $case:expr) => {{
63        assert_count!($input<Circuit>, $operation<Output = $output<Circuit>>, $case)
64    }};
65    // Binary case
66    ($operation:tt($input_a:ident, $input_b:ident) => $output:ident, $case:expr) => {{
67        assert_count!($input_a<Circuit>, $operation<$input_b<Circuit>, Output = $output<Circuit>>, $case)
68    }};
69    // Ternary case (special): Hardcoded for a conditional ternary operator.
70    // Note: $input_b is not used, as the Ternary trait does not require one.
71    ($operation:tt($boolean:ident, $input_a:ident, $input_b:ident) => $output:ident, $case:expr) => {{
72        assert_count!($input_a<Circuit>, $operation<Boolean = $boolean<Circuit>, Output = $output<Circuit>>, $case)
73    }};
74
75    ////////////
76    // Custom //
77    ////////////
78
79    // Empty case (special & custom): Sets a Boolean associated type.
80    ($operation:tt<$boolean:ident>() => $output:ident<$($parameter:ident),+>, $case:expr) => {{
81        assert_count!($output<Circuit, $($parameter),+>, $operation<Boolean = $boolean<Circuit>>, $case)
82    }};
83    // Unary case (custom).
84    ($operation:tt($input:ident<$($parameter_a:ident),+>) => $output:ident<$($parameter_b:ident),+>, $case:expr) => {{
85        assert_count!($input<Circuit, $($parameter_a),+>, $operation<Output = $output<Circuit, $($parameter_b),+>>, $case)
86    }};
87    // Binary case (custom)
88    ($operation:tt($input_a:ident<$($parameter_a:ident),+>, $input_b:ident<$($parameter_b:ident),+>) => $output:ident, $case:expr) => {{
89        assert_count!($input_a<Circuit, $($parameter_a),+>, $operation<$input_b<Circuit, $($parameter_b),+>, Output = $output<Circuit>>, $case)
90    }};
91    // Binary case (custom)
92    ($operation:tt($input_a:ident<$($parameter_a:ident),+>, $input_b:ident<$($parameter_b:ident),+>) => $output:ident<$($parameter_c:ident),+>, $case:expr) => {{
93        assert_count!($input_a<Circuit, $($parameter_a),+>, $operation<$input_b<Circuit, $($parameter_b),+>, Output = $output<Circuit, $($parameter_c),+>>, $case)
94    }};
95    // Ternary case (special & custom): Hardcoded for a conditional ternary operator.
96    // Note: $input_b is not used, as the Ternary trait does not require one.
97    ($operation:tt($boolean:ident, $input_a:ident<$($parameter_a:ident),+>, $input_b:ident<$($parameter_b:ident),+>) => $output:ident<$($parameter_c:ident),+>, $case:expr) => {{
98        assert_count!($input_a<Circuit, $($parameter_a),+>, $operation<Boolean = $boolean<Circuit>, Output = $output<Circuit, $($parameter_c),+>>, $case)
99    }};
100}
101
102/// Asserts the count for a given operation and case, and ensure the circuit is NOT satisfied.
103///
104/// ## Example
105/// ```ignore
106/// assert_count_fails!(Add(Field, Field) => Field, &(mode_a, mode_b))
107/// ```
108#[macro_export]
109macro_rules! assert_count_fails {
110    ($type_:ty, $operation:path, $case:expr) => {{
111        $crate::print_scope!();
112
113        let Count(num_constants, num_public, num_private, num_constraints) = count!($type_, $operation, $case);
114        assert!(num_constants.matches(Circuit::num_constants_in_scope()), "(num_constants)");
115        assert!(num_public.matches(Circuit::num_public_in_scope()), "(num_public)");
116        assert!(num_private.matches(Circuit::num_private_in_scope()), "(num_private)");
117        assert!(num_constraints.matches(Circuit::num_constraints_in_scope()), "(num_constraints)");
118        assert!(!Circuit::is_satisfied_in_scope(), "(!is_satisfied_in_scope)");
119    }};
120
121    //////////////
122    // Standard //
123    //////////////
124
125    // Special case: FromBoolean trait deviates from the normal convention.
126    (FromBoolean($input:ident) => $output:ident, $case:expr) => {{
127        assert_count_fails!($output<Circuit>, FromBoolean<Boolean = $input<Circuit>>, $case)
128    }};
129    // Empty case (special): Sets a Boolean associated type.
130    ($operation:tt<$boolean:ident>() => $output:ident, $case:expr) => {{
131        assert_count_fails!($output<Circuit>, $operation<Boolean = $boolean<Circuit>>, $case)
132    }};
133    // Unary case
134    ($operation:tt($input:ident) => $output:ident, $case:expr) => {{
135        assert_count_fails!($input<Circuit>, $operation<Output = $output<Circuit>>, $case)
136    }};
137    // Binary case
138    ($operation:tt($input_a:ident, $input_b:ident) => $output:ident, $case:expr) => {{
139        assert_count_fails!($input_a<Circuit>, $operation<$input_b<Circuit>, Output = $output<Circuit>>, $case)
140    }};
141    // Ternary case (special): Hardcoded for a conditional ternary operator.
142    // Note: $input_b is not used, as the Ternary trait does not require one.
143    ($operation:tt($boolean:ident, $input_a:ident, $input_b:ident) => $output:ident, $case:expr) => {{
144        assert_count_fails!($input_a<Circuit>, $operation<Boolean = $boolean<Circuit>, Output = $output<Circuit>>, $case)
145    }};
146
147    ////////////
148    // Custom //
149    ////////////
150
151    // Empty case (special & custom): Sets a Boolean associated type.
152    ($operation:tt<$boolean:ident>() => $output:ident<$($parameter:ident),+>, $case:expr) => {{
153        assert_count_fails!($output<Circuit, $($parameter),+>, $operation<Boolean = $boolean<Circuit>>, $case)
154    }};
155    // Unary case (custom).
156    ($operation:tt($input:ident<$($parameter_a:ident),+>) => $output:ident<$($parameter_b:ident),+>, $case:expr) => {{
157        assert_count_fails!($input<Circuit, $($parameter_a),+>, $operation<Output = $output<Circuit, $($parameter_b),+>>, $case)
158    }};
159    // Binary case (custom)
160    ($operation:tt($input_a:ident<$($parameter_a:ident),+>, $input_b:ident<$($parameter_b:ident),+>) => $output:ident, $case:expr) => {{
161        assert_count_fails!($input_a<Circuit, $($parameter_a),+>, $operation<$input_b<Circuit, $($parameter_b),+>, Output = $output<Circuit>>, $case)
162    }};
163    // Binary case (custom)
164    ($operation:tt($input_a:ident<$($parameter_a:ident),+>, $input_b:ident<$($parameter_b:ident),+>) => $output:ident<$($parameter_c:ident),+>, $case:expr) => {{
165        assert_count_fails!($input_a<Circuit, $($parameter_a),+>, $operation<$input_b<Circuit, $($parameter_b),+>, Output = $output<Circuit, $($parameter_c),+>>, $case)
166    }};
167    // Ternary case (special & custom): Hardcoded for a conditional ternary operator.
168    // Note: $input_b is not used, as the Ternary trait does not require one.
169    ($operation:tt($boolean:ident, $input_a:ident<$($parameter_a:ident),+>, $input_b:ident<$($parameter_b:ident),+>) => $output:ident<$($parameter_c:ident),+>, $case:expr) => {{
170        assert_count_fails!($input_a<Circuit, $($parameter_a),+>, $operation<Boolean = $boolean<Circuit>, Output = $output<Circuit, $($parameter_c),+>>, $case)
171    }};
172}
173
174/// Asserts the output mode for a given operation and case.
175///
176/// ## Example
177/// ```ignore
178/// assert_output_mode!(candidate, Add(Field, Field) => Field, &(mode_a, mode_b))
179/// ```
180#[macro_export]
181macro_rules! assert_output_mode {
182    ($type_:ty, $operation:path, $case:expr, $candidate:expr) => {{
183        let expected_mode = output_mode!($type_, $operation, $case);
184        assert_eq!(expected_mode, $candidate.eject_mode());
185    }};
186
187    //////////////
188    // Standard //
189    //////////////
190
191    // Special case: FromBoolean trait deviates from the normal convention.
192    (FromBoolean($input:ident) => $output:ident, $case:expr, $candidate:expr) => {{
193        assert_output_mode!($output<Circuit>, FromBoolean<Boolean = $input<Circuit>>, $case, $candidate)
194    }};
195    // Empty case (special): Sets a Boolean associated type.
196    ($operation:tt<$boolean:ident>() => $output:ident, $case:expr, $candidate:expr) => {{
197        assert_output_mode!($output<Circuit>, $operation<Boolean = $boolean<Circuit>>, $case, $candidate)
198    }};
199    // Unary case
200    ($operation:tt($input:ident) => $output:ident, $case:expr, $candidate:expr) => {{
201        assert_output_mode!($input<Circuit>, $operation<Output = $output<Circuit>>, $case, $candidate)
202    }};
203    // Binary case
204    ($operation:tt($input_a:ident, $input_b:ident) => $output:ident, $case:expr, $candidate:expr) => {{
205        assert_output_mode!($input_a<Circuit>, $operation<$input_b<Circuit>, Output = $output<Circuit>>, $case, $candidate)
206    }};
207    // Ternary case (special): Hardcoded for a conditional ternary operator.
208    // Note: $input_b is not used, as the Ternary trait does not require one.
209    ($operation:tt($boolean:ident, $input_a:ident, $input_b:ident) => $output:ident, $case:expr, $candidate:expr) => {{
210        assert_output_mode!($input_a<Circuit>, $operation<Boolean = $boolean<Circuit>, Output = $output<Circuit>>, $case, $candidate)
211    }};
212
213    ////////////
214    // Custom //
215    ////////////
216
217    // Empty case (special & custom): Sets a Boolean associated type.
218    ($operation:tt<$boolean:ident>() => $output:ident<$($parameter:ident),+>, $case:expr, $candidate:expr) => {{
219        assert_output_mode!($output<Circuit, $($parameter),+>, $operation<Boolean = $boolean<Circuit>>, $case, $candidate)
220    }};
221    // Unary case (custom).
222    ($operation:tt($input:ident<$($parameter_a:ident),+>) => $output:ident<$($parameter_b:ident),+>, $case:expr, $candidate:expr) => {{
223        assert_output_mode!($input<Circuit, $($parameter_a),+>, $operation<Output = $output<Circuit, $($parameter_b),+>>, $case, $candidate)
224    }};
225    // Binary case (custom)
226    ($operation:tt($input_a:ident<$($parameter_a:ident),+>, $input_b:ident<$($parameter_b:ident),+>) => $output:ident, $case:expr, $candidate:expr) => {{
227        assert_output_mode!($input_a<Circuit, $($parameter_a),+>, $operation<$input_b<Circuit, $($parameter_b),+>, Output = $output<Circuit>>, $case, $candidate)
228    }};
229    // Binary case (custom)
230    ($operation:tt($input_a:ident<$($parameter_a:ident),+>, $input_b:ident<$($parameter_b:ident),+>) => $output:ident<$($parameter_c:ident),+>, $case:expr, $candidate:expr) => {{
231        assert_output_mode!($input_a<Circuit, $($parameter_a),+>, $operation<$input_b<Circuit, $($parameter_b),+>, Output = $output<Circuit, $($parameter_c),+>>, $case, $candidate)
232    }};
233    // Ternary case (special & custom): Hardcoded for a conditional ternary operator.
234    // Note: $input_b is not used, as the Ternary trait does not require one.
235    ($operation:tt($boolean:ident, $input_a:ident<$($parameter_a:ident),+>, $input_b:ident<$($parameter_b:ident),+>) => $output:ident<$($parameter_c:ident),+>, $case:expr, $candidate:expr) => {{
236        assert_output_mode!($input_a<Circuit, $($parameter_a),+>, $operation<Boolean = $boolean<Circuit>, Output = $output<Circuit, $($parameter_c),+>>, $case, $candidate)
237    }};
238}