/src/serenity/AK/BigIntBase.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/BuiltinWrappers.h> |
10 | | #include <AK/Span.h> |
11 | | #include <AK/StdLibExtras.h> |
12 | | #include <AK/Types.h> |
13 | | |
14 | | namespace AK { |
15 | | |
16 | | namespace Detail { |
17 | | |
18 | | template<typename T> |
19 | | struct DoubleWordHelper; |
20 | | |
21 | | template<> |
22 | | struct DoubleWordHelper<u32> { |
23 | | using Type = u64; |
24 | | using SignedType = i64; |
25 | | }; |
26 | | template<typename T> |
27 | | using DoubleWord = typename DoubleWordHelper<T>::Type; |
28 | | template<typename T> |
29 | | using SignedDoubleWord = typename DoubleWordHelper<T>::SignedType; |
30 | | |
31 | | // Ideally, we want to store data in the native processor's words. However, for some algorithms, |
32 | | // particularly multiplication, we require double of the amount of the native word size. |
33 | | #if defined(__SIZEOF_INT128__) && defined(AK_ARCH_64_BIT) |
34 | | template<> |
35 | | struct DoubleWordHelper<u64> { |
36 | | using Type = unsigned __int128; |
37 | | using SignedType = __int128; |
38 | | }; |
39 | | using NativeWord = u64; |
40 | | #else |
41 | | using NativeWord = u32; |
42 | | #endif |
43 | | |
44 | | using NativeDoubleWord = DoubleWord<NativeWord>; |
45 | | using SignedNativeDoubleWord = SignedDoubleWord<NativeWord>; |
46 | | |
47 | | template<typename WordType, bool sign> |
48 | | using ConditionallySignedDoubleWord = Conditional<sign, SignedDoubleWord<WordType>, DoubleWord<WordType>>; |
49 | | |
50 | | template<typename T> |
51 | | concept BuiltInUFixedInt = OneOf<T, bool, u8, u16, u32, u64, unsigned long, unsigned long long, NativeDoubleWord>; |
52 | | |
53 | | template<typename T> |
54 | | constexpr inline size_t bit_width = sizeof(T) * 8; |
55 | | |
56 | | constexpr size_t native_word_size = bit_width<NativeWord>; |
57 | | constexpr NativeWord max_native_word = NumericLimits<NativeWord>::max(); |
58 | | static_assert(native_word_size == 32 || native_word_size == 64); |
59 | | |
60 | | // Max big integer length is 256 MiB (2.1e9 bits) for 32-bit, 4 GiB (3.4e10 bits) for 64-bit. |
61 | | constexpr size_t max_big_int_length = 1 << (native_word_size == 32 ? 26 : 29); |
62 | | |
63 | | // ===== Static storage for big integers ===== |
64 | | template<typename T, typename WordType = NativeWord> |
65 | | concept IntegerStorage = requires(T storage, size_t index) { |
66 | | { |
67 | | storage.is_negative() |
68 | | } -> SameAs<bool>; |
69 | | { |
70 | | storage.size() |
71 | | } -> SameAs<size_t>; |
72 | | { |
73 | | storage[index] |
74 | | } -> ConvertibleTo<WordType&>; |
75 | | { |
76 | | storage.data() |
77 | | } -> ConvertibleTo<WordType*>; |
78 | | }; |
79 | | |
80 | | template<typename T, typename WordType = NativeWord> |
81 | | concept IntegerReadonlyStorage = IntegerStorage<T, WordType const>; |
82 | | |
83 | | struct NullAllocator { |
84 | 0 | NativeWord* allocate(size_t) { VERIFY_NOT_REACHED(); } |
85 | | }; |
86 | | |
87 | | template<typename Word, bool is_signed_> |
88 | | struct StorageSpan : AK::Span<Word> { |
89 | | using AK::Span<Word>::Span; |
90 | | |
91 | | constexpr static bool is_signed = is_signed_; |
92 | | |
93 | | explicit constexpr StorageSpan(AK::Span<Word> span) |
94 | 0 | : AK::Span<Word>(span) |
95 | 0 | { |
96 | 0 | } Unexecuted instantiation: AK::Detail::StorageSpan<unsigned int, false>::StorageSpan(AK::Span<unsigned int>) Unexecuted instantiation: AK::Detail::StorageSpan<unsigned long, false>::StorageSpan(AK::Span<unsigned long>) |
97 | | |
98 | | constexpr bool is_negative() const |
99 | 333k | { |
100 | 333k | return is_signed && this->last() >> (bit_width<Word> - 1); |
101 | 333k | } AK::Detail::StorageSpan<unsigned long, false>::is_negative() const Line | Count | Source | 99 | 287k | { | 100 | 287k | return is_signed && this->last() >> (bit_width<Word> - 1); | 101 | 287k | } |
AK::Detail::StorageSpan<unsigned long const, false>::is_negative() const Line | Count | Source | 99 | 45.5k | { | 100 | 45.5k | return is_signed && this->last() >> (bit_width<Word> - 1); | 101 | 45.5k | } |
Unexecuted instantiation: AK::Detail::StorageSpan<unsigned int, false>::is_negative() const |
102 | | }; |
103 | | |
104 | | using UnsignedStorageSpan = StorageSpan<NativeWord, false>; |
105 | | using UnsignedStorageReadonlySpan = StorageSpan<NativeWord const, false>; |
106 | | |
107 | | // Sometimes we want to know the exact maximum amount of the bits required to represent the number. |
108 | | // However, the bit size only acts as a hint for wide multiply operations. For all other purposes, |
109 | | // `bit_size`-sized and `ceil(bit_size / word_size) * word_size`-sized `StaticStorage`s will act the |
110 | | // same. |
111 | | template<bool is_signed_, size_t bit_size> |
112 | | requires(bit_size <= max_big_int_length * native_word_size) struct StaticStorage { |
113 | | constexpr static size_t static_size = (bit_size + native_word_size - 1) / native_word_size; |
114 | | constexpr static bool is_signed = is_signed_; |
115 | | |
116 | | // We store integers in little-endian regardless of the host endianness. We use two's complement |
117 | | // representation of negative numbers and do not bother at all if `bit_size % word_size != 0` |
118 | | // (i. e. do not properly handle overflows). |
119 | | NativeWord m_data[static_size]; |
120 | | |
121 | | constexpr bool is_negative() const |
122 | 14.7M | { |
123 | 14.7M | return is_signed_ && m_data[static_size - 1] >> (native_word_size - 1); |
124 | 14.7M | } AK::Detail::StaticStorage<false, 64ul>::is_negative() const Line | Count | Source | 122 | 10.0M | { | 123 | 10.0M | return is_signed_ && m_data[static_size - 1] >> (native_word_size - 1); | 124 | 10.0M | } |
AK::Detail::StaticStorage<false, 128ul>::is_negative() const Line | Count | Source | 122 | 4.72M | { | 123 | 4.72M | return is_signed_ && m_data[static_size - 1] >> (native_word_size - 1); | 124 | 4.72M | } |
Unexecuted instantiation: AK::Detail::StaticStorage<false, 32ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 4096ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 256ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 512ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 8ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 384ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 768ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 257ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 513ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 385ul>::is_negative() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 769ul>::is_negative() const |
125 | | |
126 | | constexpr static size_t size() |
127 | 22.3M | { |
128 | 22.3M | return static_size; |
129 | 22.3M | } AK::Detail::StaticStorage<false, 64ul>::size() Line | Count | Source | 127 | 12.5M | { | 128 | 12.5M | return static_size; | 129 | 12.5M | } |
AK::Detail::StaticStorage<false, 128ul>::size() Line | Count | Source | 127 | 9.76M | { | 128 | 9.76M | return static_size; | 129 | 9.76M | } |
Unexecuted instantiation: AK::Detail::StaticStorage<false, 32ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 4096ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 4160ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 5248ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 8ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 192ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 256ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 512ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 384ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 768ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 257ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 321ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 513ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 577ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 385ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 449ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 769ul>::size() Unexecuted instantiation: AK::Detail::StaticStorage<false, 833ul>::size() |
130 | | |
131 | | constexpr NativeWord operator[](size_t i) const |
132 | 20.2M | { |
133 | 20.2M | return m_data[i]; |
134 | 20.2M | } AK::Detail::StaticStorage<false, 64ul>::operator[](unsigned long) const Line | Count | Source | 132 | 10.0M | { | 133 | 10.0M | return m_data[i]; | 134 | 10.0M | } |
AK::Detail::StaticStorage<false, 128ul>::operator[](unsigned long) const Line | Count | Source | 132 | 10.2M | { | 133 | 10.2M | return m_data[i]; | 134 | 10.2M | } |
Unexecuted instantiation: AK::Detail::StaticStorage<false, 32ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 4096ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 8ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 256ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 512ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 384ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 768ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 257ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 513ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 385ul>::operator[](unsigned long) const Unexecuted instantiation: AK::Detail::StaticStorage<false, 769ul>::operator[](unsigned long) const |
135 | | |
136 | | constexpr NativeWord& operator[](size_t i) |
137 | 12.2M | { |
138 | 12.2M | return m_data[i]; |
139 | 12.2M | } AK::Detail::StaticStorage<false, 64ul>::operator[](unsigned long) Line | Count | Source | 137 | 2.51M | { | 138 | 2.51M | return m_data[i]; | 139 | 2.51M | } |
AK::Detail::StaticStorage<false, 128ul>::operator[](unsigned long) Line | Count | Source | 137 | 9.76M | { | 138 | 9.76M | return m_data[i]; | 139 | 9.76M | } |
Unexecuted instantiation: AK::Detail::StaticStorage<false, 32ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 4096ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 4160ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 5248ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 192ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 256ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 512ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 384ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 768ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 257ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 321ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 513ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 577ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 385ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 449ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 769ul>::operator[](unsigned long) Unexecuted instantiation: AK::Detail::StaticStorage<false, 833ul>::operator[](unsigned long) |
140 | | |
141 | | constexpr NativeWord const* data() const |
142 | 0 | { |
143 | 0 | return m_data; |
144 | 0 | } Unexecuted instantiation: AK::Detail::StaticStorage<false, 64ul>::data() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 32ul>::data() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 4096ul>::data() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 5248ul>::data() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 256ul>::data() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 384ul>::data() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 257ul>::data() const Unexecuted instantiation: AK::Detail::StaticStorage<false, 385ul>::data() const |
145 | | |
146 | | constexpr NativeWord* data() |
147 | 0 | { |
148 | 0 | return m_data; |
149 | 0 | } Unexecuted instantiation: AK::Detail::StaticStorage<false, 256ul>::data() Unexecuted instantiation: AK::Detail::StaticStorage<false, 384ul>::data() |
150 | | |
151 | 0 | constexpr operator StorageSpan<NativeWord, is_signed>() { return { m_data, static_size }; }Unexecuted instantiation: AK::Detail::StaticStorage<false, 4160ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 4096ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 192ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 128ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 321ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 257ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 577ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 256ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 513ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 449ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 385ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 833ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 384ul>::operator AK::Detail::StorageSpan<unsigned long, false>() Unexecuted instantiation: AK::Detail::StaticStorage<false, 769ul>::operator AK::Detail::StorageSpan<unsigned long, false>() |
152 | | }; |
153 | | |
154 | | struct IntegerWrapper { |
155 | | StaticStorage<false, bit_width<int>> m_data; |
156 | | |
157 | | // There is no reason to ban u128{0} + 1 (although the second argument type is signed, the value |
158 | | // is known at the compile time to be non-negative). In order to do so, we provide overloads in |
159 | | // UFixedBigInt which take IntegerWrapper as an argument. |
160 | | consteval IntegerWrapper(int value) |
161 | 0 | { |
162 | 0 | if (value < 0) |
163 | 0 | compiletime_fail("Requested implicit conversion of an integer to the unsigned one will underflow."); |
164 | 0 | m_data[0] = static_cast<NativeWord>(value); |
165 | 0 | } |
166 | | }; |
167 | | |
168 | 0 | constexpr inline auto get_storage_of(IntegerWrapper value) { return value.m_data; } |
169 | | |
170 | | template<BuiltInUFixedInt T> |
171 | | constexpr StaticStorage<false, bit_width<T>> get_storage_of(T value) |
172 | 7.54M | { |
173 | | if constexpr (sizeof(T) > sizeof(NativeWord)) { |
174 | | static_assert(sizeof(T) == 2 * sizeof(NativeWord)); |
175 | | return { static_cast<NativeWord>(value), static_cast<NativeWord>(value >> native_word_size) }; |
176 | | } |
177 | 7.54M | return { static_cast<NativeWord>(value) }; |
178 | 7.54M | } _ZN2AK6Detail14get_storage_ofITkNS0_16BuiltInUFixedIntEmEENS0_13StaticStorageILb0EX9bit_widthIT_EEEES3_ Line | Count | Source | 172 | 7.54M | { | 173 | | if constexpr (sizeof(T) > sizeof(NativeWord)) { | 174 | | static_assert(sizeof(T) == 2 * sizeof(NativeWord)); | 175 | | return { static_cast<NativeWord>(value), static_cast<NativeWord>(value >> native_word_size) }; | 176 | | } | 177 | 7.54M | return { static_cast<NativeWord>(value) }; | 178 | 7.54M | } |
Unexecuted instantiation: _ZN2AK6Detail14get_storage_ofITkNS0_16BuiltInUFixedIntEjEENS0_13StaticStorageILb0EX9bit_widthIT_EEEES3_ Unexecuted instantiation: _ZN2AK6Detail14get_storage_ofITkNS0_16BuiltInUFixedIntEyEENS0_13StaticStorageILb0EX9bit_widthIT_EEEES3_ Unexecuted instantiation: _ZN2AK6Detail14get_storage_ofITkNS0_16BuiltInUFixedIntEhEENS0_13StaticStorageILb0EX9bit_widthIT_EEEES3_ Unexecuted instantiation: _ZN2AK6Detail14get_storage_ofITkNS0_16BuiltInUFixedIntEbEENS0_13StaticStorageILb0EX9bit_widthIT_EEEES3_ |
179 | | |
180 | | // ===== Utilities ===== |
181 | | template<typename Word> |
182 | | ALWAYS_INLINE constexpr Word extend_sign(bool sign) |
183 | 10.0M | { |
184 | 10.0M | return sign ? NumericLimits<Word>::max() : 0; |
185 | 10.0M | } unsigned long AK::Detail::extend_sign<unsigned long>(bool) Line | Count | Source | 183 | 10.0M | { | 184 | 10.0M | return sign ? NumericLimits<Word>::max() : 0; | 185 | 10.0M | } |
Unexecuted instantiation: unsigned int AK::Detail::extend_sign<unsigned int>(bool) |
186 | | |
187 | | // FIXME: If available, we might try to use AVX2 and AVX512. |
188 | | template<typename WordType> |
189 | | ALWAYS_INLINE constexpr WordType add_words(WordType word1, WordType word2, bool& carry) |
190 | 7.99M | { |
191 | 7.99M | if (!is_constant_evaluated()) { |
192 | 7.99M | #if __has_builtin(__builtin_addc) |
193 | 7.99M | WordType ncarry, output; |
194 | | if constexpr (SameAs<WordType, unsigned int>) |
195 | 0 | output = __builtin_addc(word1, word2, carry, reinterpret_cast<unsigned int*>(&ncarry)); |
196 | | else if constexpr (SameAs<WordType, unsigned long>) |
197 | 7.99M | output = __builtin_addcl(word1, word2, carry, reinterpret_cast<unsigned long*>(&ncarry)); |
198 | | else if constexpr (SameAs<WordType, unsigned long long>) |
199 | | output = __builtin_addcll(word1, word2, carry, reinterpret_cast<unsigned long long*>(&ncarry)); |
200 | | else |
201 | 7.99M | VERIFY_NOT_REACHED(); |
202 | 7.99M | carry = ncarry; |
203 | 7.99M | return output; |
204 | | #elif ARCH(X86_64) |
205 | | if constexpr (SameAs<WordType, unsigned int>) { |
206 | | unsigned int output; |
207 | | carry = __builtin_ia32_addcarryx_u32(carry, word1, word2, &output); |
208 | | return output; |
209 | | } else if constexpr (OneOf<WordType, unsigned long, unsigned long long>) { |
210 | | unsigned long long output; |
211 | | carry = __builtin_ia32_addcarryx_u64(carry, word1, word2, &output); |
212 | | return output; |
213 | | } else { |
214 | | VERIFY_NOT_REACHED(); |
215 | | } |
216 | | #endif |
217 | 7.99M | } |
218 | | // Note: This is usually too confusing for both GCC and Clang. |
219 | 0 | WordType output; |
220 | 0 | bool ncarry = __builtin_add_overflow(word1, word2, &output); |
221 | 0 | if (carry) { |
222 | 0 | ++output; |
223 | 0 | if (output == 0) |
224 | 0 | ncarry = true; |
225 | 0 | } |
226 | 0 | carry = ncarry; |
227 | 0 | return output; |
228 | 7.99M | } unsigned long AK::Detail::add_words<unsigned long>(unsigned long, unsigned long, bool&) Line | Count | Source | 190 | 7.99M | { | 191 | 7.99M | if (!is_constant_evaluated()) { | 192 | 7.99M | #if __has_builtin(__builtin_addc) | 193 | 7.99M | WordType ncarry, output; | 194 | | if constexpr (SameAs<WordType, unsigned int>) | 195 | | output = __builtin_addc(word1, word2, carry, reinterpret_cast<unsigned int*>(&ncarry)); | 196 | | else if constexpr (SameAs<WordType, unsigned long>) | 197 | 7.99M | output = __builtin_addcl(word1, word2, carry, reinterpret_cast<unsigned long*>(&ncarry)); | 198 | | else if constexpr (SameAs<WordType, unsigned long long>) | 199 | | output = __builtin_addcll(word1, word2, carry, reinterpret_cast<unsigned long long*>(&ncarry)); | 200 | | else | 201 | 7.99M | VERIFY_NOT_REACHED(); | 202 | 7.99M | carry = ncarry; | 203 | 7.99M | return output; | 204 | | #elif ARCH(X86_64) | 205 | | if constexpr (SameAs<WordType, unsigned int>) { | 206 | | unsigned int output; | 207 | | carry = __builtin_ia32_addcarryx_u32(carry, word1, word2, &output); | 208 | | return output; | 209 | | } else if constexpr (OneOf<WordType, unsigned long, unsigned long long>) { | 210 | | unsigned long long output; | 211 | | carry = __builtin_ia32_addcarryx_u64(carry, word1, word2, &output); | 212 | | return output; | 213 | | } else { | 214 | | VERIFY_NOT_REACHED(); | 215 | | } | 216 | | #endif | 217 | 7.99M | } | 218 | | // Note: This is usually too confusing for both GCC and Clang. | 219 | 0 | WordType output; | 220 | 0 | bool ncarry = __builtin_add_overflow(word1, word2, &output); | 221 | 0 | if (carry) { | 222 | 0 | ++output; | 223 | 0 | if (output == 0) | 224 | 0 | ncarry = true; | 225 | 0 | } | 226 | 0 | carry = ncarry; | 227 | 0 | return output; | 228 | 7.99M | } |
Unexecuted instantiation: unsigned int AK::Detail::add_words<unsigned int>(unsigned int, unsigned int, bool&) |
229 | | |
230 | | template<typename WordType> |
231 | | ALWAYS_INLINE constexpr WordType sub_words(WordType word1, WordType word2, bool& carry) |
232 | 0 | { |
233 | 0 | if (!is_constant_evaluated()) { |
234 | 0 | #if __has_builtin(__builtin_subc) && !defined(AK_BUILTIN_SUBC_BROKEN) |
235 | 0 | WordType ncarry, output; |
236 | | if constexpr (SameAs<WordType, unsigned int>) |
237 | 0 | output = __builtin_subc(word1, word2, carry, reinterpret_cast<unsigned int*>(&ncarry)); |
238 | | else if constexpr (SameAs<WordType, unsigned long>) |
239 | 0 | output = __builtin_subcl(word1, word2, carry, reinterpret_cast<unsigned long*>(&ncarry)); |
240 | | else if constexpr (SameAs<WordType, unsigned long long>) |
241 | | output = __builtin_subcll(word1, word2, carry, reinterpret_cast<unsigned long long*>(&ncarry)); |
242 | | else |
243 | 0 | VERIFY_NOT_REACHED(); |
244 | 0 | carry = ncarry; |
245 | 0 | return output; |
246 | | #elif ARCH(X86_64) && defined(AK_COMPILER_GCC) |
247 | | if constexpr (SameAs<WordType, unsigned int>) { |
248 | | unsigned int output; |
249 | | carry = __builtin_ia32_sbb_u32(carry, word1, word2, &output); |
250 | | return output; |
251 | | } else if constexpr (OneOf<WordType, unsigned long, unsigned long long>) { |
252 | | unsigned long long output; |
253 | | carry = __builtin_ia32_sbb_u64(carry, word1, word2, &output); |
254 | | return output; |
255 | | } else { |
256 | | VERIFY_NOT_REACHED(); |
257 | | } |
258 | | #endif |
259 | 0 | } |
260 | | // Note: This is usually too confusing for both GCC and Clang. |
261 | 0 | WordType output; |
262 | 0 | bool ncarry = __builtin_sub_overflow(word1, word2, &output); |
263 | 0 | if (carry) { |
264 | 0 | if (output == 0) |
265 | 0 | ncarry = true; |
266 | 0 | --output; |
267 | 0 | } |
268 | 0 | carry = ncarry; |
269 | 0 | return output; |
270 | 0 | } Unexecuted instantiation: unsigned long AK::Detail::sub_words<unsigned long>(unsigned long, unsigned long, bool&) Unexecuted instantiation: unsigned int AK::Detail::sub_words<unsigned int>(unsigned int, unsigned int, bool&) |
271 | | |
272 | | template<typename WordType> |
273 | | ALWAYS_INLINE constexpr DoubleWord<WordType> wide_multiply(WordType word1, WordType word2) |
274 | 0 | { |
275 | 0 | return static_cast<DoubleWord<WordType>>(word1) * word2; |
276 | 0 | } Unexecuted instantiation: AK::Detail::DoubleWordHelper<unsigned long>::Type AK::Detail::wide_multiply<unsigned long>(unsigned long, unsigned long) Unexecuted instantiation: AK::Detail::DoubleWordHelper<unsigned int>::Type AK::Detail::wide_multiply<unsigned int>(unsigned int, unsigned int) |
277 | | |
278 | | template<typename WordType> |
279 | | constexpr DoubleWord<WordType> dword(WordType low, WordType high) |
280 | 0 | { |
281 | 0 | return (static_cast<DoubleWord<WordType>>(high) << bit_width<WordType>) | low; |
282 | 0 | } Unexecuted instantiation: AK::Detail::DoubleWordHelper<unsigned long>::Type AK::Detail::dword<unsigned long>(unsigned long, unsigned long) Unexecuted instantiation: AK::Detail::DoubleWordHelper<unsigned int>::Type AK::Detail::dword<unsigned int>(unsigned int, unsigned int) |
283 | | |
284 | | // Calculate ((dividend_high << word_size) + dividend_low) / divisor. Quotient should be guaranteed to fit |
285 | | // into WordType. |
286 | | template<typename WordType> |
287 | | ALWAYS_INLINE constexpr WordType div_mod_words(WordType dividend_low, WordType dividend_high, WordType divisor, WordType& remainder) |
288 | 0 | { |
289 | 0 | auto dividend = dword(dividend_low, dividend_high); |
290 | 0 | remainder = static_cast<WordType>(dividend % divisor); |
291 | 0 | return static_cast<WordType>(dividend / divisor); |
292 | 0 | } Unexecuted instantiation: unsigned long AK::Detail::div_mod_words<unsigned long>(unsigned long, unsigned long, unsigned long, unsigned long&) Unexecuted instantiation: unsigned int AK::Detail::div_mod_words<unsigned int>(unsigned int, unsigned int, unsigned int, unsigned int&) |
293 | | |
294 | | // ===== Operations on integer storages ===== |
295 | | // Naming scheme for variables belonging to one of the operands or the result is as follows: |
296 | | // trailing digit in a name is 1 if a variable belongs to `operand1` (or the only `operand`), 2 -- |
297 | | // for `operand2` and no trailing digit -- for `result`. |
298 | | template<typename WordType = NativeWord> |
299 | | struct StorageOperations { |
300 | | static constexpr size_t word_size = bit_width<WordType>; |
301 | | using DoubleWordType = DoubleWord<WordType>; |
302 | | |
303 | | static constexpr void copy(IntegerReadonlyStorage<WordType> auto const& operand, IntegerStorage<WordType> auto&& result, size_t offset = 0) |
304 | 2.52M | { |
305 | 2.52M | auto fill = extend_sign<WordType>(operand.is_negative()); |
306 | 2.52M | size_t size1 = operand.size(), size = result.size(); |
307 | | |
308 | 5.05M | for (size_t i = 0; i < size; ++i) |
309 | 2.52M | result[i] = i + offset < size1 ? operand[i + offset] : fill; |
310 | 2.52M | } _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm64EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_OT0_m Line | Count | Source | 304 | 2.51M | { | 305 | 2.51M | auto fill = extend_sign<WordType>(operand.is_negative()); | 306 | 2.51M | size_t size1 = operand.size(), size = result.size(); | 307 | | | 308 | 5.03M | for (size_t i = 0; i < size; ++i) | 309 | 2.51M | result[i] = i + offset < size1 ? operand[i + offset] : fill; | 310 | 2.51M | } |
Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_OT0_m _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm64EEETkNS0_14IntegerStorageIS5_EENS0_11StorageSpanImLb0EEEEEvRKS5_OT0_m Line | Count | Source | 304 | 10.6k | { | 305 | 10.6k | auto fill = extend_sign<WordType>(operand.is_negative()); | 306 | 10.6k | size_t size1 = operand.size(), size = result.size(); | 307 | | | 308 | 21.3k | for (size_t i = 0; i < size; ++i) | 309 | 10.6k | result[i] = i + offset < size1 ? operand[i + offset] : fill; | 310 | 10.6k | } |
Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm4096EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm4096EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm128EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm4096EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm4160EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm192EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm128EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm256EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm512EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm256EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm8EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm256EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm384EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm768EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm384EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm8EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm384EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm257EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm257EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm257EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm257EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm321EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm257EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm256EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm513EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm513EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm256EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm513EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm577EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm385EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm385EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm385EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm385EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm449EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm385EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm384EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm769EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm769EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm384EEEEEvRKS5_OT0_m Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE4copyITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm769EEETkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm833EEEEEvRKS5_OT0_m |
311 | | |
312 | | static constexpr void set(WordType value, auto&& result) |
313 | 0 | { |
314 | 0 | result[0] = value; |
315 | 0 | for (size_t i = 1; i < result.size(); ++i) |
316 | 0 | result[i] = 0; |
317 | 0 | } Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::set<AK::Detail::StaticStorage<false, 4096ul>&>(unsigned long, AK::Detail::StaticStorage<false, 4096ul>&) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::set<AK::Detail::StaticStorage<false, 128ul>&>(unsigned long, AK::Detail::StaticStorage<false, 128ul>&) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::set<AK::Detail::StaticStorage<false, 257ul>&>(unsigned long, AK::Detail::StaticStorage<false, 257ul>&) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::set<AK::Detail::StaticStorage<false, 256ul>&>(unsigned long, AK::Detail::StaticStorage<false, 256ul>&) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::set<AK::Detail::StaticStorage<false, 513ul>&>(unsigned long, AK::Detail::StaticStorage<false, 513ul>&) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::set<AK::Detail::StaticStorage<false, 385ul>&>(unsigned long, AK::Detail::StaticStorage<false, 385ul>&) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::set<AK::Detail::StaticStorage<false, 384ul>&>(unsigned long, AK::Detail::StaticStorage<false, 384ul>&) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::set<AK::Detail::StaticStorage<false, 769ul>&>(unsigned long, AK::Detail::StaticStorage<false, 769ul>&) |
318 | | |
319 | | // `is_for_inequality' is a hint to compiler that we do not need to differentiate between < and >. |
320 | | static constexpr int compare(IntegerReadonlyStorage<WordType> auto const& operand1, IntegerReadonlyStorage<WordType> auto const& operand2, bool is_for_inequality) |
321 | 10.6k | { |
322 | 10.6k | bool sign1 = operand1.is_negative(), sign2 = operand2.is_negative(); |
323 | 10.6k | size_t size1 = operand1.size(), size2 = operand2.size(); |
324 | | |
325 | 10.6k | if (sign1 != sign2) { |
326 | 0 | if (sign1) |
327 | 0 | return -1; |
328 | 0 | return 1; |
329 | 0 | } |
330 | | |
331 | 10.6k | WordType compare_value = extend_sign<WordType>(sign1); |
332 | 10.6k | bool differ_in_high_bits = false; |
333 | | |
334 | 10.6k | if (size1 > size2) { |
335 | 0 | for (size_t i = size1; i-- > size2;) |
336 | 0 | if (operand1[i] != compare_value) |
337 | 0 | differ_in_high_bits = true; |
338 | 10.6k | } else if (size1 < size2) { |
339 | 0 | for (size_t i = size2; i-- > size1;) |
340 | 0 | if (operand2[i] != compare_value) |
341 | 0 | differ_in_high_bits = true; |
342 | 0 | } |
343 | | |
344 | 10.6k | if (differ_in_high_bits) |
345 | 0 | return (size1 > size2) ^ sign1 ? 1 : -1; |
346 | | |
347 | | // FIXME: Using min(size1, size2) in the next line triggers -Warray-bounds on GCC with -O2 and |
348 | | // -fsanitize=address. I have not reported this. |
349 | | // Reduced testcase: https://godbolt.org/z/TE3MbfhnE |
350 | 23.6k | for (size_t i = (size1 > size2 ? size2 : size1); i--;) { |
351 | 20.1k | auto word1 = operand1[i], word2 = operand2[i]; |
352 | | |
353 | 20.1k | if (is_for_inequality) { |
354 | 0 | if (word1 != word2) |
355 | 0 | return 1; |
356 | 20.1k | } else { |
357 | 20.1k | if (word1 > word2) |
358 | 3.23k | return 1; |
359 | 16.9k | if (word1 < word2) |
360 | 3.92k | return -1; |
361 | 16.9k | } |
362 | 20.1k | } |
363 | 3.54k | return 0; |
364 | 10.6k | } _ZN2AK6Detail17StorageOperationsImE7compareITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanIKmLb0EEETkNS4_IS5_EES8_EEiRKS5_RKT0_b Line | Count | Source | 321 | 10.6k | { | 322 | 10.6k | bool sign1 = operand1.is_negative(), sign2 = operand2.is_negative(); | 323 | 10.6k | size_t size1 = operand1.size(), size2 = operand2.size(); | 324 | | | 325 | 10.6k | if (sign1 != sign2) { | 326 | 0 | if (sign1) | 327 | 0 | return -1; | 328 | 0 | return 1; | 329 | 0 | } | 330 | | | 331 | 10.6k | WordType compare_value = extend_sign<WordType>(sign1); | 332 | 10.6k | bool differ_in_high_bits = false; | 333 | | | 334 | 10.6k | if (size1 > size2) { | 335 | 0 | for (size_t i = size1; i-- > size2;) | 336 | 0 | if (operand1[i] != compare_value) | 337 | 0 | differ_in_high_bits = true; | 338 | 10.6k | } else if (size1 < size2) { | 339 | 0 | for (size_t i = size2; i-- > size1;) | 340 | 0 | if (operand2[i] != compare_value) | 341 | 0 | differ_in_high_bits = true; | 342 | 0 | } | 343 | | | 344 | 10.6k | if (differ_in_high_bits) | 345 | 0 | return (size1 > size2) ^ sign1 ? 1 : -1; | 346 | | | 347 | | // FIXME: Using min(size1, size2) in the next line triggers -Warray-bounds on GCC with -O2 and | 348 | | // -fsanitize=address. I have not reported this. | 349 | | // Reduced testcase: https://godbolt.org/z/TE3MbfhnE | 350 | 23.6k | for (size_t i = (size1 > size2 ? size2 : size1); i--;) { | 351 | 20.1k | auto word1 = operand1[i], word2 = operand2[i]; | 352 | | | 353 | 20.1k | if (is_for_inequality) { | 354 | 0 | if (word1 != word2) | 355 | 0 | return 1; | 356 | 20.1k | } else { | 357 | 20.1k | if (word1 > word2) | 358 | 3.23k | return 1; | 359 | 16.9k | if (word1 < word2) | 360 | 3.92k | return -1; | 361 | 16.9k | } | 362 | 20.1k | } | 363 | 3.54k | return 0; | 364 | 10.6k | } |
Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE7compareITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm4096EEETkNS4_IS5_EES7_EEiRKS5_RKT0_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE7compareITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS4_IS5_EENS6_ILb0ELm32EEEEEiRKS5_RKT0_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE7compareITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS4_IS5_EENS6_ILb0ELm32EEEEEiRKS5_RKT0_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE7compareITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS4_IS5_EES7_EEiRKS5_RKT0_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE7compareITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS4_IS5_EES7_EEiRKS5_RKT0_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE7compareITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS4_IS5_EENS6_ILb0ELm32EEEEEiRKS5_RKT0_b |
365 | | |
366 | | enum class Bitwise { |
367 | | AND, |
368 | | OR, |
369 | | XOR, |
370 | | INVERT, |
371 | | }; |
372 | | |
373 | | // Requirements: |
374 | | // - !operand1.is_signed && !operand2.is_signed && !result.is_signed (the function will also work |
375 | | // for signed storages but will extend them with zeroes regardless of the actual sign). |
376 | | template<Bitwise operation> |
377 | | static constexpr void compute_bitwise(IntegerReadonlyStorage<WordType> auto const& operand1, IntegerReadonlyStorage<WordType> auto const& operand2, IntegerStorage<WordType> auto&& result) |
378 | 0 | { |
379 | 0 | size_t size1 = operand1.size(), size2 = operand2.size(), size = result.size(); |
380 | |
|
381 | 0 | for (size_t i = 0; i < size; ++i) { |
382 | 0 | auto word1 = i < size1 ? operand1[i] : 0; |
383 | 0 | auto word2 = i < size2 ? operand2[i] : 0; |
384 | |
|
385 | | if constexpr (operation == Bitwise::AND) |
386 | 0 | result[i] = word1 & word2; |
387 | | else if constexpr (operation == Bitwise::OR) |
388 | 0 | result[i] = word1 | word2; |
389 | | else if constexpr (operation == Bitwise::XOR) |
390 | 0 | result[i] = word1 ^ word2; |
391 | | else if constexpr (operation == Bitwise::INVERT) |
392 | 0 | result[i] = ~word1; |
393 | | else |
394 | | static_assert(((void)operation, false)); |
395 | 0 | } |
396 | 0 | } Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS5_IS6_EENS7_ILb0ELm8EEETkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE0ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS5_IS6_EENS7_ILb0ELm32EEETkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE0ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE3ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE0ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS5_IS6_EENS7_ILb0ELm32EEETkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE0ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE3ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE2ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE0ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE15compute_bitwiseILNS2_7BitwiseE3ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ |
397 | | |
398 | | // See `storage_compute_bitwise` for the signedness requirements. |
399 | | // |
400 | | // NOTE: We want to be able to call all of the storage_* functions like |
401 | | // `storage_*(operand1, operand2, result)`, even if some of the operands are unused (in order |
402 | | // to then easily generate most of the operators via defines). That is why we have unused |
403 | | // first operand here. |
404 | | template<Bitwise operation> |
405 | | static constexpr void compute_inplace_bitwise(IntegerReadonlyStorage<WordType> auto const&, IntegerReadonlyStorage<WordType> auto const& operand2, IntegerStorage<WordType> auto&& result) |
406 | 0 | { |
407 | 0 | size_t min_size = min(result.size(), operand2.size()); |
408 | |
|
409 | 0 | for (size_t i = 0; i < min_size; ++i) { |
410 | | if constexpr (operation == Bitwise::AND) |
411 | | result[i] &= operand2[i]; |
412 | | else if constexpr (operation == Bitwise::OR) |
413 | 0 | result[i] |= operand2[i]; |
414 | | else if constexpr (operation == Bitwise::XOR) |
415 | | result[i] ^= operand2[i]; |
416 | | else |
417 | | static_assert(((void)operation, false)); |
418 | 0 | } |
419 | 0 | } Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE23compute_inplace_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE23compute_inplace_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS5_IS6_EENS7_ILb0ELm8EEETkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE23compute_inplace_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS5_IS6_EES8_TkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE23compute_inplace_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS5_IS6_EENS7_ILb0ELm8EEETkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE23compute_inplace_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS5_IS6_EENS7_ILb0ELm32EEETkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE23compute_inplace_bitwiseILNS2_7BitwiseE1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS5_IS6_EENS7_ILb0ELm32EEETkNS0_14IntegerStorageIS6_EERS8_EEvRKT0_RKT1_OT2_ |
420 | | |
421 | | // Requirements for the next two functions: |
422 | | // - shift < result.size() * word_size; |
423 | | // - result.size() == operand.size(). |
424 | | static constexpr void shift_left(IntegerReadonlyStorage<WordType> auto const& operand, size_t shift, IntegerStorage<WordType> auto&& result) |
425 | 11.6k | { |
426 | 11.6k | size_t size = operand.size(); |
427 | 11.6k | size_t offset = shift / word_size, remainder = shift % word_size; |
428 | | |
429 | 11.6k | if (shift % word_size == 0) { |
430 | 1.37k | for (size_t i = size; i-- > offset;) |
431 | 1.05k | result[i] = operand[i - offset]; |
432 | 2.53k | for (size_t i = 0; i < offset; ++i) |
433 | 2.21k | result[i] = 0; |
434 | 11.3k | } else { |
435 | 118k | for (size_t i = size; --i > offset;) |
436 | 107k | result[i] = (operand[i - offset] << remainder) | (operand[i - offset - 1] >> (word_size - remainder)); |
437 | 11.3k | result[offset] = operand[0] << remainder; |
438 | 50.1k | for (size_t i = 0; i < offset; ++i) |
439 | 38.8k | result[i] = 0; |
440 | 11.3k | } |
441 | 11.6k | } Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ _ZN2AK6Detail17StorageOperationsImE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Line | Count | Source | 425 | 11.6k | { | 426 | 11.6k | size_t size = operand.size(); | 427 | 11.6k | size_t offset = shift / word_size, remainder = shift % word_size; | 428 | | | 429 | 11.6k | if (shift % word_size == 0) { | 430 | 1.37k | for (size_t i = size; i-- > offset;) | 431 | 1.05k | result[i] = operand[i - offset]; | 432 | 2.53k | for (size_t i = 0; i < offset; ++i) | 433 | 2.21k | result[i] = 0; | 434 | 11.3k | } else { | 435 | 118k | for (size_t i = size; --i > offset;) | 436 | 107k | result[i] = (operand[i - offset] << remainder) | (operand[i - offset - 1] >> (word_size - remainder)); | 437 | 11.3k | result[offset] = operand[0] << remainder; | 438 | 50.1k | for (size_t i = 0; i < offset; ++i) | 439 | 38.8k | result[i] = 0; | 440 | 11.3k | } | 441 | 11.6k | } |
Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm4096EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsIjE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanIjLb0EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm257EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm513EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm385EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE10shift_leftITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm769EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ |
442 | | |
443 | | static constexpr void shift_right(IntegerReadonlyStorage<WordType> auto const& operand, size_t shift, IntegerStorage<WordType> auto&& result) |
444 | 0 | { |
445 | 0 | size_t size = operand.size(); |
446 | 0 | size_t offset = shift / word_size, remainder = shift % word_size; |
447 | |
|
448 | 0 | if (shift % word_size == 0) { |
449 | 0 | for (size_t i = 0; i < size - offset; ++i) |
450 | 0 | result[i] = operand[i + offset]; |
451 | 0 | for (size_t i = size - offset; i < size; ++i) |
452 | 0 | result[i] = 0; |
453 | 0 | } else { |
454 | 0 | for (size_t i = 0; i < size - offset - 1; ++i) |
455 | 0 | result[i] = (operand[i + offset] >> remainder) | (operand[i + offset + 1] << (word_size - remainder)); |
456 | 0 | result[size - offset - 1] = operand[size - 1] >> remainder; |
457 | 0 | for (size_t i = size - offset; i < size; ++i) |
458 | 0 | result[i] = 0; |
459 | 0 | } |
460 | 0 | } Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE11shift_rightITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE11shift_rightITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm4096EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsIjE11shift_rightITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanIKjLb0EEETkNS0_14IntegerStorageIS5_EENS6_IjLb0EEEEEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsIjE11shift_rightITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanIjLb0EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE11shift_rightITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE11shift_rightITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm512EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE11shift_rightITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE11shift_rightITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm768EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE11shift_rightITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS0_14IntegerStorageIS5_EERS7_EEvRKS5_mOT0_ |
461 | | |
462 | | // Requirements: |
463 | | // - result.size() >= max(operand1.size(), operand2.size()) (not a real constraint but overflow |
464 | | // detection will not work otherwise). |
465 | | // |
466 | | // Return value: |
467 | | // Let r be the return value of the function and a, b, c -- the integer values stored in `operand1`, |
468 | | // `operand2` and `result`, respectively. Then, |
469 | | // a + b * (-1) ** subtract = c + r * 2 ** (result.size() * word_size). |
470 | | // In particular, r equals 0 iff no overflow has happened. |
471 | | template<bool subtract> |
472 | | static constexpr int add(IntegerReadonlyStorage<WordType> auto const& operand1, IntegerReadonlyStorage<WordType> auto const& operand2, IntegerStorage<WordType> auto&& result, bool carry = false) |
473 | 2.49M | { |
474 | 2.49M | bool sign1 = operand1.is_negative(), sign2 = operand2.is_negative(); |
475 | 2.49M | auto fill1 = extend_sign<WordType>(sign1), fill2 = extend_sign<WordType>(sign2); |
476 | 2.49M | size_t size1 = operand1.size(), size2 = operand2.size(), size = result.size(); |
477 | | |
478 | 9.65M | for (size_t i = 0; i < size; ++i) { |
479 | 7.16M | auto word1 = i < size1 ? operand1[i] : fill1; |
480 | 7.16M | auto word2 = i < size2 ? operand2[i] : fill2; |
481 | | |
482 | | if constexpr (!subtract) |
483 | 7.16M | result[i] = add_words(word1, word2, carry); |
484 | | else |
485 | 0 | result[i] = sub_words(word1, word2, carry); |
486 | 7.16M | } |
487 | | |
488 | | if constexpr (!subtract) |
489 | 2.49M | return -sign1 - sign2 + carry + result.is_negative(); |
490 | | else |
491 | 0 | return -sign1 + sign2 - carry + result.is_negative(); |
492 | 2.49M | } _ZN2AK6Detail17StorageOperationsImE3addILb0ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS4_IS5_EENS6_ILb0ELm64EEETkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Line | Count | Source | 473 | 2.36M | { | 474 | 2.36M | bool sign1 = operand1.is_negative(), sign2 = operand2.is_negative(); | 475 | 2.36M | auto fill1 = extend_sign<WordType>(sign1), fill2 = extend_sign<WordType>(sign2); | 476 | 2.36M | size_t size1 = operand1.size(), size2 = operand2.size(), size = result.size(); | 477 | | | 478 | 7.08M | for (size_t i = 0; i < size; ++i) { | 479 | 4.72M | auto word1 = i < size1 ? operand1[i] : fill1; | 480 | 4.72M | auto word2 = i < size2 ? operand2[i] : fill2; | 481 | | | 482 | | if constexpr (!subtract) | 483 | 4.72M | result[i] = add_words(word1, word2, carry); | 484 | | else | 485 | | result[i] = sub_words(word1, word2, carry); | 486 | 4.72M | } | 487 | | | 488 | | if constexpr (!subtract) | 489 | 2.36M | return -sign1 - sign2 + carry + result.is_negative(); | 490 | | else | 491 | | return -sign1 + sign2 - carry + result.is_negative(); | 492 | 2.36M | } |
_ZN2AK6Detail17StorageOperationsImE3addILb0ETkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS4_IS5_EENS0_13StaticStorageILb0ELm64EEETkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Line | Count | Source | 473 | 131k | { | 474 | 131k | bool sign1 = operand1.is_negative(), sign2 = operand2.is_negative(); | 475 | 131k | auto fill1 = extend_sign<WordType>(sign1), fill2 = extend_sign<WordType>(sign2); | 476 | 131k | size_t size1 = operand1.size(), size2 = operand2.size(), size = result.size(); | 477 | | | 478 | 2.57M | for (size_t i = 0; i < size; ++i) { | 479 | 2.44M | auto word1 = i < size1 ? operand1[i] : fill1; | 480 | 2.44M | auto word2 = i < size2 ? operand2[i] : fill2; | 481 | | | 482 | | if constexpr (!subtract) | 483 | 2.44M | result[i] = add_words(word1, word2, carry); | 484 | | else | 485 | | result[i] = sub_words(word1, word2, carry); | 486 | 2.44M | } | 487 | | | 488 | | if constexpr (!subtract) | 489 | 131k | return -sign1 - sign2 + carry + result.is_negative(); | 490 | | else | 491 | | return -sign1 + sign2 - carry + result.is_negative(); | 492 | 131k | } |
Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb0ETkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb0ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm4096EEETkNS4_IS5_EENS6_ILb0ELm32EEETkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsIjE3addILb0ETkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanIjLb0EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb0ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS4_IS5_EENS6_ILb0ELm32EEETkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb0ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS4_IS5_EENS6_ILb0ELm32EEETkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm257EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE3addILb1ETkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm385EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_EEiRKT0_RKT1_OT2_b |
493 | | |
494 | | // See `storage_add` for the meaning of the return value. |
495 | | template<bool subtract> |
496 | | static constexpr int increment(IntegerStorage<WordType> auto&& operand) |
497 | | { |
498 | | bool carry = true; |
499 | | bool sign = operand.is_negative(); |
500 | | size_t size = operand.size(); |
501 | | |
502 | | for (size_t i = 0; i < size; ++i) { |
503 | | if constexpr (!subtract) |
504 | | operand[i] = add_words<WordType>(operand[i], 0, carry); |
505 | | else |
506 | | operand[i] = sub_words<WordType>(operand[i], 0, carry); |
507 | | } |
508 | | |
509 | | if constexpr (!subtract) |
510 | | return -sign + carry + operand.is_negative(); |
511 | | else |
512 | | return -sign - carry + operand.is_negative(); |
513 | | } |
514 | | |
515 | | // Requirements: |
516 | | // - result.size() == operand.size(). |
517 | | // |
518 | | // Return value: operand != 0. |
519 | | static constexpr bool negate(IntegerReadonlyStorage<WordType> auto const& operand, IntegerStorage<WordType> auto&& result) |
520 | 0 | { |
521 | 0 | bool carry = false; |
522 | 0 | size_t size = operand.size(); |
523 | 0 | for (size_t i = 0; i < size; ++i) |
524 | 0 | result[i] = sub_words<WordType>(0, operand[i], carry); |
525 | 0 | return carry; |
526 | 0 | } Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm64EEETkNS0_14IntegerStorageIS5_EENS0_11StorageSpanImLb0EEEEEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm128EEETkNS0_14IntegerStorageIS5_EERS7_EEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS0_14IntegerStorageIS5_EES7_EEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS0_14IntegerStorageIS5_EERS7_EEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanIKmLb0EEETkNS0_14IntegerStorageIS5_EENS6_ImLb0EEEEEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS0_14IntegerStorageIS5_EENS0_11StorageSpanImLb0EEEEEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm4096EEETkNS0_14IntegerStorageIS5_EENS0_11StorageSpanImLb0EEEEEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm4096EEETkNS0_14IntegerStorageIS5_EERS7_EEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS0_14IntegerStorageIS5_EENS0_11StorageSpanImLb0EEEEEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm512EEETkNS0_14IntegerStorageIS5_EERS7_EEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS0_14IntegerStorageIS5_EERS7_EEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS0_14IntegerStorageIS5_EENS0_11StorageSpanImLb0EEEEEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm768EEETkNS0_14IntegerStorageIS5_EERS7_EEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS0_14IntegerStorageIS5_EERS7_EEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm257EEETkNS0_14IntegerStorageIS5_EENS0_11StorageSpanImLb0EEEEEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm257EEETkNS0_14IntegerStorageIS5_EERS7_EEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm385EEETkNS0_14IntegerStorageIS5_EENS0_11StorageSpanImLb0EEEEEbRKS5_OT0_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE6negateITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm385EEETkNS0_14IntegerStorageIS5_EERS7_EEbRKS5_OT0_ |
527 | | |
528 | | // No allocations will occur if both operands are unsigned. |
529 | | template<IntegerReadonlyStorage<WordType> Operand1, IntegerReadonlyStorage<WordType> Operand2> |
530 | | static constexpr void baseline_mul(Operand1 const& operand1, Operand2 const& operand2, IntegerStorage<WordType> auto&& __restrict__ result, auto&& buffer) |
531 | 2.54M | { |
532 | 2.54M | bool sign1 = operand1.is_negative(), sign2 = operand2.is_negative(); |
533 | 2.54M | size_t size1 = operand1.size(), size2 = operand2.size(), size = result.size(); |
534 | | |
535 | 2.54M | if (size1 == 1 && size2 == 1) { |
536 | | // We do not want to compete with the cleverness of the compiler of multiplying NativeWords. |
537 | 2.51M | ConditionallySignedDoubleWord<WordType, Operand1::is_signed> word1 = operand1[0]; |
538 | 2.51M | ConditionallySignedDoubleWord<WordType, Operand2::is_signed> word2 = operand2[0]; |
539 | 2.51M | auto value = static_cast<DoubleWordType>(word1 * word2); |
540 | | |
541 | 2.51M | result[0] = value; |
542 | 2.51M | if (size > 1) { |
543 | 2.51M | result[1] = value >> word_size; |
544 | | |
545 | 2.51M | auto fill = extend_sign<WordType>(sign1 ^ sign2); |
546 | 2.51M | for (size_t i = 2; i < result.size(); ++i) |
547 | 0 | result[i] = fill; |
548 | 2.51M | } |
549 | 2.51M | return; |
550 | 2.51M | } |
551 | | |
552 | 24.1k | if (size1 < size2) { |
553 | 8.99k | baseline_mul(operand2, operand1, result, buffer); |
554 | 8.99k | return; |
555 | 8.99k | } |
556 | | // Now size1 >= size2 |
557 | | |
558 | | // Normalize signs |
559 | 15.1k | auto data1 = operand1.data(), data2 = operand2.data(); |
560 | 15.1k | if (size2 < size) { |
561 | 15.1k | if (sign1) { |
562 | 0 | auto inverted = buffer.allocate(size1); |
563 | 0 | negate(operand1, StorageSpan<WordType, false> { inverted, size1 }); |
564 | 0 | data1 = inverted; |
565 | 0 | } |
566 | 15.1k | if (sign2) { |
567 | 0 | auto inverted = buffer.allocate(size2); |
568 | 0 | negate(operand2, StorageSpan<WordType, false> { inverted, size2 }); |
569 | 0 | data2 = inverted; |
570 | 0 | } |
571 | 15.1k | } |
572 | 15.1k | size1 = min(size1, size), size2 = min(size2, size); |
573 | | |
574 | | // Do schoolbook O(size1 * size2). |
575 | 15.1k | DoubleWordType carry = 0; |
576 | 196k | for (size_t i = 0; i < size; ++i) { |
577 | 181k | result[i] = static_cast<WordType>(carry); |
578 | 181k | carry >>= word_size; |
579 | | |
580 | 181k | size_t start_index = i >= size2 ? i - size2 + 1 : 0; |
581 | 181k | size_t end_index = min(i + 1, size1); |
582 | | |
583 | 1.00M | for (size_t j = start_index; j < end_index; ++j) { |
584 | 826k | auto x = static_cast<DoubleWordType>(data1[j]) * data2[i - j]; |
585 | | |
586 | 826k | bool ncarry = false; |
587 | 826k | result[i] = add_words(result[i], static_cast<WordType>(x), ncarry); |
588 | 826k | carry += (x >> word_size) + ncarry; |
589 | 826k | } |
590 | 181k | } |
591 | | |
592 | 15.1k | if (size2 < size && (sign1 ^ sign2)) |
593 | 0 | negate(result, result); |
594 | 15.1k | } _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm64EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm128EEERNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Line | Count | Source | 531 | 2.51M | { | 532 | 2.51M | bool sign1 = operand1.is_negative(), sign2 = operand2.is_negative(); | 533 | 2.51M | size_t size1 = operand1.size(), size2 = operand2.size(), size = result.size(); | 534 | | | 535 | 2.51M | if (size1 == 1 && size2 == 1) { | 536 | | // We do not want to compete with the cleverness of the compiler of multiplying NativeWords. | 537 | 2.51M | ConditionallySignedDoubleWord<WordType, Operand1::is_signed> word1 = operand1[0]; | 538 | 2.51M | ConditionallySignedDoubleWord<WordType, Operand2::is_signed> word2 = operand2[0]; | 539 | 2.51M | auto value = static_cast<DoubleWordType>(word1 * word2); | 540 | | | 541 | 2.51M | result[0] = value; | 542 | 2.51M | if (size > 1) { | 543 | 2.51M | result[1] = value >> word_size; | 544 | | | 545 | 2.51M | auto fill = extend_sign<WordType>(sign1 ^ sign2); | 546 | 2.51M | for (size_t i = 2; i < result.size(); ++i) | 547 | 0 | result[i] = fill; | 548 | 2.51M | } | 549 | 2.51M | return; | 550 | 2.51M | } | 551 | | | 552 | 0 | if (size1 < size2) { | 553 | 0 | baseline_mul(operand2, operand1, result, buffer); | 554 | 0 | return; | 555 | 0 | } | 556 | | // Now size1 >= size2 | 557 | | | 558 | | // Normalize signs | 559 | 0 | auto data1 = operand1.data(), data2 = operand2.data(); | 560 | 0 | if (size2 < size) { | 561 | 0 | if (sign1) { | 562 | 0 | auto inverted = buffer.allocate(size1); | 563 | 0 | negate(operand1, StorageSpan<WordType, false> { inverted, size1 }); | 564 | 0 | data1 = inverted; | 565 | 0 | } | 566 | 0 | if (sign2) { | 567 | 0 | auto inverted = buffer.allocate(size2); | 568 | 0 | negate(operand2, StorageSpan<WordType, false> { inverted, size2 }); | 569 | 0 | data2 = inverted; | 570 | 0 | } | 571 | 0 | } | 572 | 0 | size1 = min(size1, size), size2 = min(size2, size); | 573 | | | 574 | | // Do schoolbook O(size1 * size2). | 575 | 0 | DoubleWordType carry = 0; | 576 | 0 | for (size_t i = 0; i < size; ++i) { | 577 | 0 | result[i] = static_cast<WordType>(carry); | 578 | 0 | carry >>= word_size; | 579 | |
| 580 | 0 | size_t start_index = i >= size2 ? i - size2 + 1 : 0; | 581 | 0 | size_t end_index = min(i + 1, size1); | 582 | |
| 583 | 0 | for (size_t j = start_index; j < end_index; ++j) { | 584 | 0 | auto x = static_cast<DoubleWordType>(data1[j]) * data2[i - j]; | 585 | |
| 586 | 0 | bool ncarry = false; | 587 | 0 | result[i] = add_words(result[i], static_cast<WordType>(x), ncarry); | 588 | 0 | carry += (x >> word_size) + ncarry; | 589 | 0 | } | 590 | 0 | } | 591 | |
| 592 | 0 | if (size2 < size && (sign1 ^ sign2)) | 593 | 0 | negate(result, result); | 594 | 0 | } |
Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS4_IS5_EENS0_13StaticStorageILb0ELm64EEETkNS0_14IntegerStorageIS5_EES7_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm64EEETkNS4_IS5_EENS0_11StorageSpanImLb0EEETkNS0_14IntegerStorageIS5_EERS9_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS4_IS5_EENS0_13StaticStorageILb0ELm64EEETkNS0_14IntegerStorageIS5_EERS7_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS4_IS5_EENS6_IKmLb0EEETkNS0_14IntegerStorageIS5_EES7_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Line | Count | Source | 531 | 15.1k | { | 532 | 15.1k | bool sign1 = operand1.is_negative(), sign2 = operand2.is_negative(); | 533 | 15.1k | size_t size1 = operand1.size(), size2 = operand2.size(), size = result.size(); | 534 | | | 535 | 15.1k | if (size1 == 1 && size2 == 1) { | 536 | | // We do not want to compete with the cleverness of the compiler of multiplying NativeWords. | 537 | 0 | ConditionallySignedDoubleWord<WordType, Operand1::is_signed> word1 = operand1[0]; | 538 | 0 | ConditionallySignedDoubleWord<WordType, Operand2::is_signed> word2 = operand2[0]; | 539 | 0 | auto value = static_cast<DoubleWordType>(word1 * word2); | 540 | |
| 541 | 0 | result[0] = value; | 542 | 0 | if (size > 1) { | 543 | 0 | result[1] = value >> word_size; | 544 | |
| 545 | 0 | auto fill = extend_sign<WordType>(sign1 ^ sign2); | 546 | 0 | for (size_t i = 2; i < result.size(); ++i) | 547 | 0 | result[i] = fill; | 548 | 0 | } | 549 | 0 | return; | 550 | 0 | } | 551 | | | 552 | 15.1k | if (size1 < size2) { | 553 | 8.99k | baseline_mul(operand2, operand1, result, buffer); | 554 | 8.99k | return; | 555 | 8.99k | } | 556 | | // Now size1 >= size2 | 557 | | | 558 | | // Normalize signs | 559 | 6.15k | auto data1 = operand1.data(), data2 = operand2.data(); | 560 | 6.15k | if (size2 < size) { | 561 | 6.15k | if (sign1) { | 562 | 0 | auto inverted = buffer.allocate(size1); | 563 | 0 | negate(operand1, StorageSpan<WordType, false> { inverted, size1 }); | 564 | 0 | data1 = inverted; | 565 | 0 | } | 566 | 6.15k | if (sign2) { | 567 | 0 | auto inverted = buffer.allocate(size2); | 568 | 0 | negate(operand2, StorageSpan<WordType, false> { inverted, size2 }); | 569 | 0 | data2 = inverted; | 570 | 0 | } | 571 | 6.15k | } | 572 | 6.15k | size1 = min(size1, size), size2 = min(size2, size); | 573 | | | 574 | | // Do schoolbook O(size1 * size2). | 575 | 6.15k | DoubleWordType carry = 0; | 576 | 48.0k | for (size_t i = 0; i < size; ++i) { | 577 | 41.9k | result[i] = static_cast<WordType>(carry); | 578 | 41.9k | carry >>= word_size; | 579 | | | 580 | 41.9k | size_t start_index = i >= size2 ? i - size2 + 1 : 0; | 581 | 41.9k | size_t end_index = min(i + 1, size1); | 582 | | | 583 | 122k | for (size_t j = start_index; j < end_index; ++j) { | 584 | 80.6k | auto x = static_cast<DoubleWordType>(data1[j]) * data2[i - j]; | 585 | | | 586 | 80.6k | bool ncarry = false; | 587 | 80.6k | result[i] = add_words(result[i], static_cast<WordType>(x), ncarry); | 588 | 80.6k | carry += (x >> word_size) + ncarry; | 589 | 80.6k | } | 590 | 41.9k | } | 591 | | | 592 | 6.15k | if (size2 < size && (sign1 ^ sign2)) | 593 | 0 | negate(result, result); | 594 | 6.15k | } |
_ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanIKmLb0EEETkNS4_IS5_EENS6_ImLb0EEETkNS0_14IntegerStorageIS5_EERS9_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Line | Count | Source | 531 | 8.99k | { | 532 | 8.99k | bool sign1 = operand1.is_negative(), sign2 = operand2.is_negative(); | 533 | 8.99k | size_t size1 = operand1.size(), size2 = operand2.size(), size = result.size(); | 534 | | | 535 | 8.99k | if (size1 == 1 && size2 == 1) { | 536 | | // We do not want to compete with the cleverness of the compiler of multiplying NativeWords. | 537 | 0 | ConditionallySignedDoubleWord<WordType, Operand1::is_signed> word1 = operand1[0]; | 538 | 0 | ConditionallySignedDoubleWord<WordType, Operand2::is_signed> word2 = operand2[0]; | 539 | 0 | auto value = static_cast<DoubleWordType>(word1 * word2); | 540 | |
| 541 | 0 | result[0] = value; | 542 | 0 | if (size > 1) { | 543 | 0 | result[1] = value >> word_size; | 544 | |
| 545 | 0 | auto fill = extend_sign<WordType>(sign1 ^ sign2); | 546 | 0 | for (size_t i = 2; i < result.size(); ++i) | 547 | 0 | result[i] = fill; | 548 | 0 | } | 549 | 0 | return; | 550 | 0 | } | 551 | | | 552 | 8.99k | if (size1 < size2) { | 553 | 0 | baseline_mul(operand2, operand1, result, buffer); | 554 | 0 | return; | 555 | 0 | } | 556 | | // Now size1 >= size2 | 557 | | | 558 | | // Normalize signs | 559 | 8.99k | auto data1 = operand1.data(), data2 = operand2.data(); | 560 | 8.99k | if (size2 < size) { | 561 | 8.99k | if (sign1) { | 562 | 0 | auto inverted = buffer.allocate(size1); | 563 | 0 | negate(operand1, StorageSpan<WordType, false> { inverted, size1 }); | 564 | 0 | data1 = inverted; | 565 | 0 | } | 566 | 8.99k | if (sign2) { | 567 | 0 | auto inverted = buffer.allocate(size2); | 568 | 0 | negate(operand2, StorageSpan<WordType, false> { inverted, size2 }); | 569 | 0 | data2 = inverted; | 570 | 0 | } | 571 | 8.99k | } | 572 | 8.99k | size1 = min(size1, size), size2 = min(size2, size); | 573 | | | 574 | | // Do schoolbook O(size1 * size2). | 575 | 8.99k | DoubleWordType carry = 0; | 576 | 148k | for (size_t i = 0; i < size; ++i) { | 577 | 139k | result[i] = static_cast<WordType>(carry); | 578 | 139k | carry >>= word_size; | 579 | | | 580 | 139k | size_t start_index = i >= size2 ? i - size2 + 1 : 0; | 581 | 139k | size_t end_index = min(i + 1, size1); | 582 | | | 583 | 885k | for (size_t j = start_index; j < end_index; ++j) { | 584 | 745k | auto x = static_cast<DoubleWordType>(data1[j]) * data2[i - j]; | 585 | | | 586 | 745k | bool ncarry = false; | 587 | 745k | result[i] = add_words(result[i], static_cast<WordType>(x), ncarry); | 588 | 745k | carry += (x >> word_size) + ncarry; | 589 | 745k | } | 590 | 139k | } | 591 | | | 592 | 8.99k | if (size2 < size && (sign1 ^ sign2)) | 593 | 0 | negate(result, result); | 594 | 8.99k | } |
Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_11StorageSpanImLb0EEETkNS4_IS5_EENS6_IKmLb0EEETkNS0_14IntegerStorageIS5_EERS7_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm32EEETkNS4_IS5_EENS6_ILb0ELm4096EEETkNS0_14IntegerStorageIS5_EERS8_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm4096EEETkNS4_IS5_EENS6_ILb0ELm32EEETkNS0_14IntegerStorageIS5_EERS7_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm512EEERNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm256EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERNS6_ILb0ELm768EEERNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm384EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm257EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ Unexecuted instantiation: _ZN2AK6Detail17StorageOperationsImE12baseline_mulITkNS0_22IntegerReadonlyStorageIT_EENS0_13StaticStorageILb0ELm385EEETkNS4_IS5_EES7_TkNS0_14IntegerStorageIS5_EERS7_RNS0_13NullAllocatorEEEvRKS5_RKT0_OT1_OT2_ |
595 | | |
596 | | template<bool restore_remainder = false> |
597 | | static constexpr void div_mod_internal( |
598 | | StorageSpan<WordType, false> dividend, StorageSpan<WordType, false> divisor, |
599 | | StorageSpan<WordType, false> quotient, StorageSpan<WordType, false> remainder, |
600 | | size_t dividend_len, size_t divisor_len) |
601 | 0 | { |
602 | | // Knuth's algorithm D |
603 | | // D1. Normalize |
604 | | // FIXME: Investigate GCC producing bogus -Warray-bounds when dividing u128 by u32. This code |
605 | | // should not be reachable at all in this case because fast paths above cover all cases |
606 | | // when `operand2.size() == 1`. |
607 | 0 | AK_IGNORE_DIAGNOSTIC("-Warray-bounds", size_t shift = count_leading_zeroes(divisor[divisor_len - 1]);) |
608 | 0 | shift_left(dividend, shift, dividend); |
609 | 0 | shift_left(divisor, shift, divisor); |
610 | |
|
611 | 0 | auto divisor_approx = divisor[divisor_len - 1]; |
612 | |
|
613 | 0 | for (size_t i = dividend_len + 1; i-- > divisor_len;) { |
614 | | // D3. Calculate qhat |
615 | 0 | WordType qhat; |
616 | 0 | VERIFY(dividend[i] <= divisor_approx); |
617 | 0 | if (dividend[i] == divisor_approx) { |
618 | 0 | qhat = NumericLimits<WordType>::max(); |
619 | 0 | } else { |
620 | 0 | WordType rhat; |
621 | 0 | qhat = div_mod_words(dividend[i - 1], dividend[i], divisor_approx, rhat); |
622 | |
|
623 | 0 | auto is_qhat_too_large = [&] { |
624 | 0 | return wide_multiply(qhat, divisor[divisor_len - 2]) > dword(dividend[i - 2], rhat); |
625 | 0 | }; Unexecuted instantiation: AK::Detail::StorageOperations<unsigned int>::div_mod_internal<true>(AK::Detail::StorageSpan<unsigned int, false>, AK::Detail::StorageSpan<unsigned int, false>, AK::Detail::StorageSpan<unsigned int, false>, AK::Detail::StorageSpan<unsigned int, false>, unsigned long, unsigned long)::{lambda()#1}::operator()() constUnexecuted instantiation: AK::Detail::StorageOperations<unsigned long>::div_mod_internal<true>(AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, unsigned long, unsigned long)::{lambda()#1}::operator()() const |
626 | 0 | if (is_qhat_too_large()) { |
627 | 0 | --qhat; |
628 | 0 | bool carry = false; |
629 | 0 | rhat = add_words(rhat, divisor_approx, carry); |
630 | 0 | if (!carry && is_qhat_too_large()) |
631 | 0 | --qhat; |
632 | 0 | } |
633 | 0 | } |
634 | | |
635 | | // D4. Multiply & subtract |
636 | 0 | WordType mul_carry = 0; |
637 | 0 | bool sub_carry = false; |
638 | 0 | for (size_t j = 0; j < divisor_len; ++j) { |
639 | 0 | auto mul_result = wide_multiply(qhat, divisor[j]) + mul_carry; |
640 | 0 | auto& output = dividend[i + j - divisor_len]; |
641 | 0 | output = sub_words(output, static_cast<WordType>(mul_result), sub_carry); |
642 | 0 | mul_carry = mul_result >> word_size; |
643 | 0 | } |
644 | 0 | dividend[i] = sub_words(dividend[i], mul_carry, sub_carry); |
645 | |
|
646 | 0 | if (sub_carry) { |
647 | | // D6. Add back |
648 | 0 | auto dividend_part = StorageSpan<WordType, false> { dividend.slice(i - divisor_len, divisor_len + 1) }; |
649 | 0 | auto overflow = add<false>(dividend_part, divisor, dividend_part); |
650 | 0 | VERIFY(overflow == 1); |
651 | 0 | } |
652 | | |
653 | 0 | quotient[i - divisor_len] = qhat - sub_carry; |
654 | 0 | } |
655 | | |
656 | 0 | for (size_t i = dividend_len - divisor_len + 1; i < quotient.size(); ++i) |
657 | 0 | quotient[i] = 0; |
658 | | |
659 | | // D8. Unnormalize |
660 | | if constexpr (restore_remainder) |
661 | 0 | shift_right(StorageSpan<WordType, false> { dividend.trim(remainder.size()) }, shift, remainder); |
662 | 0 | } Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::div_mod_internal<false>(AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, unsigned long, unsigned long) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned int>::div_mod_internal<true>(AK::Detail::StorageSpan<unsigned int, false>, AK::Detail::StorageSpan<unsigned int, false>, AK::Detail::StorageSpan<unsigned int, false>, AK::Detail::StorageSpan<unsigned int, false>, unsigned long, unsigned long) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::div_mod_internal<true>(AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, unsigned long, unsigned long) Unexecuted instantiation: void AK::Detail::StorageOperations<unsigned long>::div_mod_internal<true>(AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, AK::Detail::StorageSpan<unsigned long, false>, unsigned long, unsigned long) |
663 | | }; |
664 | | |
665 | | } |
666 | | |
667 | | using Detail::StorageOperations, Detail::NativeWord, Detail::native_word_size, Detail::max_native_word, |
668 | | Detail::UnsignedStorageSpan, Detail::UnsignedStorageReadonlySpan; |
669 | | |
670 | | inline Detail::NullAllocator g_null_allocator; |
671 | | |
672 | | } |