• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ergoplatform / sigma-rust / 15758737784

19 Jun 2025 01:13PM UTC coverage: 78.451% (+0.008%) from 78.443%
15758737784

push

github

web-flow
Merge pull request #813 from sethdusek/soptionserialization

SOption[T] serialization

64 of 97 new or added lines in 5 files covered. (65.98%)

9 existing lines in 3 files now uncovered.

11959 of 15244 relevant lines covered (78.45%)

2.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.6
/ergotree-interpreter/src/eval/snumeric.rs
1
use alloc::vec::Vec;
2
use ergotree_ir::{
3
    bigint256::BigInt256,
4
    mir::{constant::TryExtractInto, value::Value},
5
    types::{
6
        smethod::SMethod,
7
        snumeric::{
8
            self,
9
            sbigint::{TO_UNSIGNED_METHOD_ID, TO_UNSIGNED_MOD_METHOD_ID},
10
            sunsignedbigint::{
11
                MOD_INVERSE_METHOD_ID, MOD_METHOD_ID, MULTIPLY_MOD_METHOD_ID, PLUS_MOD_METHOD_ID,
12
                SUBTRACT_MOD_METHOD_ID, TO_SIGNED_METHOD_ID,
13
            },
14
        },
15
        stype_companion::STypeCompanion,
16
    },
17
    unsignedbigint256::UnsignedBigInt,
18
};
19
use num_traits::{CheckedRem, CheckedShl, CheckedShr};
20

21
use super::{EvalError, EvalFn};
22

23
const TO_BYTES_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
24
    Ok(match obj {
25
        Value::Byte(obj) => obj.to_be_bytes().to_vec().into(),
26
        Value::Short(obj) => obj.to_be_bytes().to_vec().into(),
27
        Value::Int(obj) => obj.to_be_bytes().to_vec().into(),
28
        Value::Long(obj) => obj.to_be_bytes().to_vec().into(),
29
        Value::BigInt(obj) => obj.to_be_bytes().to_vec().into(),
30
        Value::UnsignedBigInt(obj) => obj.to_be_bytes().to_vec().into(),
31
        other => {
32
            return Err(EvalError::UnexpectedValue(format!(
33
                "Expected numeric type, got {other:?}"
34
            )))
35
        }
36
    })
37
};
38

39
static TO_BITS_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
2✔
40
    fn byte_to_bits(mut byte: u8) -> [bool; 8] {
1✔
41
        let mut res = [false; 8];
1✔
42
        let mut i = 8;
1✔
43
        while byte != 0 {
3✔
44
            i -= 1;
1✔
45
            res[i] = (byte & 1) == 1;
4✔
46
            byte >>= 1;
2✔
47
        }
48
        res
2✔
49
    }
50
    fn to_bits(bytes: &[u8]) -> Value<'static> {
1✔
51
        bytes
1✔
52
            .iter()
53
            .copied()
54
            .flat_map(byte_to_bits)
55
            .collect::<Vec<_>>()
56
            .into()
57
    }
58
    Ok(match obj {
4✔
59
        Value::Byte(obj) => to_bits(obj.to_be_bytes().as_slice()),
2✔
60
        Value::Short(obj) => to_bits(obj.to_be_bytes().as_slice()),
2✔
61
        Value::Int(obj) => to_bits(obj.to_be_bytes().as_slice()),
2✔
62
        Value::Long(obj) => to_bits(obj.to_be_bytes().as_slice()),
2✔
63
        Value::BigInt(obj) => to_bits(obj.to_be_bytes().as_slice()),
2✔
64
        Value::UnsignedBigInt(obj) => to_bits(obj.to_be_bytes().as_slice()),
2✔
65
        other => {
×
66
            return Err(EvalError::UnexpectedValue(format!(
×
67
                "Expected numeric type, got {other:?}"
68
            )))
69
        }
70
    })
71
};
72

73
static BITWISE_INVERSE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
1✔
74
    Ok(match obj {
2✔
75
        Value::Byte(obj) => (!obj).into(),
2✔
76
        Value::Short(obj) => (!obj).into(),
2✔
77
        Value::Int(obj) => (!obj).into(),
2✔
78
        Value::Long(obj) => (!obj).into(),
2✔
79
        Value::BigInt(obj) => (!obj).into(),
2✔
80
        Value::UnsignedBigInt(obj) => (!obj).into(),
2✔
81
        other => {
×
82
            return Err(EvalError::UnexpectedValue(format!(
×
83
                "Expected numeric type, got {other:?}"
84
            )))
85
        }
86
    })
87
};
88

89
static BITWISE_OR_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
90
    let rhs = args[0].clone();
2✔
91
    Ok(match _mc.obj_type {
2✔
92
        STypeCompanion::SByte => {
93
            (obj.try_extract_into::<i8>()? | rhs.try_extract_into::<i8>()?).into()
2✔
94
        }
95
        STypeCompanion::SShort => {
96
            (obj.try_extract_into::<i16>()? | rhs.try_extract_into::<i16>()?).into()
2✔
97
        }
98
        STypeCompanion::SInt => {
99
            (obj.try_extract_into::<i32>()? | rhs.try_extract_into::<i32>()?).into()
2✔
100
        }
101
        STypeCompanion::SLong => {
102
            (obj.try_extract_into::<i64>()? | rhs.try_extract_into::<i64>()?).into()
2✔
103
        }
104
        STypeCompanion::SBigInt => {
105
            (obj.try_extract_into::<BigInt256>()? | rhs.try_extract_into::<BigInt256>()?).into()
2✔
106
        }
107
        STypeCompanion::SUnsignedBigInt => (obj.try_extract_into::<UnsignedBigInt>()?
3✔
108
            | rhs.try_extract_into::<UnsignedBigInt>()?)
1✔
109
        .into(),
110
        other => {
×
111
            return Err(EvalError::UnexpectedValue(format!(
×
112
                "Expected numeric type, got {other:?}"
113
            )))
114
        }
115
    })
116
};
117

118
static BITWISE_AND_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
119
    let rhs = args[0].clone();
