Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, the SerenityOS developers.
3
 * Copyright (c) 2022, David Tuin <davidot@serenityos.org>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include "SignedBigInteger.h"
9
#include <AK/StringBuilder.h>
10
#include <math.h>
11
12
namespace Crypto {
13
14
SignedBigInteger::SignedBigInteger(double value)
15
0
    : m_sign(value < 0.0)
16
0
    , m_unsigned_data(fabs(value))
17
0
{
18
0
}
19
20
SignedBigInteger SignedBigInteger::import_data(u8 const* ptr, size_t length)
21
0
{
22
0
    bool sign = *ptr;
23
0
    auto unsigned_data = UnsignedBigInteger::import_data(ptr + 1, length - 1);
24
0
    return { move(unsigned_data), sign };
25
0
}
26
27
size_t SignedBigInteger::export_data(Bytes data, bool remove_leading_zeros) const
28
0
{
29
    // FIXME: Support this:
30
    //        m <0XX> -> m <XX> (if remove_leading_zeros)
31
0
    VERIFY(!remove_leading_zeros);
32
33
0
    data[0] = m_sign;
34
0
    auto bytes_view = data.slice(1, data.size() - 1);
35
0
    return m_unsigned_data.export_data(bytes_view, remove_leading_zeros) + 1;
36
0
}
37
38
ErrorOr<SignedBigInteger> SignedBigInteger::from_base(u16 N, StringView str)
39
160
{
40
160
    auto sign = false;
41
160
    if (str.length() > 1) {
42
160
        auto maybe_sign = str[0];
43
160
        if (maybe_sign == '-') {
44
80
            str = str.substring_view(1);
45
80
            sign = true;
46
80
        }
47
160
        if (maybe_sign == '+')
48
0
            str = str.substring_view(1);
49
160
    }
50
160
    auto unsigned_data = TRY(UnsignedBigInteger::from_base(N, str));
51
160
    return SignedBigInteger { move(unsigned_data), sign };
52
160
}
53
54
ErrorOr<String> SignedBigInteger::to_base(u16 N) const
55
0
{
56
0
    StringBuilder builder;
57
58
0
    if (m_sign)
59
0
        TRY(builder.try_append('-'));
60
61
0
    auto unsigned_as_base = TRY(m_unsigned_data.to_base(N));
62
0
    TRY(builder.try_append(unsigned_as_base.bytes_as_string_view()));
63
64
0
    return builder.to_string();
65
0
}
66
67
ByteString SignedBigInteger::to_base_deprecated(u16 N) const
68
0
{
69
0
    return MUST(to_base(N)).to_byte_string();
70
0
}
71
72
u64 SignedBigInteger::to_u64() const
73
0
{
74
0
    u64 unsigned_value = m_unsigned_data.to_u64();
75
0
    if (!m_sign)
76
0
        return unsigned_value;
77
0
    return ~(unsigned_value - 1); // equivalent to `-unsigned_value`, but doesn't trigger UBSAN
78
0
}
79
80
double SignedBigInteger::to_double(UnsignedBigInteger::RoundingMode rounding_mode) const
81
0
{
82
0
    double unsigned_value = m_unsigned_data.to_double(rounding_mode);
83
0
    if (!m_sign)
84
0
        return unsigned_value;
85
86
0
    VERIFY(!is_zero());
87
0
    return -unsigned_value;
88
0
}
89
90
FLATTEN SignedBigInteger SignedBigInteger::plus(SignedBigInteger const& other) const
91
0
{
92
    // If both are of the same sign, just add the unsigned data and return.
93
0
    if (m_sign == other.m_sign)
94
0
        return { other.m_unsigned_data.plus(m_unsigned_data), m_sign };
95
96
    // One value is signed while the other is not.
97
0
    return m_sign ? other.minus(this->m_unsigned_data) : minus(other.m_unsigned_data);
98
0
}
99
100
FLATTEN SignedBigInteger SignedBigInteger::minus(SignedBigInteger const& other) const
101
0
{
102
    // If the signs are different, convert the op to an addition.
103
0
    if (m_sign != other.m_sign) {
104
        // -x - y = - (x + y)
105
        // x - -y = (x + y)
106
0
        SignedBigInteger result { other.m_unsigned_data.plus(this->m_unsigned_data) };
107
0
        if (m_sign)
108
0
            result.negate();
109
0
        return result;
110
0
    }
111
112
0
    if (!m_sign) {
113
        // Both operands are positive.
114
        // x - y = - (y - x)
115
0
        if (m_unsigned_data < other.m_unsigned_data) {
116
            // The result will be negative.
117
0
            return { other.m_unsigned_data.minus(m_unsigned_data), true };
118
0
        }
119
120
        // The result will be either zero, or positive.
121
0
        return SignedBigInteger { m_unsigned_data.minus(other.m_unsigned_data) };
122
0
    }
123
124
    // Both operands are negative.
125
    // -x - -y = y - x
126
0
    if (m_unsigned_data < other.m_unsigned_data) {
127
        // The result will be positive.
128
0
        return SignedBigInteger { other.m_unsigned_data.minus(m_unsigned_data) };
129
0
    }
130
    // y - x = - (x - y)
131
0
    if (m_unsigned_data > other.m_unsigned_data) {
132
        // The result will be negative.
133
0
        return SignedBigInteger { m_unsigned_data.minus(other.m_unsigned_data), true };
134
0
    }
135
    // Both operands have the same magnitude, the result is positive zero.
136
0
    return SignedBigInteger { 0 };
137
0
}
138
139
FLATTEN SignedBigInteger SignedBigInteger::plus(UnsignedBigInteger const& other) const
140
0
{
141
0
    if (m_sign) {
142
0
        if (other < m_unsigned_data)
143
0
            return { m_unsigned_data.minus(other), true };
144
145
0
        return { other.minus(m_unsigned_data), false };
146
0
    }
147
148
0
    return { m_unsigned_data.plus(other), false };
149
0
}
150
151
FLATTEN SignedBigInteger SignedBigInteger::minus(UnsignedBigInteger const& other) const
152
0
{
153
0
    if (m_sign)
154
0
        return { m_unsigned_data.plus(m_unsigned_data), true };
155
156
0
    if (other < m_unsigned_data)
157
0
        return { m_unsigned_data.minus(other), false };
158
159
0
    return { other.minus(m_unsigned_data), true };
160
0
}
161
162
FLATTEN SignedBigInteger SignedBigInteger::bitwise_not() const
163
0
{
164
    // Bitwise operators assume two's complement, while SignedBigInteger uses sign-magnitude.
165
    // In two's complement, -x := ~x + 1.
166
    // Hence, ~x == -x -1 == -(x + 1).
167
0
    SignedBigInteger result = plus(SignedBigInteger { 1 });
168
0
    result.negate();
169
0
    return result;
170
0
}
171
172
FLATTEN SignedBigInteger SignedBigInteger::multiplied_by(UnsignedBigInteger const& other) const
173
0
{
174
0
    return { unsigned_value().multiplied_by(other), m_sign };
175
0
}
176
177
FLATTEN SignedDivisionResult SignedBigInteger::divided_by(UnsignedBigInteger const& divisor) const
178
0
{
179
0
    auto division_result = unsigned_value().divided_by(divisor);
180
0
    return {
181
0
        { move(division_result.quotient), m_sign },
182
0
        { move(division_result.remainder), m_sign },
183
0
    };
184
0
}
185
186
FLATTEN SignedBigInteger SignedBigInteger::bitwise_or(SignedBigInteger const& other) const
187
0
{
188
    // See bitwise_and() for derivations.
189
0
    if (!is_negative() && !other.is_negative())
190
0
        return { unsigned_value().bitwise_or(other.unsigned_value()), false };
191
192
    // -A | B == (~A + 1) | B == ~(A - 1) | B. The result is negative, so need to two's complement at the end to move the sign into the m_sign field.
193
    // That can be simplified to:
194
    //   -(-A | B) == ~(~(A - 1) | B) + 1 = (A - 1) & ~B + 1
195
    // This saves one ~.
196
0
    if (is_negative() && !other.is_negative()) {
197
0
        size_t index = unsigned_value().one_based_index_of_highest_set_bit();
198
0
        return { unsigned_value().minus(1).bitwise_and(other.unsigned_value().bitwise_not_fill_to_one_based_index(index)).plus(1), true };
199
0
    }
200
201
    // -(A | -B) == ~A & (B - 1) + 1
202
0
    if (!is_negative() && other.is_negative()) {
203
0
        size_t index = other.unsigned_value().one_based_index_of_highest_set_bit();
204
0
        return { unsigned_value().bitwise_not_fill_to_one_based_index(index).bitwise_and(other.unsigned_value().minus(1)).plus(1), true };
205
0
    }
206
207
0
    return { unsigned_value().minus(1).bitwise_and(other.unsigned_value().minus(1)).plus(1), true };
208
0
}
209
210
FLATTEN SignedBigInteger SignedBigInteger::bitwise_and(SignedBigInteger const& other) const
211
0
{
212
0
    if (!is_negative() && !other.is_negative())
213
0
        return { unsigned_value().bitwise_and(other.unsigned_value()), false };
214
215
    // These two just use that -x == ~x + 1 (see below).
216
217
    // -A & B == (~A + 1) & B.
218
0
    if (is_negative() && !other.is_negative()) {
219
0
        size_t index = other.unsigned_value().one_based_index_of_highest_set_bit();
220
0
        return { unsigned_value().bitwise_not_fill_to_one_based_index(index).plus(1).bitwise_and(other.unsigned_value()), false };
221
0
    }
222
223
    // A & -B == A & (~B + 1).
224
0
    if (!is_negative() && other.is_negative()) {
225
0
        size_t index = unsigned_value().one_based_index_of_highest_set_bit();
226
0
        return { unsigned_value().bitwise_and(other.unsigned_value().bitwise_not_fill_to_one_based_index(index).plus(1)), false };
227
0
    }
228
229
    // Both numbers are negative.
230
    // x + ~x == 0xff...ff, up to however many bits x is wide.
231
    // In two's complement, x + ~x + 1 == 0 since the 1 in the overflowing bit position is masked out.
232
    // Rearranging terms, ~x = -x - 1 (eq1).
233
    // Substituting x = y - 1, ~(y - 1) == -(y - 1) - 1 == -y +1 -1 == -y, or ~(y - 1) == -y (eq2).
234
    // Since both numbers are negative, we want to compute -A & -B.
235
    // Per (eq2):
236
    //   -A & -B == ~(A - 1) & ~(B - 1)
237
    // Inverting both sides:
238
    //   ~(-A & -B) == ~(~(A - 1) & ~(B - 1)) == ~~(A - 1) | ~~(B - 1) == (A - 1) | (B - 1).
239
    // Applying (q1) on the LHS:
240
    //   -(-A & -B) - 1 == (A - 1) | (B - 1)
241
    // Adding 1 on both sides and then multiplying both sides by -1:
242
    //   -A & -B == -( (A - 1) | (B - 1) + 1)
243
    // So we can compute the bitwise and by returning a negative number with magnitude (A - 1) | (B - 1) + 1.
244
    // This is better than the naive (~A + 1) & (~B + 1) because it needs just one O(n) scan for the or instead of 2 for the ~s.
245
0
    return { unsigned_value().minus(1).bitwise_or(other.unsigned_value().minus(1)).plus(1), true };
246
0
}
247
248
FLATTEN SignedBigInteger SignedBigInteger::bitwise_xor(SignedBigInteger const& other) const
249
0
{
250
0
    return bitwise_or(other).minus(bitwise_and(other));
251
0
}
252
253
bool SignedBigInteger::operator==(UnsignedBigInteger const& other) const
254
0
{
255
0
    if (m_sign && m_unsigned_data != 0)
256
0
        return false;
257
0
    return m_unsigned_data == other;
258
0
}
259
260
bool SignedBigInteger::operator!=(UnsignedBigInteger const& other) const
261
0
{
262
0
    if (m_sign)
263
0
        return true;
264
0
    return m_unsigned_data != other;
265
0
}
266
267
bool SignedBigInteger::operator<(UnsignedBigInteger const& other) const
268
0
{
269
0
    if (m_sign)
270
0
        return true;
271
0
    return m_unsigned_data < other;
272
0
}
273
274
bool SignedBigInteger::operator>(UnsignedBigInteger const& other) const
275
0
{
276
0
    return *this != other && !(*this < other);
277
0
}
278
279
FLATTEN SignedBigInteger SignedBigInteger::shift_left(size_t num_bits) const
280
0
{
281
0
    return SignedBigInteger { m_unsigned_data.shift_left(num_bits), m_sign };
282
0
}
283
284
FLATTEN SignedBigInteger SignedBigInteger::shift_right(size_t num_bits) const
285
0
{
286
0
    return SignedBigInteger { m_unsigned_data.shift_right(num_bits), m_sign };
287
0
}
288
289
FLATTEN SignedBigInteger SignedBigInteger::multiplied_by(SignedBigInteger const& other) const
290
0
{
291
0
    bool result_sign = m_sign ^ other.m_sign;
292
0
    return { m_unsigned_data.multiplied_by(other.m_unsigned_data), result_sign };
293
0
}
294
295
FLATTEN SignedDivisionResult SignedBigInteger::divided_by(SignedBigInteger const& divisor) const
296
0
{
297
    // Aa / Bb -> (A^B)q, Ar
298
0
    bool result_sign = m_sign ^ divisor.m_sign;
299
0
    auto unsigned_division_result = m_unsigned_data.divided_by(divisor.m_unsigned_data);
300
0
    return {
301
0
        { move(unsigned_division_result.quotient), result_sign },
302
0
        { move(unsigned_division_result.remainder), m_sign }
303
0
    };
304
0
}
305
306
FLATTEN SignedBigInteger SignedBigInteger::negated_value() const
307
0
{
308
0
    auto result { *this };
309
0
    result.negate();
310
0
    return result;
311
0
}
312
313
u32 SignedBigInteger::hash() const
314
0
{
315
0
    return m_unsigned_data.hash() * (1 - (2 * m_sign));
316
0
}
317
318
void SignedBigInteger::set_bit_inplace(size_t bit_index)
319
0
{
320
0
    m_unsigned_data.set_bit_inplace(bit_index);
321
0
}
322
323
bool SignedBigInteger::operator==(SignedBigInteger const& other) const
324
0
{
325
0
    if (is_invalid() != other.is_invalid())
326
0
        return false;
327
328
0
    if (m_unsigned_data == 0 && other.m_unsigned_data == 0)
329
0
        return true;
330
331
0
    return m_sign == other.m_sign && m_unsigned_data == other.m_unsigned_data;
332
0
}
333
334
bool SignedBigInteger::operator!=(SignedBigInteger const& other) const
335
0
{
336
0
    return !(*this == other);
337
0
}
338
339
bool SignedBigInteger::operator<(SignedBigInteger const& other) const
340
0
{
341
0
    if (m_sign ^ other.m_sign)
342
0
        return m_sign;
343
344
0
    if (m_sign)
345
0
        return other.m_unsigned_data < m_unsigned_data;
346
347
0
    return m_unsigned_data < other.m_unsigned_data;
348
0
}
349
350
bool SignedBigInteger::operator<=(SignedBigInteger const& other) const
351
0
{
352
0
    return *this < other || *this == other;
353
0
}
354
355
bool SignedBigInteger::operator>(SignedBigInteger const& other) const
356
0
{
357
0
    return *this != other && !(*this < other);
358
0
}
359
360
bool SignedBigInteger::operator>=(SignedBigInteger const& other) const
361
0
{
362
0
    return !(*this < other);
363
0
}
364
365
UnsignedBigInteger::CompareResult SignedBigInteger::compare_to_double(double value) const
366
0
{
367
0
    bool bigint_is_negative = m_sign;
368
369
0
    bool value_is_negative = value < 0;
370
371
0
    if (value_is_negative != bigint_is_negative)
372
0
        return bigint_is_negative ? UnsignedBigInteger::CompareResult::DoubleGreaterThanBigInt : UnsignedBigInteger::CompareResult::DoubleLessThanBigInt;
373
374
    // Now both bigint and value have the same sign, so let's compare our magnitudes.
375
0
    auto magnitudes_compare_result = m_unsigned_data.compare_to_double(fabs(value));
376
377
    // If our mangnitudes are euqal, then we're equal.
378
0
    if (magnitudes_compare_result == UnsignedBigInteger::CompareResult::DoubleEqualsBigInt)
379
0
        return UnsignedBigInteger::CompareResult::DoubleEqualsBigInt;
380
381
    // If we're negative, revert the comparison result, otherwise return the same result.
382
0
    if (value_is_negative) {
383
0
        if (magnitudes_compare_result == UnsignedBigInteger::CompareResult::DoubleLessThanBigInt)
384
0
            return UnsignedBigInteger::CompareResult::DoubleGreaterThanBigInt;
385
0
        else
386
0
            return UnsignedBigInteger::CompareResult::DoubleLessThanBigInt;
387
0
    } else {
388
0
        return magnitudes_compare_result;
389
0
    }
390
0
}
391
392
}
393
394
ErrorOr<void> AK::Formatter<Crypto::SignedBigInteger>::format(FormatBuilder& fmtbuilder, Crypto::SignedBigInteger const& value)
395
0
{
396
0
    if (value.is_negative())
397
0
        TRY(fmtbuilder.put_string("-"sv));
398
0
    return Formatter<Crypto::UnsignedBigInteger>::format(fmtbuilder, value.unsigned_value());
399
0
}