Path: blob/master/src/sage/modular/arithgroup/farey.cpp
8820 views
//1// farey.cpp2//3// Implementation of FareySymbol4//5//****************************************************************************6// Copyright (C) 2011 Hartmut Monien <[email protected]>7// Stefan Krämer <[email protected]>8//9// Distributed under the terms of the GNU General Public License (GPL)10//11// This code is distributed in the hope that it will be useful,12// but WITHOUT ANY WARRANTY; without even the implied warranty of13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU14// General Public License for more details.15//16// The full text of the GPL is available at:17//18// http://www.gnu.org/licenses/19//****************************************************************************2021#include <vector>22#include <fstream>23#include <sstream>24#include <algorithm>25#include <cassert>2627#include <gmpxx.h>28#include <Python.h>2930#include "farey.hpp"3132extern "C" long convert_to_long(PyObject *);33extern "C" PyObject *convert_to_Integer(mpz_class);34extern "C" PyObject *convert_to_rational(mpq_class);35extern "C" PyObject *convert_to_cusp(mpq_class);36extern "C" PyObject *convert_to_SL2Z(SL2Z);373839using namespace std;4041inline42ostream& tab(ostream& os) { os << "\t"; return os; }4344template <class T>45inline46ostream& operator<<(ostream& os, const vector<T>& v) {47os << v.size() << " ";48for(typename vector<T>::const_iterator i=v.begin(); i!=v.end(); i++) {49os << *i << " ";50}51return os;52}5354template <class T>55inline56istream& operator>>(istream& is, vector<T>& v) {57size_t n;58is >> n;59for(size_t i=0; i<n; i++) {60T tmp;61is >> tmp;62v.push_back(tmp);63}64return is;65}6667template <>68inline69istream& operator>>(istream& is, vector<SL2Z>& v) {70size_t n;71is >> n;72for(size_t i=0; i<n; i++) {73SL2Z tmp(1, 0, 0, 1);74is >> tmp;75v.push_back(tmp);76}77return is;78}7980inline81istream& operator>>(istream& is, FareySymbol& F) {82is >> F.pairing_max83>> F.pairing84>> F.cusp_classes85>> F.a86>> F.b87>> F.x88>> F.coset89>> F.generators90>> F.cusps91>> F.cusp_widths92>> F.reductions93>> F.even94>> F.pairing_in_group;95return is;96}9798inline99ostream& operator<<(ostream& os, const FareySymbol& F) {100os << F.pairing_max << " "101<< F.pairing102<< F.cusp_classes103<< F.a104<< F.b105<< F.x106<< F.coset107<< F.generators108<< F.cusps109<< F.cusp_widths110<< F.reductions111<< F.even << " "112<< F.pairing_in_group;113return os;114}115116inline117mpq_class operator/(const mpz_class& a, const mpz_class& b) {118mpq_class result(a, b);119result.canonicalize();120return result;121}122123inline124mpq_class125operator*(const SL2Z& M, const mpq_class& z) {126mpz_class p = z.get_num(), q = z.get_den();127if( M.c()*p+M.d()*q == 0 ) {128throw string(__FUNCTION__) + ": division by zero.";129}130mpq_class result(M.a()*p+M.b()*q, M.c()*p+M.d()*q);131result.canonicalize();132return result;133}134135inline136vector<mpq_class>137operator*(const SL2Z& M, const vector<mpq_class>& v) {138vector<mpq_class> result;139for(size_t j=0; j<v.size(); j++) result.push_back(M*v[j]);140return result;141}142143inline144mpz_class145floor(const mpq_class r) {146mpz_class result = r.get_num()/r.get_den();147if( r >= 0 ) {148return result;149} else {150return result - 1;151}152}153154inline155mpz_class lcm(const mpz_class& a, const mpz_class& b) {156mpz_class result;157mpz_lcm(result.get_mpz_t(), a.get_mpz_t(), b.get_mpz_t());158return result;159}160161inline162mpz_class lcm(const vector<mpz_class>& v) {163mpz_class q(1);164for(size_t i=0; i<v.size(); i++) {165q = lcm(q, v[i]);166}167return q;168}169170inline171mpz_class gcd(const mpz_class& a, const mpz_class& b) {172mpz_class result;173mpz_gcd( result.get_mpz_t(), a.get_mpz_t(), b.get_mpz_t());174return result;175}176177inline178void179gcd_ext(mpz_class& g, mpz_class& r, mpz_class& s, const mpz_class& a, const mpz_class& b) {180mpz_gcdext(g.get_mpz_t(),181r.get_mpz_t(), s.get_mpz_t(),182a.get_mpz_t(), b.get_mpz_t());183}184185inline186/* cf. Rademacher, The Fourier Coefficients of the Modular Invariant187J(\tau), p. 502 */188SL2Z rademacher_matrix(const mpq_class& q) {189mpz_class h = q.get_num(), k = q.get_den(), j;190mpz_class g, r, s;191gcd_ext(g, r, s, h, k);192if( r < 0 ) {193j = -r;194} else {195j = k-r;196}197return SL2Z(j, -(h*j+1)/k, k, -h);198}199200//--- Helper class for checking membership of SL2Z matrix in group GammaH ----201202is_element_GammaH::is_element_GammaH(int p_, PyObject* gen_list) : p(p_) {203typedef vector<long>::const_iterator const_iterator;204// list of generators205vector<long> gen;206Py_ssize_t ngen = PyList_Size(gen_list);207for(Py_ssize_t i=0; i<ngen; i++) {208PyObject* item = PyList_GetItem(gen_list, i);209gen.push_back(convert_to_long(item));210}211// generate H from generators212H = gen;213for(;;) {214vector<long> m;215for(const_iterator i=gen.begin(); i!=gen.end(); i++) {216for(const_iterator j=H.begin(); j!=H.end(); j++) {217long q = ((*i)*(*j))%p;218if( find(H.begin(), H.end(), q) == H.end() and219find(m.begin(), m.end(), q) == m.end() ) {220m.push_back(q);221}222}223}224if( m.size() == 0 ) break;225for(const_iterator i=m.begin(); i!=m.end(); i++) H.push_back(*i);226}227// sort for binary searches228sort(H.begin(), H.end());229}230231is_element_GammaH::~is_element_GammaH() {232}233234bool is_element_GammaH::is_member(const SL2Z& m) const {235mpz_class a = m.a()%p; if(a < 0) a+=p;236mpz_class d = m.d()%p; if(d < 0) d+=p;237if( m.c()%p != 0 ) return false;238if( not binary_search(H.begin(), H.end(), a.get_si()) ) return false;239if( not binary_search(H.begin(), H.end(), d.get_si()) ) return false;240return true;241}242243//--- Helper class for checking membership of SL2Z matrix in group -------------244// group defined by the python object.245246is_element_general::is_element_general(PyObject* group_) : group(group_) {247if( PyObject_HasAttrString(group, "__contains__") ) {248method = PyObject_GetAttrString(group, "__contains__");249} else {250cerr << "group has to define __contains__" << endl;251throw string(__FUNCTION__) + ": error.";252}253}254255is_element_general::~is_element_general() {256Py_DECREF(method);257}258259bool is_element_general::is_member(const SL2Z& m) const {260PyObject* arg = convert_to_SL2Z(m);261PyObject* tuple = PyTuple_New(1);262PyTuple_SetItem(tuple, 0, arg);263PyObject *result = PyEval_CallObject(method, tuple);264Py_DECREF(tuple);265if( not PyBool_Check(result) ) {266cerr << "__contains__ does not return bool." << endl;267throw string(__FUNCTION__) + ": error.";268}269bool value = (result == Py_True);270Py_DECREF(result);271return value;272}273274//--- FareySymbol ------------------------------------------------------------275276// SL2Z277278FareySymbol::FareySymbol() {279pairing = vector<int>(2);280pairing[0] = EVEN;281pairing[1] = ODD;282pairing_max = NO;283a.push_back(0);284b.push_back(1);285cusp_widths.push_back(1);286coset.push_back(SL2Z::E);287generators.push_back(SL2Z::S);288generators.push_back(SL2Z::S*SL2Z::R);289cusp_classes.push_back(0);290even = true;291pairing_in_group.push_back(true);292pairing_in_group.push_back(true);293for(size_t i=0; i<a.size(); i++) x.push_back(a[i]/b[i]);294}295296FareySymbol::FareySymbol(istream& is) {297is >> (*this);298}299300// User defined group and restoring from pickle301302FareySymbol::FareySymbol(PyObject* o) {303if( PyString_Check(o) ) {304// restoration from data305istringstream is(PyString_AsString(o));306is >> (*this);307} else {308// init with user defined group309is_element_general *group = new is_element_general(o);310// check for user defined SL2Z311if( group->is_member(SL2Z::S) and312group->is_member(SL2Z::T) ) {313pairing = vector<int>(2);314pairing[0] = EVEN;315pairing[1] = ODD;316pairing_max = NO;317a.push_back(0);318b.push_back(1);319cusp_widths.push_back(1);320coset.push_back(SL2Z::E);321generators.push_back(SL2Z::S);322generators.push_back(SL2Z::S*SL2Z::R);323cusp_classes.push_back(0);324even = true;325pairing_in_group.push_back(true);326pairing_in_group.push_back(true);327x.push_back(a[0]/b[0]);328reductions.push_back(SL2Z::E);329}330// check for index two subgroup331else if( group->is_member(SL2Z( 0, 1, -1, -1)) and332group->is_member(SL2Z(-1, 1, -1, 0)) ) {333pairing = vector<int>(2);334pairing[0] = ODD;335pairing[1] = ODD;336pairing_max = NO;337a.push_back(0);338b.push_back(1);339x.push_back(a[0]/b[0]);340coset.push_back(SL2Z::E);341if ( group->is_member(SL2Z(0, -1, 1, 1)) ) {342// index 2 even subgroup343generators.push_back(SL2Z( 0, 1, -1, -1));344generators.push_back(SL2Z(-1, 1, -1, 0));345coset.push_back(SL2Z(0, 1, -1, 0));346} else {347// index 4 odd subgroup348generators.push_back(SL2Z(0, 1, -1, -1));349coset.push_back(SL2Z(0, -1, 1, 0));350coset.push_back(SL2Z(1, -1, 1, 0));351coset.push_back(SL2Z(1, 0, -1, 1));352generators.push_back(SL2Z(-1, 1, -1, 0));353}354cusp_classes.push_back(0);355reductions.push_back(SL2Z::E);356if (group->is_member(SL2Z::I)) even = true;357else even = false;358pairing_in_group = init_sl2z_lift(group);359} else {360// everything else361init_pairing(group);362cusp_widths = init_cusp_widths();363coset = init_coset_reps();364generators = init_generators(group);365cusp_classes = init_cusp_classes();366for(size_t i=0; i<a.size(); i++) x.push_back(a[i]/b[i]);367cusps = init_cusps();368reductions = init_reductions();369if (group->is_member(SL2Z::I)) even = true;370else even = false;371pairing_in_group = init_sl2z_lift(group);372}373delete group;374}375}376377// Predefined subgroups of SL2Z378379FareySymbol::FareySymbol(PyObject* o, const is_element_group* group) {380init_pairing(group);381cusp_widths = init_cusp_widths();382coset = init_coset_reps();383generators = init_generators(group);384cusp_classes = init_cusp_classes();385for(size_t i=0; i<a.size(); i++) x.push_back(a[i]/b[i]);386cusps = init_cusps();387reductions = init_reductions();388if (group->is_member(SL2Z::I)) even = true;389else even = false;390pairing_in_group = init_sl2z_lift(group);391}392393FareySymbol::~FareySymbol() {394}395396// for debugging purposes397398void FareySymbol::dump(ostream& os) const {399os << "Dumping FareySymbol:" << endl400<< tab << "pairing_max: " << pairing_max << endl401<< tab << "pairing: " << pairing << endl402<< tab << "a: " << a << endl403<< tab << "b: " << b << endl404<< tab << "x: " << x << endl405<< tab << "coset: " << coset << endl406<< tab << "generators: " << generators << endl407<< tab << "cusps: " << cusps << endl408<< tab << "cusp classes: " << cusp_classes << endl409<< tab << "cusp widths: " << cusp_widths << endl410<< tab << "reductions: " << reductions << endl;411}412413void FareySymbol::init_pairing(const is_element_group* group) {414pairing = vector<int>(3, NO);415const mpq_class infinity(10000000);416pairing_max = NO;417if( group->is_member(SL2Z(-1, 1, -1, 0)) ) {418a.push_back(-1);419a.push_back(0);420b.push_back(1);421b.push_back(1);422} else {423a.push_back(0);424a.push_back(1);425b.push_back(1);426b.push_back(1);427}428check_pair(group, 0);429check_pair(group, 1);430check_pair(group, 2);431for(;;) {432int missing_pair(-1);433mpq_class largest_diameter(0);434for(size_t i=0; i<pairing.size(); i++) {435if( pairing[i] == NO ) {436if( i+1 != pairing.size() ) {437if( i != 0 ) {438mpq_class d = a[i]/b[i] - a[i-1]/b[i-1];439if( d > largest_diameter ) {440largest_diameter = d;441missing_pair = (int)(i);442}443} else {444largest_diameter = infinity;445missing_pair = 0;446}447} else {448largest_diameter = infinity;449missing_pair = (int)(pairing.size()-1);450break;451}452}453}454if( missing_pair == -1 ) {455break;456} else {457mpz_class A, B;458if( missing_pair+1 == pairing.size() ) {459A = a[missing_pair-1] + 1;460B = b[missing_pair-1] + 0;461} else {462if( missing_pair == 0 ) {463A = a[0] - 1;464B = b[0] + 0;465} else {466A = a[missing_pair-1]+a[missing_pair];467B = b[missing_pair-1]+b[missing_pair];468}469}470add_term(missing_pair, A/B);471}472check_pair(group, missing_pair);473check_pair(group, missing_pair+1);474}475}476477void FareySymbol::add_term(const int i, const mpq_class& r) {478a.insert(a.begin()+i, r.get_num());479b.insert(b.begin()+i, r.get_den());480pairing.insert(pairing.begin()+i, NO);481}482483void FareySymbol::check_pair(const is_element_group* group, const int i) {484if( pairing[i] == NO ) {485vector<int> even(pairing), odd(pairing);486even[i] = EVEN;487odd [i] = ODD;488SL2Z A = pairing_matrix(even, i);489SL2Z B = pairing_matrix(odd , i);490if( group->is_member(A) or group->is_member(-A) ) {491pairing[i] = EVEN;492return;493} else if( group->is_member(B) or group->is_member(-B) ) {494pairing[i] = ODD;495return;496}497}498if( pairing[i] == NO ) {499for(size_t j=0; j<pairing.size(); j++) {500if( pairing[j] == NO and i != j ) {501vector<int> p(pairing);502p[i] = pairing_max+1;503p[j] = pairing_max+1;504SL2Z C = pairing_matrix(p, i);505if( group->is_member(C) or group->is_member(-C) ) {506pairing_max++;507pairing[i] = pairing_max;508pairing[j] = pairing_max;509return;510}511}512}513}514}515516SL2Z FareySymbol::pairing_matrix(const vector<int>& p, const size_t i) const {517mpz_class ai, ai1, bi, bi1, aj, aj1, bj, bj1;518if( i == 0 ) {519ai = -1; bi = 0; ai1 = a[0], bi1 = b[0];520} else if( i+1 == p.size() ) {521ai = a[i-1]; bi = b[i-1]; ai1 = 1; bi1 = 0;522} else {523ai = a[i-1]; bi = b[i-1]; ai1 = a[i]; bi1 = b[i];524}525if( p[i] == NO ) {526throw string(__FUNCTION__)+string(": error");527} else if( p[i] == EVEN ) {528return SL2Z(ai1*bi1+ai*bi, -ai*ai-ai1*ai1,529bi*bi+bi1*bi1, -ai1*bi1-ai*bi);530} else if( p[i] == ODD ) {531return SL2Z(ai1*bi1+ai*bi1+ai*bi, -ai*ai-ai*ai1-ai1*ai1,532bi*bi+bi*bi1+bi1*bi1, -ai1*bi1-ai1*bi-ai*bi);533} else if( p[i] > NO ) {534const size_t j = paired_side(p, i);535if( j == 0 ) {536aj = -1; bj = 0; aj1 = a[0]; bj1 = b[0];537} else if( j == a.size() ) {538aj = a[j-1]; bj = b[j-1]; aj1 = 1; bj1 = 0;539} else {540aj = a[j-1]; bj = b[j-1]; aj1 = a[j]; bj1 = b[j];541}542return SL2Z(aj1*bi1+aj*bi, -aj*ai-aj1*ai1,543bj*bi+bj1*bi1, -ai1*bj1-ai*bj);544}545return SL2Z::E;546}547548inline549SL2Z FareySymbol::pairing_matrix(const size_t i) const {550return pairing_matrix(pairing, i);551}552553SL2Z FareySymbol::pairing_matrix_in_group(const size_t i) const {554if (pairing_in_group[i])555return pairing_matrix(i);556else557return SL2Z::I*pairing_matrix(i);558}559560size_t FareySymbol::paired_side(const vector<int>& p, const size_t n) const {561if( p[n] == EVEN or p[n] == ODD ) {562return n;563} else if( p[n] > NO ) {564vector<int>::const_iterator i = find(p.begin(), p.end(), p[n]);565if( i-p.begin() != n ) {566return i-p.begin();567} else {568vector<int>::const_iterator j = find(i+1, p.end(), p[n]);569return j-p.begin();570}571}572throw string(__FUNCTION__)+string(": error");573return 0;574}575576vector<SL2Z> FareySymbol::init_generators(const is_element_group *group) const {577const SL2Z I(-1, 0, 0, -1);578vector<SL2Z> gen;579vector<int> p;580for(size_t i=0; i<pairing.size(); i++) {581if( find(p.begin(), p.end(), pairing[i]) == p.end() ) {582SL2Z m = pairing_matrix(i);583if( not group->is_member(m) ) m = I*m;584if( pairing[i] == ODD and group->is_member(I) ) m = I*m;585gen.push_back(m);586if( pairing[i] > NO ) p.push_back(pairing[i]);587}588}589if( nu2() == 0 and nu3() == 0 and group->is_member(I) ) gen.push_back(I);590return gen;591}592593vector<mpq_class> FareySymbol::init_cusp_widths() const {594static const mpq_class one_half(1, 2);595vector<mpz_class> A(a), B(b);596A.push_back(1);597B.push_back(0);598vector<mpq_class> w(A.size(), 0);599for(size_t i=0; i<w.size(); i++) {600size_t im = (i==0 ? A.size()-1 : i-1);601size_t ip = (i+1==A.size() ? 0 : i+1);602w[i] = abs(A[im]*B[ip]-A[ip]*B[im]);603if( pairing[i ] == ODD ) w[i] += one_half;604if( pairing[ip] == ODD ) w[i] += one_half;605}606return w;607}608609vector<SL2Z> FareySymbol::init_coset_reps() const {610static const mpq_class one_half(1, 2);611vector<mpz_class> A(a), B(b);612A.insert(A.begin(), -1);613B.insert(B.begin(), 0);614vector<mpq_class> cw(cusp_widths);615rotate(cw.begin(), cw.end()-1, cw.end());616vector<int> p(pairing);617rotate(p.begin(), p.end()-1, p.end());618vector<SL2Z> reps;619for(size_t i=0; i<p.size(); i++) {620size_t j = (i+1) % p.size();621mpz_class c(A[j]), d(B[j]);622if( d == 0 ) c = 1;623mpq_class upper_bound(cw[i]);624if( p[i] == ODD ) upper_bound += one_half;625if( p[j] == ODD ) upper_bound -= one_half;626for(size_t k=0; k<upper_bound; k++) {627reps.push_back(SL2Z(1, -(int)(k), 0, 1)/SL2Z(-A[i], c, -B[i], d));628}629}630return reps;631}632633vector<bool> FareySymbol::init_sl2z_lift(const is_element_group *group) const {634vector<bool> result;635for (size_t i=0; i<pairing.size(); i++)636if (group->is_member(pairing_matrix(i)) )637result.push_back(true);638else639result.push_back(false);640return result;641}642643// init cusp classes is a class of the pairing alone !!!644645vector<int> FareySymbol::init_cusp_classes() const {646vector<int> c(pairing.size(), 0);647int cusp_number(1);648for(size_t m=0; m<c.size(); m++) {649if( c[m] != 0 ) {650continue;651}652c[m] = cusp_number;653size_t i(m), I, J;654for(;;) {655if( pairing[i] == NO ) {656I = i;657J = (i==0? pairing.size()-1 : (i-1)%c.size());658} else {659I = (i+1)%c.size();660J = I;661}662if( pairing[I] == ODD or pairing[I] == EVEN ) {663if( c[I] == cusp_number ) {664cusp_number++;665break;666}667c[J] = cusp_number;668i = J;669continue;670} else if( pairing[I] > NO ) {671size_t j;672for(size_t k=0; k<c.size(); k++) {673if( pairing[k] == pairing[I] and k != I ) j = k;674}675if( I != i ) {676if( c[j] == cusp_number ) {677cusp_number++;678break;679}680c[j] = cusp_number;681i = j;682continue;683} else {684if( c[j-1] == cusp_number ) {685cusp_number++;686break;687}688c[j-1] = cusp_number;689i = j-1;690continue;691}692}693}694}695for(size_t j=0; j<c.size(); j++) c[j]--;696return c;697}698699vector<mpq_class> FareySymbol::init_cusps() const {700// initialize cusps by identifying fractions using the cusp_class number701vector<mpq_class> c;702for(int i=0; i<number_of_cusps(); i++) {703for(size_t j=0; j<cusp_classes.size(); j++) {704if( cusp_classes[j] == i and cusp_classes[j] != cusp_classes.back() ) {705c.push_back(x[j]);706break;707}708}709}710// in earlier version: shift negative cusps to positve ones711sort(c.begin(), c.end());712return c;713}714715vector<SL2Z> FareySymbol::init_reductions() const {716vector<SL2Z> reduction(x.size(), SL2Z::E);717for(size_t j=0; j<x.size(); j++) {718if( binary_search(cusps.begin(), cusps.end(), x[j]) ) continue;719size_t k = j;720mpq_class q = x[j];721for(;;) {722SL2Z p = pairing_matrix(k);723reduction[j] = p*reduction[j];724if( p.c()*q + p.d() == 0 ) break; // cusp ~ infinity725q = p*q;726if( binary_search(cusps.begin(), cusps.end(), q) ) break;727k = lower_bound(x.begin(), x.end(), q) - x.begin();728}729}730return reduction;731}732733size_t FareySymbol::nu2() const {734return count(pairing.begin(), pairing.end(), EVEN);735}736737size_t FareySymbol::nu3() const {738return count(pairing.begin(), pairing.end(), ODD);739}740741size_t FareySymbol::rank_pi() const {742if( index() == 2 ) return 1;743return count_if(pairing.begin(), pairing.end(),744bind2nd(greater<int>(), 0))/2;745}746747size_t FareySymbol::number_of_cusps() const {748return size_t(*max_element(cusp_classes.begin(), cusp_classes.end()))+1;749}750751size_t FareySymbol::genus() const {752return (rank_pi()-number_of_cusps()+1)/2;753}754755size_t FareySymbol::level() const {756if( index() == 1 ) return 1;757if( index() == 2 ) return 2;758vector<mpz_class> A(a), B(b);759A.push_back(1);760B.push_back(0);761vector<mpz_class> width;762for(size_t i=0; i<number_of_cusps(); i++) {763mpq_class cusp_width(0);764for(size_t j=0; j<cusp_widths.size(); j++) {765if( cusp_classes[j] == i ) {766cusp_width += cusp_widths[j];767}768}769width.push_back(cusp_width.get_num());770}771return lcm(width).get_ui();772}773774long FareySymbol::side_index(const mpz_class& a0, const mpz_class& b0,775const mpz_class& a1, const mpz_class& b1) const {776if( b0 == 0) {777if( ( a1 == a[0] and b1 == b[0]) or778(-a1 == a[0] and -b1 == b[0])) return 0;779} else if( b1 == 0 ) {780if( ( a0 == a.back() and b0 == b.back()) or781(-a0 == a.back() and -b0 == b.back())) return a.size();782} else {783mpq_class p = a1/b1, q = a0/b0;784for(size_t j=1; j<a.size(); j++) {785if( x[j-1] == q and x[j] == p ) return j;786}787}788return -1;789}790791void792FareySymbol::LLT_algorithm(const SL2Z& M, vector<int>& p, SL2Z& beta) const {793// Based on: Kurth/Long - Computations with finite index subgroups794// of PSL_2(ZZ) using Farey Symbols, p.13f795beta = M;796p.clear();797bool found = false;798mpq_class m;799for(;;) {800size_t k;801found = false;802mpz_class A=beta.a(), B=beta.b(), C=beta.c(), D=beta.d();803if( D == 0 ) {804if( A/C < x[0] ) {805found = true;806k = 0;807} else if( x.back() < A/C ) {808found = true;809k = pairing.size()-1;810}811} else if( C == 0 ) {812if( x.back() < B/D ) {813found = true;814k = pairing.size()-1;815} else if( B/D < x[0] ) {816found = true;817k = 0;818}819} else if( A/C <= x[0] and B/D <= x[0] ) {820found = true;821k = 0;822} else if( x.back() <= B/D and x.back() <= A/C ) {823found = true;824k = pairing.size()-1;825} else {826for(size_t i=0; i+1<x.size(); i++) {827if( (x[i] < B/D and B/D < A/C and A/C <= x[i+1]) or828(x[i] <= B/D and B/D < A/C and A/C < x[i+1]) or829(x[i] < A/C and A/C < B/D and B/D <= x[i+1]) or830(x[i] <= A/C and A/C < B/D and B/D < x[i+1]) ) {831found = true;832k = i+1;833break;834}835}836}837if( not found ) break;838// If the pairing, we found is ODD, we need to split the interval.839// If the arc is in the right part, we choose the pairing matrix840// If it is in the left part, we choose its inverse.841// Note, that this choice differs from the article.842if( pairing[k] == ODD ) {843if( k == 0 ) {844m = mpq_class(a[0]-1, b[0]);845} else if ( k == pairing.size()-1 ) {846m = mpq_class(a[0]+1, b[0]);847} else {848m = mpq_class(a[k-1]+a[k],b[k-1]+b[k]);849}850if ( C == 0 and B/D <=m) {851beta = pairing_matrix_in_group(k).inverse()*beta;852p.push_back(-(int)k);853} else if( C == 0 and B/D >= m) {854beta = pairing_matrix_in_group(k)*beta;855p.push_back((int)k);856} else if( D == 0 and A/C <= m) {857beta = pairing_matrix_in_group(k).inverse()*beta;858p.push_back(-(int)k);859} else if( D == 0 and A/C >=m) {860beta = pairing_matrix_in_group(k)*beta;861p.push_back((int)k);862} else if(A/C <= m and B/D <= m) {863beta = pairing_matrix_in_group(k).inverse()*beta;864p.push_back(-(int)k);865} else if(A/C >= m and B/D >= m) {866beta = pairing_matrix_in_group(k)*beta;867p.push_back((int)k);868} else {869//Based on Lemma 4 of the article by Kurth/Long,870//this case can not occure.871throw string("Mathematical compilcations in ") +872__FUNCTION__;873return;874}875} else { // case of EVEN or FREE pairing876beta = pairing_matrix_in_group(k)*beta;877p.push_back((int)k);878}879}880}881882bool FareySymbol::is_element(const SL2Z& M) const {883// based on the same article as LLT_algorithm:884// Kurth/Long - Computations with finite index ...885vector<int> p;886SL2Z beta = SL2Z::E;887size_t k = 0;888mpq_class smaller;889LLT_algorithm(M, p, beta);890891// Case (1) of the article (p14)892if( even ) {893if( beta == SL2Z::E or beta == SL2Z::I ) return true;894} else if( beta == SL2Z::E ) return true;895896// Case (3) of the article897if( beta == SL2Z::S or beta == SL2Z::U ) {898// If 0 and infty are adjacent vertices, they only can occure899// at the beginning or the end.900if( x[0] == 0 and pairing[0] == EVEN ) return true;901if( x.back() == 0 and pairing.back() == EVEN ) return true;902}903904// Case (2) of the article905if( beta.c() != 0 and beta.d() != 0 ) {906// Find index of the "edge beta"907smaller = !((beta.b()/beta.d())<(beta.a()/beta.c())) ?908(beta.a()/beta.c()) : (beta.b()/beta.d());909for(size_t i=0; i<x.size(); i++)910if( x[i] == smaller ) {911k = i;912break;913}914// Is it a free pairing?915if( pairing[k+1] > NO ) {916size_t s = paired_side(pairing, k+1);917if ( s == 0 and x[0] == 0 and beta.a()/beta.c() > beta.b()/beta.d() )918// paired with (0,infty) at the beginning?919if( even ) {920return true;921} else {922beta = pairing_matrix_in_group(k)*beta;923924if ( beta == SL2Z::E ) return true;925else return false;926}927928if( s == pairing.size() -1 and929x.back() == 0 and930beta.a()/beta.c() > beta.b()/beta.d() ) {931// paired with (0,infty) at the end?932return true;933}934}935} else if( beta.c() == 0 and pairing.back() > NO ) {936// In this case (a,infty) (for some a>0) is paired with (0,infty)937size_t s = paired_side(pairing, pairing.size()-1);938if( s == 0 and x.back() == beta.b()/beta.d() ) {939if( even ) {940return true;941} else {942beta = pairing_matrix_in_group(pairing.size()-1)*beta;943if( beta == SL2Z::E ) return true;944else return false;945}946}947}948return false;949}950951SL2Z FareySymbol::reduce_to_fraction(const mpq_class& q) const {952SL2Z M = rademacher_matrix(q);953for(size_t j=0; j<coset.size(); j++) {954SL2Z T = coset[j].inverse()*M;955if( is_element(T) ) return T;956}957return SL2Z::E;958}959960SL2Z FareySymbol::reduce_to_elementary_cusp(const mpq_class& q) const {961SL2Z M = reduce_to_fraction(q);962if( M.c()*q+M.d() == 0 ) return M;963mpq_class Q = M*q;964vector<mpq_class>::const_iterator k = find(x.begin(), x.end(), Q);965if( k != x.end() ) {966return reductions[k-x.begin()]*M;967} else {968return M;969}970}971972size_t FareySymbol::cusp_class(const mpq_class& q) const {973typedef vector<int>::const_iterator const_iterator;974SL2Z M = FareySymbol::reduce_to_elementary_cusp(q);975if( M.c()*q + M.d() == 0 ) return cusp_classes.back();976mpq_class Q = M*q;977size_t k = lower_bound(x.begin(), x.end(), Q) - x.begin();978return cusp_classes[k];979}980981//--- communication with sage ------------------------------------------------982983PyObject* FareySymbol::get_transformation_to_cusp(const mpz_t a,984const mpz_t b) const {985mpz_class p(a), q(b);986if( q == 0 ) return convert_to_SL2Z(SL2Z::E);987mpq_class r(p, q);988r.canonicalize();989PyObject* M = convert_to_SL2Z(reduce_to_elementary_cusp(r));990return M;991}992993size_t FareySymbol::index() const {994return coset.size();995}996997PyObject* FareySymbol::is_element(const mpz_t a, const mpz_t b,998const mpz_t c, const mpz_t d) const {999const SL2Z M = SL2Z(mpz_class(a), mpz_class(b), mpz_class(c), mpz_class(d));1000if( is_element(M) ) {1001Py_RETURN_TRUE;1002} else {1003Py_RETURN_FALSE;1004}1005}10061007PyObject* FareySymbol::get_coset() const {1008PyObject* coset_list = PyList_New(coset.size());1009for(size_t i=0; i<coset.size(); i++) {1010PyObject* m = convert_to_SL2Z(coset[i]);1011PyList_SetItem(coset_list, i, m);1012}1013return coset_list;1014}10151016PyObject* FareySymbol::get_generators() const {1017PyObject* generators_list = PyList_New(generators.size());1018for(size_t i=0; i<generators.size(); i++) {1019PyObject* m = convert_to_SL2Z(generators[i]);1020PyList_SetItem(generators_list, i, m);1021}1022return generators_list;1023}10241025PyObject* FareySymbol::get_cusps() const {1026PyObject* cusps_list = PyList_New(cusps.size());1027for(size_t i=0; i<cusps.size(); i++) {1028PyObject* m = convert_to_cusp(cusps[i]);1029PyList_SetItem(cusps_list, i, m);1030}1031return cusps_list;1032}10331034PyObject* FareySymbol::get_cusp_widths() const {1035vector<mpz_class> width;1036for(size_t i=0; i<number_of_cusps(); i++) {1037mpq_class cusp_width(0);1038for(size_t j=0; j<cusp_widths.size(); j++) {1039if( cusp_classes[j] == i ) {1040cusp_width += cusp_widths[j];1041}1042}1043width.push_back(cusp_width.get_num());1044}1045PyObject* cusp_widths_list = PyList_New(width.size());1046for(size_t i=0; i<width.size(); i++) {1047PyObject* m = convert_to_rational(width[i]);1048PyList_SetItem(cusp_widths_list, i, m);1049}1050return cusp_widths_list;1051}105210531054size_t1055FareySymbol::get_cusp_class(const mpz_t p, const mpz_t q) const {1056mpz_class a(p), b(q);1057if( a != 0 and b == 0 ) return cusp_classes.back();1058mpq_class c(a, b);1059c.canonicalize();1060return cusp_class(c);1061}10621063PyObject* FareySymbol::get_fractions() const {1064PyObject* x_list = PyList_New(x.size());1065for(size_t i=0; i<x.size(); i++) {1066PyObject* m = convert_to_rational(x[i]);1067PyList_SetItem(x_list, i, m);1068}1069return x_list;1070}10711072PyObject* FareySymbol::get_pairings() const {1073PyObject* pairing_list = PyList_New(pairing.size());1074for(size_t i=0; i<pairing.size(); i++) {1075PyObject* m = PyInt_FromLong(long(pairing[i]));1076PyList_SetItem(pairing_list, i, m);1077}1078return pairing_list;1079}10801081PyObject* FareySymbol::get_paired_sides() const {1082vector<int> p;1083for(size_t i=0; i<pairing.size(); i++) {1084if( pairing[i] > NO and1085p.end() == find(p.begin(), p.end(), pairing[i]) ) {1086p.push_back(pairing[i]);1087}1088}1089sort(p.begin(), p.end());1090PyObject* pairing_list = PyList_New(p.size());1091for(vector<int>::const_iterator i=p.begin(); i!=p.end(); i++) {1092vector<int>::const_iterator j = find(pairing.begin(), pairing.end(), *i);1093vector<int>::const_iterator k = find(j+1, pairing.end(), *i);1094PyObject* J = PyInt_FromLong(long(j-pairing.begin()));1095PyObject* K = PyInt_FromLong(long(k-pairing.begin()));1096PyObject* tuple = PyTuple_New(2);1097PyTuple_SetItem(tuple, 0, J);1098PyTuple_SetItem(tuple, 1, K);1099PyList_SetItem(pairing_list, i-p.begin(), tuple);1100}1101return pairing_list;1102}11031104PyObject* FareySymbol::get_pairing_matrices() const {1105PyObject* pairing_matrix_list = PyList_New(pairing.size());1106for(size_t i=0; i<pairing.size(); i++) {1107PyObject* pm = convert_to_SL2Z(pairing_matrix(i));1108PyList_SetItem(pairing_matrix_list, i, pm);1109}1110return pairing_matrix_list;1111}11121113PyObject* FareySymbol::dumps() const {1114std::ostringstream os(ostringstream::out|ostringstream::binary);1115os << (*this);1116PyObject* d = PyString_FromString(os.str().c_str());1117return d;1118}1119112011211122