Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/modular/arithgroup/farey.cpp
8820 views
1
//
2
// farey.cpp
3
//
4
// Implementation of FareySymbol
5
//
6
//****************************************************************************
7
// Copyright (C) 2011 Hartmut Monien <[email protected]>
8
// Stefan Krämer <[email protected]>
9
//
10
// Distributed under the terms of the GNU General Public License (GPL)
11
//
12
// This code is distributed in the hope that it will be useful,
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
// General Public License for more details.
16
//
17
// The full text of the GPL is available at:
18
//
19
// http://www.gnu.org/licenses/
20
//****************************************************************************
21
22
#include <vector>
23
#include <fstream>
24
#include <sstream>
25
#include <algorithm>
26
#include <cassert>
27
28
#include <gmpxx.h>
29
#include <Python.h>
30
31
#include "farey.hpp"
32
33
extern "C" long convert_to_long(PyObject *);
34
extern "C" PyObject *convert_to_Integer(mpz_class);
35
extern "C" PyObject *convert_to_rational(mpq_class);
36
extern "C" PyObject *convert_to_cusp(mpq_class);
37
extern "C" PyObject *convert_to_SL2Z(SL2Z);
38
39
40
using namespace std;
41
42
inline
43
ostream& tab(ostream& os) { os << "\t"; return os; }
44
45
template <class T>
46
inline
47
ostream& operator<<(ostream& os, const vector<T>& v) {
48
os << v.size() << " ";
49
for(typename vector<T>::const_iterator i=v.begin(); i!=v.end(); i++) {
50
os << *i << " ";
51
}
52
return os;
53
}
54
55
template <class T>
56
inline
57
istream& operator>>(istream& is, vector<T>& v) {
58
size_t n;
59
is >> n;
60
for(size_t i=0; i<n; i++) {
61
T tmp;
62
is >> tmp;
63
v.push_back(tmp);
64
}
65
return is;
66
}
67
68
template <>
69
inline
70
istream& operator>>(istream& is, vector<SL2Z>& v) {
71
size_t n;
72
is >> n;
73
for(size_t i=0; i<n; i++) {
74
SL2Z tmp(1, 0, 0, 1);
75
is >> tmp;
76
v.push_back(tmp);
77
}
78
return is;
79
}
80
81
inline
82
istream& operator>>(istream& is, FareySymbol& F) {
83
is >> F.pairing_max
84
>> F.pairing
85
>> F.cusp_classes
86
>> F.a
87
>> F.b
88
>> F.x
89
>> F.coset
90
>> F.generators
91
>> F.cusps
92
>> F.cusp_widths
93
>> F.reductions
94
>> F.even
95
>> F.pairing_in_group;
96
return is;
97
}
98
99
inline
100
ostream& operator<<(ostream& os, const FareySymbol& F) {
101
os << F.pairing_max << " "
102
<< F.pairing
103
<< F.cusp_classes
104
<< F.a
105
<< F.b
106
<< F.x
107
<< F.coset
108
<< F.generators
109
<< F.cusps
110
<< F.cusp_widths
111
<< F.reductions
112
<< F.even << " "
113
<< F.pairing_in_group;
114
return os;
115
}
116
117
inline
118
mpq_class operator/(const mpz_class& a, const mpz_class& b) {
119
mpq_class result(a, b);
120
result.canonicalize();
121
return result;
122
}
123
124
inline
125
mpq_class
126
operator*(const SL2Z& M, const mpq_class& z) {
127
mpz_class p = z.get_num(), q = z.get_den();
128
if( M.c()*p+M.d()*q == 0 ) {
129
throw string(__FUNCTION__) + ": division by zero.";
130
}
131
mpq_class result(M.a()*p+M.b()*q, M.c()*p+M.d()*q);
132
result.canonicalize();
133
return result;
134
}
135
136
inline
137
vector<mpq_class>
138
operator*(const SL2Z& M, const vector<mpq_class>& v) {
139
vector<mpq_class> result;
140
for(size_t j=0; j<v.size(); j++) result.push_back(M*v[j]);
141
return result;
142
}
143
144
inline
145
mpz_class
146
floor(const mpq_class r) {
147
mpz_class result = r.get_num()/r.get_den();
148
if( r >= 0 ) {
149
return result;
150
} else {
151
return result - 1;
152
}
153
}
154
155
inline
156
mpz_class lcm(const mpz_class& a, const mpz_class& b) {
157
mpz_class result;
158
mpz_lcm(result.get_mpz_t(), a.get_mpz_t(), b.get_mpz_t());
159
return result;
160
}
161
162
inline
163
mpz_class lcm(const vector<mpz_class>& v) {
164
mpz_class q(1);
165
for(size_t i=0; i<v.size(); i++) {
166
q = lcm(q, v[i]);
167
}
168
return q;
169
}
170
171
inline
172
mpz_class gcd(const mpz_class& a, const mpz_class& b) {
173
mpz_class result;
174
mpz_gcd( result.get_mpz_t(), a.get_mpz_t(), b.get_mpz_t());
175
return result;
176
}
177
178
inline
179
void
180
gcd_ext(mpz_class& g, mpz_class& r, mpz_class& s, const mpz_class& a, const mpz_class& b) {
181
mpz_gcdext(g.get_mpz_t(),
182
r.get_mpz_t(), s.get_mpz_t(),
183
a.get_mpz_t(), b.get_mpz_t());
184
}
185
186
inline
187
/* cf. Rademacher, The Fourier Coefficients of the Modular Invariant
188
J(\tau), p. 502 */
189
SL2Z rademacher_matrix(const mpq_class& q) {
190
mpz_class h = q.get_num(), k = q.get_den(), j;
191
mpz_class g, r, s;
192
gcd_ext(g, r, s, h, k);
193
if( r < 0 ) {
194
j = -r;
195
} else {
196
j = k-r;
197
}
198
return SL2Z(j, -(h*j+1)/k, k, -h);
199
}
200
201
//--- Helper class for checking membership of SL2Z matrix in group GammaH ----
202
203
is_element_GammaH::is_element_GammaH(int p_, PyObject* gen_list) : p(p_) {
204
typedef vector<long>::const_iterator const_iterator;
205
// list of generators
206
vector<long> gen;
207
Py_ssize_t ngen = PyList_Size(gen_list);
208
for(Py_ssize_t i=0; i<ngen; i++) {
209
PyObject* item = PyList_GetItem(gen_list, i);
210
gen.push_back(convert_to_long(item));
211
}
212
// generate H from generators
213
H = gen;
214
for(;;) {
215
vector<long> m;
216
for(const_iterator i=gen.begin(); i!=gen.end(); i++) {
217
for(const_iterator j=H.begin(); j!=H.end(); j++) {
218
long q = ((*i)*(*j))%p;
219
if( find(H.begin(), H.end(), q) == H.end() and
220
find(m.begin(), m.end(), q) == m.end() ) {
221
m.push_back(q);
222
}
223
}
224
}
225
if( m.size() == 0 ) break;
226
for(const_iterator i=m.begin(); i!=m.end(); i++) H.push_back(*i);
227
}
228
// sort for binary searches
229
sort(H.begin(), H.end());
230
}
231
232
is_element_GammaH::~is_element_GammaH() {
233
}
234
235
bool is_element_GammaH::is_member(const SL2Z& m) const {
236
mpz_class a = m.a()%p; if(a < 0) a+=p;
237
mpz_class d = m.d()%p; if(d < 0) d+=p;
238
if( m.c()%p != 0 ) return false;
239
if( not binary_search(H.begin(), H.end(), a.get_si()) ) return false;
240
if( not binary_search(H.begin(), H.end(), d.get_si()) ) return false;
241
return true;
242
}
243
244
//--- Helper class for checking membership of SL2Z matrix in group -------------
245
// group defined by the python object.
246
247
is_element_general::is_element_general(PyObject* group_) : group(group_) {
248
if( PyObject_HasAttrString(group, "__contains__") ) {
249
method = PyObject_GetAttrString(group, "__contains__");
250
} else {
251
cerr << "group has to define __contains__" << endl;
252
throw string(__FUNCTION__) + ": error.";
253
}
254
}
255
256
is_element_general::~is_element_general() {
257
Py_DECREF(method);
258
}
259
260
bool is_element_general::is_member(const SL2Z& m) const {
261
PyObject* arg = convert_to_SL2Z(m);
262
PyObject* tuple = PyTuple_New(1);
263
PyTuple_SetItem(tuple, 0, arg);
264
PyObject *result = PyEval_CallObject(method, tuple);
265
Py_DECREF(tuple);
266
if( not PyBool_Check(result) ) {
267
cerr << "__contains__ does not return bool." << endl;
268
throw string(__FUNCTION__) + ": error.";
269
}
270
bool value = (result == Py_True);
271
Py_DECREF(result);
272
return value;
273
}
274
275
//--- FareySymbol ------------------------------------------------------------
276
277
// SL2Z
278
279
FareySymbol::FareySymbol() {
280
pairing = vector<int>(2);
281
pairing[0] = EVEN;
282
pairing[1] = ODD;
283
pairing_max = NO;
284
a.push_back(0);
285
b.push_back(1);
286
cusp_widths.push_back(1);
287
coset.push_back(SL2Z::E);
288
generators.push_back(SL2Z::S);
289
generators.push_back(SL2Z::S*SL2Z::R);
290
cusp_classes.push_back(0);
291
even = true;
292
pairing_in_group.push_back(true);
293
pairing_in_group.push_back(true);
294
for(size_t i=0; i<a.size(); i++) x.push_back(a[i]/b[i]);
295
}
296
297
FareySymbol::FareySymbol(istream& is) {
298
is >> (*this);
299
}
300
301
// User defined group and restoring from pickle
302
303
FareySymbol::FareySymbol(PyObject* o) {
304
if( PyString_Check(o) ) {
305
// restoration from data
306
istringstream is(PyString_AsString(o));
307
is >> (*this);
308
} else {
309
// init with user defined group
310
is_element_general *group = new is_element_general(o);
311
// check for user defined SL2Z
312
if( group->is_member(SL2Z::S) and
313
group->is_member(SL2Z::T) ) {
314
pairing = vector<int>(2);
315
pairing[0] = EVEN;
316
pairing[1] = ODD;
317
pairing_max = NO;
318
a.push_back(0);
319
b.push_back(1);
320
cusp_widths.push_back(1);
321
coset.push_back(SL2Z::E);
322
generators.push_back(SL2Z::S);
323
generators.push_back(SL2Z::S*SL2Z::R);
324
cusp_classes.push_back(0);
325
even = true;
326
pairing_in_group.push_back(true);
327
pairing_in_group.push_back(true);
328
x.push_back(a[0]/b[0]);
329
reductions.push_back(SL2Z::E);
330
}
331
// check for index two subgroup
332
else if( group->is_member(SL2Z( 0, 1, -1, -1)) and
333
group->is_member(SL2Z(-1, 1, -1, 0)) ) {
334
pairing = vector<int>(2);
335
pairing[0] = ODD;
336
pairing[1] = ODD;
337
pairing_max = NO;
338
a.push_back(0);
339
b.push_back(1);
340
x.push_back(a[0]/b[0]);
341
coset.push_back(SL2Z::E);
342
if ( group->is_member(SL2Z(0, -1, 1, 1)) ) {
343
// index 2 even subgroup
344
generators.push_back(SL2Z( 0, 1, -1, -1));
345
generators.push_back(SL2Z(-1, 1, -1, 0));
346
coset.push_back(SL2Z(0, 1, -1, 0));
347
} else {
348
// index 4 odd subgroup
349
generators.push_back(SL2Z(0, 1, -1, -1));
350
coset.push_back(SL2Z(0, -1, 1, 0));
351
coset.push_back(SL2Z(1, -1, 1, 0));
352
coset.push_back(SL2Z(1, 0, -1, 1));
353
generators.push_back(SL2Z(-1, 1, -1, 0));
354
}
355
cusp_classes.push_back(0);
356
reductions.push_back(SL2Z::E);
357
if (group->is_member(SL2Z::I)) even = true;
358
else even = false;
359
pairing_in_group = init_sl2z_lift(group);
360
} else {
361
// everything else
362
init_pairing(group);
363
cusp_widths = init_cusp_widths();
364
coset = init_coset_reps();
365
generators = init_generators(group);
366
cusp_classes = init_cusp_classes();
367
for(size_t i=0; i<a.size(); i++) x.push_back(a[i]/b[i]);
368
cusps = init_cusps();
369
reductions = init_reductions();
370
if (group->is_member(SL2Z::I)) even = true;
371
else even = false;
372
pairing_in_group = init_sl2z_lift(group);
373
}
374
delete group;
375
}
376
}
377
378
// Predefined subgroups of SL2Z
379
380
FareySymbol::FareySymbol(PyObject* o, const is_element_group* group) {
381
init_pairing(group);
382
cusp_widths = init_cusp_widths();
383
coset = init_coset_reps();
384
generators = init_generators(group);
385
cusp_classes = init_cusp_classes();
386
for(size_t i=0; i<a.size(); i++) x.push_back(a[i]/b[i]);
387
cusps = init_cusps();
388
reductions = init_reductions();
389
if (group->is_member(SL2Z::I)) even = true;
390
else even = false;
391
pairing_in_group = init_sl2z_lift(group);
392
}
393
394
FareySymbol::~FareySymbol() {
395
}
396
397
// for debugging purposes
398
399
void FareySymbol::dump(ostream& os) const {
400
os << "Dumping FareySymbol:" << endl
401
<< tab << "pairing_max: " << pairing_max << endl
402
<< tab << "pairing: " << pairing << endl
403
<< tab << "a: " << a << endl
404
<< tab << "b: " << b << endl
405
<< tab << "x: " << x << endl
406
<< tab << "coset: " << coset << endl
407
<< tab << "generators: " << generators << endl
408
<< tab << "cusps: " << cusps << endl
409
<< tab << "cusp classes: " << cusp_classes << endl
410
<< tab << "cusp widths: " << cusp_widths << endl
411
<< tab << "reductions: " << reductions << endl;
412
}
413
414
void FareySymbol::init_pairing(const is_element_group* group) {
415
pairing = vector<int>(3, NO);
416
const mpq_class infinity(10000000);
417
pairing_max = NO;
418
if( group->is_member(SL2Z(-1, 1, -1, 0)) ) {
419
a.push_back(-1);
420
a.push_back(0);
421
b.push_back(1);
422
b.push_back(1);
423
} else {
424
a.push_back(0);
425
a.push_back(1);
426
b.push_back(1);
427
b.push_back(1);
428
}
429
check_pair(group, 0);
430
check_pair(group, 1);
431
check_pair(group, 2);
432
for(;;) {
433
int missing_pair(-1);
434
mpq_class largest_diameter(0);
435
for(size_t i=0; i<pairing.size(); i++) {
436
if( pairing[i] == NO ) {
437
if( i+1 != pairing.size() ) {
438
if( i != 0 ) {
439
mpq_class d = a[i]/b[i] - a[i-1]/b[i-1];
440
if( d > largest_diameter ) {
441
largest_diameter = d;
442
missing_pair = (int)(i);
443
}
444
} else {
445
largest_diameter = infinity;
446
missing_pair = 0;
447
}
448
} else {
449
largest_diameter = infinity;
450
missing_pair = (int)(pairing.size()-1);
451
break;
452
}
453
}
454
}
455
if( missing_pair == -1 ) {
456
break;
457
} else {
458
mpz_class A, B;
459
if( missing_pair+1 == pairing.size() ) {
460
A = a[missing_pair-1] + 1;
461
B = b[missing_pair-1] + 0;
462
} else {
463
if( missing_pair == 0 ) {
464
A = a[0] - 1;
465
B = b[0] + 0;
466
} else {
467
A = a[missing_pair-1]+a[missing_pair];
468
B = b[missing_pair-1]+b[missing_pair];
469
}
470
}
471
add_term(missing_pair, A/B);
472
}
473
check_pair(group, missing_pair);
474
check_pair(group, missing_pair+1);
475
}
476
}
477
478
void FareySymbol::add_term(const int i, const mpq_class& r) {
479
a.insert(a.begin()+i, r.get_num());
480
b.insert(b.begin()+i, r.get_den());
481
pairing.insert(pairing.begin()+i, NO);
482
}
483
484
void FareySymbol::check_pair(const is_element_group* group, const int i) {
485
if( pairing[i] == NO ) {
486
vector<int> even(pairing), odd(pairing);
487
even[i] = EVEN;
488
odd [i] = ODD;
489
SL2Z A = pairing_matrix(even, i);
490
SL2Z B = pairing_matrix(odd , i);
491
if( group->is_member(A) or group->is_member(-A) ) {
492
pairing[i] = EVEN;
493
return;
494
} else if( group->is_member(B) or group->is_member(-B) ) {
495
pairing[i] = ODD;
496
return;
497
}
498
}
499
if( pairing[i] == NO ) {
500
for(size_t j=0; j<pairing.size(); j++) {
501
if( pairing[j] == NO and i != j ) {
502
vector<int> p(pairing);
503
p[i] = pairing_max+1;
504
p[j] = pairing_max+1;
505
SL2Z C = pairing_matrix(p, i);
506
if( group->is_member(C) or group->is_member(-C) ) {
507
pairing_max++;
508
pairing[i] = pairing_max;
509
pairing[j] = pairing_max;
510
return;
511
}
512
}
513
}
514
}
515
}
516
517
SL2Z FareySymbol::pairing_matrix(const vector<int>& p, const size_t i) const {
518
mpz_class ai, ai1, bi, bi1, aj, aj1, bj, bj1;
519
if( i == 0 ) {
520
ai = -1; bi = 0; ai1 = a[0], bi1 = b[0];
521
} else if( i+1 == p.size() ) {
522
ai = a[i-1]; bi = b[i-1]; ai1 = 1; bi1 = 0;
523
} else {
524
ai = a[i-1]; bi = b[i-1]; ai1 = a[i]; bi1 = b[i];
525
}
526
if( p[i] == NO ) {
527
throw string(__FUNCTION__)+string(": error");
528
} else if( p[i] == EVEN ) {
529
return SL2Z(ai1*bi1+ai*bi, -ai*ai-ai1*ai1,
530
bi*bi+bi1*bi1, -ai1*bi1-ai*bi);
531
} else if( p[i] == ODD ) {
532
return SL2Z(ai1*bi1+ai*bi1+ai*bi, -ai*ai-ai*ai1-ai1*ai1,
533
bi*bi+bi*bi1+bi1*bi1, -ai1*bi1-ai1*bi-ai*bi);
534
} else if( p[i] > NO ) {
535
const size_t j = paired_side(p, i);
536
if( j == 0 ) {
537
aj = -1; bj = 0; aj1 = a[0]; bj1 = b[0];
538
} else if( j == a.size() ) {
539
aj = a[j-1]; bj = b[j-1]; aj1 = 1; bj1 = 0;
540
} else {
541
aj = a[j-1]; bj = b[j-1]; aj1 = a[j]; bj1 = b[j];
542
}
543
return SL2Z(aj1*bi1+aj*bi, -aj*ai-aj1*ai1,
544
bj*bi+bj1*bi1, -ai1*bj1-ai*bj);
545
}
546
return SL2Z::E;
547
}
548
549
inline
550
SL2Z FareySymbol::pairing_matrix(const size_t i) const {
551
return pairing_matrix(pairing, i);
552
}
553
554
SL2Z FareySymbol::pairing_matrix_in_group(const size_t i) const {
555
if (pairing_in_group[i])
556
return pairing_matrix(i);
557
else
558
return SL2Z::I*pairing_matrix(i);
559
}
560
561
size_t FareySymbol::paired_side(const vector<int>& p, const size_t n) const {
562
if( p[n] == EVEN or p[n] == ODD ) {
563
return n;
564
} else if( p[n] > NO ) {
565
vector<int>::const_iterator i = find(p.begin(), p.end(), p[n]);
566
if( i-p.begin() != n ) {
567
return i-p.begin();
568
} else {
569
vector<int>::const_iterator j = find(i+1, p.end(), p[n]);
570
return j-p.begin();
571
}
572
}
573
throw string(__FUNCTION__)+string(": error");
574
return 0;
575
}
576
577
vector<SL2Z> FareySymbol::init_generators(const is_element_group *group) const {
578
const SL2Z I(-1, 0, 0, -1);
579
vector<SL2Z> gen;
580
vector<int> p;
581
for(size_t i=0; i<pairing.size(); i++) {
582
if( find(p.begin(), p.end(), pairing[i]) == p.end() ) {
583
SL2Z m = pairing_matrix(i);
584
if( not group->is_member(m) ) m = I*m;
585
if( pairing[i] == ODD and group->is_member(I) ) m = I*m;
586
gen.push_back(m);
587
if( pairing[i] > NO ) p.push_back(pairing[i]);
588
}
589
}
590
if( nu2() == 0 and nu3() == 0 and group->is_member(I) ) gen.push_back(I);
591
return gen;
592
}
593
594
vector<mpq_class> FareySymbol::init_cusp_widths() const {
595
static const mpq_class one_half(1, 2);
596
vector<mpz_class> A(a), B(b);
597
A.push_back(1);
598
B.push_back(0);
599
vector<mpq_class> w(A.size(), 0);
600
for(size_t i=0; i<w.size(); i++) {
601
size_t im = (i==0 ? A.size()-1 : i-1);
602
size_t ip = (i+1==A.size() ? 0 : i+1);
603
w[i] = abs(A[im]*B[ip]-A[ip]*B[im]);
604
if( pairing[i ] == ODD ) w[i] += one_half;
605
if( pairing[ip] == ODD ) w[i] += one_half;
606
}
607
return w;
608
}
609
610
vector<SL2Z> FareySymbol::init_coset_reps() const {
611
static const mpq_class one_half(1, 2);
612
vector<mpz_class> A(a), B(b);
613
A.insert(A.begin(), -1);
614
B.insert(B.begin(), 0);
615
vector<mpq_class> cw(cusp_widths);
616
rotate(cw.begin(), cw.end()-1, cw.end());
617
vector<int> p(pairing);
618
rotate(p.begin(), p.end()-1, p.end());
619
vector<SL2Z> reps;
620
for(size_t i=0; i<p.size(); i++) {
621
size_t j = (i+1) % p.size();
622
mpz_class c(A[j]), d(B[j]);
623
if( d == 0 ) c = 1;
624
mpq_class upper_bound(cw[i]);
625
if( p[i] == ODD ) upper_bound += one_half;
626
if( p[j] == ODD ) upper_bound -= one_half;
627
for(size_t k=0; k<upper_bound; k++) {
628
reps.push_back(SL2Z(1, -(int)(k), 0, 1)/SL2Z(-A[i], c, -B[i], d));
629
}
630
}
631
return reps;
632
}
633
634
vector<bool> FareySymbol::init_sl2z_lift(const is_element_group *group) const {
635
vector<bool> result;
636
for (size_t i=0; i<pairing.size(); i++)
637
if (group->is_member(pairing_matrix(i)) )
638
result.push_back(true);
639
else
640
result.push_back(false);
641
return result;
642
}
643
644
// init cusp classes is a class of the pairing alone !!!
645
646
vector<int> FareySymbol::init_cusp_classes() const {
647
vector<int> c(pairing.size(), 0);
648
int cusp_number(1);
649
for(size_t m=0; m<c.size(); m++) {
650
if( c[m] != 0 ) {
651
continue;
652
}
653
c[m] = cusp_number;
654
size_t i(m), I, J;
655
for(;;) {
656
if( pairing[i] == NO ) {
657
I = i;
658
J = (i==0? pairing.size()-1 : (i-1)%c.size());
659
} else {
660
I = (i+1)%c.size();
661
J = I;
662
}
663
if( pairing[I] == ODD or pairing[I] == EVEN ) {
664
if( c[I] == cusp_number ) {
665
cusp_number++;
666
break;
667
}
668
c[J] = cusp_number;
669
i = J;
670
continue;
671
} else if( pairing[I] > NO ) {
672
size_t j;
673
for(size_t k=0; k<c.size(); k++) {
674
if( pairing[k] == pairing[I] and k != I ) j = k;
675
}
676
if( I != i ) {
677
if( c[j] == cusp_number ) {
678
cusp_number++;
679
break;
680
}
681
c[j] = cusp_number;
682
i = j;
683
continue;
684
} else {
685
if( c[j-1] == cusp_number ) {
686
cusp_number++;
687
break;
688
}
689
c[j-1] = cusp_number;
690
i = j-1;
691
continue;
692
}
693
}
694
}
695
}
696
for(size_t j=0; j<c.size(); j++) c[j]--;
697
return c;
698
}
699
700
vector<mpq_class> FareySymbol::init_cusps() const {
701
// initialize cusps by identifying fractions using the cusp_class number
702
vector<mpq_class> c;
703
for(int i=0; i<number_of_cusps(); i++) {
704
for(size_t j=0; j<cusp_classes.size(); j++) {
705
if( cusp_classes[j] == i and cusp_classes[j] != cusp_classes.back() ) {
706
c.push_back(x[j]);
707
break;
708
}
709
}
710
}
711
// in earlier version: shift negative cusps to positve ones
712
sort(c.begin(), c.end());
713
return c;
714
}
715
716
vector<SL2Z> FareySymbol::init_reductions() const {
717
vector<SL2Z> reduction(x.size(), SL2Z::E);
718
for(size_t j=0; j<x.size(); j++) {
719
if( binary_search(cusps.begin(), cusps.end(), x[j]) ) continue;
720
size_t k = j;
721
mpq_class q = x[j];
722
for(;;) {
723
SL2Z p = pairing_matrix(k);
724
reduction[j] = p*reduction[j];
725
if( p.c()*q + p.d() == 0 ) break; // cusp ~ infinity
726
q = p*q;
727
if( binary_search(cusps.begin(), cusps.end(), q) ) break;
728
k = lower_bound(x.begin(), x.end(), q) - x.begin();
729
}
730
}
731
return reduction;
732
}
733
734
size_t FareySymbol::nu2() const {
735
return count(pairing.begin(), pairing.end(), EVEN);
736
}
737
738
size_t FareySymbol::nu3() const {
739
return count(pairing.begin(), pairing.end(), ODD);
740
}
741
742
size_t FareySymbol::rank_pi() const {
743
if( index() == 2 ) return 1;
744
return count_if(pairing.begin(), pairing.end(),
745
bind2nd(greater<int>(), 0))/2;
746
}
747
748
size_t FareySymbol::number_of_cusps() const {
749
return size_t(*max_element(cusp_classes.begin(), cusp_classes.end()))+1;
750
}
751
752
size_t FareySymbol::genus() const {
753
return (rank_pi()-number_of_cusps()+1)/2;
754
}
755
756
size_t FareySymbol::level() const {
757
if( index() == 1 ) return 1;
758
if( index() == 2 ) return 2;
759
vector<mpz_class> A(a), B(b);
760
A.push_back(1);
761
B.push_back(0);
762
vector<mpz_class> width;
763
for(size_t i=0; i<number_of_cusps(); i++) {
764
mpq_class cusp_width(0);
765
for(size_t j=0; j<cusp_widths.size(); j++) {
766
if( cusp_classes[j] == i ) {
767
cusp_width += cusp_widths[j];
768
}
769
}
770
width.push_back(cusp_width.get_num());
771
}
772
return lcm(width).get_ui();
773
}
774
775
long FareySymbol::side_index(const mpz_class& a0, const mpz_class& b0,
776
const mpz_class& a1, const mpz_class& b1) const {
777
if( b0 == 0) {
778
if( ( a1 == a[0] and b1 == b[0]) or
779
(-a1 == a[0] and -b1 == b[0])) return 0;
780
} else if( b1 == 0 ) {
781
if( ( a0 == a.back() and b0 == b.back()) or
782
(-a0 == a.back() and -b0 == b.back())) return a.size();
783
} else {
784
mpq_class p = a1/b1, q = a0/b0;
785
for(size_t j=1; j<a.size(); j++) {
786
if( x[j-1] == q and x[j] == p ) return j;
787
}
788
}
789
return -1;
790
}
791
792
void
793
FareySymbol::LLT_algorithm(const SL2Z& M, vector<int>& p, SL2Z& beta) const {
794
// Based on: Kurth/Long - Computations with finite index subgroups
795
// of PSL_2(ZZ) using Farey Symbols, p.13f
796
beta = M;
797
p.clear();
798
bool found = false;
799
mpq_class m;
800
for(;;) {
801
size_t k;
802
found = false;
803
mpz_class A=beta.a(), B=beta.b(), C=beta.c(), D=beta.d();
804
if( D == 0 ) {
805
if( A/C < x[0] ) {
806
found = true;
807
k = 0;
808
} else if( x.back() < A/C ) {
809
found = true;
810
k = pairing.size()-1;
811
}
812
} else if( C == 0 ) {
813
if( x.back() < B/D ) {
814
found = true;
815
k = pairing.size()-1;
816
} else if( B/D < x[0] ) {
817
found = true;
818
k = 0;
819
}
820
} else if( A/C <= x[0] and B/D <= x[0] ) {
821
found = true;
822
k = 0;
823
} else if( x.back() <= B/D and x.back() <= A/C ) {
824
found = true;
825
k = pairing.size()-1;
826
} else {
827
for(size_t i=0; i+1<x.size(); i++) {
828
if( (x[i] < B/D and B/D < A/C and A/C <= x[i+1]) or
829
(x[i] <= B/D and B/D < A/C and A/C < x[i+1]) or
830
(x[i] < A/C and A/C < B/D and B/D <= x[i+1]) or
831
(x[i] <= A/C and A/C < B/D and B/D < x[i+1]) ) {
832
found = true;
833
k = i+1;
834
break;
835
}
836
}
837
}
838
if( not found ) break;
839
// If the pairing, we found is ODD, we need to split the interval.
840
// If the arc is in the right part, we choose the pairing matrix
841
// If it is in the left part, we choose its inverse.
842
// Note, that this choice differs from the article.
843
if( pairing[k] == ODD ) {
844
if( k == 0 ) {
845
m = mpq_class(a[0]-1, b[0]);
846
} else if ( k == pairing.size()-1 ) {
847
m = mpq_class(a[0]+1, b[0]);
848
} else {
849
m = mpq_class(a[k-1]+a[k],b[k-1]+b[k]);
850
}
851
if ( C == 0 and B/D <=m) {
852
beta = pairing_matrix_in_group(k).inverse()*beta;
853
p.push_back(-(int)k);
854
} else if( C == 0 and B/D >= m) {
855
beta = pairing_matrix_in_group(k)*beta;
856
p.push_back((int)k);
857
} else if( D == 0 and A/C <= m) {
858
beta = pairing_matrix_in_group(k).inverse()*beta;
859
p.push_back(-(int)k);
860
} else if( D == 0 and A/C >=m) {
861
beta = pairing_matrix_in_group(k)*beta;
862
p.push_back((int)k);
863
} else if(A/C <= m and B/D <= m) {
864
beta = pairing_matrix_in_group(k).inverse()*beta;
865
p.push_back(-(int)k);
866
} else if(A/C >= m and B/D >= m) {
867
beta = pairing_matrix_in_group(k)*beta;
868
p.push_back((int)k);
869
} else {
870
//Based on Lemma 4 of the article by Kurth/Long,
871
//this case can not occure.
872
throw string("Mathematical compilcations in ") +
873
__FUNCTION__;
874
return;
875
}
876
} else { // case of EVEN or FREE pairing
877
beta = pairing_matrix_in_group(k)*beta;
878
p.push_back((int)k);
879
}
880
}
881
}
882
883
bool FareySymbol::is_element(const SL2Z& M) const {
884
// based on the same article as LLT_algorithm:
885
// Kurth/Long - Computations with finite index ...
886
vector<int> p;
887
SL2Z beta = SL2Z::E;
888
size_t k = 0;
889
mpq_class smaller;
890
LLT_algorithm(M, p, beta);
891
892
// Case (1) of the article (p14)
893
if( even ) {
894
if( beta == SL2Z::E or beta == SL2Z::I ) return true;
895
} else if( beta == SL2Z::E ) return true;
896
897
// Case (3) of the article
898
if( beta == SL2Z::S or beta == SL2Z::U ) {
899
// If 0 and infty are adjacent vertices, they only can occure
900
// at the beginning or the end.
901
if( x[0] == 0 and pairing[0] == EVEN ) return true;
902
if( x.back() == 0 and pairing.back() == EVEN ) return true;
903
}
904
905
// Case (2) of the article
906
if( beta.c() != 0 and beta.d() != 0 ) {
907
// Find index of the "edge beta"
908
smaller = !((beta.b()/beta.d())<(beta.a()/beta.c())) ?
909
(beta.a()/beta.c()) : (beta.b()/beta.d());
910
for(size_t i=0; i<x.size(); i++)
911
if( x[i] == smaller ) {
912
k = i;
913
break;
914
}
915
// Is it a free pairing?
916
if( pairing[k+1] > NO ) {
917
size_t s = paired_side(pairing, k+1);
918
if ( s == 0 and x[0] == 0 and beta.a()/beta.c() > beta.b()/beta.d() )
919
// paired with (0,infty) at the beginning?
920
if( even ) {
921
return true;
922
} else {
923
beta = pairing_matrix_in_group(k)*beta;
924
925
if ( beta == SL2Z::E ) return true;
926
else return false;
927
}
928
929
if( s == pairing.size() -1 and
930
x.back() == 0 and
931
beta.a()/beta.c() > beta.b()/beta.d() ) {
932
// paired with (0,infty) at the end?
933
return true;
934
}
935
}
936
} else if( beta.c() == 0 and pairing.back() > NO ) {
937
// In this case (a,infty) (for some a>0) is paired with (0,infty)
938
size_t s = paired_side(pairing, pairing.size()-1);
939
if( s == 0 and x.back() == beta.b()/beta.d() ) {
940
if( even ) {
941
return true;
942
} else {
943
beta = pairing_matrix_in_group(pairing.size()-1)*beta;
944
if( beta == SL2Z::E ) return true;
945
else return false;
946
}
947
}
948
}
949
return false;
950
}
951
952
SL2Z FareySymbol::reduce_to_fraction(const mpq_class& q) const {
953
SL2Z M = rademacher_matrix(q);
954
for(size_t j=0; j<coset.size(); j++) {
955
SL2Z T = coset[j].inverse()*M;
956
if( is_element(T) ) return T;
957
}
958
return SL2Z::E;
959
}
960
961
SL2Z FareySymbol::reduce_to_elementary_cusp(const mpq_class& q) const {
962
SL2Z M = reduce_to_fraction(q);
963
if( M.c()*q+M.d() == 0 ) return M;
964
mpq_class Q = M*q;
965
vector<mpq_class>::const_iterator k = find(x.begin(), x.end(), Q);
966
if( k != x.end() ) {
967
return reductions[k-x.begin()]*M;
968
} else {
969
return M;
970
}
971
}
972
973
size_t FareySymbol::cusp_class(const mpq_class& q) const {
974
typedef vector<int>::const_iterator const_iterator;
975
SL2Z M = FareySymbol::reduce_to_elementary_cusp(q);
976
if( M.c()*q + M.d() == 0 ) return cusp_classes.back();
977
mpq_class Q = M*q;
978
size_t k = lower_bound(x.begin(), x.end(), Q) - x.begin();
979
return cusp_classes[k];
980
}
981
982
//--- communication with sage ------------------------------------------------
983
984
PyObject* FareySymbol::get_transformation_to_cusp(const mpz_t a,
985
const mpz_t b) const {
986
mpz_class p(a), q(b);
987
if( q == 0 ) return convert_to_SL2Z(SL2Z::E);
988
mpq_class r(p, q);
989
r.canonicalize();
990
PyObject* M = convert_to_SL2Z(reduce_to_elementary_cusp(r));
991
return M;
992
}
993
994
size_t FareySymbol::index() const {
995
return coset.size();
996
}
997
998
PyObject* FareySymbol::is_element(const mpz_t a, const mpz_t b,
999
const mpz_t c, const mpz_t d) const {
1000
const SL2Z M = SL2Z(mpz_class(a), mpz_class(b), mpz_class(c), mpz_class(d));
1001
if( is_element(M) ) {
1002
Py_RETURN_TRUE;
1003
} else {
1004
Py_RETURN_FALSE;
1005
}
1006
}
1007
1008
PyObject* FareySymbol::get_coset() const {
1009
PyObject* coset_list = PyList_New(coset.size());
1010
for(size_t i=0; i<coset.size(); i++) {
1011
PyObject* m = convert_to_SL2Z(coset[i]);
1012
PyList_SetItem(coset_list, i, m);
1013
}
1014
return coset_list;
1015
}
1016
1017
PyObject* FareySymbol::get_generators() const {
1018
PyObject* generators_list = PyList_New(generators.size());
1019
for(size_t i=0; i<generators.size(); i++) {
1020
PyObject* m = convert_to_SL2Z(generators[i]);
1021
PyList_SetItem(generators_list, i, m);
1022
}
1023
return generators_list;
1024
}
1025
1026
PyObject* FareySymbol::get_cusps() const {
1027
PyObject* cusps_list = PyList_New(cusps.size());
1028
for(size_t i=0; i<cusps.size(); i++) {
1029
PyObject* m = convert_to_cusp(cusps[i]);
1030
PyList_SetItem(cusps_list, i, m);
1031
}
1032
return cusps_list;
1033
}
1034
1035
PyObject* FareySymbol::get_cusp_widths() const {
1036
vector<mpz_class> width;
1037
for(size_t i=0; i<number_of_cusps(); i++) {
1038
mpq_class cusp_width(0);
1039
for(size_t j=0; j<cusp_widths.size(); j++) {
1040
if( cusp_classes[j] == i ) {
1041
cusp_width += cusp_widths[j];
1042
}
1043
}
1044
width.push_back(cusp_width.get_num());
1045
}
1046
PyObject* cusp_widths_list = PyList_New(width.size());
1047
for(size_t i=0; i<width.size(); i++) {
1048
PyObject* m = convert_to_rational(width[i]);
1049
PyList_SetItem(cusp_widths_list, i, m);
1050
}
1051
return cusp_widths_list;
1052
}
1053
1054
1055
size_t
1056
FareySymbol::get_cusp_class(const mpz_t p, const mpz_t q) const {
1057
mpz_class a(p), b(q);
1058
if( a != 0 and b == 0 ) return cusp_classes.back();
1059
mpq_class c(a, b);
1060
c.canonicalize();
1061
return cusp_class(c);
1062
}
1063
1064
PyObject* FareySymbol::get_fractions() const {
1065
PyObject* x_list = PyList_New(x.size());
1066
for(size_t i=0; i<x.size(); i++) {
1067
PyObject* m = convert_to_rational(x[i]);
1068
PyList_SetItem(x_list, i, m);
1069
}
1070
return x_list;
1071
}
1072
1073
PyObject* FareySymbol::get_pairings() const {
1074
PyObject* pairing_list = PyList_New(pairing.size());
1075
for(size_t i=0; i<pairing.size(); i++) {
1076
PyObject* m = PyInt_FromLong(long(pairing[i]));
1077
PyList_SetItem(pairing_list, i, m);
1078
}
1079
return pairing_list;
1080
}
1081
1082
PyObject* FareySymbol::get_paired_sides() const {
1083
vector<int> p;
1084
for(size_t i=0; i<pairing.size(); i++) {
1085
if( pairing[i] > NO and
1086
p.end() == find(p.begin(), p.end(), pairing[i]) ) {
1087
p.push_back(pairing[i]);
1088
}
1089
}
1090
sort(p.begin(), p.end());
1091
PyObject* pairing_list = PyList_New(p.size());
1092
for(vector<int>::const_iterator i=p.begin(); i!=p.end(); i++) {
1093
vector<int>::const_iterator j = find(pairing.begin(), pairing.end(), *i);
1094
vector<int>::const_iterator k = find(j+1, pairing.end(), *i);
1095
PyObject* J = PyInt_FromLong(long(j-pairing.begin()));
1096
PyObject* K = PyInt_FromLong(long(k-pairing.begin()));
1097
PyObject* tuple = PyTuple_New(2);
1098
PyTuple_SetItem(tuple, 0, J);
1099
PyTuple_SetItem(tuple, 1, K);
1100
PyList_SetItem(pairing_list, i-p.begin(), tuple);
1101
}
1102
return pairing_list;
1103
}
1104
1105
PyObject* FareySymbol::get_pairing_matrices() const {
1106
PyObject* pairing_matrix_list = PyList_New(pairing.size());
1107
for(size_t i=0; i<pairing.size(); i++) {
1108
PyObject* pm = convert_to_SL2Z(pairing_matrix(i));
1109
PyList_SetItem(pairing_matrix_list, i, pm);
1110
}
1111
return pairing_matrix_list;
1112
}
1113
1114
PyObject* FareySymbol::dumps() const {
1115
std::ostringstream os(ostringstream::out|ostringstream::binary);
1116
os << (*this);
1117
PyObject* d = PyString_FromString(os.str().c_str());
1118
return d;
1119
}
1120
1121
1122