snarkvm_circuit_program/data/literal/cast_lossy/
scalar.rs

1// Copyright 2024-2025 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 Scalar<E> {
19    /// Casts a `Scalar` to an `Address`.
20    ///
21    /// This operation converts the scalar 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> CastLossy<Boolean<E>> for Scalar<E> {
32    /// Casts a `Scalar` to a `Boolean`, with lossy truncation.
33    /// This operation returns the least significant bit of the scalar.
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        bits_le[0].clone()
39    }
40}
41
42impl<E: Environment> CastLossy<Group<E>> for Scalar<E> {
43    /// Casts a `Scalar` to a `Group`.
44    ///
45    /// This operation converts the scalar into a field element, and then attempts to recover
46    /// the group element. See the documentation of `Field::cast_lossy` on the `Group` type
47    /// for more details.
48    #[inline]
49    fn cast_lossy(&self) -> Group<E> {
50        let field: Field<E> = self.cast_lossy();
51        field.cast_lossy()
52    }
53}
54
55impl<E: Environment> CastLossy<Field<E>> for Scalar<E> {
56    /// Casts a `Scalar` to a `Field`.
57    /// This operation is **always** lossless.
58    #[inline]
59    fn cast_lossy(&self) -> Field<E> {
60        self.to_field()
61    }
62}
63
64impl<E: Environment, I: IntegerType> CastLossy<Integer<E, I>> for Scalar<E> {
65    /// Casts a `Scalar` to an `Integer`, with lossy truncation.
66    #[inline]
67    fn cast_lossy(&self) -> Integer<E, I> {
68        // Note: We are reconstituting the integer from the scalar field.
69        // This is safe as the number of bits in the integer is less than the scalar field modulus,
70        // and thus will always fit within a single scalar field element.
71        debug_assert!(I::BITS < <console::Scalar<E::Network> as console::SizeInBits>::size_in_bits() as u64);
72
73        // Truncate the field to the size of the integer domain.
74        // Slicing here is safe as the base field is larger than the integer domain.
75        Integer::<E, I>::from_bits_le(&self.to_bits_le()[..usize::try_from(I::BITS).unwrap()])
76    }
77}
78
79impl<E: Environment> CastLossy<Scalar<E>> for Scalar<E> {
80    /// Casts a `Scalar` to a `Scalar`.
81    /// This is an identity cast, so it is **always** lossless.
82    #[inline]
83    fn cast_lossy(&self) -> Scalar<E> {
84        self.clone()
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use console::CastLossy as _;
92    use console_root::{
93        network::MainnetV0,
94        prelude::{One, TestRng, Uniform, Zero},
95    };
96    use snarkvm_circuit_types::environment::{Circuit, Eject, Inject, Mode, UpdatableCount, count_is, count_less_than};
97
98    use std::fmt::Debug;
99
100    const ITERATIONS: usize = 100;
101
102    fn sample_values(
103        i: usize,
104        mode: Mode,
105        rng: &mut TestRng,
106    ) -> (console_root::types::Scalar<MainnetV0>, Scalar<Circuit>) {
107        let console_value = match i {
108            0 => console_root::types::Scalar::<MainnetV0>::zero(),
109            1 => console_root::types::Scalar::<MainnetV0>::one(),
110            _ => Uniform::rand(rng),
111        };
112        let circuit_value = Scalar::<Circuit>::new(mode, console_value);
113        (console_value, circuit_value)
114    }
115
116    check_cast_lossy!(cast_lossy, Scalar<Circuit>, console_root::types::Scalar::<MainnetV0>);
117
118    #[test]
119    fn test_scalar_to_address() {
120        check_cast_lossy::<Address<Circuit>, console_root::types::Address<MainnetV0>>(
121            Mode::Constant,
122            count_less_than!(4303, 0, 0, 0),
123        );
124        check_cast_lossy::<Address<Circuit>, console_root::types::Address<MainnetV0>>(
125            Mode::Public,
126            count_is!(2029, 0, 6745, 6750),
127        );
128        check_cast_lossy::<Address<Circuit>, console_root::types::Address<MainnetV0>>(
129            Mode::Private,
130            count_is!(2029, 0, 6745, 6750),
131        );
132    }
133
134    #[test]
135    fn test_scalar_to_boolean() {
136        check_cast_lossy::<Boolean<Circuit>, console_root::types::Boolean<MainnetV0>>(
137            Mode::Constant,
138            count_is!(251, 0, 0, 0),
139        );
140        check_cast_lossy::<Boolean<Circuit>, console_root::types::Boolean<MainnetV0>>(
141            Mode::Public,
142            count_is!(0, 0, 501, 503),
143        );
144        check_cast_lossy::<Boolean<Circuit>, console_root::types::Boolean<MainnetV0>>(
145            Mode::Private,
146            count_is!(0, 0, 501, 503),
147        );
148    }
149
150    #[test]
151    fn test_scalar_to_field() {
152        check_cast_lossy::<Field<Circuit>, console_root::types::Field<MainnetV0>>(
153            Mode::Constant,
154            count_is!(0, 0, 0, 0),
155        );
156        check_cast_lossy::<Field<Circuit>, console_root::types::Field<MainnetV0>>(Mode::Public, count_is!(0, 0, 0, 0));
157        check_cast_lossy::<Field<Circuit>, console_root::types::Field<MainnetV0>>(Mode::Private, count_is!(0, 0, 0, 0));
158    }
159
160    #[test]
161    fn test_scalar_to_group() {
162        check_cast_lossy::<Group<Circuit>, console_root::types::Group<MainnetV0>>(
163            Mode::Constant,
164            count_less_than!(4303, 0, 0, 0),
165        );
166        check_cast_lossy::<Group<Circuit>, console_root::types::Group<MainnetV0>>(
167            Mode::Public,
168            count_is!(2029, 0, 6745, 6750),
169        );
170        check_cast_lossy::<Group<Circuit>, console_root::types::Group<MainnetV0>>(
171            Mode::Private,
172            count_is!(2029, 0, 6745, 6750),
173        );
174    }
175
176    #[test]
177    fn test_scalar_to_i8() {
178        check_cast_lossy::<I8<Circuit>, console_root::types::I8<MainnetV0>>(Mode::Constant, count_is!(251, 0, 0, 0));
179        check_cast_lossy::<I8<Circuit>, console_root::types::I8<MainnetV0>>(Mode::Public, count_is!(0, 0, 501, 503));
180        check_cast_lossy::<I8<Circuit>, console_root::types::I8<MainnetV0>>(Mode::Private, count_is!(0, 0, 501, 503));
181    }
182
183    #[test]
184    fn test_scalar_to_i16() {
185        check_cast_lossy::<I16<Circuit>, console_root::types::I16<MainnetV0>>(Mode::Constant, count_is!(251, 0, 0, 0));
186        check_cast_lossy::<I16<Circuit>, console_root::types::I16<MainnetV0>>(Mode::Public, count_is!(0, 0, 501, 503));
187        check_cast_lossy::<I16<Circuit>, console_root::types::I16<MainnetV0>>(Mode::Private, count_is!(0, 0, 501, 503));
188    }
189
190    #[test]
191    fn test_scalar_to_i32() {
192        check_cast_lossy::<I32<Circuit>, console_root::types::I32<MainnetV0>>(Mode::Constant, count_is!(251, 0, 0, 0));
193        check_cast_lossy::<I32<Circuit>, console_root::types::I32<MainnetV0>>(Mode::Public, count_is!(0, 0, 501, 503));
194        check_cast_lossy::<I32<Circuit>, console_root::types::I32<MainnetV0>>(Mode::Private, count_is!(0, 0, 501, 503));
195    }
196
197    #[test]
198    fn test_scalar_to_i64() {
199        check_cast_lossy::<I64<Circuit>, console_root::types::I64<MainnetV0>>(Mode::Constant, count_is!(251, 0, 0, 0));
200        check_cast_lossy::<I64<Circuit>, console_root::types::I64<MainnetV0>>(Mode::Public, count_is!(0, 0, 501, 503));
201        check_cast_lossy::<I64<Circuit>, console_root::types::I64<MainnetV0>>(Mode::Private, count_is!(0, 0, 501, 503));
202    }
203
204    #[test]
205    fn test_scalar_to_i128() {
206        check_cast_lossy::<I128<Circuit>, console_root::types::I128<MainnetV0>>(
207            Mode::Constant,
208            count_is!(251, 0, 0, 0),
209        );
210        check_cast_lossy::<I128<Circuit>, console_root::types::I128<MainnetV0>>(
211            Mode::Public,
212            count_is!(0, 0, 501, 503),
213        );
214        check_cast_lossy::<I128<Circuit>, console_root::types::I128<MainnetV0>>(
215            Mode::Private,
216            count_is!(0, 0, 501, 503),
217        );
218    }
219
220    #[test]
221    fn test_scalar_to_scalar() {
222        check_cast_lossy::<Scalar<Circuit>, console_root::types::Scalar<MainnetV0>>(
223            Mode::Constant,
224            count_is!(0, 0, 0, 0),
225        );
226        check_cast_lossy::<Scalar<Circuit>, console_root::types::Scalar<MainnetV0>>(
227            Mode::Public,
228            count_is!(0, 0, 0, 0),
229        );
230        check_cast_lossy::<Scalar<Circuit>, console_root::types::Scalar<MainnetV0>>(
231            Mode::Private,
232            count_is!(0, 0, 0, 0),
233        );
234    }
235
236    #[test]
237    fn test_scalar_to_u8() {
238        check_cast_lossy::<U8<Circuit>, console_root::types::U8<MainnetV0>>(Mode::Constant, count_is!(251, 0, 0, 0));
239        check_cast_lossy::<U8<Circuit>, console_root::types::U8<MainnetV0>>(Mode::Public, count_is!(0, 0, 501, 503));
240        check_cast_lossy::<U8<Circuit>, console_root::types::U8<MainnetV0>>(Mode::Private, count_is!(0, 0, 501, 503));
241    }
242
243    #[test]
244    fn test_scalar_to_u16() {
245        check_cast_lossy::<U16<Circuit>, console_root::types::U16<MainnetV0>>(Mode::Constant, count_is!(251, 0, 0, 0));
246        check_cast_lossy::<U16<Circuit>, console_root::types::U16<MainnetV0>>(Mode::Public, count_is!(0, 0, 501, 503));
247        check_cast_lossy::<U16<Circuit>, console_root::types::U16<MainnetV0>>(Mode::Private, count_is!(0, 0, 501, 503));
248    }
249
250    #[test]
251    fn test_scalar_to_u32() {
252        check_cast_lossy::<U32<Circuit>, console_root::types::U32<MainnetV0>>(Mode::Constant, count_is!(251, 0, 0, 0));
253        check_cast_lossy::<U32<Circuit>, console_root::types::U32<MainnetV0>>(Mode::Public, count_is!(0, 0, 501, 503));
254        check_cast_lossy::<U32<Circuit>, console_root::types::U32<MainnetV0>>(Mode::Private, count_is!(0, 0, 501, 503));
255    }
256
257    #[test]
258    fn test_scalar_to_u64() {
259        check_cast_lossy::<U64<Circuit>, console_root::types::U64<MainnetV0>>(Mode::Constant, count_is!(251, 0, 0, 0));
260        check_cast_lossy::<U64<Circuit>, console_root::types::U64<MainnetV0>>(Mode::Public, count_is!(0, 0, 501, 503));
261        check_cast_lossy::<U64<Circuit>, console_root::types::U64<MainnetV0>>(Mode::Private, count_is!(0, 0, 501, 503));
262    }
263
264    #[test]
265    fn test_scalar_to_u128() {
266        check_cast_lossy::<U128<Circuit>, console_root::types::U128<MainnetV0>>(
267            Mode::Constant,
268            count_is!(251, 0, 0, 0),
269        );
270        check_cast_lossy::<U128<Circuit>, console_root::types::U128<MainnetV0>>(
271            Mode::Public,
272            count_is!(0, 0, 501, 503),
273        );
274        check_cast_lossy::<U128<Circuit>, console_root::types::U128<MainnetV0>>(
275            Mode::Private,
276            count_is!(0, 0, 501, 503),
277        );
278    }
279}