2✔
120
    Ok(match _mc.obj_type {
2✔
121
        STypeCompanion::SByte => {
122
            (obj.try_extract_into::<i8>()? & rhs.try_extract_into::<i8>()?).into()
2✔
123
        }
124
        STypeCompanion::SShort => {
125
            (obj.try_extract_into::<i16>()? & rhs.try_extract_into::<i16>()?).into()
2✔
126
        }
127
        STypeCompanion::SInt => {
128
            (obj.try_extract_into::<i32>()? & rhs.try_extract_into::<i32>()?).into()
2✔
129
        }
130
        STypeCompanion::SLong => {
131
            (obj.try_extract_into::<i64>()? & rhs.try_extract_into::<i64>()?).into()
2✔
132
        }
133
        STypeCompanion::SBigInt => {
134
            (obj.try_extract_into::<BigInt256>()? & rhs.try_extract_into::<BigInt256>()?).into()
2✔
135
        }
136
        STypeCompanion::SUnsignedBigInt => (obj.try_extract_into::<UnsignedBigInt>()?
3✔
137
            & rhs.try_extract_into::<UnsignedBigInt>()?)
1✔
138
        .into(),
139
        other => {
×
140
            return Err(EvalError::UnexpectedValue(format!(
×
141
                "Expected numeric type, got {other:?}"
142
            )))
143
        }
144
    })
145
};
146

147
static BITWISE_XOR_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
148
    let rhs = args
3✔
149
        .first()
150
        .ok_or_else(|| EvalError::UnexpectedValue("rhs missing".into()))?
×
151
        .clone();
1✔
152
    Ok(match _mc.obj_type {
3✔
153
        STypeCompanion::SByte => {
154
            (obj.try_extract_into::<i8>()? ^ rhs.try_extract_into::<i8>()?).into()
2✔
155
        }
156
        STypeCompanion::SShort => {
157
            (obj.try_extract_into::<i16>()? ^ rhs.try_extract_into::<i16>()?).into()
2✔
158
        }
159
        STypeCompanion::SInt => {
160
            (obj.try_extract_into::<i32>()? ^ rhs.try_extract_into::<i32>()?).into()
2✔
161
        }
162
        STypeCompanion::SLong => {
163
            (obj.try_extract_into::<i64>()? ^ rhs.try_extract_into::<i64>()?).into()
2✔
164
        }
165
        STypeCompanion::SBigInt => {
166
            (obj.try_extract_into::<BigInt256>()? ^ rhs.try_extract_into::<BigInt256>()?).into()
2✔
167
        }
168
        STypeCompanion::SUnsignedBigInt => (obj.try_extract_into::<UnsignedBigInt>()?
3✔
169
            ^ rhs.try_extract_into::<UnsignedBigInt>()?)
1✔
170
        .into(),
171
        other => {
×
172
            return Err(EvalError::UnexpectedValue(format!(
×
173
                "Expected numeric type, got {other:?}"
174
            )))
175
        }
176
    })
177
};
178

179
#[inline(never)]
180
fn invalid_shift_err() -> EvalError {
1✔
181
    EvalError::Misc("shift value is out of bounds".into())
1✔
182
}
183

184
static SHIFT_LEFT_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
185
    let shift_value: u32 = args
4✔
186
        .first()
187
        .ok_or_else(|| EvalError::UnexpectedValue("shift arg missing".into()))?
×
188
        .clone()
189
        .try_extract_into::<i32>()?
×
190
        .try_into()
UNCOV
191
        .map_err(|_| EvalError::UnexpectedValue("expected non-negative shift value".into()))?;
×
192
    Ok(match obj {
2✔
193
        Value::Byte(obj) => obj
4✔
194
            .checked_shl(shift_value)
195
            .ok_or_else(invalid_shift_err)?
1✔
196
            .into(),
197
        Value::Short(obj) => obj
3✔
198
            .checked_shl(shift_value)
199
            .ok_or_else(invalid_shift_err)?
1✔
200
            .into(),
201
        Value::Int(obj) => obj
3✔
202
            .checked_shl(shift_value)
203
            .ok_or_else(invalid_shift_err)?
1✔
204
            .into(),
205
        Value::Long(obj) => obj
3✔
206
            .checked_shl(shift_value)
207
            .ok_or_else(invalid_shift_err)?
1✔
208
            .into(),
209
        Value::BigInt(obj) => obj
5✔
210
            .checked_shl(shift_value)
211
            .ok_or_else(invalid_shift_err)?
1✔
212
            .into(),
213
        Value::UnsignedBigInt(obj) => obj
4✔
214
            .checked_shl(shift_value)
215
            .ok_or_else(invalid_shift_err)?
1✔
216
            .into(),
217
        other => {
×
218
            return Err(EvalError::UnexpectedValue(format!(
×
219
                "Expected numeric type, got {other:?}"
220
            )))
221
        }
222
    })
223
};
224

225
static SHIFT_RIGHT_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
226
    let shift_value: u32 = args
2✔
227
        .first()
228
        .ok_or_else(|| EvalError::UnexpectedValue("shift arg missing".into()))?
×
229
        .clone()
230
        .try_extract_into::<i32>()?
×
231
        .try_into()
UNCOV
232
        .map_err(|_| EvalError::UnexpectedValue("expected non-negative shift value".into()))?;
×
233
    Ok(match obj {
2✔
234
        Value::Byte(obj) => obj
5✔
235
            .checked_shr(shift_value)
236
            .ok_or_else(invalid_shift_err)?
1✔
237
            .into(),
238
        Value::Short(obj) => obj
3✔
239
            .checked_shr(shift_value)
240
            .ok_or_else(invalid_shift_err)?
1✔
241
            .into(),
242
        Value::Int(obj) => obj
3✔
243
            .checked_shr(shift_value)
244
            .ok_or_else(invalid_shift_err)?
1✔
245
            .into(),
246
        Value::Long(obj) => obj
3✔
247
            .checked_shr(shift_value)
248
            .ok_or_else(invalid_shift_err)?
1✔
249
            .into(),
250
        Value::BigInt(obj) => obj
3✔
251
            .checked_shr(shift_value)
252
            .ok_or_else(invalid_shift_err)?
1✔
253
            .into(),
254
        Value::UnsignedBigInt(obj) => obj
4✔
255
            .checked_shr(shift_value)
256
            .ok_or_else(invalid_shift_err)?
1✔
257
            .into(),
258
        other => {
×
259
            return Err(EvalError::UnexpectedValue(format!(
×
260
                "Expected numeric type, got {other:?}"
261
            )))
262
        }
263
    })
