aboutsummaryrefslogtreecommitdiff
path: root/templates
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-01-21 08:03:15 +0100
committerSebastiano Tronto <sebastiano@tronto.net>2025-01-21 08:03:21 +0100
commit97c94018402c95f2ed1f59645f4b1cbdd255b978 (patch)
tree01143ccecff8b4ac74b233767d380ab4485338ac /templates
parent6cfb7b4d8e6b0838bfcde290e5fac18f46f18fb4 (diff)
downloadtaming-cpp-97c94018402c95f2ed1f59645f4b1cbdd255b978.tar.gz
taming-cpp-97c94018402c95f2ed1f59645f4b1cbdd255b978.zip
Final examples for templates
Diffstat (limited to 'templates')
-rw-r--r--templates/bigint.h255
-rw-r--r--templates/zmodn-1.cpp1
-rw-r--r--templates/zmodn-2.cpp1
-rw-r--r--templates/zmodn-3.cpp57
-rw-r--r--templates/zmodn-4.cpp71
5 files changed, 383 insertions, 2 deletions
diff --git a/templates/bigint.h b/templates/bigint.h
new file mode 100644
index 0000000..8380211
--- /dev/null
+++ b/templates/bigint.h
@@ -0,0 +1,255 @@
1// Taken from https://git.tronto.net/zmodn
2
3#ifndef BIGUNSIGNED_H
4#define BIGUNSIGNED_H
5
6#include <cstdint>
7#include <iostream>
8#include <string_view>
9
10constexpr uint64_t abs64(int64_t);
11constexpr uint64_t pow10(uint64_t);
12
13// Big integer class for numbers of at most N decimal digits.
14// The number E is used to tune the size of each digit, mostly for
15// testing purposes.
16
17template<uint64_t N = 50, uint64_t E = 9>
18requires (E < 10)
19class BigInt {
20public:
21 // The member variables sign and digits are declared public so that
22 // BigInt becomes a structural type and can be used in templates.
23
24 static constexpr uint64_t M = pow10(E);
25 static constexpr uint64_t D = (N / E) + 1;
26
27 bool sign;
28 uint64_t digits[D];
29
30 constexpr BigInt() : sign{true} {
31 std::fill(digits, digits+D, 0);
32 }
33
34 constexpr BigInt(int64_t n) : sign{n >= 0} {
35 std::fill(digits, digits+D, 0);
36 digits[0] = abs64(n);
37 carryover();
38 }
39
40 constexpr BigInt(const std::string_view s) : sign{true} {
41 std::fill(digits, digits+D, 0);
42 if (s.size() == 0)
43 return;
44 for (int i = s.size()-1, j = 0; i >= 0; i--, j++) {
45 if (s[i] == '\'')
46 continue;
47 if (i == 0 && s[i] == '-') {
48 sign = false;
49 break;
50 }
51 digits[j/E] += (pow10(j % E))
52 * static_cast<uint64_t>(s[i] - '0');
53 }
54 }
55
56 constexpr auto operator<=>(const BigInt& other) const {
57 if (sign != other.sign)
58 return sign <=> other.sign;
59
60 for (int i = D-1; i >= 0; i--)
61 if (digits[i] != other.digits[i])
62 return sign ?
63 digits[i] <=> other.digits[i] :
64 other.digits[i] <=> digits[i];
65
66 return 0 <=> 0;
67 }
68
69 constexpr bool operator==(const BigInt& other) const = default;
70
71 constexpr BigInt abs() const {
72 BigInt ret = *this;
73 ret.sign = true;
74 return ret;
75 }
76
77 constexpr BigInt operator-() const {
78 if (*this == 0)
79 return 0;
80 BigInt ret = *this;
81 ret.sign = !ret.sign;
82 return ret;
83 }
84
85 constexpr BigInt operator+(const BigInt& z) const {
86 if (sign && z.sign)
87 return positive_sum(*this, z);
88 else if (sign && !z.sign)
89 return positive_diff(*this, -z);
90 else if (!sign && z.sign)
91 return positive_diff(z, -*this);
92 else
93 return -positive_sum(-*this, -z);
94 }
95
96 constexpr BigInt operator-(const BigInt& z) const {
97 return *this + (-z);
98 }
99
100 constexpr BigInt operator*(const BigInt& z) const {
101 BigInt ret;
102 ret.sign = !(sign ^ z.sign);
103 for (int i = 0; i < D; i++)
104 for (int j = 0; i+j < D; j++)
105 ret.digits[i+j] += digits[i] * z.digits[j];
106 ret.carryover();
107 return ret;
108 }
109
110 constexpr BigInt operator/(const BigInt& z) const {
111 auto [q, r] = euclidean_division(*this, z);
112 return q;
113 }
114
115 constexpr BigInt operator%(const BigInt& z) const {
116 auto [q, r] = euclidean_division(*this, z);
117 return r;
118 }
119
120 constexpr BigInt operator+=(const BigInt& z) { return *this = *this + z; }
121 constexpr BigInt operator++() { return *this += 1; }
122 constexpr BigInt operator-=(const BigInt& z) { return *this = *this - z; }
123 constexpr BigInt operator--() { return *this -= 1; }
124 constexpr BigInt operator*=(const BigInt& z) { return *this = *this * z; }
125 constexpr BigInt operator/=(const BigInt& z) { return *this = *this / z; }
126 constexpr BigInt operator%=(const BigInt& z) { return *this = *this % z; }
127
128 friend std::ostream& operator<<(std::ostream& os, const BigInt<N, E>& z) {
129 if (z == 0) {
130 os << "0";
131 return os;
132 }
133
134 if (!z.sign)
135 os << "-";
136
137 int j;
138 for (j = z.D-1; z.digits[j] == 0; j--) ;
139 os << z.digits[j]; // Top digit is not padded
140
141 for (int i = j-1; i >= 0; i--) {
142 std::string num = std::to_string(z.digits[i]);
143 os << std::string(E - num.length(), '0') << num;
144 }
145 return os;
146 }
147
148private:
149 constexpr void carryover() {
150 for (int i = 1; i < D; i++) {
151 auto c = digits[i-1] / M;
152 digits[i-1] -= c * M;
153 digits[i] += c;
154 }
155 }
156
157 constexpr BigInt half() const {
158 BigInt ret;
159 uint64_t carry = 0;
160 for (int i = D-1; i >= 0; i--) {
161 ret.digits[i] += (digits[i] + M * carry) / 2;
162 carry = digits[i] % 2;
163 }
164 return ret;
165 }
166
167 static constexpr BigInt powM(uint64_t e) {
168 BigInt ret;
169 ret.digits[e] = 1;
170 return ret;
171 }
172
173 // Sum of non-negative integers
174 static constexpr BigInt positive_sum(const BigInt& x, const BigInt& y) {
175 BigInt ret;
176 for (int i = 0; i < D; i++)
177 ret.digits[i] = x.digits[i] + y.digits[i];
178 ret.carryover();
179 return ret;
180 }
181
182 // Difference of non-negative integers (result may be negative)
183 static constexpr BigInt positive_diff(const BigInt& x, const BigInt& y) {
184 if (y > x)
185 return -positive_diff(y, x);
186
187 BigInt ret;
188 uint64_t carry = 0;
189 for (int i = 0; i < D; i++) {
190 uint64_t oldcarry = carry;
191 if (x.digits[i] < y.digits[i] + oldcarry) {
192 ret.digits[i] = M;
193 carry = 1;
194 } else {
195 carry = 0;
196 }
197 ret.digits[i] += x.digits[i];
198 ret.digits[i] -= y.digits[i] + oldcarry;
199 }
200 ret.carryover();
201 return ret;
202 }
203
204 // Division with remainder, UB if y == 0
205 static constexpr std::pair<BigInt, BigInt>
206 euclidean_division(const BigInt& x, const BigInt& y) {
207 auto [q, r] = positive_div(x.abs(), y.abs());
208 if (x.sign && y.sign)
209 return std::pair(q, r);
210 else if (x.sign && !y.sign)
211 return r == 0 ? std::pair(-q, 0) : std::pair(-q-1, y+r);
212 else if (!x.sign && y.sign)
213 return r == 0 ? std::pair(-q, r) : std::pair(-q-1, y-r);
214 else
215 return std::pair(q, -r);
216 }
217
218 // Division with remainder of non-negative integers, UB if y == 0
219 // This method is inefficient (O(log(x/y)) BigInt multiplications)
220 static constexpr std::pair<BigInt, BigInt>
221 positive_div(const BigInt& x, const BigInt& y) {
222 BigInt q = 0;
223 BigInt r = x;
224
225 if (y > x)
226 return std::pair(q, r);
227
228 BigInt lb = 0;
229 BigInt ub = x;
230 while (true) {
231 BigInt q = (ub + lb).half();
232 BigInt r = x - y*q;
233
234 if (r < 0)
235 ub = q;
236 else if (r >= y)
237 lb = q+1;
238 else
239 return std::pair(q, r);
240 }
241 }
242};
243
244constexpr uint64_t abs64(int64_t x) {
245 return static_cast<uint64_t>(x > 0 ? x : -x);
246}
247
248constexpr uint64_t pow10(uint64_t e) {
249 if (e == 0)
250 return 1;
251 else
252 return 10 * pow10(e-1);
253}
254
255#endif
diff --git a/templates/zmodn-1.cpp b/templates/zmodn-1.cpp
index a8e26ca..1c1450b 100644
--- a/templates/zmodn-1.cpp
+++ b/templates/zmodn-1.cpp
@@ -14,7 +14,6 @@ public:
14 int value; 14 int value;
15 15
16 Zmod(int z) : value{(z%N + N) % N} {} 16 Zmod(int z) : value{(z%N + N) % N} {}
17 int toint() const { return value; }
18 17
19 Zmod operator+(const Zmod& z) const { return value + z.value; } 18 Zmod operator+(const Zmod& z) const { return value + z.value; }
20 Zmod operator-(const Zmod& z) const { return value - z.value; } 19 Zmod operator-(const Zmod& z) const { return value - z.value; }
diff --git a/templates/zmodn-2.cpp b/templates/zmodn-2.cpp
index f96ef8c..d71f2ac 100644
--- a/templates/zmodn-2.cpp
+++ b/templates/zmodn-2.cpp
@@ -15,7 +15,6 @@ public:
15 int value; 15 int value;
16 16
17 Zmod(int z) : value{(z%N + N) % N} {} 17 Zmod(int z) : value{(z%N + N) % N} {}
18 int toint() const { return value; }
19 18
20 Zmod operator+(const Zmod& z) const { return value + z.value; } 19 Zmod operator+(const Zmod& z) const { return value + z.value; }
21 Zmod operator-(const Zmod& z) const { return value - z.value; } 20 Zmod operator-(const Zmod& z) const { return value - z.value; }
diff --git a/templates/zmodn-3.cpp b/templates/zmodn-3.cpp
new file mode 100644
index 0000000..ca73783
--- /dev/null
+++ b/templates/zmodn-3.cpp
@@ -0,0 +1,57 @@
1#include "bigint.h"
2
3#include <iostream>
4#include <optional>
5#include <tuple>
6#include <type_traits>
7
8template<typename T>
9std::tuple<T, T, T> extended_gcd(T a, T b) {
10 if (b == 0) return {a, 1, 0};
11 auto [g, x, y] = extended_gcd(b, a%b);
12 return {g, y, x - y*(a/b)};
13}
14
15template<auto N>
16requires (N > 1)
17class Zmod {
18public:
19 decltype(N) value;
20
21 Zmod(decltype(N) z) : value{(z%N + N) % N} {}
22
23 Zmod operator+(const Zmod& z) const { return value + z.value; }
24 Zmod operator-(const Zmod& z) const { return value - z.value; }
25 Zmod operator*(const Zmod& z) const { return value * z.value; }
26
27 std::optional<Zmod> inverse() const {
28 auto [g, a, _] = extended_gcd(value, N);
29 return g == 1 ? Zmod(a) : std::optional<Zmod>{};
30 }
31
32 std::optional<Zmod> operator/(const Zmod& d) const {
33 auto i = d.inverse();
34 return i ? (*this) * i.value() : i;
35 }
36
37 std::optional<Zmod> operator/=(const Zmod& d) {
38 auto q = *this / d;
39 return q ? (*this = q.value()) : q;
40 }
41};
42
43int main() {
44 constexpr BigInt N("1000000000000000000000000000000");
45 Zmod<N> x(BigInt("123456781234567812345678"));
46 Zmod<N> y(BigInt("987654321987654321"));
47
48 std::cout << x.value << " * "
49 << y.value << " (mod " << N << ") = "
50 << (x * y).value << std::endl;
51
52 // The following gives a compile error on the first % operation
53 // constexpr double M = 3.14;
54 // Zmod<M> z(4);
55
56 return 0;
57}
diff --git a/templates/zmodn-4.cpp b/templates/zmodn-4.cpp
new file mode 100644
index 0000000..d3530c2
--- /dev/null
+++ b/templates/zmodn-4.cpp
@@ -0,0 +1,71 @@
1#include "bigint.h"
2
3#include <iostream>
4#include <optional>
5#include <tuple>
6#include <type_traits>
7
8template<typename T>
9concept Integer = requires(T a, T b, int i) {
10 {T(i)};
11
12 {a + b} -> std::same_as<T>;
13 {a - b} -> std::same_as<T>;
14 {a * b} -> std::same_as<T>;
15 {a / b} -> std::same_as<T>;
16 {a % b} -> std::same_as<T>;
17
18 {a == b} -> std::same_as<bool>;
19 {a != b} -> std::same_as<bool>;
20};
21
22template<Integer T>
23std::tuple<T, T, T> extended_gcd(T a, T b) {
24 if (b == 0) return {a, 1, 0};
25 auto [g, x, y] = extended_gcd(b, a%b);
26 return {g, y, x - y*(a/b)};
27}
28
29template<Integer auto N>
30requires (N > 1)
31class Zmod {
32public:
33 decltype(N) value;
34
35 Zmod(decltype(N) z) : value{(z%N + N) % N} {}
36
37 Zmod operator+(const Zmod& z) const { return value + z.value; }
38 Zmod operator-(const Zmod& z) const { return value - z.value; }
39 Zmod operator*(const Zmod& z) const { return value * z.value; }
40
41 std::optional<Zmod> inverse() const {
42 auto [g, a, _] = extended_gcd(value, N);
43 return g == 1 ? Zmod(a) : std::optional<Zmod>{};
44 }
45
46 std::optional<Zmod> operator/(const Zmod& d) const {
47 auto i = d.inverse();
48 return i ? (*this) * i.value() : i;
49 }
50
51 std::optional<Zmod> operator/=(const Zmod& d) {
52 auto q = *this / d;
53 return q ? (*this = q.value()) : q;
54 }
55};
56
57int main() {
58 constexpr BigInt N("1000000000000000000000000000000");
59 Zmod<N> x(BigInt("123456781234567812345678"));
60 Zmod<N> y(BigInt("987654321987654321"));
61
62 std::cout << x.value << " * "
63 << y.value << " (mod " << N << ") = "
64 << (x * y).value << std::endl;
65
66 // The following line gives an error when trying to specialize Zmod<M>
67 // constexpr double M = 3.14;
68 // Zmod<M> z(4);
69
70 return 0;
71}

Generated with cgit - Back to sebastiano.tronto.net