scalar.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the alphavm 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 use super::*; 17 18 impl<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 31 impl<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 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 42 impl<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 55 impl<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 let result = self.to_field(); 61 debug_assert!(result.is_ok(), "A scalar should always be able to be converted to a field"); 62 result.unwrap() 63 } 64 } 65 66 impl<E: Environment, I: IntegerType> CastLossy<Integer<E, I>> for Scalar<E> { 67 /// Casts a `Scalar` to an `Integer`, with lossy truncation. 68 #[inline] 69 fn cast_lossy(&self) -> Integer<E, I> { 70 // Note: We are reconstituting the integer from the scalar field. 71 // This is safe as the number of bits in the integer is less than the scalar field modulus, 72 // and thus will always fit within a single scalar field element. 73 debug_assert!(I::BITS < Scalar::<E>::size_in_bits() as u64); 74 75 // Truncate the field to the size of the integer domain. 76 // Slicing here is safe as the base field is larger than the integer domain. 77 let result = Integer::<E, I>::from_bits_le(&self.to_bits_le()[..usize::try_from(I::BITS).unwrap()]); 78 debug_assert!(result.is_ok(), "A lossy integer should always be able to be constructed from scalar bits"); 79 result.unwrap() 80 } 81 } 82 83 impl<E: Environment> CastLossy<Scalar<E>> for Scalar<E> { 84 /// Casts a `Scalar` to a `Scalar`. 85 /// This is an identity cast, so it is **always** lossless. 86 #[inline] 87 fn cast_lossy(&self) -> Scalar<E> { 88 *self 89 } 90 } 91 92 #[cfg(test)] 93 mod tests { 94 use super::*; 95 96 type CurrentEnvironment = Console; 97 98 const ITERATIONS: u64 = 10_000; 99 100 #[test] 101 fn test_scalar_to_address() { 102 let rng = &mut TestRng::default(); 103 104 let scalar = Scalar::<CurrentEnvironment>::one(); 105 let address: Address<CurrentEnvironment> = scalar.cast_lossy(); 106 assert_eq!(address, Address::new(Group::generator())); 107 assert_eq!(address.to_group(), &Group::generator()); 108 109 let scalar = Scalar::<CurrentEnvironment>::zero(); 110 let address: Address<CurrentEnvironment> = scalar.cast_lossy(); 111 assert_eq!(address, Address::zero()); 112 assert_eq!(address.to_group(), &Group::zero()); 113 114 for _ in 0..ITERATIONS { 115 // Sample a random scalar. 116 let scalar = Scalar::<CurrentEnvironment>::rand(rng); 117 // Perform the operation. 118 let candidate = scalar.cast_lossy(); 119 // Compare the result against the group element. (This is the most we can do.) 120 let expected: Group<CurrentEnvironment> = scalar.cast_lossy(); 121 assert_eq!(Address::new(expected), candidate); 122 } 123 } 124 125 #[test] 126 fn test_scalar_to_boolean() { 127 let rng = &mut TestRng::default(); 128 129 let scalar = Scalar::<CurrentEnvironment>::one(); 130 let boolean: Boolean<CurrentEnvironment> = scalar.cast_lossy(); 131 assert_eq!(boolean, Boolean::new(true)); 132 133 let scalar = Scalar::<CurrentEnvironment>::zero(); 134 let boolean: Boolean<CurrentEnvironment> = scalar.cast_lossy(); 135 assert_eq!(boolean, Boolean::new(false)); 136 137 for _ in 0..ITERATIONS { 138 // Sample a random scalar. 139 let scalar = Scalar::<CurrentEnvironment>::rand(rng); 140 // Perform the operation. 141 let candidate = scalar.cast_lossy(); 142 // Compare the result against the least significant bit of the scalar. 143 let expected = Boolean::new(scalar.to_bits_be().pop().unwrap()); 144 assert_eq!(expected, candidate); 145 } 146 } 147 148 #[test] 149 fn test_scalar_to_field() { 150 let rng = &mut TestRng::default(); 151 152 for _ in 0..ITERATIONS { 153 // Sample a random scalar. 154 let scalar = Scalar::<CurrentEnvironment>::rand(rng); 155 // Perform the operation. 156 let candidate = scalar.cast_lossy(); 157 assert_eq!(scalar.to_field().unwrap(), candidate); 158 } 159 } 160 161 #[test] 162 fn test_scalar_to_group() { 163 let rng = &mut TestRng::default(); 164 165 let scalar = Scalar::<CurrentEnvironment>::one(); 166 let group: Group<CurrentEnvironment> = scalar.cast_lossy(); 167 assert_eq!(group, Group::generator()); 168 169 let scalar = Scalar::<CurrentEnvironment>::zero(); 170 let group: Group<CurrentEnvironment> = scalar.cast_lossy(); 171 assert_eq!(group, Group::zero()); 172 173 for _ in 0..ITERATIONS { 174 // Sample a random scalar. 175 let scalar = Scalar::<CurrentEnvironment>::rand(rng); 176 // Perform the operation. 177 let candidate: Group<CurrentEnvironment> = scalar.cast_lossy(); 178 // Compare the result against the address. (This is the most we can do.) 179 let expected: Address<CurrentEnvironment> = scalar.cast_lossy(); 180 assert_eq!(expected.to_group(), &candidate); 181 } 182 } 183 184 #[test] 185 fn test_scalar_to_scalar() { 186 let rng = &mut TestRng::default(); 187 188 for _ in 0..ITERATIONS { 189 // Sample a random scalar. 190 let scalar = Scalar::<CurrentEnvironment>::rand(rng); 191 // Perform the operation. 192 let candidate: Scalar<CurrentEnvironment> = scalar.cast_lossy(); 193 assert_eq!(scalar, candidate); 194 } 195 } 196 197 macro_rules! check_scalar_to_integer { 198 ($type:ty) => { 199 let rng = &mut TestRng::default(); 200 201 let scalar = Scalar::<CurrentEnvironment>::one(); 202 let integer: Integer<CurrentEnvironment, $type> = scalar.cast_lossy(); 203 assert_eq!(integer, Integer::<CurrentEnvironment, $type>::one()); 204 205 let scalar = Scalar::<CurrentEnvironment>::zero(); 206 let integer: Integer<CurrentEnvironment, $type> = scalar.cast_lossy(); 207 assert_eq!(integer, Integer::<CurrentEnvironment, $type>::zero()); 208 209 for _ in 0..ITERATIONS { 210 // Sample a random scalar. 211 let scalar = Scalar::<CurrentEnvironment>::rand(rng); 212 // Perform the operation. 213 let candidate: Integer<CurrentEnvironment, $type> = scalar.cast_lossy(); 214 // Compare the result against the least significant bits of the scalar. 215 let expected = Integer::<CurrentEnvironment, $type>::from_bits_le( 216 &scalar.to_bits_le()[..usize::try_from(<$type>::BITS).unwrap()], 217 ) 218 .unwrap(); 219 assert_eq!(expected, candidate); 220 } 221 }; 222 } 223 224 #[test] 225 fn test_scalar_to_i8() { 226 check_scalar_to_integer!(i8); 227 } 228 229 #[test] 230 fn test_scalar_to_i16() { 231 check_scalar_to_integer!(i16); 232 } 233 234 #[test] 235 fn test_scalar_to_i32() { 236 check_scalar_to_integer!(i32); 237 } 238 239 #[test] 240 fn test_scalar_to_i64() { 241 check_scalar_to_integer!(i64); 242 } 243 244 #[test] 245 fn test_scalar_to_i128() { 246 check_scalar_to_integer!(i128); 247 } 248 249 #[test] 250 fn test_scalar_to_u8() { 251 check_scalar_to_integer!(u8); 252 } 253 254 #[test] 255 fn test_scalar_to_u16() { 256 check_scalar_to_integer!(u16); 257 } 258 259 #[test] 260 fn test_scalar_to_u32() { 261 check_scalar_to_integer!(u32); 262 } 263 264 #[test] 265 fn test_scalar_to_u64() { 266 check_scalar_to_integer!(u64); 267 } 268 269 #[test] 270 fn test_scalar_to_u128() { 271 check_scalar_to_integer!(u128); 272 } 273 }