264
};
265

266
static TO_UNSIGNED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
1✔
267
    let signed = obj.try_extract_into::<BigInt256>()?;
2✔
268
    UnsignedBigInt::try_from(signed)
1✔
269
        .map_err(|err| EvalError::ArithmeticException(err.into()))
2✔
270
        .map(Value::from)
271
};
272

273
static TO_UNSIGNED_MOD_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
274
    let signed = obj.try_extract_into::<BigInt256>()?;
2✔
275
    let modulus = args
1✔
276
        .first()
277
        .cloned()
278
        .ok_or_else(|| EvalError::UnexpectedValue("toUnsignedMod: missing first argument".into()))?
×
279
        .try_extract_into::<UnsignedBigInt>()?;
×
280
    UnsignedBigInt::from_signed_mod(signed, modulus)
1✔
281
        .map(Value::from)
282
        .ok_or_else(|| EvalError::ArithmeticException("toUnsignedMod: can't divide by 0".into()))
×
283
};
284

285
static MOD_INVERSE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
286
    let obj = obj.try_extract_into::<UnsignedBigInt>()?;
2✔
287
    let modulus = args
1✔
288
        .first()
289
        .cloned()
290
        .ok_or_else(|| EvalError::UnexpectedValue("modInv: missing first argument".into()))?
×
291
        .try_extract_into::<UnsignedBigInt>()?;
×
292
    obj.mod_inv(modulus)
1✔
293
        .map(Value::from)
294
        .ok_or_else(|| EvalError::ArithmeticException("modInv: can't divide by 0".into()))
2✔
295
};
296

297
static PLUS_MOD_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
298
    let obj = obj.try_extract_into::<UnsignedBigInt>()?;
2✔
299
    let b = args
1✔
300
        .first()
301
        .cloned()
302
        .ok_or_else(|| EvalError::UnexpectedValue("plusMod: missing first argument".into()))?
×
303
        .try_extract_into::<UnsignedBigInt>()?;
×
304
    let modulus = args
1✔
305
        .get(1)
306
        .cloned()
307
        .ok_or_else(|| EvalError::UnexpectedValue("plusMod: missing first argument".into()))?
×
308
        .try_extract_into::<UnsignedBigInt>()?;
×
309
    obj.checked_mod_add(b, modulus)
1✔
310
        .map(Value::from)
311
        .ok_or_else(|| EvalError::ArithmeticException("plusMod: can't divide by 0".into()))
×
312
};
313

314
static SUBTRACT_MOD_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
315
    let obj = obj.try_extract_into::<UnsignedBigInt>()?;
2✔
316
    let b = args
1✔
317
        .first()
318
        .cloned()
319
        .ok_or_else(|| EvalError::UnexpectedValue("subtractMod: missing first argument".into()))?
×
320
        .try_extract_into::<UnsignedBigInt>()?;
×
321
    let modulus = args
1✔
322
        .get(1)
323
        .cloned()
324
        .ok_or_else(|| EvalError::UnexpectedValue("subtractMod: missing first argument".into()))?
×
325
        .try_extract_into::<UnsignedBigInt>()?;
×
326
    obj.checked_mod_sub(b, modulus)
1✔
327
        .map(Value::from)
328
        .ok_or_else(|| EvalError::ArithmeticException("subtractMod: can't divide by 0".into()))
×
329
};
330

331
static MULTIPLY_MOD_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
332
    let obj = obj.try_extract_into::<UnsignedBigInt>()?;
2✔
333
    let b = args
1✔
334
        .first()
335
        .cloned()
336
        .ok_or_else(|| EvalError::UnexpectedValue("multiplyMod: missing first argument".into()))?
×
337
        .try_extract_into::<UnsignedBigInt>()?;
×
338
    let modulus = args
1✔
339
        .get(1)
340
        .cloned()
341
        .ok_or_else(|| EvalError::UnexpectedValue("multiplyMod: missing first argument".into()))?
×
342
        .try_extract_into::<UnsignedBigInt>()?;
×
343
    obj.checked_mod_mul(b, modulus)
1✔
344
        .map(Value::from)
345
        .ok_or_else(|| EvalError::ArithmeticException("multiplyMod: can't divide by 0".into()))
×
346
};
347

348
static MOD_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
1✔
349
    let obj = obj.try_extract_into::<UnsignedBigInt>()?;
2✔
350
    let modulus = args
1✔
351
        .first()
352
        .cloned()
353
        .ok_or_else(|| EvalError::UnexpectedValue("mod: missing first argument".into()))?
×
354
        .try_extract_into::<UnsignedBigInt>()?;
×
355
    obj.checked_rem(&modulus)
1✔
356
        .map(Value::from)
357
        .ok_or_else(|| EvalError::ArithmeticException("mod: can't divide by 0".into()))
×
358
};
359

360
static TO_SIGNED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
1✔
361
    let obj = obj.try_extract_into::<UnsignedBigInt>()?;
2✔
362
    BigInt256::try_from(obj)
1✔
363
        .map_err(|e| EvalError::ArithmeticException(e.into()))
2✔
364
        .map(Value::from)
365
};
366

