snarkvm_circuit_types_field/
lib.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#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![cfg_attr(test, allow(clippy::assertions_on_result_states))]
19
20extern crate snarkvm_console_types_field as console;
21
22mod helpers;
23
24pub mod add;
25pub mod compare;
26pub mod div;
27pub mod div_unchecked;
28pub mod double;
29pub mod equal;
30pub mod inverse;
31pub mod mul;
32pub mod neg;
33pub mod pow;
34pub mod square;
35pub mod square_root;
36pub mod sub;
37pub mod ternary;
38
39#[cfg(test)]
40use console::{TestRng, Uniform};
41#[cfg(test)]
42use snarkvm_circuit_environment::{assert_count, assert_output_mode, assert_scope, count, output_mode};
43
44use snarkvm_circuit_environment::prelude::*;
45use snarkvm_circuit_types_boolean::Boolean;
46
47use std::cell::OnceCell;
48
49#[derive(Clone)]
50pub struct Field<E: Environment> {
51    /// The linear combination contains the primary representation of the field.
52    linear_combination: LinearCombination<E::BaseField>,
53    /// An optional secondary representation in little-endian bits is provided,
54    /// so that calls to `ToBits` only incur constraint costs once.
55    bits_le: OnceCell<Vec<Boolean<E>>>,
56}
57
58impl<E: Environment> FieldTrait for Field<E> {}
59
60impl<E: Environment> Default for Field<E> {
61    /// Returns the default field element.
62    fn default() -> Self {
63        Self::zero()
64    }
65}
66
67impl<E: Environment> Inject for Field<E> {
68    type Primitive = console::Field<E::Network>;
69
70    /// Initializes a field circuit from a console field.
71    fn new(mode: Mode, field: Self::Primitive) -> Self {
72        Self { linear_combination: E::new_variable(mode, *field).into(), bits_le: Default::default() }
73    }
74}
75
76impl<E: Environment> Eject for Field<E> {
77    type Primitive = console::Field<E::Network>;
78
79    /// Ejects the mode of the field circuit.
80    fn eject_mode(&self) -> Mode {
81        self.linear_combination.mode()
82    }
83
84    /// Ejects the field circuit as a console field.
85    fn eject_value(&self) -> Self::Primitive {
86        console::Field::new(self.linear_combination.value())
87    }
88}
89
90impl<E: Environment> Parser for Field<E> {
91    /// Parses a string into a field circuit.
92    #[inline]
93    fn parse(string: &str) -> ParserResult<Self> {
94        // Parse the field from the string.
95        let (string, field) = console::Field::parse(string)?;
96        // Parse the mode from the string.
97        let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
98
99        match mode {
100            Some((_, mode)) => Ok((string, Field::new(mode, field))),
101            None => Ok((string, Field::new(Mode::Constant, field))),
102        }
103    }
104}
105
106impl<E: Environment> FromStr for Field<E> {
107    type Err = Error;
108
109    /// Parses a string into a field circuit.
110    #[inline]
111    fn from_str(string: &str) -> Result<Self> {
112        match Self::parse(string) {
113            Ok((remainder, object)) => {
114                // Ensure the remainder is empty.
115                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
116                // Return the object.
117                Ok(object)
118            }
119            Err(error) => bail!("Failed to parse string. {error}"),
120        }
121    }
122}
123
124impl<E: Environment> TypeName for Field<E> {
125    /// Returns the type name of the circuit as a string.
126    #[inline]
127    fn type_name() -> &'static str {
128        console::Field::<E::Network>::type_name()
129    }
130}
131
132impl<E: Environment> Debug for Field<E> {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        Display::fmt(self, f)
135    }
136}
137
138impl<E: Environment> Display for Field<E> {
139    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140        write!(f, "{}.{}", self.eject_value(), self.eject_mode())
141    }
142}
143
144impl<E: Environment> From<LinearCombination<E::BaseField>> for Field<E> {
145    fn from(linear_combination: LinearCombination<E::BaseField>) -> Self {
146        Self { linear_combination, bits_le: Default::default() }
147    }
148}
149
150impl<E: Environment> From<&LinearCombination<E::BaseField>> for Field<E> {
151    fn from(linear_combination: &LinearCombination<E::BaseField>) -> Self {
152        From::from(linear_combination.clone())
153    }
154}
155
156impl<E: Environment> From<Field<E>> for LinearCombination<E::BaseField> {
157    fn from(field: Field<E>) -> Self {
158        From::from(&field)
159    }
160}
161
162impl<E: Environment> From<&Field<E>> for LinearCombination<E::BaseField> {
163    fn from(field: &Field<E>) -> Self {
164        field.linear_combination.clone()
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use snarkvm_circuit_environment::Circuit;
172
173    const ITERATIONS: u64 = 10_000;
174
175    /// Attempts to construct a field from the given element and mode,
176    /// format it in display mode, and recover a field from it.
177    fn check_display(mode: Mode, element: console::Field<<Circuit as Environment>::Network>) -> Result<()> {
178        let candidate = Field::<Circuit>::new(mode, element);
179        assert_eq!(format!("{element}.{mode}"), format!("{candidate}"));
180
181        let candidate_recovered = Field::<Circuit>::from_str(&format!("{candidate}"))?;
182        assert_eq!(candidate.eject_value(), candidate_recovered.eject_value());
183        Ok(())
184    }
185
186    #[test]
187    fn test_display() -> Result<()> {
188        let mut rng = TestRng::default();
189
190        for _ in 0..ITERATIONS {
191            let element = Uniform::rand(&mut rng);
192
193            // Constant
194            check_display(Mode::Constant, element)?;
195            // Public
196            check_display(Mode::Public, element)?;
197            // Private
198            check_display(Mode::Private, element)?;
199        }
200        Ok(())
201    }
202
203    #[test]
204    fn test_display_zero() {
205        let zero = console::Field::<<Circuit as Environment>::Network>::zero();
206
207        // Constant
208        let candidate = Field::<Circuit>::new(Mode::Constant, zero);
209        assert_eq!("0field.constant", &format!("{candidate}"));
210
211        // Public
212        let candidate = Field::<Circuit>::new(Mode::Public, zero);
213        assert_eq!("0field.public", &format!("{candidate}"));
214
215        // Private
216        let candidate = Field::<Circuit>::new(Mode::Private, zero);
217        assert_eq!("0field.private", &format!("{candidate}"));
218    }
219
220    #[test]
221    fn test_display_one() {
222        let one = console::Field::<<Circuit as Environment>::Network>::one();
223
224        // Constant
225        let candidate = Field::<Circuit>::new(Mode::Constant, one);
226        assert_eq!("1field.constant", &format!("{candidate}"));
227
228        // Public
229        let candidate = Field::<Circuit>::new(Mode::Public, one);
230        assert_eq!("1field.public", &format!("{candidate}"));
231
232        // Private
233        let candidate = Field::<Circuit>::new(Mode::Private, one);
234        assert_eq!("1field.private", &format!("{candidate}"));
235    }
236
237    #[test]
238    fn test_display_two() {
239        let one = console::Field::<<Circuit as Environment>::Network>::one();
240        let two = one + one;
241
242        // Constant
243        let candidate = Field::<Circuit>::new(Mode::Constant, two);
244        assert_eq!("2field.constant", &format!("{candidate}"));
245
246        // Public
247        let candidate = Field::<Circuit>::new(Mode::Public, two);
248        assert_eq!("2field.public", &format!("{candidate}"));
249
250        // Private
251        let candidate = Field::<Circuit>::new(Mode::Private, two);
252        assert_eq!("2field.private", &format!("{candidate}"));
253    }
254
255    #[test]
256    fn test_parser() {
257        type Primitive = console::Field<<Circuit as Environment>::Network>;
258
259        // Constant
260
261        let (_, candidate) = Field::<Circuit>::parse("5field").unwrap();
262        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
263        assert!(candidate.is_constant());
264
265        let (_, candidate) = Field::<Circuit>::parse("5_field").unwrap();
266        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
267        assert!(candidate.is_constant());
268
269        let (_, candidate) = Field::<Circuit>::parse("1_5_field").unwrap();
270        assert_eq!(Primitive::from_str("15field").unwrap(), candidate.eject_value());
271        assert!(candidate.is_constant());
272
273        let (_, candidate) = Field::<Circuit>::parse("5field.constant").unwrap();
274        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
275        assert!(candidate.is_constant());
276
277        let (_, candidate) = Field::<Circuit>::parse("5_field.constant").unwrap();
278        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
279        assert!(candidate.is_constant());
280
281        let (_, candidate) = Field::<Circuit>::parse("1_5_field.constant").unwrap();
282        assert_eq!(Primitive::from_str("15field").unwrap(), candidate.eject_value());
283        assert!(candidate.is_constant());
284
285        // Public
286
287        let (_, candidate) = Field::<Circuit>::parse("5field.public").unwrap();
288        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
289        assert!(candidate.is_public());
290
291        let (_, candidate) = Field::<Circuit>::parse("5_field.public").unwrap();
292        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
293        assert!(candidate.is_public());
294
295        let (_, candidate) = Field::<Circuit>::parse("1_5_field.public").unwrap();
296        assert_eq!(Primitive::from_str("15field").unwrap(), candidate.eject_value());
297        assert!(candidate.is_public());
298
299        // Private
300
301        let (_, candidate) = Field::<Circuit>::parse("5field.private").unwrap();
302        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
303        assert!(candidate.is_private());
304
305        let (_, candidate) = Field::<Circuit>::parse("5_field.private").unwrap();
306        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
307        assert!(candidate.is_private());
308
309        let (_, candidate) = Field::<Circuit>::parse("1_5_field.private").unwrap();
310        assert_eq!(Primitive::from_str("15field").unwrap(), candidate.eject_value());
311        assert!(candidate.is_private());
312
313        // Random
314
315        let mut rng = TestRng::default();
316
317        for mode in [Mode::Constant, Mode::Public, Mode::Private] {
318            for _ in 0..ITERATIONS {
319                let value = Uniform::rand(&mut rng);
320                let expected = Field::<Circuit>::new(mode, value);
321
322                let (_, candidate) = Field::<Circuit>::parse(&format!("{expected}")).unwrap();
323                assert_eq!(expected.eject_value(), candidate.eject_value());
324                assert_eq!(mode, candidate.eject_mode());
325            }
326        }
327    }
328}