snarkvm_circuit_types_field/
div.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 super::*;
17
18impl<E: Environment> Div<Field<E>> for Field<E> {
19    type Output = Field<E>;
20
21    fn div(self, other: Field<E>) -> Self::Output {
22        self / &other
23    }
24}
25
26impl<E: Environment> Div<&Field<E>> for Field<E> {
27    type Output = Field<E>;
28
29    fn div(self, other: &Field<E>) -> Self::Output {
30        &self / other
31    }
32}
33
34impl<E: Environment> Div<Field<E>> for &Field<E> {
35    type Output = Field<E>;
36
37    fn div(self, other: Field<E>) -> Self::Output {
38        self / &other
39    }
40}
41
42impl<E: Environment> Div<&Field<E>> for &Field<E> {
43    type Output = Field<E>;
44
45    fn div(self, other: &Field<E>) -> Self::Output {
46        let mut output = self.clone();
47        output /= other;
48        output
49    }
50}
51
52impl<E: Environment> DivAssign<Self> for Field<E> {
53    fn div_assign(&mut self, other: Self) {
54        *self /= &other;
55    }
56}
57
58impl<E: Environment> DivAssign<&Self> for Field<E> {
59    #[allow(clippy::suspicious_op_assign_impl)]
60    fn div_assign(&mut self, other: &Self) {
61        match other.is_constant() {
62            // If `other` is a constant and zero, halt since the inverse of zero is undefined.
63            true if other.eject_value().is_zero() => E::halt("Attempted to divide by zero."),
64            // If `other` is a constant and non-zero, we can perform multiplication and inversion for 0 constraints.
65            // If `self` is a constant, we can perform multiplication and inversion for 1 constraint.
66            // Otherwise, we can perform multiplication and inversion for 2 constraints.
67            _ => *self *= other.inverse(),
68        }
69    }
70}
71
72impl<E: Environment> Metrics<dyn Div<Field<E>, Output = Field<E>>> for Field<E> {
73    type Case = (Mode, Mode);
74
75    fn count(case: &Self::Case) -> Count {
76        match case {
77            (Mode::Constant, Mode::Constant) | (_, Mode::Constant) => Count::is(1, 0, 0, 0),
78            (Mode::Constant, _) => Count::is(0, 0, 1, 1),
79            (_, _) => Count::is(0, 0, 2, 2),
80        }
81    }
82}
83
84impl<E: Environment> OutputMode<dyn Div<Field<E>, Output = Field<E>>> for Field<E> {
85    type Case = (CircuitType<Field<E>>, CircuitType<Field<E>>);
86
87    fn output_mode(case: &Self::Case) -> Mode {
88        match (case.0.mode(), case.1.mode()) {
89            (Mode::Constant, Mode::Constant) => Mode::Constant,
90            (Mode::Public, Mode::Constant) => match &case.1 {
91                CircuitType::Constant(constant) => match constant.eject_value().is_one() {
92                    true => Mode::Public,
93                    false => Mode::Private,
94                },
95                _ => E::halt("The constant is required to determine the output mode of Public + Constant"),
96            },
97            (_, _) => Mode::Private,
98        }
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use snarkvm_circuit_environment::{Circuit, assert_count_fails};
106
107    const ITERATIONS: u64 = 1000;
108
109    fn check_div(
110        name: &str,
111        first: &console::Field<<Circuit as Environment>::Network>,
112        second: &console::Field<<Circuit as Environment>::Network>,
113        mode_a: Mode,
114        mode_b: Mode,
115    ) {
116        let a = &Field::<Circuit>::new(mode_a, *first);
117        let b = &Field::<Circuit>::new(mode_b, *second);
118
119        match second.is_zero() {
120            true => match mode_b.is_constant() {
121                true => {
122                    let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| Field::div(a.clone(), b)));
123                    assert!(result.is_err());
124                }
125                false => {
126                    Circuit::scope(name, || {
127                        let _ = a / b;
128                        assert_count_fails!(Div(Field, Field) => Field, &(a.eject_mode(), b.eject_mode()));
129                    });
130                }
131            },
132            false => {
133                let expected = *first / *second;
134                Circuit::scope(name, || {
135                    let candidate = a / b;
136                    assert_eq!(expected, candidate.eject_value(), "({} / {})", a.eject_value(), b.eject_value());
137                    assert_count!(Div(Field, Field) => Field, &(a.eject_mode(), b.eject_mode()));
138                    assert_output_mode!(Div(Field, Field) => Field, &(CircuitType::from(a), CircuitType::from(b)), candidate);
139                });
140            }
141        }
142    }
143
144    fn check_div_assign(
145        name: &str,
146        first: &console::Field<<Circuit as Environment>::Network>,
147        second: &console::Field<<Circuit as Environment>::Network>,
148        mode_a: Mode,
149        mode_b: Mode,
150    ) {
151        let a = &Field::<Circuit>::new(mode_a, *first);
152        let b = &Field::<Circuit>::new(mode_b, *second);
153
154        match second.is_zero() {
155            true => match mode_b.is_constant() {
156                true => {
157                    let result =
158                        std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| Field::div_assign(&mut a.clone(), b)));
159                    assert!(result.is_err());
160                }
161                false => {
162                    Circuit::scope(name, || {
163                        let mut candidate = a.clone();
164                        candidate /= b;
165                        assert_count_fails!(Div(Field, Field) => Field, &(a.eject_mode(), b.eject_mode()));
166                    });
167                }
168            },
169            false => {
170                let expected = *first / *second;
171                Circuit::scope(name, || {
172                    let mut candidate = a.clone();
173                    candidate /= b;
174                    assert_eq!(expected, candidate.eject_value(), "({} /= {})", a.eject_value(), b.eject_value());
175                    assert_count!(Div(Field, Field) => Field, &(a.eject_mode(), b.eject_mode()));
176                    assert_output_mode!(Div(Field, Field) => Field, &(CircuitType::from(a), CircuitType::from(b)), candidate);
177                });
178            }
179        }
180    }
181
182    fn run_test(mode_a: Mode, mode_b: Mode) {
183        let mut rng = TestRng::default();
184
185        for i in 0..ITERATIONS {
186            let first = Uniform::rand(&mut rng);
187            let second = Uniform::rand(&mut rng);
188
189            let name = format!("Div: a / b {i}");
190            check_div(&name, &first, &second, mode_a, mode_b);
191            let name = format!("DivAssign: a / b {i}");
192            check_div_assign(&name, &first, &second, mode_a, mode_b);
193
194            // Check division by one.
195            let one = console::Field::<<Circuit as Environment>::Network>::one();
196            let name = format!("Div By One {i}");
197            check_div(&name, &first, &one, mode_a, mode_b);
198            let name = format!("DivAssign By One {i}");
199            check_div_assign(&name, &first, &one, mode_a, mode_b);
200
201            // Check division by zero.
202            let zero = console::Field::<<Circuit as Environment>::Network>::zero();
203            let name = format!("Div By Zero {i}");
204            check_div(&name, &first, &zero, mode_a, mode_b);
205            let name = format!("DivAssign By Zero {i}");
206            check_div_assign(&name, &first, &zero, mode_a, mode_b);
207        }
208    }
209
210    #[test]
211    fn test_constant_div_constant() {
212        run_test(Mode::Constant, Mode::Constant);
213    }
214
215    #[test]
216    fn test_constant_div_public() {
217        run_test(Mode::Constant, Mode::Public);
218    }
219
220    #[test]
221    fn test_constant_div_private() {
222        run_test(Mode::Constant, Mode::Private);
223    }
224
225    #[test]
226    fn test_public_div_constant() {
227        run_test(Mode::Public, Mode::Constant);
228    }
229
230    #[test]
231    fn test_public_div_public() {
232        run_test(Mode::Public, Mode::Public);
233    }
234
235    #[test]
236    fn test_public_div_private() {
237        run_test(Mode::Public, Mode::Private);
238    }
239
240    #[test]
241    fn test_private_div_constant() {
242        run_test(Mode::Private, Mode::Constant);
243    }
244
245    #[test]
246    fn test_private_div_public() {
247        run_test(Mode::Private, Mode::Public);
248    }
249
250    #[test]
251    fn test_private_div_private() {
252        run_test(Mode::Private, Mode::Private);
253    }
254
255    #[test]
256    fn test_div_by_zero_fails() {
257        let zero = console::Field::<<Circuit as Environment>::Network>::zero();
258        let one = console::Field::<<Circuit as Environment>::Network>::one();
259
260        let result = std::panic::catch_unwind(|| Field::<Circuit>::one() / Field::zero());
261        assert!(result.is_err()); // Probe further for specific error type here, if desired
262
263        let result =
264            std::panic::catch_unwind(|| Field::<Circuit>::new(Mode::Constant, one) / Field::new(Mode::Constant, zero));
265        assert!(result.is_err()); // Probe further for specific error type here, if desired
266
267        Circuit::scope("Public Div by Zero", || {
268            let _ = Field::<Circuit>::new(Mode::Public, one) / Field::new(Mode::Public, zero);
269            assert!(!Circuit::is_satisfied_in_scope());
270        });
271
272        Circuit::scope("Private Div by Zero", || {
273            let _ = Field::<Circuit>::new(Mode::Private, one) / Field::new(Mode::Private, zero);
274            assert!(!Circuit::is_satisfied_in_scope());
275        });
276    }
277}