367
// List of methods that are available for all numeric types
368
fn snumeric_evalfn(method: &SMethod) -> Result<EvalFn, EvalError> {
1✔
369
    match method.method_id() {
1✔
370
        // The following methods are explicitly not supported
371
        snumeric::TO_BYTE_METHOD_ID
×
372
        | snumeric::TO_INT_METHOD_ID
373
        | snumeric::TO_SHORT_METHOD_ID
374
        | snumeric::TO_LONG_METHOD_ID
375
        | snumeric::TO_BIGINT_METHOD_ID => Err(EvalError::NotFound("Not implemented".into())),
376
        snumeric::TO_BYTES_METHOD_ID => Ok(TO_BYTES_EVAL_FN),
1✔
377
        snumeric::TO_BITS_METHOD_ID => Ok(TO_BITS_EVAL_FN),
1✔
378
        snumeric::BITWISE_INVERSE_METHOD_ID => Ok(BITWISE_INVERSE_EVAL_FN),
1✔
379
        snumeric::BITWISE_OR_METHOD_ID => Ok(BITWISE_OR_EVAL_FN),
1✔
380
        snumeric::BITWISE_AND_METHOD_ID => Ok(BITWISE_AND_EVAL_FN),
1✔
381
        snumeric::BITWISE_XOR_METHOD_ID => Ok(BITWISE_XOR_EVAL_FN),
1✔
382
        snumeric::SHIFT_LEFT_METHOD_ID => Ok(SHIFT_LEFT_EVAL_FN),
1✔
383
        snumeric::SHIFT_RIGHT_METHOD_ID => Ok(SHIFT_RIGHT_EVAL_FN),
1✔
384
        _ => Err(EvalError::NotFound(format!(
1✔
385
            "Method {:?} not found",
386
            method.method_id()
1✔
387
        ))),
388
    }
389
}
390

391
fn bigint_evalfn(method: &SMethod) -> Result<EvalFn, EvalError> {
1✔
392
    match snumeric_evalfn(method) {
1✔
393
        Ok(eval_fn) => Ok(eval_fn),
1✔
394
        Err(_) => match method.method_id() {
2✔
395
            TO_UNSIGNED_METHOD_ID => Ok(TO_UNSIGNED_EVAL_FN),
1✔
396
            TO_UNSIGNED_MOD_METHOD_ID => Ok(TO_UNSIGNED_MOD_EVAL_FN),
1✔
397
            _ => Err(EvalError::NotFound(format!(
×
398
                "SBigInt: Method id {:?} not found",
399
                method.method_id()
×
400
            ))),
401
        },
402
    }
403
}
404

405
fn unsigned_bigint_evalfn(method: &SMethod) -> Result<EvalFn, EvalError> {
1✔
406
    match snumeric_evalfn(method) {
1✔
407
        Ok(eval_fn) => Ok(eval_fn),
1✔
408
        Err(_) => match method.method_id() {
2✔
409
            MOD_INVERSE_METHOD_ID => Ok(MOD_INVERSE_EVAL_FN),
1✔
410
            PLUS_MOD_METHOD_ID => Ok(PLUS_MOD_EVAL_FN),
1✔
411
            SUBTRACT_MOD_METHOD_ID => Ok(SUBTRACT_MOD_EVAL_FN),
1✔
412
            MULTIPLY_MOD_METHOD_ID => Ok(MULTIPLY_MOD_EVAL_FN),
1✔
413
            MOD_METHOD_ID => Ok(MOD_EVAL_FN),
1✔
414
            TO_SIGNED_METHOD_ID => Ok(TO_SIGNED_EVAL_FN),
1✔
415
            _ => Err(EvalError::NotFound(format!(
×
416
                "SUnsignedBigInt: Method id {:?} not found",
417
                method.method_id()
×
418
            ))),
419
        },
420
    }
421
}
422

423
pub(crate) fn numeric_method_evalfn(method: &SMethod) -> Result<EvalFn, EvalError> {
2✔
424
    match method.obj_type.type_code() {
1✔
425
        snumeric::sbyte::TYPE_CODE
1✔
426
        | snumeric::sshort::TYPE_CODE
427
        | snumeric::sint::TYPE_CODE
428
        | snumeric::slong::TYPE_CODE => snumeric_evalfn(method),
429
        snumeric::sbigint::TYPE_CODE => bigint_evalfn(method),
1✔
430
        snumeric::sunsignedbigint::TYPE_CODE => unsigned_bigint_evalfn(method),
1✔
431
        _ => Err(EvalError::UnexpectedValue(format!(
×
432
            "Expected numeric type, found {:?}",
433
            method.obj_type.type_code()
×
434
        ))),
435
    }
436
}
437

