Coverage Report

Created: 2026-07-25 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibJS/Runtime/Intl/MathematicalValue.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <LibJS/Runtime/AbstractOperations.h>
8
#include <LibJS/Runtime/GlobalObject.h>
9
#include <LibJS/Runtime/Intl/MathematicalValue.h>
10
#include <math.h>
11
12
namespace JS::Intl {
13
14
bool MathematicalValue::is_number() const
15
0
{
16
0
    return m_value.has<double>();
17
0
}
18
19
double MathematicalValue::as_number() const
20
0
{
21
0
    VERIFY(is_number());
22
0
    return m_value.get<double>();
23
0
}
24
25
bool MathematicalValue::is_bigint() const
26
0
{
27
0
    return m_value.has<Crypto::SignedBigInteger>();
28
0
}
29
30
Crypto::SignedBigInteger const& MathematicalValue::as_bigint() const
31
0
{
32
0
    VERIFY(is_bigint());
33
0
    return m_value.get<Crypto::SignedBigInteger>();
34
0
}
35
36
bool MathematicalValue::is_mathematical_value() const
37
0
{
38
0
    return is_number() || is_bigint();
39
0
}
40
41
bool MathematicalValue::is_positive_infinity() const
42
0
{
43
0
    if (is_mathematical_value())
44
0
        return false;
45
0
    return m_value.get<Symbol>() == Symbol::PositiveInfinity;
46
0
}
47
48
bool MathematicalValue::is_negative_infinity() const
49
0
{
50
0
    if (is_mathematical_value())
51
0
        return false;
52
0
    return m_value.get<Symbol>() == Symbol::NegativeInfinity;
53
0
}
54
55
bool MathematicalValue::is_negative_zero() const
56
0
{
57
0
    if (is_mathematical_value())
58
0
        return false;
59
0
    return m_value.get<Symbol>() == Symbol::NegativeZero;
60
0
}
61
62
bool MathematicalValue::is_nan() const
63
0
{
64
0
    if (is_mathematical_value())
65
0
        return false;
66
0
    return m_value.get<Symbol>() == Symbol::NotANumber;
67
0
}
68
69
void MathematicalValue::negate()
70
0
{
71
0
    m_value.visit(
72
0
        [](double& value) {
73
0
            VERIFY(value != 0.0);
74
0
            value *= -1.0;
75
0
        },
76
0
        [](Crypto::SignedBigInteger& value) { value.negate(); },
77
0
        [](auto) { VERIFY_NOT_REACHED(); });
78
0
}
79
80
MathematicalValue MathematicalValue::plus(Checked<i32> addition) const
81
0
{
82
0
    return m_value.visit(
83
0
        [&](double value) {
84
0
            return MathematicalValue { value + addition.value() };
85
0
        },
86
0
        [&](Crypto::SignedBigInteger const& value) {
87
0
            return MathematicalValue { value.plus(Crypto::SignedBigInteger { addition.value() }) };
88
0
        },
89
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
90
0
}
91
92
MathematicalValue MathematicalValue::plus(MathematicalValue const& addition) const
93
0
{
94
0
    return m_value.visit(
95
0
        [&](double value) {
96
0
            return MathematicalValue { value + addition.as_number() };
97
0
        },
98
0
        [&](Crypto::SignedBigInteger const& value) {
99
0
            return MathematicalValue { value.plus(addition.as_bigint()) };
100
0
        },
101
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
102
0
}
103
104
MathematicalValue MathematicalValue::minus(Checked<i32> subtraction) const
105
0
{
106
0
    return m_value.visit(
107
0
        [&](double value) {
108
0
            return MathematicalValue { value - subtraction.value() };
109
0
        },
110
0
        [&](Crypto::SignedBigInteger const& value) {
111
0
            return MathematicalValue { value.minus(Crypto::SignedBigInteger { subtraction.value() }) };
112
0
        },
113
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
114
0
}
115
116
MathematicalValue MathematicalValue::minus(MathematicalValue const& subtraction) const
117
0
{
118
0
    return m_value.visit(
119
0
        [&](double value) {
120
0
            return MathematicalValue { value - subtraction.as_number() };
121
0
        },
122
0
        [&](Crypto::SignedBigInteger const& value) {
123
0
            return MathematicalValue { value.minus(subtraction.as_bigint()) };
124
0
        },
125
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
126
0
}
127
128
MathematicalValue MathematicalValue::multiplied_by(Checked<i32> multiplier) const
129
0
{
130
0
    return m_value.visit(
131
0
        [&](double value) {
132
0
            return MathematicalValue { value * multiplier.value() };
133
0
        },
134
0
        [&](Crypto::SignedBigInteger const& value) {
135
0
            return MathematicalValue { value.multiplied_by(Crypto::SignedBigInteger { multiplier.value() }) };
136
0
        },
137
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
138
0
}
139
140
MathematicalValue MathematicalValue::multiplied_by(MathematicalValue const& multiplier) const
141
0
{
142
0
    return m_value.visit(
143
0
        [&](double value) {
144
0
            return MathematicalValue { value * multiplier.as_number() };
145
0
        },
146
0
        [&](Crypto::SignedBigInteger const& value) {
147
0
            return MathematicalValue { value.multiplied_by(multiplier.as_bigint()) };
148
0
        },
149
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
150
0
}
151
152
MathematicalValue MathematicalValue::divided_by(Checked<i32> divisor) const
153
0
{
154
0
    return m_value.visit(
155
0
        [&](double value) {
156
0
            return MathematicalValue { value / divisor.value() };
157
0
        },
158
0
        [&](Crypto::SignedBigInteger const& value) {
159
0
            return MathematicalValue { value.divided_by(Crypto::SignedBigInteger { divisor.value() }).quotient };
160
0
        },
161
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
162
0
}
163
164
MathematicalValue MathematicalValue::divided_by(MathematicalValue const& divisor) const
165
0
{
166
0
    return m_value.visit(
167
0
        [&](double value) {
168
0
            return MathematicalValue { value / divisor.as_number() };
169
0
        },
170
0
        [&](Crypto::SignedBigInteger const& value) {
171
0
            return MathematicalValue { value.divided_by(divisor.as_bigint()).quotient };
172
0
        },
173
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
174
0
}
175
176
static Crypto::SignedBigInteger bigint_power(Checked<i32> exponent)
177
0
{
178
0
    VERIFY(exponent >= 0);
179
180
0
    static auto base = Crypto::SignedBigInteger { 10 };
181
0
    auto result = Crypto::SignedBigInteger { 1 };
182
183
0
    for (i32 i = 0; i < exponent; ++i)
184
0
        result = result.multiplied_by(base);
185
186
0
    return result;
187
0
}
188
189
MathematicalValue MathematicalValue::multiplied_by_power(Checked<i32> exponent) const
190
0
{
191
0
    return m_value.visit(
192
0
        [&](double value) {
193
0
            return MathematicalValue { value * pow(10, exponent.value()) };
194
0
        },
195
0
        [&](Crypto::SignedBigInteger const& value) {
196
0
            if (exponent < 0)
197
0
                return MathematicalValue { value.divided_by(bigint_power(-exponent.value())).quotient };
198
0
            return MathematicalValue { value.multiplied_by(bigint_power(exponent)) };
199
0
        },
200
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
201
0
}
202
203
MathematicalValue MathematicalValue::divided_by_power(Checked<i32> exponent) const
204
0
{
205
0
    return m_value.visit(
206
0
        [&](double value) {
207
0
            if (exponent < 0)
208
0
                return MathematicalValue { value * pow(10, -exponent.value()) };
209
0
            return MathematicalValue { value / pow(10, exponent.value()) };
210
0
        },
211
0
        [&](Crypto::SignedBigInteger const& value) {
212
0
            if (exponent < 0)
213
0
                return MathematicalValue { value.multiplied_by(bigint_power(-exponent.value())) };
214
0
            return MathematicalValue { value.divided_by(bigint_power(exponent)).quotient };
215
0
        },
216
0
        [](auto) -> MathematicalValue { VERIFY_NOT_REACHED(); });
217
0
}
218
219
bool MathematicalValue::modulo_is_zero(Checked<i32> mod) const
220
0
{
221
0
    return m_value.visit(
222
0
        [&](double value) {
223
0
            auto result = MathematicalValue { modulo(value, mod.value()) };
224
0
            return result.is_equal_to(MathematicalValue { 0.0 });
225
0
        },
226
0
        [&](Crypto::SignedBigInteger const& value) {
227
0
            return modulo(value, Crypto::SignedBigInteger { mod.value() }).is_zero();
228
0
        },
229
0
        [](auto) -> bool { VERIFY_NOT_REACHED(); });
230
0
}
231
232
int MathematicalValue::logarithmic_floor() const
233
0
{
234
0
    return m_value.visit(
235
0
        [](double value) {
236
0
            return static_cast<int>(floor(log10(value)));
237
0
        },
238
0
        [&](Crypto::SignedBigInteger const& value) {
239
            // FIXME: Can we do this without string conversion?
240
0
            auto value_as_string = MUST(value.to_base(10));
241
0
            return static_cast<int>(value_as_string.bytes_as_string_view().length() - 1);
242
0
        },
243
0
        [](auto) -> int { VERIFY_NOT_REACHED(); });
244
0
}
245
246
bool MathematicalValue::is_equal_to(MathematicalValue const& other) const
247
0
{
248
0
    return m_value.visit(
249
0
        [&](double value) {
250
0
            static constexpr double epsilon = 5e-14;
251
0
            return fabs(value - other.as_number()) < epsilon;
252
0
        },
253
0
        [&](Crypto::SignedBigInteger const& value) {
254
0
            return value == other.as_bigint();
255
0
        },
256
0
        [](auto) -> bool { VERIFY_NOT_REACHED(); });
257
0
}
258
259
bool MathematicalValue::is_less_than(MathematicalValue const& other) const
260
0
{
261
0
    return m_value.visit(
262
0
        [&](double value) {
263
0
            if (is_equal_to(other))
264
0
                return false;
265
0
            return value < other.as_number();
266
0
        },
267
0
        [&](Crypto::SignedBigInteger const& value) {
268
0
            return value < other.as_bigint();
269
0
        },
270
0
        [](auto) -> bool { VERIFY_NOT_REACHED(); });
271
0
}
272
273
bool MathematicalValue::is_negative() const
274
0
{
275
0
    return m_value.visit(
276
0
        [](double value) { return value < 0.0; },
277
0
        [](Crypto::SignedBigInteger const& value) { return value.is_negative(); },
278
0
        [](Symbol symbol) { return symbol == Symbol::NegativeInfinity; });
279
0
}
280
281
bool MathematicalValue::is_positive() const
282
0
{
283
0
    return m_value.visit(
284
0
        [](double value) { return value > 0.0; },
285
0
        [](Crypto::SignedBigInteger const& value) { return !value.is_zero() && !value.is_negative(); },
286
0
        [](Symbol symbol) { return symbol == Symbol::PositiveInfinity; });
287
0
}
288
289
bool MathematicalValue::is_zero() const
290
0
{
291
0
    return m_value.visit(
292
0
        [&](double value) { return value == 0.0; },
293
0
        [](Crypto::SignedBigInteger const& value) { return value.is_zero(); },
294
0
        [](auto) { return false; });
295
0
}
296
297
String MathematicalValue::to_string() const
298
0
{
299
0
    return m_value.visit(
300
0
        [&](double value) {
301
0
            return number_to_string(value, NumberToStringMode::WithoutExponent);
302
0
        },
303
0
        [&](Crypto::SignedBigInteger const& value) {
304
0
            return MUST(value.to_base(10));
305
0
        },
306
0
        [&](auto) -> String { VERIFY_NOT_REACHED(); });
307
0
}
308
309
Value MathematicalValue::to_value(VM& vm) const
310
0
{
311
0
    return m_value.visit(
312
0
        [](double value) {
313
0
            return Value(value);
314
0
        },
315
0
        [&](Crypto::SignedBigInteger const& value) {
316
0
            return Value(BigInt::create(vm, value));
317
0
        },
318
0
        [](auto symbol) {
319
0
            switch (symbol) {
320
0
            case Symbol::PositiveInfinity:
321
0
                return js_infinity();
322
0
            case Symbol::NegativeInfinity:
323
0
                return js_negative_infinity();
324
0
            case Symbol::NegativeZero:
325
0
                return Value(-0.0);
326
0
            case Symbol::NotANumber:
327
0
                return js_nan();
328
0
            }
329
330
0
            VERIFY_NOT_REACHED();
331
0
        });
332
0
}
333
334
MathematicalValue::ValueType MathematicalValue::value_from_number(double number)
335
0
{
336
0
    Value value(number);
337
338
0
    if (value.is_positive_infinity())
339
0
        return Symbol::PositiveInfinity;
340
0
    if (value.is_negative_infinity())
341
0
        return Symbol::NegativeInfinity;
342
0
    if (value.is_negative_zero())
343
0
        return Symbol::NegativeZero;
344
0
    if (value.is_nan())
345
0
        return Symbol::NotANumber;
346
0
    return number;
347
0
}
348
349
}