Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/rings/bernmm/bern_rat.cpp
4057 views
1
/*
2
bern_rat.cpp: multi-modular algorithm for computing Bernoulli numbers
3
4
Copyright (C) 2008, 2009, David Harvey
5
6
This file is part of the bernmm package (version 1.1).
7
8
bernmm is released under a BSD-style license. See the README file in
9
the source distribution for details.
10
*/
11
12
#include <gmp.h>
13
#include <NTL/ZZ.h>
14
#include <cmath>
15
#include <vector>
16
#include <set>
17
#include "bern_modp_util.h"
18
#include "bern_modp.h"
19
#include "bern_rat.h"
20
21
#ifdef USE_THREADS
22
#include <pthread.h>
23
#endif
24
25
26
using namespace std;
27
using namespace NTL;
28
29
30
namespace bernmm {
31
32
33
/*
34
Computes the denominator of B_k using Clausen/von Staudt.
35
*/
36
void bern_den(mpz_t res, long k, const PrimeTable& table)
37
{
38
mpz_set_ui(res, 1);
39
40
// loop through factors of k
41
for (long f = 1; f*f <= k; f++)
42
{
43
// if f divides k....
44
if (k % f == 0)
45
{
46
// ... then both f + 1 and k/f + 1 are candidates for primes
47
// dividing the denominator of B_k
48
if (table.is_prime(f + 1))
49
mpz_mul_ui(res, res, f + 1);
50
51
if (f*f != k)
52
if (table.is_prime(k/f + 1))
53
mpz_mul_ui(res, res, k/f + 1);
54
}
55
}
56
}
57
58
59
// width of interval for each block
60
#define BLOCK_SIZE 1000
61
62
63
/*
64
Represents that B_k is congruent to _residue_ modulo _modulus_.
65
*/
66
struct Item
67
{
68
mpz_t modulus;
69
mpz_t residue;
70
71
Item()
72
{
73
mpz_init(modulus);
74
mpz_init(residue);
75
}
76
77
~Item()
78
{
79
mpz_clear(residue);
80
mpz_clear(modulus);
81
}
82
};
83
84
85
/*
86
Items get sorted by modulus.
87
*/
88
struct Item_cmp
89
{
90
bool operator()(const Item* x, const Item* y)
91
{
92
return mpz_cmp(x->modulus, y->modulus) < 0;
93
}
94
};
95
96
97
/*
98
Returns new Item that combines information from op1 and op2 via CRT.
99
*/
100
Item* CRT(Item* op1, Item* op2)
101
{
102
Item* res = new Item;
103
104
// let n1, n2 be the moduli, and r1, r2 be the residues
105
106
// res->modulus = t, where t = 0 mod n1, t = 1 mod n2
107
mpz_invert(res->modulus, op1->modulus, op2->modulus);
108
mpz_mul(res->modulus, res->modulus, op1->modulus);
109
110
// res->residue = r2 - r1
111
mpz_sub(res->residue, op2->residue, op1->residue);
112
// res->residue = t * (r2 - r1)
113
mpz_mul(res->residue, res->residue, res->modulus);
114
// res->residue = r1 + t * (r2 - r1)
115
mpz_add(res->residue, res->residue, op1->residue);
116
// res->modulus = n1 * n2
117
mpz_mul(res->modulus, op1->modulus, op2->modulus);
118
// res->residue = r1 mod n1, r2 = mod n2
119
mpz_mod(res->residue, res->residue, res->modulus);
120
121
return res;
122
}
123
124
125
struct State
126
{
127
long k;
128
long bound; // only use primes less than this bound
129
const PrimeTable* table;
130
131
// index of block that should be processed next
132
long next;
133
134
std::set<Item*, Item_cmp> items;
135
#ifdef USE_THREADS
136
pthread_mutex_t lock;
137
#endif
138
139
State(long k, long bound, const PrimeTable& table)
140
{
141
this->k = k;
142
this->bound = bound;
143
this->next = 0;
144
this->table = &table;
145
#ifdef USE_THREADS
146
pthread_mutex_init(&lock, NULL);
147
#endif
148
}
149
150
~State()
151
{
152
#ifdef USE_THREADS
153
pthread_mutex_destroy(&lock);
154
#endif
155
}
156
};
157
158
159
void* worker(void* arg)
160
{
161
State& state = *((State*) arg);
162
long k = state.k;
163
164
#ifdef USE_THREADS
165
pthread_mutex_lock(&state.lock);
166
#endif
167
168
while (1)
169
{
170
if (state.next * BLOCK_SIZE < state.bound)
171
{
172
// need to generate more modular data
173
174
long next = state.next++;
175
#ifdef USE_THREADS
176
pthread_mutex_unlock(&state.lock);
177
#endif
178
179
Item* item = new Item;
180
181
mpz_set_ui(item->modulus, 1);
182
mpz_set_ui(item->residue, 0);
183
184
for (long p = max(5, state.table->next_prime(next * BLOCK_SIZE));
185
p < state.bound && p < (next+1) * BLOCK_SIZE;
186
p = state.table->next_prime(p))
187
{
188
if (k % (p-1) == 0)
189
continue;
190
191
// compute B_k mod p
192
long b = bern_modp(p, k);
193
194
// CRT into running total
195
long x = MulMod(SubMod(b, mpz_fdiv_ui(item->residue, p), p),
196
InvMod(mpz_fdiv_ui(item->modulus, p), p), p);
197
mpz_addmul_ui(item->residue, item->modulus, x);
198
mpz_mul_ui(item->modulus, item->modulus, p);
199
}
200
201
#ifdef USE_THREADS
202
pthread_mutex_lock(&state.lock);
203
#endif
204
state.items.insert(item);
205
}
206
else
207
{
208
// all modular data has been generated
209
210
if (state.items.size() <= 1)
211
{
212
// no more CRTs for this thread to perform
213
#ifdef USE_THREADS
214
pthread_mutex_unlock(&state.lock);
215
#endif
216
return NULL;
217
}
218
219
// CRT two smallest items together
220
Item* item1 = *(state.items.begin());
221
state.items.erase(state.items.begin());
222
Item* item2 = *(state.items.begin());
223
state.items.erase(state.items.begin());
224
#ifdef USE_THREADS
225
pthread_mutex_unlock(&state.lock);
226
#endif
227
228
Item* item3 = CRT(item1, item2);
229
delete item1;
230
delete item2;
231
232
#ifdef USE_THREADS
233
pthread_mutex_lock(&state.lock);
234
#endif
235
state.items.insert(item3);
236
}
237
}
238
}
239
240
241
void bern_rat(mpq_t res, long k, int num_threads)
242
{
243
// special cases
244
245
if (k == 0)
246
{
247
// B_0 = 1
248
mpq_set_ui(res, 1, 1);
249
return;
250
}
251
252
if (k == 1)
253
{
254
// B_1 = -1/2
255
mpq_set_si(res, -1, 2);
256
return;
257
}
258
259
if (k == 2)
260
{
261
// B_2 = 1/6
262
mpq_set_si(res, 1, 6);
263
return;
264
}
265
266
if (k & 1)
267
{
268
// B_k = 0 if k is odd
269
mpq_set_ui(res, 0, 1);
270
return;
271
}
272
273
if (num_threads <= 0)
274
num_threads = 1;
275
276
mpz_t num, den;
277
mpz_init(num);
278
mpz_init(den);
279
280
const double log2 = 0.69314718055994528622676;
281
const double invlog2 = 1.44269504088896340735992; // = 1/log(2)
282
283
// compute preliminary prime bound and build prime table
284
long bound1 = (long) max(37.0, ceil((k + 0.5) * log(k) * invlog2));
285
PrimeTable table(bound1);
286
287
// compute denominator of B_k
288
bern_den(den, k, table);
289
290
// compute number of bits we need to resolve the numerator
291
long bits = (long) ceil((k + 0.5) * log(k) * invlog2 - 4.094 * k + 2.470
292
+ log(mpz_get_d(den)) * invlog2);
293
294
// compute tighter prime bound
295
// (note: we can safely get away with double-precision here. It would
296
// only start being insufficient around k = 10^13 or so, which is totally
297
// impractical at present.)
298
double prod = 1.0;
299
long prod_bits = 0;
300
long p;
301
for (p = 5; prod_bits < bits + 1; p = table.next_prime(p))
302
{
303
if (p >= NTL_SP_BOUND)
304
abort(); // !!!!! not sure what else we can do here...
305
if (k % (p-1) != 0)
306
prod *= (double) p;
307
int exp;
308
prod = frexp(prod, &exp);
309
prod_bits += exp;
310
}
311
long bound2 = p;
312
313
State state(k, bound2, table);
314
315
#ifdef USE_THREADS
316
vector<pthread_t> threads(num_threads - 1);
317
318
pthread_attr_t attr;
319
pthread_attr_init(&attr);
320
#ifdef THREAD_STACK_SIZE
321
pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE * 1024);
322
#endif
323
324
// spawn worker threads to process blocks
325
for (long i = 0; i < num_threads - 1; i++)
326
pthread_create(&threads[i], &attr, worker, &state);
327
#endif
328
329
worker(&state); // make this thread a worker too
330
331
#ifdef USE_THREADS
332
for (long i = 0; i < num_threads - 1; i++)
333
pthread_join(threads[i], NULL);
334
#endif
335
336
pthread_attr_destroy (&attr);
337
338
// reconstruct B_k as a rational number
339
Item* item = *(state.items.begin());
340
mpz_mul(num, item->residue, den);
341
mpz_mod(num, num, item->modulus);
342
343
if (k % 4 == 0)
344
{
345
// B_k is negative
346
mpz_sub(num, item->modulus, num);
347
mpz_neg(num, num);
348
}
349
350
delete item;
351
352
mpz_swap(num, mpq_numref(res));
353
mpz_swap(den, mpq_denref(res));
354
355
mpz_clear(num);
356
mpz_clear(den);
357
}
358
359
360
361
}; // end namespace
362
363
364
// end of file ================================================================
365
366