snarkvm_console_program/data/literal/cast_lossy/
integer.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, I: IntegerType> CastLossy<Address<E>> for Integer<E, I> {
19    /// Casts an `Integer` to an `Address`.
20    ///
21    /// This operation converts the integer into a field element, and then attempts to recover
22    /// the group element 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        let field: Field<E> = self.cast_lossy();
27        field.cast_lossy()
28    }
29}
30
31impl<E: Environment, I: IntegerType> CastLossy<Boolean<E>> for Integer<E, I> {
32    /// Casts an `Integer` to a `Boolean`, with lossy truncation.
33    /// This operation returns the least significant bit of the field.
34    #[inline]
35    fn cast_lossy(&self) -> Boolean<E> {
36        let bits_le = self.to_bits_le();
37        debug_assert!(!bits_le.is_empty(), "An integer must have at least one bit");
38        Boolean::new(bits_le[0])
39    }
40}
41
42impl<E: Environment, I: IntegerType> CastLossy<Field<E>> for Integer<E, I> {
43    /// Casts an `Integer` to a `Field`.
44    /// This is safe because casting from an integer to a field is **always** lossless.
45    #[inline]
46    fn cast_lossy(&self) -> Field<E> {
47        let result = self.to_field();
48        debug_assert!(result.is_ok(), "Casting an integer to field cannot fail");
49        result.unwrap()
50    }
51}
52
53impl<E: Environment, I: IntegerType> CastLossy<Group<E>> for Integer<E, I> {
54    /// Casts an `Integer` to a `Group`.
55    ///
56    /// This operation converts the integer into a field element, and then attempts to recover
57    /// the group element. See the documentation of `Field::cast_lossy` on the `Group` type
58    /// for more details.
59    #[inline]
60    fn cast_lossy(&self) -> Group<E> {
61        let field: Field<E> = self.cast_lossy();
62        field.cast_lossy()
63    }
64}
65
66impl<E: Environment, I0: IntegerType + AsPrimitive<I1>, I1: IntegerType> CastLossy<Integer<E, I1>> for Integer<E, I0> {
67    /// Casts an `Integer` to an `Integer` of a different type, with lossy truncation.
68    #[inline]
69    fn cast_lossy(&self) -> Integer<E, I1> {
70        Integer::new((**self).as_())
71    }
72}
73
74impl<E: Environment, I: IntegerType> CastLossy<Scalar<E>> for Integer<E, I> {
75    /// Casts an `Integer` to a `Scalar`.
76    /// This is safe because casting from an integer to a scalar is **always** lossless.
77    #[inline]
78    fn cast_lossy(&self) -> Scalar<E> {
79        self.to_scalar()
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    type CurrentEnvironment = snarkvm_console_network::Console;
88
89    const ITERATIONS: u64 = 1_000;
90
91    #[test]
92    fn test_integer_to_address() {
93        macro_rules! check_integer_to_address {
94            ($type:ty) => {
95                let rng = &mut TestRng::default();
96
97                let integer = Integer::<CurrentEnvironment, $type>::one();
98                let address: Address<CurrentEnvironment> = integer.cast_lossy();
99                assert_eq!(address, Address::new(Group::generator()));
100                assert_eq!(address.to_group(), &Group::generator());
101
102                let integer = Integer::<CurrentEnvironment, $type>::zero();
103                let address: Address<CurrentEnvironment> = integer.cast_lossy();
104                assert_eq!(address, Address::zero());
105                assert_eq!(address.to_group(), &Group::zero());
106
107                for _ in 0..ITERATIONS {
108                    // Sample a random integer.
109                    let integer = Integer::<CurrentEnvironment, $type>::rand(rng);
110                    // Perform the operation.
111                    let candidate: Address<CurrentEnvironment> = integer.cast_lossy();
112                    // Compare the result against the group element. (This is the most we can do.)
113                    let expected: Group<CurrentEnvironment> = integer.cast_lossy();
114                    assert_eq!(Address::new(expected), candidate);
115                }
116            };
117        }
118
119        check_integer_to_address!(i8);
120        check_integer_to_address!(i16);
121        check_integer_to_address!(i32);
122        check_integer_to_address!(i64);
123        check_integer_to_address!(i128);
124        check_integer_to_address!(u8);
125        check_integer_to_address!(u16);
126        check_integer_to_address!(u32);
127        check_integer_to_address!(u64);
128        check_integer_to_address!(u128);
129    }
130
131    #[test]
132    fn test_integer_to_boolean() {
133        macro_rules! check_integer_to_boolean {
134            ($type:ty) => {
135                let rng = &mut TestRng::default();
136
137                let integer = Integer::<CurrentEnvironment, $type>::one();
138                let boolean: Boolean<CurrentEnvironment> = integer.cast_lossy();
139                assert_eq!(boolean, Boolean::new(true));
140
141                let integer = Integer::<CurrentEnvironment, $type>::zero();
142                let boolean: Boolean<CurrentEnvironment> = integer.cast_lossy();
143                assert_eq!(boolean, Boolean::new(false));
144
145                for _ in 0..ITERATIONS {
146                    // Sample a random integer.
147                    let integer = Integer::<CurrentEnvironment, $type>::rand(rng);
148                    // Perform the operation.
149                    let candidate: Boolean<CurrentEnvironment> = integer.cast_lossy();
150                    // Compare the result against the least significant bit of the integer.
151                    let expected = Boolean::new(integer.to_bits_be().pop().unwrap());
152                    assert_eq!(expected, candidate);
153                }
154            };
155        }
156
157        check_integer_to_boolean!(i8);
158        check_integer_to_boolean!(i16);
159        check_integer_to_boolean!(i32);
160        check_integer_to_boolean!(i64);
161        check_integer_to_boolean!(i128);
162        check_integer_to_boolean!(u8);
163        check_integer_to_boolean!(u16);
164        check_integer_to_boolean!(u32);
165        check_integer_to_boolean!(u64);
166        check_integer_to_boolean!(u128);
167    }
168
169    #[test]
170    fn test_integer_to_field() {
171        macro_rules! check_integer_to_field {
172            ($type:ty) => {
173                let rng = &mut TestRng::default();
174
175                for _ in 0..ITERATIONS {
176                    // Sample a random integer.
177                    let integer = Integer::<CurrentEnvironment, $type>::rand(rng);
178                    // Perform the operation.
179                    let candidate: Field<CurrentEnvironment> = integer.cast_lossy();
180                    // Compare the result against the field representation of the integer.
181                    let expected = integer.to_field().unwrap();
182                    assert_eq!(expected, candidate);
183                }
184            };
185        }
186
187        check_integer_to_field!(i8);
188        check_integer_to_field!(i16);
189        check_integer_to_field!(i32);
190        check_integer_to_field!(i64);
191        check_integer_to_field!(i128);
192        check_integer_to_field!(u8);
193        check_integer_to_field!(u16);
194        check_integer_to_field!(u32);
195        check_integer_to_field!(u64);
196        check_integer_to_field!(u128);
197    }
198
199    #[test]
200    fn test_integer_to_group() {
201        macro_rules! check_integer_to_group {
202            ($type:ty) => {
203                let rng = &mut TestRng::default();
204
205                let integer = Integer::<CurrentEnvironment, $type>::one();
206                let group: Group<CurrentEnvironment> = integer.cast_lossy();
207                assert_eq!(group, Group::generator());
208
209                let integer = Integer::<CurrentEnvironment, $type>::zero();
210                let group: Group<CurrentEnvironment> = integer.cast_lossy();
211                assert_eq!(group, Group::zero());
212
213                for _ in 0..ITERATIONS {
214                    // Sample a random integer.
215                    let integer = Integer::<CurrentEnvironment, $type>::rand(rng);
216                    // Perform the operation.
217                    let candidate: Group<CurrentEnvironment> = integer.cast_lossy();
218                    // Compare the result against the group representation of the integer.
219                    let expected: Group<CurrentEnvironment> = integer.to_field().unwrap().cast_lossy();
220                    assert_eq!(expected, candidate);
221                }
222            };
223        }
224
225        check_integer_to_group!(i8);
226        check_integer_to_group!(i16);
227        check_integer_to_group!(i32);
228        check_integer_to_group!(i64);
229        check_integer_to_group!(i128);
230        check_integer_to_group!(u8);
231        check_integer_to_group!(u16);
232        check_integer_to_group!(u32);
233        check_integer_to_group!(u64);
234        check_integer_to_group!(u128);
235    }
236
237    #[test]
238    fn test_integer_to_scalar() {
239        macro_rules! check_integer_to_scalar {
240            ($type:ty) => {
241                let rng = &mut TestRng::default();
242
243                for _ in 0..ITERATIONS {
244                    // Sample a random integer.
245                    let integer = Integer::<CurrentEnvironment, $type>::rand(rng);
246                    // Perform the operation.
247                    let candidate: Scalar<CurrentEnvironment> = integer.cast_lossy();
248                    // Compare the result against the scalar representation of the integer.
249                    let expected = integer.to_scalar();
250                    assert_eq!(expected, candidate);
251                }
252            };
253        }
254
255        check_integer_to_scalar!(i8);
256        check_integer_to_scalar!(i16);
257        check_integer_to_scalar!(i32);
258        check_integer_to_scalar!(i64);
259        check_integer_to_scalar!(i128);
260        check_integer_to_scalar!(u8);
261        check_integer_to_scalar!(u16);
262        check_integer_to_scalar!(u32);
263        check_integer_to_scalar!(u64);
264        check_integer_to_scalar!(u128);
265    }
266
267    #[test]
268    fn test_integer_to_integer() {
269        macro_rules! check_integer_to_integer {
270            ($type_a:ty, $type_b:ty) => {
271                let rng = &mut TestRng::default();
272
273                println!("Checking {} -> {}", stringify!($type_a), stringify!($type_b));
274
275                for _ in 0..ITERATIONS {
276                    // Sample a random integer.
277                    let integer = Integer::<CurrentEnvironment, $type_a>::rand(rng);
278                    // Perform the operation.
279                    let candidate: Integer<CurrentEnvironment, $type_b> = integer.cast_lossy();
280
281                    // Retrieve the lesser number of bits of the two types.
282                    let data_bits = std::cmp::min(<$type_a>::BITS, <$type_b>::BITS) as usize;
283                    // Compare the data bits of the candidate against the data bits of the integer.
284                    for (expected_bit, candidate_bit) in
285                        integer.to_bits_le()[0..data_bits].iter().zip_eq(&candidate.to_bits_le()[0..data_bits])
286                    {
287                        assert_eq!(
288                            expected_bit, candidate_bit,
289                            "Data bits do not match - ({:b} != {:b})",
290                            *integer, *candidate
291                        );
292                    }
293                    // Ensure the remaining bits are 0 (unsigned) or MSB clones (signed).
294                    for candidate_bit in &candidate.to_bits_le()[data_bits..] {
295                        let expected_bit = match <$type_a>::is_signed() {
296                            true => integer.to_bits_le()[data_bits - 1],
297                            false => false,
298                        };
299                        assert_eq!(
300                            expected_bit, *candidate_bit,
301                            "Remaining bits are not correct - ({:b} != {:b})",
302                            *integer, *candidate
303                        );
304                    }
305                }
306            };
307        }
308        {
309            check_integer_to_integer!(i8, i8);
310            check_integer_to_integer!(i8, i16);
311            check_integer_to_integer!(i8, i32);
312            check_integer_to_integer!(i8, i64);
313            check_integer_to_integer!(i8, i128);
314            check_integer_to_integer!(i8, u8);
315            check_integer_to_integer!(i8, u16);
316            check_integer_to_integer!(i8, u32);
317            check_integer_to_integer!(i8, u64);
318            check_integer_to_integer!(i8, u128);
319        }
320        {
321            check_integer_to_integer!(i16, i8);
322            check_integer_to_integer!(i16, i16);
323            check_integer_to_integer!(i16, i32);
324            check_integer_to_integer!(i16, i64);
325            check_integer_to_integer!(i16, i128);
326            check_integer_to_integer!(i16, u8);
327            check_integer_to_integer!(i16, u16);
328            check_integer_to_integer!(i16, u32);
329            check_integer_to_integer!(i16, u64);
330            check_integer_to_integer!(i16, u128);
331        }
332        {
333            check_integer_to_integer!(i32, i8);
334            check_integer_to_integer!(i32, i16);
335            check_integer_to_integer!(i32, i32);
336            check_integer_to_integer!(i32, i64);
337            check_integer_to_integer!(i32, i128);
338            check_integer_to_integer!(i32, u8);
339            check_integer_to_integer!(i32, u16);
340            check_integer_to_integer!(i32, u32);
341            check_integer_to_integer!(i32, u64);
342            check_integer_to_integer!(i32, u128);
343        }
344        {
345            check_integer_to_integer!(i64, i8);
346            check_integer_to_integer!(i64, i16);
347            check_integer_to_integer!(i64, i32);
348            check_integer_to_integer!(i64, i64);
349            check_integer_to_integer!(i64, i128);
350            check_integer_to_integer!(i64, u8);
351            check_integer_to_integer!(i64, u16);
352            check_integer_to_integer!(i64, u32);
353            check_integer_to_integer!(i64, u64);
354            check_integer_to_integer!(i64, u128);
355        }
356        {
357            check_integer_to_integer!(i128, i8);
358            check_integer_to_integer!(i128, i16);
359            check_integer_to_integer!(i128, i32);
360            check_integer_to_integer!(i128, i64);
361            check_integer_to_integer!(i128, i128);
362            check_integer_to_integer!(i128, u8);
363            check_integer_to_integer!(i128, u16);
364            check_integer_to_integer!(i128, u32);
365            check_integer_to_integer!(i128, u64);
366            check_integer_to_integer!(i128, u128);
367        }
368        {
369            check_integer_to_integer!(u8, i8);
370            check_integer_to_integer!(u8, i16);
371            check_integer_to_integer!(u8, i32);
372            check_integer_to_integer!(u8, i64);
373            check_integer_to_integer!(u8, i128);
374            check_integer_to_integer!(u8, u8);
375            check_integer_to_integer!(u8, u16);
376            check_integer_to_integer!(u8, u32);
377            check_integer_to_integer!(u8, u64);
378            check_integer_to_integer!(u8, u128);
379        }
380        {
381            check_integer_to_integer!(u16, i8);
382            check_integer_to_integer!(u16, i16);
383            check_integer_to_integer!(u16, i32);
384            check_integer_to_integer!(u16, i64);
385            check_integer_to_integer!(u16, i128);
386            check_integer_to_integer!(u16, u8);
387            check_integer_to_integer!(u16, u16);
388            check_integer_to_integer!(u16, u32);
389            check_integer_to_integer!(u16, u64);
390            check_integer_to_integer!(u16, u128);
391        }
392        {
393            check_integer_to_integer!(u32, i8);
394            check_integer_to_integer!(u32, i16);
395            check_integer_to_integer!(u32, i32);
396            check_integer_to_integer!(u32, i64);
397            check_integer_to_integer!(u32, i128);
398            check_integer_to_integer!(u32, u8);
399            check_integer_to_integer!(u32, u16);
400            check_integer_to_integer!(u32, u32);
401            check_integer_to_integer!(u32, u64);
402            check_integer_to_integer!(u32, u128);
403        }
404        {
405            check_integer_to_integer!(u64, i8);
406            check_integer_to_integer!(u64, i16);
407            check_integer_to_integer!(u64, i32);
408            check_integer_to_integer!(u64, i64);
409            check_integer_to_integer!(u64, i128);
410            check_integer_to_integer!(u64, u8);
411            check_integer_to_integer!(u64, u16);
412            check_integer_to_integer!(u64, u32);
413            check_integer_to_integer!(u64, u64);
414            check_integer_to_integer!(u64, u128);
415        }
416    }
417}