438
#[cfg(test)]
439
#[cfg(feature = "arbitrary")]
440
#[allow(clippy::unwrap_used)]
441
mod test {
442
    use core::{iter::Sum, ops::Not};
443

444
    use ergotree_ir::{
445
        bigint256::BigInt256,
446
        mir::{
447
            constant::{Constant, TryExtractFrom},
448
            expr::Expr,
449
            method_call::MethodCall,
450
            value::Value,
451
        },
452
        types::{
453
            sglobal,
454
            smethod::{SMethod, SMethodDesc},
455
            snumeric::{
456
                self,
457
                sbigint::{TO_UNSIGNED_METHOD_DESC, TO_UNSIGNED_MOD_METHOD_DESC},
458
                sunsignedbigint::{
459
                    MOD_INVERSE_METHOD_DESC, MOD_METHOD_DESC, MULTIPLY_MOD_METHOD_DESC,
460
                    PLUS_MOD_METHOD_DESC, SUBTRACT_MOD_METHOD_DESC, TO_SIGNED_METHOD_DESC,
461
                },
462
                BITWISE_AND_METHOD_ID, BITWISE_INVERSE_METHOD_ID, BITWISE_OR_METHOD_ID,
463
                BITWISE_XOR_METHOD_ID, SHIFT_LEFT_METHOD_ID, SHIFT_RIGHT_METHOD_ID,
464
                TO_BITS_METHOD_ID, TO_BYTES_METHOD_ID,
465
            },
466
            stype::LiftIntoSType,
467
            stype_companion::STypeCompanion,
468
            stype_param::STypeVar,
469
        },
470
        unsignedbigint256::UnsignedBigInt,
471
    };
472
    use num_traits::{CheckedRem, CheckedShl, CheckedShr, One, Zero};
473
    use proptest::prelude::*;
474

475
    use crate::eval::{
476
        test_util::{eval_out_wo_ctx, try_eval_out_wo_ctx},
477
        EvalError,
478
    };
479

480
    fn eval_modular_op(
481
        desc: &SMethodDesc,
482
        args: &[UnsignedBigInt],
483
    ) -> Result<UnsignedBigInt, EvalError> {
484
        let mc: Expr = MethodCall::new(
485
            Constant::from(args[0]).into(),
486
            SMethod::new(STypeCompanion::SUnsignedBigInt, desc.clone()),
487
            args[1..]
488
                .iter()
489
                .copied()
490
                .map(Constant::from)
491
                .map(Expr::from)
492
                .collect(),
493
        )
494
        .unwrap()
495
        .into();
496
        try_eval_out_wo_ctx(&mc)
497
    }
498
    trait Numeric:
499
        LiftIntoSType
500
        + TryExtractFrom<Value<'static>>
501
        + PartialEq
502
        + Into<Constant>
503
        + core::fmt::Debug
504
        + Clone
505
        + 'static
506
        + One
507
        + Zero
508
        + CheckedShl
509
        + CheckedShr
510
        + Not<Output = Self>
511
        + Sum
512
    {
513
    }
514
    impl Numeric for i8 {}
515
    impl Numeric for i16 {}
516
    impl Numeric for i32 {}
517
    impl Numeric for i64 {}
518
    impl Numeric for BigInt256 {}
519
    impl Numeric for UnsignedBigInt {}
520
    fn big_endian_roundtrip<T: Numeric>(v: T, methods: &[SMethod]) {
521
        let type_args = std::iter::once((STypeVar::t(), T::stype())).collect();
522
        let to_be_bytes_expr: Expr = MethodCall::new(
523
            <T as Into<Constant>>::into(v.clone()).into(),
524
            methods
525
                .iter()
526
                .find(|method| method.method_id() == TO_BYTES_METHOD_ID)
527
                .unwrap()
528
                .clone(),
529
            vec![],
530
        )
531
        .unwrap()
532
        .into();
533
        let expr: Expr = MethodCall::with_type_args(
534
            Expr::Global,
535
            sglobal::FROM_BIGENDIAN_BYTES_METHOD
536
                .clone()
537
                .with_concrete_types(&type_args),
538
            vec![to_be_bytes_expr],
539
            type_args,
540
        )
541
        .unwrap()
542
        .into();
543
        assert_eq!(eval_out_wo_ctx::<T>(&expr), v);
544
    }
545
    fn bits_roundtrip<T: Numeric>(v: T, methods: &[SMethod]) {
546
        let to_bits: Expr = MethodCall::new(
547
            <T as Into<Constant>>::into(v.clone()).into(),
548
            methods
549
                .iter()
550
                .find(|method| method.method_id() == TO_BITS_METHOD_ID)
551
                .unwrap()
552
                .clone(),
553
            vec![],
554
        )
555
        .unwrap()
556
        .into();
557
        let res: Vec<bool> = eval_out_wo_ctx(&to_bits);
558
        // perform binary multiplication to make sure bits equals long
559
        let res = res
560
            .into_iter()
561
            .rev()
562
            .map(|bit| if bit { T::one() } else { T::zero() })
563
            .enumerate()
564
            .map(|(i, bit)| bit << i as u32)
565
            .sum::<T>();
566

567
        assert_eq!(res, v);
568
    }
569

570
    fn bitwise_inverse_test<T: Numeric>(v: T, methods: &[SMethod]) {
571
        let inverse_mc: Expr = MethodCall::new(
572
            <T as Into<Constant>>::into(v.clone()).into(),
573
            methods
574
                .iter()
575
                .find(|method| method.method_id() == BITWISE_INVERSE_METHOD_ID)
576
                .unwrap()
577
                .clone(),
578
            vec![],
579
        )
580
        .unwrap()
581
        .into();
582
        let res: T = eval_out_wo_ctx(&inverse_mc);
583
        assert_eq!(res, !v);
584
    }
585

586
    fn bitwise_or<T: Numeric>(v: T, rhs: T, methods: &[SMethod]) -> T {
587
        let or_mc: Expr = MethodCall::new(
588
            <T as Into<Constant>>::into(v.clone()).into(),
589
            methods
590
                .iter()
591
                .find(|method| method.method_id() == BITWISE_OR_METHOD_ID)
592
                .unwrap()
593
                .clone(),
594
            vec![<T as Into<Constant>>::into(rhs).into()],
595
        )
596
        .unwrap()
597
        .into();
598
        eval_out_wo_ctx(&or_mc)
599
    }
600

601
    fn bitwise_and<T: Numeric>(v: T, rhs: T, methods: &[SMethod]) -> T {
602
        let and_mc: Expr = MethodCall::new(
603
            <T as Into<Constant>>::into(v.clone()).into(),
604
            methods
605
                .iter()
606
                .find(|method| method.method_id() == BITWISE_AND_METHOD_ID)
607
                .unwrap()
608
                .clone(),
609
            vec![<T as Into<Constant>>::into(rhs).into()],
610
        )
611
        .unwrap()
612
        .into();
613
        eval_out_wo_ctx(&and_mc)
614
    }
615
    fn bitwise_xor<T: Numeric>(v: T, rhs: T, methods: &[SMethod]) -> T {
616
        let xor_mc: Expr = MethodCall::new(
617
            <T as Into<Constant>>::into(v.clone()).into(),
618
            methods
619
                .iter()
620
                .find(|method| method.method_id() == BITWISE_XOR_METHOD_ID)
621
                .unwrap()
622
                .clone(),
623
            vec![<T as Into<Constant>>::into(rhs).into()],
624
        )
625
        .unwrap()
626
        .into();
627
        eval_out_wo_ctx(&xor_mc)
628
    }
629
    fn shl<T: Numeric>(v: T, rhs: u32, methods: &[SMethod]) -> T {
630
        let shl_mc: Expr = MethodCall::new(
631
            <T as Into<Constant>>::into(v.clone()).into(),
632
            methods
633
                .iter()
634
                .find(|method| method.method_id() == SHIFT_LEFT_METHOD_ID)
635
                .unwrap()
636
                .clone(),
637
            vec![Constant::from(rhs as i32).into()],
638
        )
639
        .unwrap()
640
        .into();
641
        eval_out_wo_ctx(&shl_mc)
642
    }
643
    fn shr<T: Numeric>(v: T, rhs: u32, methods: &[SMethod]) -> T {
644
        let shr_mc: Expr = MethodCall::new(
645
            <T as Into<Constant>>::into(v.clone()).into(),
646
            methods
647
                .iter()
648
                .find(|method| method.method_id() == SHIFT_RIGHT_METHOD_ID)
649
                .unwrap()
650
                .clone(),
651
            vec![Constant::from(rhs as i32).into()],
652
        )
653
        .unwrap()
654
        .into();
655
        eval_out_wo_ctx(&shr_mc)
656
    }
657
    #[test]
658
    fn bitwise_or_byte() {
659
        assert_eq!(bitwise_or(127i8, -128i8, &snumeric::sbyte::METHODS), -1i8);
660
    }
661
    proptest! {
662
        #[test]
663
        fn byte_big_endian_roundtrip(byte in any::<i8>()) {
664
            big_endian_roundtrip(byte, &snumeric::sbyte::METHODS);
665
        }
666
        #[test]
667
        fn short_big_endian_roundtrip(short in any::<i16>()) {
668
            big_endian_roundtrip(short, &snumeric::sshort::METHODS);
669
        }
670
        #[test]
671
        fn int_big_endian_roundtrip(int in any::<i32>()) {
672
            big_endian_roundtrip(int, &snumeric::sint::METHODS);
673
        }
674
        #[test]
675
        fn long_big_endian_roundtrip(long in any::<i64>()) {
676
            big_endian_roundtrip(long, &snumeric::slong::METHODS);
677
        }
678
        #[test]
679
        fn bigint_big_endian_roundtrip(bigint in any::<BigInt256>()) {
680
            big_endian_roundtrip(bigint, &snumeric::sbigint::METHODS);
681
        }
682
        #[test]
683
        fn unsigned_bigint_big_endian_roundtrip(bigint in any::<UnsignedBigInt>()) {
684
            big_endian_roundtrip(bigint, &snumeric::sunsignedbigint::METHODS);
685
        }
686
        #[test]
687
        fn to_bits_byte(byte in any::<i8>()) {
688
            bits_roundtrip(byte, &snumeric::sbyte::METHODS);
689
        }
690
        #[test]
691
        fn to_bits_short(short in any::<i16>()) {
692
            bits_roundtrip(short, &snumeric::sshort::METHODS);
693
        }
694
        #[test]
695
        fn to_bits_int(int in any::<i32>()) {
696
            bits_roundtrip(int, &snumeric::sint::METHODS);
697
        }
698
        #[test]
699
        fn to_bits_long(long in any::<i64>()) {
700
            bits_roundtrip(long, &snumeric::slong::METHODS);
701
        }
702
        #[test]
703
        fn to_bits_bigint(bigint in any::<BigInt256>()) {
704
            bits_roundtrip(bigint, &snumeric::sbigint::METHODS);
705
        }
706
        #[test]
707
        fn to_bits_unsigned_bigint(bigint in any::<UnsignedBigInt>()) {
708
            bits_roundtrip(bigint, &snumeric::sunsignedbigint::METHODS);
709
        }
710
        #[test]
711
        fn inverse_byte(b in any::<i8>()) {
712
            bitwise_inverse_test(b, &snumeric::sbyte::METHODS);
713
        }
714
        #[test]
715
        fn inverse_short(short in any::<i16>()) {
716
            bitwise_inverse_test(short, &snumeric::sshort::METHODS);
717
        }
718
        #[test]
719
        fn inverse_int(int in any::<i32>()) {
720
            bitwise_inverse_test(int, &snumeric::sint::METHODS);
721
        }
722
        #[test]
723
        fn inverse_long(long in any::<i64>()) {
724
            bitwise_inverse_test(long, &snumeric::slong::METHODS);
725
        }
726
        #[test]
727
        fn inverse_bigint(bigint in any::<BigInt256>()) {
728
            bitwise_inverse_test(bigint, &snumeric::sbigint::METHODS);
729
        }
730
        #[test]
731
        fn inverse_unsigned_bigint(bigint in any::<UnsignedBigInt>()) {
732
            bitwise_inverse_test(bigint, &snumeric::sunsignedbigint::METHODS);
733
        }
734

735
        #[test]
736
        fn bitwise_or_byte_arbitrary(a in any::<i8>(), b in any::<i8>()) {
737
            assert_eq!(bitwise_or(a, b, &snumeric::sbyte::METHODS), a | b);
738
        }
739
        #[test]
740
        fn bitwise_or_short_arbitrary(a in any::<i16>(), b in any::<i16>()) {
741
            assert_eq!(bitwise_or(a, b, &snumeric::sshort::METHODS), a | b);
742
        }
743
        #[test]
744
        fn bitwise_or_int_arbitrary(a in any::<i32>(), b in any::<i32>()) {
745
            assert_eq!(bitwise_or(a, b, &snumeric::sint::METHODS), a | b);
746
        }
747
        #[test]
748
        fn bitwise_or_long_arbitrary(a in any::<i64>(), b in any::<i64>()) {
749
            assert_eq!(bitwise_or(a, b, &snumeric::slong::METHODS), a | b);
750
        }
751
        #[test]
752
        fn bitwise_or_bigint_arbitrary(a in any::<BigInt256>(), b in any::<BigInt256>()) {
753
            assert_eq!(bitwise_or(a, b, &snumeric::sbigint::METHODS), a | b);
754
        }
755
        #[test]
756
        fn bitwise_or_unsigned_bigint_arbitrary(a in any::<UnsignedBigInt>(), b in any::<UnsignedBigInt>()) {
757
            assert_eq!(bitwise_or(a, b, &snumeric::sunsignedbigint::METHODS), a | b);
758
        }
759
        #[test]
760
        fn bitwise_and_byte_arbitrary(a in any::<i8>(), b in any::<i8>()) {
761
            assert_eq!(bitwise_and(a, b, &snumeric::sbyte::METHODS), a & b);
762
        }
763
        #[test]
764
        fn bitwise_and_short_arbitrary(a in any::<i16>(), b in any::<i16>()) {
765
            assert_eq!(bitwise_and(a, b, &snumeric::sshort::METHODS), a & b);
766
        }
767
        #[test]
768
        fn bitwise_and_int_arbitrary(a in any::<i32>(), b in any::<i32>()) {
769
            assert_eq!(bitwise_and(a, b, &snumeric::sint::METHODS), a & b);
770
        }
771
        #[test]
772
        fn bitwise_and_long_arbitrary(a in any::<i64>(), b in any::<i64>()) {
773
            assert_eq!(bitwise_and(a, b, &snumeric::slong::METHODS), a & b);
774
        }
775
        #[test]
776
        fn bitwise_and_bigint_arbitrary(a in any::<BigInt256>(), b in any::<BigInt256>()) {
777
            assert_eq!(bitwise_and(a, b, &snumeric::sbigint::METHODS), a & b);
778
        }
779
        #[test]
780
        fn bitwise_and_unsigned_bigint_arbitrary(a in any::<UnsignedBigInt>(), b in any::<UnsignedBigInt>()) {
781
            assert_eq!(bitwise_and(a, b, &snumeric::sunsignedbigint::METHODS), a & b);
782
        }
783
        #[test]
784
        fn bitwise_xor_byte_arbitrary(a in any::<i8>(), b in any::<i8>()) {
785
            assert_eq!(bitwise_xor(a, b, &snumeric::sbyte::METHODS), a ^ b);
786
        }
787
        #[test]
788
        fn bitwise_xor_short_arbitrary(a in any::<i16>(), b in any::<i16>()) {
789
            assert_eq!(bitwise_xor(a, b, &snumeric::sshort::METHODS), a ^ b);
790
        }
791
        #[test]
792
        fn bitwise_xor_int_arbitrary(a in any::<i32>(), b in any::<i32>()) {
793
            assert_eq!(bitwise_xor(a, b, &snumeric::sint::METHODS), a ^ b);
794
        }
795
        #[test]
796
        fn bitwise_xor_long_arbitrary(a in any::<i64>(), b in any::<i64>()) {
797
            assert_eq!(bitwise_xor(a, b, &snumeric::slong::METHODS), a ^ b);
798
        }
799
        #[test]
800
        fn bitwise_xor_bigint_arbitrary(a in any::<BigInt256>(), b in any::<BigInt256>()) {
801
            assert_eq!(bitwise_xor(a, b, &snumeric::sbigint::METHODS), a ^ b);
802
        }
803
        #[test]
804
        fn bitwise_xor_unsigned_bigint_arbitrary(a in any::<UnsignedBigInt>(), b in any::<UnsignedBigInt>()) {
805
            assert_eq!(bitwise_xor(a, b, &snumeric::sunsignedbigint::METHODS), a ^ b);
806
        }
807
        #[test]
808
        fn shl_byte_arbitrary(a in any::<i8>(), shift in 0u32..8) {
809
            assert_eq!(shl(a, shift, &snumeric::sbyte::METHODS), a << shift);
810
        }
811
        #[test]
812
        fn shl_short_arbitrary(a in any::<i16>(), shift in 0u32..16) {
813
            assert_eq!(shl(a, shift, &snumeric::sshort::METHODS), a << shift);
814
        }
815
        #[test]
816
        fn shl_int_arbitrary(a in any::<i32>(), shift in 0u32..32) {
817
            assert_eq!(shl(a, shift, &snumeric::sint::METHODS), a << shift);
818
        }
819
        #[test]
820
        fn shl_long_arbitrary(a in any::<i64>(), shift in 0u32..64) {
821
            assert_eq!(shl(a, shift, &snumeric::slong::METHODS), a << shift);
822
        }
823
        #[test]
824
        fn shl_bigint_arbitrary(a in any::<BigInt256>(), shift in 0u32..256) {
825
            assert_eq!(shl(a, shift, &snumeric::sbigint::METHODS), a << shift);
826
        }
827
        #[test]
828
        fn shl_unsigned_bigint_arbitrary(a in any::<UnsignedBigInt>(), shift in 0u32..256) {
829
            assert_eq!(shl(a, shift, &snumeric::sunsignedbigint::METHODS), a << shift);
830
        }
831
        #[test]
832
        #[should_panic]
833
        fn shl_byte_arbitrary_invalid(a in any::<i8>(), shift in 8u32..) {
834
            assert_eq!(shl(a, shift, &snumeric::sbyte::METHODS), a << shift);
835
        }
836
        #[test]
837
        #[should_panic]
838
        fn shl_short_arbitrary_invalid(a in any::<i16>(), shift in 16u32..) {
839
            assert_eq!(shl(a, shift, &snumeric::sshort::METHODS), a << shift);
840
        }
841
        #[test]
842
        #[should_panic]
843
        fn shl_int_arbitrary_invalid(a in any::<i32>(), shift in 32u32..) {
844
            assert_eq!(shl(a, shift, &snumeric::sint::METHODS), a << shift);
845
        }
846
        #[test]
847
        #[should_panic]
848
        fn shl_long_arbitrary_invalid(a in any::<i64>(), shift in 64u32..) {
849
            assert_eq!(shl(a, shift, &snumeric::slong::METHODS), a << shift);
850
        }
851
        #[test]
852
        #[should_panic]
853
        fn shl_bigint_arbitrary_invalid(a in any::<BigInt256>(), shift in 256u32..) {
854
            assert_eq!(shl(a, shift, &snumeric::sbigint::METHODS), a << shift);
855
        }
856
        #[test]
857
        #[should_panic]
858
        fn shl_unsigned_bigint_arbitrary_invalid(a in any::<UnsignedBigInt>(), shift in 256u32..) {
859
            assert_eq!(shl(a, shift, &snumeric::sunsignedbigint::METHODS), a << shift);
860
        }
861
        #[test]
862
        fn shr_byte_arbitrary(a in any::<i8>(), shift in 0u32..8) {
863
            assert_eq!(shr(a, shift, &snumeric::sbyte::METHODS), a >> shift);
864
        }
865
        #[test]
866
        fn shr_short_arbitrary(a in any::<i16>(), shift in 0u32..16) {
867
            assert_eq!(shr(a, shift, &snumeric::sshort::METHODS), a >> shift);
868
        }
869
        #[test]
870
        fn shr_int_arbitrary(a in any::<i32>(), shift in 0u32..32) {
871
            assert_eq!(shr(a, shift, &snumeric::sint::METHODS), a >> shift);
872
        }
873
        #[test]
874
        fn shr_long_arbitrary(a in any::<i64>(), shift in 0u32..64) {
875
            assert_eq!(shr(a, shift, &snumeric::slong::METHODS), a >> shift);
876
        }
877
        #[test]
878
        fn shr_bigint_arbitrary(a in any::<BigInt256>(), shift in 0u32..256) {
879
            assert_eq!(shr(a, shift, &snumeric::sbigint::METHODS), a >> shift);
880
        }
881
        #[test]
882
        fn shr_unsigned_bigint_arbitrary(a in any::<UnsignedBigInt>(), shift in 0u32..256) {
883
            assert_eq!(shr(a, shift, &snumeric::sunsignedbigint::METHODS), a >> shift);
884
        }
885
        #[test]
886
        #[should_panic]
887
        fn shr_byte_arbitrary_invalid(a in any::<i8>(), shift in 8u32..) {
888
            assert_eq!(shr(a, shift, &snumeric::sbyte::METHODS), a >> shift);
889
        }
890
        #[test]
891
        #[should_panic]
892
        fn shr_short_arbitrary_invalid(a in any::<i16>(), shift in 16u32..) {
893
            assert_eq!(shr(a, shift, &snumeric::sshort::METHODS), a >> shift);
894
        }
895
        #[test]
896
        #[should_panic]
897
        fn shr_int_arbitrary_invalid(a in any::<i32>(), shift in 32u32..) {
898
            assert_eq!(shr(a, shift, &snumeric::sint::METHODS), a >> shift);
899
        }
900
        #[test]
901
        #[should_panic]
902
        fn shr_long_arbitrary_invalid(a in any::<i64>(), shift in 64u32..) {
903
            assert_eq!(shr(a, shift, &snumeric::slong::METHODS), a >> shift);
904
        }
905
        #[test]
906
        #[should_panic]
907
        fn shr_bigint_arbitrary_invalid(a in any::<BigInt256>(), shift in 256u32..) {
908
            assert_eq!(shr(a, shift, &snumeric::sbigint::METHODS), a >> shift);
909
        }
910
        #[test]
911
        #[should_panic]
912
        fn shr_unsigned_bigint_arbitrary_invalid(a in any::<UnsignedBigInt>(), shift in 256u32..) {
913
            assert_eq!(shr(a, shift, &snumeric::sunsignedbigint::METHODS), a >> shift);
914
        }
915

916
        #[test]
917
        fn eval_to_unsigned(signed in any::<BigInt256>()) {
918
            let mc: Expr = MethodCall::new(
919
                Constant::from(signed).into(),
920
                SMethod::new(STypeCompanion::SBigInt, TO_UNSIGNED_METHOD_DESC.clone()),
921
                vec![],
922
            )
923
            .unwrap()
924
            .into();
925
            let res = try_eval_out_wo_ctx::<UnsignedBigInt>(&mc);
926
            if signed < 0.into() {
927
                assert!(res.is_err())
928
            } else {
929
                assert_eq!(res.unwrap().to_string(), signed.to_string());
930
            }
931
        }
932
        #[test]
933
        fn eval_to_unsigned_mod(signed in any::<BigInt256>(), modulus in any::<UnsignedBigInt>()) {
934
            let mc: Expr = MethodCall::new(
935
                Constant::from(signed).into(),
936
                SMethod::new(STypeCompanion::SBigInt, TO_UNSIGNED_MOD_METHOD_DESC.clone()),
937
                vec![Constant::from(modulus).into()],
938
            )
939
            .unwrap()
940
            .into();
941
            assert_eq!(try_eval_out_wo_ctx::<UnsignedBigInt>(&mc).ok(), UnsignedBigInt::from_signed_mod(signed, modulus));
942
        }
943

944
        #[test]
945
        fn eval_to_signed(unsigned in any::<UnsignedBigInt>()) {
946
            let mc: Expr = MethodCall::new(
947
                Constant::from(unsigned).into(),
948
                SMethod::new(STypeCompanion::SUnsignedBigInt, TO_SIGNED_METHOD_DESC.clone()),
949
                vec![],
950
            )
951
            .unwrap()
952
            .into();
953
            let res = try_eval_out_wo_ctx::<BigInt256>(&mc);
954
            assert_eq!(res.ok(), BigInt256::try_from(unsigned).ok());
955
        }
956

957
        #[test]
958
        fn eval_mod_ops(a in any::<UnsignedBigInt>(), b in any::<UnsignedBigInt>(), modulus in any::<UnsignedBigInt>()) {
959
            assert_eq!(eval_modular_op(&MOD_METHOD_DESC, &[a, modulus]).ok(), a.checked_rem(&modulus));
960
            assert_eq!(eval_modular_op(&PLUS_MOD_METHOD_DESC, &[a, b, modulus]).ok(), a.checked_mod_add(b, modulus));
961
            assert_eq!(eval_modular_op(&SUBTRACT_MOD_METHOD_DESC, &[a, b, modulus]).ok(), a.checked_mod_sub(b, modulus));
962
            assert_eq!(eval_modular_op(&MULTIPLY_MOD_METHOD_DESC, &[a, b, modulus]).ok(), a.checked_mod_mul(b, modulus));
963
            assert_eq!(eval_modular_op(&MOD_INVERSE_METHOD_DESC, &[a, modulus]).ok(), a.mod_inv(modulus));
964
        }
965
    }
966
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc