snarkvm_circuit_types_integers/
add_wrapped.rs

1// Copyright (c) 2019-2025 Provable Inc.
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> AddWrapped<Self> for Integer<E, I> {
19    type Output = Self;
20
21    #[inline]
22    fn add_wrapped(&self, other: &Integer<E, I>) -> Self::Output {
23        // Determine the variable mode.
24        if self.is_constant() && other.is_constant() {
25            // Compute the sum and return the new constant.
26            witness!(|self, other| console::Integer::new(self.wrapping_add(&other)))
27        } else {
28            // Instead of adding the bits of `self` and `other` directly, the integers are
29            // converted into a field elements, and summed, before converting back to integers.
30            // Note: This is safe as the field is larger than the maximum integer type supported.
31            let sum = self.to_field() + other.to_field();
32
33            // Extract the integer bits from the field element, with a carry bit.
34            let mut bits_le = sum.to_lower_bits_le(I::BITS as usize + 1);
35            // Drop the carry bit as the operation is wrapped addition.
36            bits_le.pop();
37
38            // Return the sum of `self` and `other`.
39            Integer { bits_le, phantom: Default::default() }
40        }
41    }
42}
43
44impl<E: Environment, I: IntegerType> Metrics<dyn AddWrapped<Integer<E, I>, Output = Integer<E, I>>> for Integer<E, I> {
45    type Case = (Mode, Mode);
46
47    fn count(case: &Self::Case) -> Count {
48        match (case.0, case.1) {
49            (Mode::Constant, Mode::Constant) => Count::is(I::BITS, 0, 0, 0),
50            (_, _) => Count::is(0, 0, I::BITS + 1, I::BITS + 2),
51        }
52    }
53}
54
55impl<E: Environment, I: IntegerType> OutputMode<dyn AddWrapped<Integer<E, I>, Output = Integer<E, I>>>
56    for Integer<E, I>
57{
58    type Case = (Mode, Mode);
59
60    fn output_mode(case: &Self::Case) -> Mode {
61        match (case.0, case.1) {
62            (Mode::Constant, Mode::Constant) => Mode::Constant,
63            (_, _) => Mode::Private,
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use snarkvm_circuit_environment::Circuit;
72
73    use core::ops::RangeInclusive;
74
75    const ITERATIONS: u64 = 128;
76
77    fn check_add<I: IntegerType>(
78        name: &str,
79        first: console::Integer<<Circuit as Environment>::Network, I>,
80        second: console::Integer<<Circuit as Environment>::Network, I>,
81        mode_a: Mode,
82        mode_b: Mode,
83    ) {
84        let a = Integer::<Circuit, I>::new(mode_a, first);
85        let b = Integer::new(mode_b, second);
86        let expected = first.wrapping_add(&second);
87        Circuit::scope(name, || {
88            let candidate = a.add_wrapped(&b);
89            assert_eq!(expected, *candidate.eject_value());
90            assert_eq!(console::Integer::new(expected), candidate.eject_value());
91            assert_count!(AddWrapped(Integer<I>, Integer<I>) => Integer<I>, &(mode_a, mode_b));
92            assert_output_mode!(AddWrapped(Integer<I>, Integer<I>) => Integer<I>, &(mode_a, mode_b), candidate);
93        });
94        Circuit::reset();
95    }
96
97    fn run_test<I: IntegerType>(mode_a: Mode, mode_b: Mode) {
98        let mut rng = TestRng::default();
99
100        for i in 0..ITERATIONS {
101            let first = Uniform::rand(&mut rng);
102            let second = Uniform::rand(&mut rng);
103
104            let name = format!("Add: {mode_a} + {mode_b} {i}");
105            check_add::<I>(&name, first, second, mode_a, mode_b);
106            check_add::<I>(&name, second, first, mode_a, mode_b); // Commute the operation.
107        }
108
109        // Overflow
110        check_add::<I>("MAX + 1", console::Integer::MAX, console::Integer::one(), mode_a, mode_b);
111        check_add::<I>("1 + MAX", console::Integer::one(), console::Integer::MAX, mode_a, mode_b);
112
113        // Underflow
114        if I::is_signed() {
115            check_add::<I>("MIN + (-1)", console::Integer::MIN, -console::Integer::one(), mode_a, mode_b);
116            check_add::<I>("-1 + MIN", -console::Integer::one(), console::Integer::MIN, mode_a, mode_b);
117        }
118    }
119
120    fn run_exhaustive_test<I: IntegerType>(mode_a: Mode, mode_b: Mode)
121    where
122        RangeInclusive<I>: Iterator<Item = I>,
123    {
124        for first in I::MIN..=I::MAX {
125            for second in I::MIN..=I::MAX {
126                let first = console::Integer::<_, I>::new(first);
127                let second = console::Integer::<_, I>::new(second);
128
129                let name = format!("Add: ({first} + {second})");
130                check_add::<I>(&name, first, second, mode_a, mode_b);
131            }
132        }
133    }
134
135    test_integer_binary!(run_test, i8, plus);
136    test_integer_binary!(run_test, i16, plus);
137    test_integer_binary!(run_test, i32, plus);
138    test_integer_binary!(run_test, i64, plus);
139    test_integer_binary!(run_test, i128, plus);
140
141    test_integer_binary!(run_test, u8, plus);
142    test_integer_binary!(run_test, u16, plus);
143    test_integer_binary!(run_test, u32, plus);
144    test_integer_binary!(run_test, u64, plus);
145    test_integer_binary!(run_test, u128, plus);
146
147    test_integer_binary!(#[ignore], run_exhaustive_test, u8, plus, exhaustive);
148    test_integer_binary!(#[ignore], run_exhaustive_test, i8, plus, exhaustive);
149}