snarkvm_console_program/data/literal/cast_lossy/
field.rs

1// Copyright 2024 Aleo Network Foundation
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> CastLossy<Address<E>> for Field<E> {
19    /// Casts a `Field` to an `Address`.
20    ///
21    /// This operation attempts to recover the group element from the given field,
22    /// which is then used to construct the address. See the documentation of `Field::cast_lossy`
23    /// on the `Group` type for more details.
24    #[inline]
25    fn cast_lossy(&self) -> Address<E> {
26        // Perform a lossy cast to a group element.
27        let group: Group<E> = self.cast_lossy();
28        // Convert the group element to an address.
29        Address::new(group)
30    }
31}
32
33impl<E: Environment> CastLossy<Boolean<E>> for Field<E> {
34    /// Casts a `Field` to a `Boolean`, with lossy truncation.
35    /// This operation returns the least significant bit of the field.
36    #[inline]
37    fn cast_lossy(&self) -> Boolean<E> {
38        let bits_le = self.to_bits_le();
39        debug_assert!(!bits_le.is_empty(), "An integer must have at least one bit");
40        Boolean::new(bits_le[0])
41    }
42}
43
44impl<E: Environment> CastLossy<Field<E>> for Field<E> {
45    /// Casts a `Field` to a `Field`.
46    /// This is an identity cast, so it is **always** lossless.
47    #[inline]
48    fn cast_lossy(&self) -> Field<E> {
49        *self
50    }
51}
52
53impl<E: Environment> CastLossy<Group<E>> for Field<E> {
54    /// Casts a `Field` to a `Group`.
55    ///
56    /// This operation attempts to recover the group element from the given field.
57    ///
58    /// If the field is a valid x-coordinate, then the group element is returned.
59    /// If the field is not a valid x-coordinate, then if the field is the one element,
60    /// the generator of the prime-order subgroup is returned.
61    /// Otherwise, Elligator-2 is applied to the field element to recover a group element.
62    #[inline]
63    fn cast_lossy(&self) -> Group<E> {
64        match Group::from_x_coordinate(*self) {
65            Ok(group) => group,
66            Err(_) => match self.is_one() {
67                true => Group::generator(),
68                false => {
69                    // Perform Elligator-2 on the field element, to recover a group element.
70                    let result = Elligator2::encode(self);
71                    debug_assert!(result.is_ok(), "Elligator-2 should never fail to encode a field element");
72                    result.unwrap().0
73                }
74            },
75        }
76    }
77}
78
79impl<E: Environment, I: IntegerType> CastLossy<Integer<E, I>> for Field<E> {
80    /// Casts a `Field` to an `Integer`, with lossy truncation.
81    /// This operation truncates the field to an integer.
82    #[inline]
83    fn cast_lossy(&self) -> Integer<E, I> {
84        Integer::from_field_lossy(self)
85    }
86}
87
88impl<E: Environment> CastLossy<Scalar<E>> for Field<E> {
89    /// Casts a `Field` to a `Scalar`, with lossy truncation.
90    /// This operation truncates the field to a scalar.
91    #[inline]
92    fn cast_lossy(&self) -> Scalar<E> {
93        Scalar::from_field_lossy(self)
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    use snarkvm_console_network::Console;
101
102    type CurrentEnvironment = Console;
103
104    const ITERATIONS: u64 = 10_000;
105
106    #[test]
107    fn test_field_to_address() {
108        let rng = &mut TestRng::default();
109
110        let field = Field::<CurrentEnvironment>::one();
111        let address: Address<CurrentEnvironment> = field.cast_lossy();
112        assert_eq!(address, Address::new(Group::generator()));
113        assert_eq!(address.to_group(), &Group::generator());
114
115        let field = Field::<CurrentEnvironment>::zero();
116        let address: Address<CurrentEnvironment> = field.cast_lossy();
117        assert_eq!(address, Address::zero());
118        assert_eq!(address.to_group(), &Group::zero());
119
120        for _ in 0..ITERATIONS {
121            // Sample a random field.
122            let field = Field::<CurrentEnvironment>::rand(rng);
123            // Perform the operation.
124            let candidate: Address<CurrentEnvironment> = field.cast_lossy();
125            // Compare the result against the group element. (This is the most we can do.)
126            let expected: Group<CurrentEnvironment> = field.cast_lossy();
127            assert_eq!(Address::new(expected), candidate);
128        }
129    }
130
131    #[test]
132    fn test_field_to_boolean() {
133        let rng = &mut TestRng::default();
134
135        let field = Field::<CurrentEnvironment>::one();
136        let boolean: Boolean<CurrentEnvironment> = field.cast_lossy();
137        assert_eq!(boolean, Boolean::new(true));
138
139        let field = Field::<CurrentEnvironment>::zero();
140        let boolean: Boolean<CurrentEnvironment> = field.cast_lossy();
141        assert_eq!(boolean, Boolean::new(false));
142
143        for _ in 0..ITERATIONS {
144            // Sample a random field.
145            let field = Field::<CurrentEnvironment>::rand(rng);
146            // Perform the operation.
147            let candidate: Boolean<CurrentEnvironment> = field.cast_lossy();
148            // Compare the result against the least significant bit of the field.
149            let expected = Boolean::new(field.to_bits_be().pop().unwrap());
150            assert_eq!(expected, candidate);
151        }
152    }
153
154    #[test]
155    fn test_field_to_field() {
156        let rng = &mut TestRng::default();
157
158        for _ in 0..ITERATIONS {
159            // Sample a random field.
160            let field = Field::<CurrentEnvironment>::rand(rng);
161            // Perform the operation.
162            let candidate: Field<CurrentEnvironment> = field.cast_lossy();
163            assert_eq!(field, candidate);
164        }
165    }
166
167    #[test]
168    fn test_field_to_group() {
169        let rng = &mut TestRng::default();
170
171        let field = Field::<CurrentEnvironment>::one();
172        let group: Group<CurrentEnvironment> = field.cast_lossy();
173        assert_eq!(group, Group::generator());
174
175        let field = Field::<CurrentEnvironment>::zero();
176        let group: Group<CurrentEnvironment> = field.cast_lossy();
177        assert_eq!(group, Group::zero());
178
179        for _ in 0..ITERATIONS {
180            // Sample a random field.
181            let field = Field::<CurrentEnvironment>::rand(rng);
182            // Perform the operation.
183            let candidate: Group<CurrentEnvironment> = field.cast_lossy();
184            // Compare the result against the address. (This is the most we can do.)
185            let expected: Address<CurrentEnvironment> = field.cast_lossy();
186            assert_eq!(expected.to_group(), &candidate);
187        }
188    }
189
190    #[test]
191    fn test_field_to_scalar() {
192        let rng = &mut TestRng::default();
193
194        let field = Field::<CurrentEnvironment>::one();
195        let scalar: Scalar<CurrentEnvironment> = field.cast_lossy();
196        assert_eq!(scalar, Scalar::one());
197
198        let field = Field::<CurrentEnvironment>::zero();
199        let scalar: Scalar<CurrentEnvironment> = field.cast_lossy();
200        assert_eq!(scalar, Scalar::zero());
201
202        for _ in 0..ITERATIONS {
203            // Sample a random field.
204            let field = Field::<CurrentEnvironment>::rand(rng);
205            // Perform the operation.
206            let candidate: Scalar<CurrentEnvironment> = field.cast_lossy();
207            assert_eq!(Scalar::from_field_lossy(&field), candidate);
208        }
209    }
210
211    macro_rules! check_field_to_integer {
212        ($type:ty) => {
213            let rng = &mut TestRng::default();
214
215            let field = Field::<CurrentEnvironment>::one();
216            let integer: $type = field.cast_lossy();
217            assert_eq!(integer, <$type>::one());
218
219            let field = Field::<CurrentEnvironment>::zero();
220            let integer: $type = field.cast_lossy();
221            assert_eq!(integer, <$type>::zero());
222
223            for _ in 0..ITERATIONS {
224                // Sample a random field.
225                let field = Field::<CurrentEnvironment>::rand(rng);
226                // Perform the operation.
227                let candidate: $type = field.cast_lossy();
228                assert_eq!(<$type>::from_field_lossy(&field), candidate);
229            }
230        };
231    }
232
233    #[test]
234    fn test_field_to_i8() {
235        check_field_to_integer!(I8<CurrentEnvironment>);
236    }
237
238    #[test]
239    fn test_field_to_i16() {
240        check_field_to_integer!(I16<CurrentEnvironment>);
241    }
242
243    #[test]
244    fn test_field_to_i32() {
245        check_field_to_integer!(I32<CurrentEnvironment>);
246    }
247
248    #[test]
249    fn test_field_to_i64() {
250        check_field_to_integer!(I64<CurrentEnvironment>);
251    }
252
253    #[test]
254    fn test_field_to_i128() {
255        check_field_to_integer!(I128<CurrentEnvironment>);
256    }
257
258    #[test]
259    fn test_field_to_u8() {
260        check_field_to_integer!(U8<CurrentEnvironment>);
261    }
262
263    #[test]
264    fn test_field_to_u16() {
265        check_field_to_integer!(U16<CurrentEnvironment>);
266    }
267
268    #[test]
269    fn test_field_to_u32() {
270        check_field_to_integer!(U32<CurrentEnvironment>);
271    }
272
273    #[test]
274    fn test_field_to_u64() {
275        check_field_to_integer!(U64<CurrentEnvironment>);
276    }
277
278    #[test]
279    fn test_field_to_u128() {
280        check_field_to_integer!(U128<CurrentEnvironment>);
281    }
282}