summaryrefslogtreecommitdiff
path: root/Userland/Applications/Calculator/KeypadValue.cpp
blob: fbd658bddcecc861cb2a2c274271554f510aed23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
 * Copyright (c) 2021, the SerenityOS developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "KeypadValue.h"
#include <AK/Math.h>
#include <AK/String.h>

KeypadValue::KeypadValue(i64 value, u8 decimal_places)
    : m_value(value)
    , m_decimal_places(decimal_places)
{
}

KeypadValue::KeypadValue(i64 value)
    : m_value(value)
{
}

KeypadValue KeypadValue::operator+(KeypadValue const& rhs)
{
    return operator_helper<KeypadValue>(*this, rhs, [](KeypadValue const&, KeypadValue const& more_decimal_places, i64 less_decimal_places_equalized, i64 more_decimal_places_equalized, bool) -> KeypadValue {
        return {
            more_decimal_places_equalized + less_decimal_places_equalized,
            more_decimal_places.m_decimal_places
        };
    });
}

KeypadValue KeypadValue::operator-(KeypadValue const& rhs)
{
    return *this + (-rhs);
}

KeypadValue KeypadValue::operator*(KeypadValue const& rhs)
{
    return operator_helper<KeypadValue>(*this, rhs, [](KeypadValue const& less_decimal_places, KeypadValue const& more_decimal_places, i64, i64, bool) -> KeypadValue {
        return {
            less_decimal_places.m_value * more_decimal_places.m_value,
            (u8)(less_decimal_places.m_decimal_places + more_decimal_places.m_decimal_places)
        };
    });
}

KeypadValue KeypadValue::operator-(void) const
{
    return { -m_value, m_decimal_places };
}

bool KeypadValue::operator<(KeypadValue const& rhs)
{
    return operator_helper<bool>(*this, rhs, [](KeypadValue const&, KeypadValue const&, i64 less_decimal_places_equalized, i64 more_decimal_places_equalized, bool lhs_is_less) {
        if (lhs_is_less)
            return (less_decimal_places_equalized < more_decimal_places_equalized);
        else
            return (more_decimal_places_equalized < less_decimal_places_equalized);
    });
}

bool KeypadValue::operator==(KeypadValue const& rhs)
{
    return operator_helper<bool>(*this, rhs, [](KeypadValue const&, KeypadValue const&, i64 less_decimal_places_equalized, i64 more_decimal_places_equalized, bool) {
        return less_decimal_places_equalized == more_decimal_places_equalized;
    });
}

// This is a helper function for the operators. A lot of them need to do very similar calculations, so this function
// does the calculations for them and calls them on the result. In case they don't need the result of a particular
// calculation, they simply ignore that argument.
// The arguments to this function are the operands on the left- and right-hand sides and the callback to call on the
// values computed by this function.
// The first two KeypadValues it passes to the callback are the two original operands, but sorted by the amount of
// decimal places.
// The next two i64s it passes to the callback are these sorted KeypadValues, but normalized, which means that if
// you have for example 12.1 (represented as {121, 1}) and 54.23 (represented as {5423, 2}), you will get 1210 and
// 5423, so that you can compare these two i64s directly in order to compare the original KeypadValues.
// Unfortunately, not all operators are symmetric, so the last boolean tells the callback whether the left-hand side
// was the KeypadValue with less decimal places (true), or the one with more decimal places (false).
template<typename T, typename F>
ALWAYS_INLINE T KeypadValue::operator_helper(KeypadValue const& lhs, KeypadValue const& rhs, F callback)
{
    KeypadValue const& less_decimal_places = (lhs.m_decimal_places < rhs.m_decimal_places) ? lhs : rhs;
    KeypadValue const& more_decimal_places = (lhs.m_decimal_places < rhs.m_decimal_places) ? rhs : lhs;

    i64 more_decimal_places_equalized = more_decimal_places.m_value;
    i64 less_decimal_places_equalized = (i64)AK::pow(10.0, (double)(more_decimal_places.m_decimal_places - less_decimal_places.m_decimal_places)) * less_decimal_places.m_value;

    bool lhs_is_less = (lhs.m_decimal_places < rhs.m_decimal_places);

    return callback(less_decimal_places, more_decimal_places,
        less_decimal_places_equalized, more_decimal_places_equalized,
        lhs_is_less);
}

KeypadValue::KeypadValue(double d)
{
    bool negative = false;
    if (d < 0) {
        negative = true;
        d = -d;
    }
    i8 current_pow = 0;
    while (AK::pow(10.0, (double)current_pow) <= d)
        current_pow += 1;
    current_pow -= 1;
    while (d != 0) {
        m_value *= 10;
        m_value += (u64)(d / AK::pow(10.0, (double)current_pow)) % 10;
        if (current_pow < 0)
            m_decimal_places += 1;
        current_pow -= 1;
        if (m_decimal_places > 6)
            break;
    }
    m_value = negative ? (-m_value) : m_value;
}

KeypadValue::operator double()
{
    double res = (double)m_value / AK::pow(10.0, (double)m_decimal_places);
    return res;
}