snarkvm_circuit_types_field/
inverse.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> Inv for Field<E> {
19    type Output = Self;
20
21    fn inv(self) -> Self::Output {
22        Self::inverse(&self)
23    }
24}
25
26impl<E: Environment> Inv for &Field<E> {
27    type Output = Field<E>;
28
29    fn inv(self) -> Self::Output {
30        self.inverse()
31    }
32}
33
34impl<E: Environment> Inverse for Field<E> {
35    type Output = Field<E>;
36
37    fn inverse(&self) -> Self::Output {
38        let inverse = witness!(|self| match self.inverse() {
39            Ok(inverse) => inverse,
40            _ => console::Field::zero(),
41        });
42
43        // Ensure `self` * `self^(-1)` == 1.
44        E::enforce(|| (self, &inverse, E::one()));
45
46        inverse
47    }
48}
49
50impl<E: Environment> Metrics<dyn Inverse<Output = Field<E>>> for Field<E> {
51    type Case = Mode;
52
53    fn count(case: &Self::Case) -> Count {
54        match case.is_constant() {
55            true => Count::is(1, 0, 0, 0),
56            false => Count::is(0, 0, 1, 1),
57        }
58    }
59}
60
61impl<E: Environment> OutputMode<dyn Inverse<Output = Field<E>>> for Field<E> {
62    type Case = Mode;
63
64    fn output_mode(case: &Self::Case) -> Mode {
65        match case.is_constant() {
66            true => Mode::Constant,
67            false => Mode::Private,
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use snarkvm_circuit_environment::Circuit;
76
77    const ITERATIONS: u64 = 1_000;
78
79    fn check_inverse(name: &str, mode: Mode, rng: &mut TestRng) {
80        for _ in 0..ITERATIONS {
81            // Sample a random element.
82            let given: console::Field<<Circuit as Environment>::Network> = Uniform::rand(rng);
83            // Compute it's inverse, or skip this iteration if it does not natively exist.
84            if let Ok(expected) = given.inverse() {
85                let candidate = Field::<Circuit>::new(mode, given);
86
87                Circuit::scope(name, || {
88                    let result = candidate.inverse();
89                    assert_eq!(expected, result.eject_value());
90                    assert_count!(Inverse(Field) => Field, &mode);
91                    assert_output_mode!(Inverse(Field) => Field, &mode, result);
92                });
93                Circuit::reset();
94            }
95        }
96    }
97
98    #[test]
99    fn test_inverse() {
100        let mut rng = TestRng::default();
101
102        check_inverse("Constant", Mode::Constant, &mut rng);
103        check_inverse("Public", Mode::Public, &mut rng);
104        check_inverse("Private", Mode::Private, &mut rng);
105    }
106
107    #[test]
108    fn test_zero_inverse_fails() {
109        let zero = console::Field::<<Circuit as Environment>::Network>::zero();
110
111        let result = std::panic::catch_unwind(|| Field::<Circuit>::zero().inverse());
112        assert!(result.is_err());
113        Circuit::reset();
114
115        let result = std::panic::catch_unwind(|| Field::<Circuit>::new(Mode::Constant, zero).inverse());
116        assert!(result.is_err());
117        Circuit::reset();
118
119        let candidate = Field::<Circuit>::new(Mode::Public, zero).inverse();
120        assert_eq!(zero, candidate.eject_value());
121        assert!(!Circuit::is_satisfied());
122        Circuit::reset();
123
124        let candidate = Field::<Circuit>::new(Mode::Private, zero).inverse();
125        assert_eq!(zero, candidate.eject_value());
126        assert!(!Circuit::is_satisfied());
127        Circuit::reset();
128    }
129}