1/**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef ECMASCRIPT_BASE_MATH_HELPER_H
17#define ECMASCRIPT_BASE_MATH_HELPER_H
18
19#include <cstdint>
20#include <cmath>
21
22#include "ecmascript/base/bit_helper.h"
23
24#define panda_bit_utils_ctz __builtin_ctz      // NOLINT(cppcoreguidelines-macro-usage)
25#define panda_bit_utils_ctzll __builtin_ctzll  // NOLINT(cppcoreguidelines-macro-usage)
26
27namespace panda::ecmascript::base {
28class MathHelper {
29public:
30    static constexpr uint32_t GetIntLog2(const uint32_t X)
31    {
32        return static_cast<uint32_t>(panda_bit_utils_ctz(X));
33    }
34
35    static constexpr uint64_t GetIntLog2(const uint64_t X)
36    {
37        return static_cast<uint64_t>(panda_bit_utils_ctzll(X));
38    }
39
40    static double Asinh(double input)
41    {
42#if defined(PANDA_TARGET_WINDOWS)
43        if (input == 0 && !std::signbit(input)) {
44            // +0.0(double) is the special case for std::asinh() function compiled in linux for windows.
45            return +0.0;
46        }
47#endif
48        return std::asinh(input);
49    }
50
51    static inline double Atanh(double input)
52    {
53#if defined(PANDA_TARGET_WINDOWS)
54        if (input == 0 && std::signbit(input)) {
55            // -0.0(double) is the special case for std::atanh() function compiled in linux for windows.
56            return -0.0;
57        }
58#endif
59        return std::atanh(input);
60    }
61};
62
63template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
64inline constexpr int WhichPowerOfTwo(T value)
65{
66    // Ensure the size of the integer is no more than 8 bytes (64 bits).
67    static_assert(sizeof(T) <= 8);
68    // Use __builtin_ctzll for 8 bytes (64 bits) and __builtin_ctz for 32-bit integers.
69    return sizeof(T) == 8 ? __builtin_ctzll(static_cast<uint64_t>(value)) : __builtin_ctz(static_cast<uint32_t>(value));
70}
71
72
73inline int32_t SignedDiv32(int32_t lhs, int32_t rhs)
74{
75    if (rhs == 0) {
76        return 0;
77    }
78    if (rhs == -1) {
79        return lhs == std::numeric_limits<int32_t>::min() ? lhs : -lhs;
80    }
81    return lhs / rhs;
82}
83
84inline int64_t SignedDiv64(int64_t lhs, int64_t rhs)
85{
86    if (rhs == 0) {
87        return 0;
88    }
89    if (rhs == -1) {
90        return lhs == std::numeric_limits<int64_t>::min() ? lhs : -lhs;
91    }
92    return lhs / rhs;
93}
94
95inline int32_t SignedMod32(int32_t lhs, int32_t rhs)
96{
97    if (rhs == 0 || rhs == -1) {
98        return 0;
99    }
100    return lhs % rhs;
101}
102
103
104inline bool SignedAddOverflow32(int32_t lhs, int32_t rhs, int32_t *val)
105{
106    uint32_t res = static_cast<uint32_t>(lhs) + static_cast<uint32_t>(rhs);
107    *val = base::bit_cast<int32_t>(res);
108    // Check for overflow by examining the sign bit.(bit 31 in a 32-bit integer)
109    return ((res ^ static_cast<uint32_t>(lhs)) & (res ^ static_cast<uint32_t>(rhs)) & (1U << 31)) != 0;
110}
111
112
113inline bool SignedSubOverflow32(int32_t lhs, int32_t rhs, int32_t *val)
114{
115    uint32_t res = static_cast<uint32_t>(lhs) - static_cast<uint32_t>(rhs);
116    *val = base::bit_cast<int32_t>(res);
117    // Check for overflow by examining the sign bit.(bit 31 in a 32-bit integer)
118    return ((res ^ static_cast<uint32_t>(lhs)) & (res ^ ~static_cast<uint32_t>(rhs)) & (1U << 31)) != 0;
119}
120
121inline bool SignedMulOverflow32(int32_t lhs, int32_t rhs, int32_t *val)
122{
123    int64_t result = int64_t{lhs} * int64_t{rhs};
124    *val = static_cast<int32_t>(result);
125    using limits = std::numeric_limits<int32_t>;
126    return result < limits::min() || result > limits::max();
127}
128
129
130// Returns the quotient x/y, avoiding C++ undefined behavior if y == 0.
131template <typename T>
132inline T Divide(T x, T y)
133{
134    if (y != 0) {
135        return x / y;
136    }
137    if (x == 0 || x != x) {
138        return std::numeric_limits<T>::quiet_NaN();
139    }
140    if ((x >= 0) == (std::signbit(y) == 0)) {
141        return std::numeric_limits<T>::infinity();
142    }
143    return -std::numeric_limits<T>::infinity();
144}
145
146
147template <typename SignedType>
148inline SignedType AddWithWraparound(SignedType a, SignedType b)
149{
150    static_assert(std::is_integral<SignedType>::value && std::is_signed<SignedType>::value,
151        "use this for signed integer types");
152    using UnsignedType = typename std::make_unsigned<SignedType>::type;
153    UnsignedType aUnsigned = static_cast<UnsignedType>(a);
154    UnsignedType bUnsigned = static_cast<UnsignedType>(b);
155    UnsignedType result = aUnsigned + bUnsigned;
156    return static_cast<SignedType>(result);
157}
158
159template <typename SignedType>
160inline SignedType SubWithWraparound(SignedType a, SignedType b)
161{
162    static_assert(std::is_integral<SignedType>::value && std::is_signed<SignedType>::value,
163        "use this for signed integer types");
164    using UnsignedType = typename std::make_unsigned<SignedType>::type;
165    UnsignedType aUnsigned = static_cast<UnsignedType>(a);
166    UnsignedType bUnsigned = static_cast<UnsignedType>(b);
167    UnsignedType result = aUnsigned - bUnsigned;
168    return static_cast<SignedType>(result);
169}
170
171template <typename SignedType>
172inline SignedType MulWithWraparound(SignedType a, SignedType b)
173{
174    static_assert(std::is_integral<SignedType>::value && std::is_signed<SignedType>::value,
175        "use this for signed integer types");
176    using UnsignedType = typename std::make_unsigned<SignedType>::type;
177    UnsignedType aUnsigned = static_cast<UnsignedType>(a);
178    UnsignedType bUnsigned = static_cast<UnsignedType>(b);
179    UnsignedType result = aUnsigned * bUnsigned;
180    return static_cast<SignedType>(result);
181}
182
183template <typename SignedType>
184inline SignedType ShlWithWraparound(SignedType a, SignedType b)
185{
186    using UnsignedType = typename std::make_unsigned<SignedType>::type;
187    const UnsignedType kMask = (sizeof(a) * 8) - 1;
188    return static_cast<SignedType>(static_cast<UnsignedType>(a) << (static_cast<UnsignedType>(b) & kMask));
189}
190
191template <typename SignedType>
192inline SignedType NegateWithWraparound(SignedType a)
193{
194    static_assert(std::is_integral<SignedType>::value && std::is_signed<SignedType>::value,
195        "use this for signed integer types");
196    if (a == std::numeric_limits<SignedType>::min()) {
197        return a;
198    }
199    return -a;
200}
201}  // panda::ecmascript::base
202
203#endif  // ECMASCRIPT_BASE_MATH_HELPER_H
204