/* This code proves Theorem 5.3, i.e. the morphic word avoids additive square. It also proves Theorem 5.7, i.e. the morphic word avoids abelian squares of period at least 6 Theorem 5.1 is thus proved as a corollary of Theorem 5.3 or 5.7. Code up to line 530 defines some linear algebra prerequisites: exact arithmetic in Q[sqrt(3)], vectors, matrices, Smith decomposition. Wrote in C++-11. Compilation: Compiling with -DADDITIVE produces a program checking Theorem 5.3. Remove -DADDITIVE if you want to check Theorem 5.7. with exact arithmetics (needs gmpxx) g++ -Wall -DQSQRT3 -DADDITIVE -std=c++0x -O3 -o .cpp -lgmpxx -lgmp with double: g++ -Wall -DADDITIVE -std=c++0x -O3 -o .cpp */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; /* If QSQRT3 is set, we make the computation in exact arithmetic (i.e. in Q[sqrt(3)]), otherwise the computation are made with doubles.*/ #ifdef QSQRT3 #include /* A class for exact arithmetics in Q[sqrt(3)] */ class Qsqrt3{ mpq_class a,b; // a + b * sqrt(3) public: Qsqrt3(int aa=0) : a(aa), b(0) {} Qsqrt3(const mpq_class &aa) : a(aa), b(0) {} Qsqrt3(const mpq_class &aa, const mpq_class &bb) : a(aa), b(bb) {} Qsqrt3(const mpq_class &aa, const mpq_class &bb, const mpq_class &cc) : a(aa/cc), b(bb/cc) {} explicit operator double() const { return a.get_d()+sqrt(3)*b.get_d(); } friend ostream& operator<<(ostream& out, const Qsqrt3& x) { stringstream text; if(x.b ==0)text <0)?"+":"")<< x.b <<"V(3)"; out<< text.str(); return out; } Qsqrt3& operator+=(const Qsqrt3& rhs) { a+= rhs.a; b+= rhs.b; return *this; } friend Qsqrt3 operator+(Qsqrt3 lhs, const Qsqrt3& rhs) { lhs += rhs; return lhs; } Qsqrt3& operator-=(const Qsqrt3& rhs) { a-= rhs.a; b-= rhs.b; return *this; } Qsqrt3 operator-() const { return Qsqrt3(-a,-b); } friend Qsqrt3 operator-(Qsqrt3 lhs, const Qsqrt3& rhs) { lhs -= rhs; return lhs; } friend Qsqrt3 operator*(Qsqrt3 lhs, const Qsqrt3& rhs) { mpq_class temp = lhs.a*rhs.a+3*lhs.b*rhs.b; lhs.b = lhs.a*rhs.b+lhs.b*rhs.a; lhs.a = temp; return lhs; } friend Qsqrt3 operator*(int lhs,const Qsqrt3& rhs) { return Qsqrt3(lhs*rhs.a,lhs*rhs.b); } bool isnonneg()const{ if(a<=0 && b<0) return false; if(a >=0 && b >= 0) return true; else if(a>0 && a*a >= 3 * b*b) return true; else if(a<0 && a*a <= 3 * b*b) return true; else return false; } friend Qsqrt3 operator/(Qsqrt3 lhs, const Qsqrt3& rhs) { assert( rhs.a!=0 || rhs.b!=0); mpq_class temp = lhs.a*rhs.a-3*lhs.b*rhs.b; mpq_class temp2 = rhs.a*rhs.a-3*rhs.b*rhs.b; lhs.b = lhs.b*rhs.a-lhs.a*rhs.b; lhs.a = temp; lhs.a = lhs.a/temp2; lhs.b = lhs.b/temp2; return lhs; } friend inline bool operator< (const Qsqrt3& lhs, const Qsqrt3& rhs) { Qsqrt3 temp = lhs-rhs; return !(temp.isnonneg()); } friend inline bool operator> (const Qsqrt3& lhs, const Qsqrt3& rhs) {return rhs < lhs;} friend inline bool operator<=(const Qsqrt3& lhs, const Qsqrt3& rhs) {return !(lhs > rhs);} friend inline bool operator>=(const Qsqrt3& lhs, const Qsqrt3& rhs) {return !(lhs < rhs);} friend inline bool operator==(const Qsqrt3& lhs, const Qsqrt3& rhs) {return (lhs.a == rhs.a && lhs.b == rhs.b);} friend inline bool operator!=(const Qsqrt3& lhs, const Qsqrt3& rhs) {return (lhs.a != rhs.a || lhs.b != rhs.b);} Qsqrt3 abs()const{ if (! this->isnonneg()) return -*this; return *this; } //return the integer (only if this really is an integer) int getint() const { assert(b==0 && a.get_den().get_si() == 1); // the programm abort if it not an integer return a.get_num().get_si(); } }; typedef Qsqrt3 F_t; #define F(a,b,c) F_t(a,b,c) //F(a,b,c) is a shortcut for the number (a+b*sqrt(3))/c #define Fdouble(a) (double(a)) bool isNull(const Qsqrt3 &a) { return a==0;} Qsqrt3 Fabs(const Qsqrt3 &a) { return a.abs();} int Fint(const Qsqrt3 &a) { return a.getint();} #else //QSQRT3 // otherwise, we make computations with doubles typedef double F_t; #define F(a,b,c) ((double(a)+sqrt(3)*(b))/c) #define Fdouble(a) (double(a)) bool isNull(double a) { return fabs(a)<0.000000001;} double Fabs(double a) {return fabs(a);} int Fint(double a) { return int(round(a));} #endif //QSQRT3 bool isNull(int a) { return a==0;} int Fabs(int a) { return abs(a);} template class matrix; template class vect; template void matinv(array,n> &m); template void matinv(array,n> &m); /* A class for n x m matrices of type Tvalues */ template class matrix : public array,n> { public: size_t cols() const {return m;} size_t rows() const {return n;} matrix() { for(unsigned int i=0; i &morph) { for(unsigned int i=0; i matrix & operator-=(const matrix& rhs) { for(unsigned int i=0; i matrix operator-( const matrix& rhs)const { matrix val(*this); val -= rhs; return val; } template matrix operator* (const matrix& rhs)const { matrix val; for(unsigned int i=0; i friend matrix operator*(const Tvalues& lhs, const matrix& rhs) { matrix val; for(unsigned int i=0; i transp() { matrix trans; for(unsigned int i=0; i(r); return r; } matrix abs()const { matrix val; for(unsigned int i=0; i >getstdvector()const { vector > ret(m,vect()); for(unsigned int i=0; i void matinv(array,n> &mat) { matrix tmpMat; for(unsigned int i=0; i tmpinvMat(tmpMat.inv()); matrix invMat; for(unsigned int i=0; i void matinv(array,n> &mat) { matrix invMat; for(unsigned int i=0; i class vect: public array { public: vect() { for(unsigned int i=0; i // friend vect operator*(const matrix& lhs, const vect& rhs) { // vect val; // for(unsigned int i=0; i vect operator*(const matrix& lhs, const vect& rhs) { vect val; for(unsigned int i=0; i void smith(const matrix& M,matrix& U, matrix& D, matrix& V) { for(unsigned int i=0;i& m) { for (unsigned int i=0; i"< c; vect u; Pattern():c(3,-1) { } friend ostream& operator<<(ostream& out,Pattern& p) { out << p.c[0]<<" (0,0,0,0) " << p.c[1] << " (" << p.u << ") "<< p.c[2]; return out; } inline bool operator <(const Pattern &P) const { return c x; public: F_t bound; testor(F_t& boundn, vect& x): x(x), bound(boundn) {}; template inline bool operator () (const vect& parikh) const { F_t r=0; for(int i =0; i prevec; vect lastvec; cutvec(int n):letter(-1) {} friend ostream& operator<<(ostream& out, cutvec& p) { out << "letter = "<< p.letter<< "( "; for (int i=0; i& morph, vect& eVec) { F_t ret=0; for(unsigned int i= 0; i0; k--) { temp2 += eVec(morph[ii][k]-'a'); if(Fabs(temp+temp2)>ret) ret = Fabs(temp+temp2); } } } } return ret; } /* Find the factor that maximizes the dot product of evec and the parikh vector of this factor. This allows to check the bounds on the r_i*. */ F_t findrstarfactor(const vector& morph, vect& eVec) { F_t ret=0; F_t temp; string word=morph[0]; for(int l=1; l<4; l++) for(unsigned int i=0; iret) ret = Fabs(temp); } } return ret; } /* Initialize listOfCut: this is a list of the ways a letter can be obtained in the image of an other letter. This is usefull to compute it once so we do not have to compute it every time we want to have the parent of a template. */ void init(vector* listOfCut, const vector& morph, unsigned int nb_letters) { cutvec c0h(nb_letters); listOfCut[0].push_back(c0h); for(unsigned int i=0; i,list > > alreadycomputedpreimages; set seenPattern; //this set allows us to add each pattern only once /* The following function computes the list of parents of one pattern. */ template void getParents(Pattern p, vector& listOfParents, vector* pOfLet, vector& testlist, const matrix& invU, const matrix& D, const matrix& invV, const vector >& baseoftheker, F_t mumin, const matrix& proj, const vect& projbound, const vector >& kerforimproovement) { //pOfLet[-1] for epsilon //unsigned int nbletters = p.nbletters; Pattern newPat; vect tempu; vect uparents; vector combin(baseoftheker.size(),0); vector::iterator cut[3]; //we try all the preimages possible for a letter int nb_iter = pOfLet[p.c[0]].size() * pOfLet[p.c[1]].size() *pOfLet[p.c[2]].size(); int compt =0; for(cut[0] = pOfLet[p.c[0]].begin(); cut[0] != pOfLet[p.c[0]].end(); cut[0]++) for(cut[1] = pOfLet[p.c[1]].begin() ; cut[1]!= pOfLet[p.c[1]].end(); cut[1]++) for(cut[2] = pOfLet[p.c[2]].begin() ; cut[2]!= pOfLet[p.c[2]].end(); cut[2]++) { if(compt%1000==0 && nb_iter>20000) cerr <lastvec - cut[2]->prevec + cut[0]->lastvec + cut[1]->prevec; for(int i =0; i<3; i++) newPat.c[i]= cut[i]->letter; bool ok = true; uparents = invU*tempu; for(unsigned int i=0; i PK = proj * kerforimproovement[i]; if(PK.squaredNorm()==0) continue; F_t coef = (proj*tempu).dot(PK)/PK.squaredNorm(); tempu = tempu - int(round(double(coef)))* kerforimproovement[i]; } }while(val*F(99,0,100) > (proj*tempu).squaredNorm()); //This computes the bounds for the ball as described in the part "computation of the parents" F_t boundSquared =(projbound+(proj*tempu).abs()).squaredNorm()/mumin; int ibound = floor(sqrt(double(boundSquared))); if(alreadycomputedpreimages.find(tempu)==alreadycomputedpreimages.end() ) { auto &tt(alreadycomputedpreimages[tempu]); int kk=0,ll=0; for(unsigned int i=0; i=ibound) { combin[l]=-ibound; l++; } if(l >= combin.size()) break; (combin[l])++; } } else { auto &tt(alreadycomputedpreimages[tempu]); for(auto it=tt.begin();it!=tt.end();++it) { newPat.u=*it; if(seenPattern.insert(newPat).second) listOfParents.push_back(newPat); } } } } /* Computes the image of 'a' by the morphism morph. */ string image(const vector& morph,const string &a) { string word; for(unsigned int j=0; j& morph, int n, vector preimage, vector & factors, set& seenwords) { for(unsigned int i=0; i &squares) { int max =0; vect diff; for(int i = 1; i*2<=s; i++) { diff(w[s-i]-'a')+=2; diff(w[s-2*i+1]-'a')--; diff(w[s-2*i]-'a')--; if(diff.squaredNorm() == 0) { max = i; string sq(w,s-2*i,2*i); squares.insert(sq); } } return max; } int main(int argc, char *argv[]) { //Initialization : everything is hard-coded for the morphic word of Theorem 6 vector firstmorphism(nbletters); firstmorphism[0]="ace"; firstmorphism[1]="adf"; firstmorphism[2]="bdf"; firstmorphism[3]="bdc"; firstmorphism[4]="afe"; firstmorphism[5]="bce"; matrix M(firstmorphism); // The matrix M_h for the firstmorphism matrix J; //the Jordan normal form of M J(0,1)=1; J(3,3)=3; J(4,4)=F(0,1,1); J(5,5)=F(0,-1,1); matrix P; P(0,0)=F(-1,0,2); P(0,1)=0; P(0,2)=-1; P(0,3)=1; P(0,4)=F(2,1,1); P(0,5)=F(2,-1,1); P(1,0)=F(1,0,2); P(1,1)=-1; P(1,2)=0; P(1,3)=1; P(1,4)=F(-2,-1,1); P(1,5)=F(-2,1,1); P(2,0)=F(-1,0,2); P(2,1)=1; P(2,2)=-1; P(2,3)=1; P(2,4)=-1; P(2,5)=-1; P(3,0)=0; P(3,1)=0; P(3,2)=1; P(3,3)=1; P(3,4)=F(-3,-2,1); P(3,5)=F(-3,2,1); P(4,0)=0; P(4,1)=F(1,0,2); P(4,2)=1; P(4,3)=1; P(4,4)=F(3,2,1); P(4,5)=F(3,-2,1); P(5,0)=F(1,0,2); P(5,1)=F(-1,0,2); P(5,2)=0; P(5,3)=1; P(5,4)=1; P(5,5)=1; matrix Pinv = P.inv(); assert((P*Pinv).isId()); // check that the Pinv is the inverse of P assert((P*J*Pinv-M).isNull()); // check that P*J*P^-1=M (that is we have a Jordan decomposition of M) matrix baseofKerofM; // A base of the lattice of the integer points of the kernel of M baseofKerofM(0,0)= -1; baseofKerofM(1,0)=1; baseofKerofM(2,0)= -1; baseofKerofM(3,0)= 0; baseofKerofM(4,0)= 0; baseofKerofM(5,0)= 1; baseofKerofM(0,1)= -1; baseofKerofM(1,1)=0; baseofKerofM(2,1)= -1; baseofKerofM(3,1)= 1; baseofKerofM(4,1)= 1; baseofKerofM(5,1)= 0; #ifndef ADDITIVE vector h(nbletters); h[0]="bbbaabaaac"; h[1]="bccacccbcc"; h[2]="ccccbbbcbc"; h[3]="ccccccccaa"; h[4]="bbbbbcabaa"; h[5]="aaaaaaabaa"; matrix Mh(h); // The matrix M_h for the second morphism matrix baseofKerofh;// A base of the lattice of the integer points of the kernel of Mh baseofKerofh(0,0)= 4; baseofKerofh(1,0)=-5; baseofKerofh(2,0)= 6;baseofKerofh(3,0)= 0;baseofKerofh(4,0)= -5;baseofKerofh(5,0)= 0; baseofKerofh(0,1)= 3; baseofKerofh(1,1)=3; baseofKerofh(2,1)= -4;baseofKerofh(3,1)= 0;baseofKerofh(4,1)= 0;baseofKerofh(5,1)= -2; baseofKerofh(0,2)= 0; baseofKerofh(1,2)=-2; baseofKerofh(2,2)= 1;baseofKerofh(3,2)= 1;baseofKerofh(4,2)= 0;baseofKerofh(5,2)= 0; #else matrix Mh; Mh(0,0)=1;Mh(1,0)=0;Mh(2,0)=0;Mh(3,0)=0;Mh(4,0)=0;Mh(5,0)=0; Mh(0,1)=1;Mh(1,1)=1;Mh(2,1)=1;Mh(3,1)=0;Mh(4,1)=0;Mh(5,1)=0; Mh(0,2)=1;Mh(1,2)=2;Mh(2,2)=1;Mh(3,2)=0;Mh(4,2)=0;Mh(5,2)=0; Mh(0,3)=1;Mh(1,3)=0;Mh(2,3)=1;Mh(3,3)=0;Mh(4,3)=0;Mh(5,3)=0; Mh(0,4)=1;Mh(1,4)=2;Mh(2,4)=0;Mh(3,4)=0;Mh(4,4)=0;Mh(5,4)=0; Mh(0,5)=1;Mh(1,5)=1;Mh(2,5)=0;Mh(3,5)=0;Mh(4,5)=0;Mh(5,5)=0; matrix baseofKerofh;// A base of the lattice of the integer points of the kernel of Mh baseofKerofh(0,0)= 0; baseofKerofh(1,0)=2; baseofKerofh(2,0)= -1;baseofKerofh(3,0)= -1;baseofKerofh(4,0)= 0;baseofKerofh(5,0)= 0; baseofKerofh(0,1)= -1; baseofKerofh(1,1)=0; baseofKerofh(2,1)= 0;baseofKerofh(3,1)= 0;baseofKerofh(4,1)= -1;baseofKerofh(5,1)= 2; baseofKerofh(0,2)= 1; baseofKerofh(1,2)=1; baseofKerofh(2,2)= 0;baseofKerofh(3,2)= -1;baseofKerofh(4,2)= 0;baseofKerofh(5,2)= -1; #endif cout << "*************** Initialization of the first morphism ***************"< firstmorphism2(nbletters); // The composition of firstmorphism by itself for(unsigned int i=0;i(firstmorphism2)-M*M).isNull()); #ifndef ADDITIVE cout << "*************** Initialization of the second morphism ***************"< listtest; // we keep here the r_i and r_i* of the proposition 6 F_t last=0; /* This matrix contains the subvectors of P on which we can project the kernel. This corresponds to the matrix Q of the "Computation of the parents" in the article. */ matrix constrkerofmorphism; vect boundkerofmorphism; // this contains the corresponding bounds int nbconstrkerofmorphism=0; /* This matrix contains all the subevtors of P corresponding to eigein-values of norm <1. This correspond to the matrix Q from the proposition 10 in the article. This is usefull later to compute the parents by the second morphism */ matrix constrVect; vect constr; // this contains the corresponding bounds int nbconstr = 0; /* We compute the r_i* as described on the article. We use the square of the morphism to get better bounds. */ for(int i=nbletters-1; i>=0; i--) { if(Fabs(J(i,i))<1) {// We need to take care of the eigen-values of norm less than 1 vect eVec; for(unsigned int j=0; j::iterator it=listtest.begin();it!=listtest.end();++it) { it->bound*=1.00001; } #endif ///////////////////////////////////////////////////////// ////////// Computing bounds on the kernel /////////////// ///////////////////////////////////////////////////////// cout< tempmath = constrVect*baseofKerofh; cout << "The matrix corresponding to Q'B in the proof of proposition 10 in the article is: "< conconadjointh =tempmath*tempmath.transp(); cout << "(Q'B)x(Q'B)* = "< listOfParents; Pattern p; listOfParents.push_back(p); seenPattern.insert(p); vector pOfLeth[nbletters+1]; init(pOfLeth, h, nbletters); matrix Uh,Vh,Diagh; smith(Mh, Uh, Diagh, Vh); matrix invVh(Vh.inv()),invUh(Uh.inv()); assert((Vh*invVh).isId() && (Uh*invUh).isId()); assert((Uh*Diagh*Vh-Mh).isNull()); // check we have a correct Smith decomposition vector > kerForImproovement(baseofKerofh.getstdvector()); /* We could reduce the basis of Ker(h) (e.g. by LLL) to improve x_0, and thus the bound and the execution time. But this is useless here. */ vector > copybaseofKerofh(baseofKerofh.getstdvector()); // this computes the parents by h that are realizable by the first morphism getParents<3>(listOfParents[0], listOfParents, &pOfLeth[1], listtest, invUh, Diagh, invVh, copybaseofKerofh, muminh, constrVect, constr, kerForImproovement); cout<<"There are " < listOfParents; Pattern p; vect combin; int ibound= (int)floor(sqrt((double)(squaredbound))); combin(0)=combin(1)=combin(2)=-ibound; while(true){ p.u= baseofKerofh*combin; bool ok = true; for(unsigned int i=0; i< listtest.size(); i++) if(!listtest[i](p.u)){ok = false; break;} if(ok) { seenPattern.insert(p); listOfParents.push_back(p); cout <=ibound) { combin(l)=-ibound; l++; } if(l >= 3) break; (combin(l))+=1; } #endif ///////////////////////////////////////////////////////// ////////////////// Computing bounds ///////////////////// ///////////////////////////////////////////////////////// cout< tempmatmorph =(constrkerofmorphism*baseofKerofM); cout< conconadjointmorph =tempmatmorph * tempmatmorph.transp(); cout <<"(Q'B)x(Q'B)* = "< pOfLet[nbletters+1]; init(pOfLet, firstmorphism, nbletters); matrix U,V,Diag; smith(M, U, Diag, V); matrix invV(V.inv()),invU(U.inv()); assert((V*invV).isId() && (U*invU).isId()); assert((U*Diag*V-M).isNull()); // check if we have a correct Smith decomposition vector > kerforimproovementmorph(baseofKerofM.getstdvector()); vector > copybaseofKerofM(baseofKerofM.getstdvector()); unsigned int ind=0; while(ind(listOfParents[ind], listOfParents, &pOfLet[1], listtest, invU, Diag, invV, copybaseofKerofM, muminmorph, projmorphism, projboundmorphism, kerforimproovementmorph); if(++ind%10000==1)cout<::iterator it= listOfParents.begin(), ie= listOfParents.end(); it != ie ; ++it) { int normL1=0; for(unsigned int i=0; iu.size(); i++) normL1 += abs(it->u(i)); if(normL1>Delta) Delta = normL1; } int delta =0; for(unsigned int i=0; i delta) delta = firstmorphism[i].size(); int lengthToCheck = Delta + 2*delta+2+1; cout <<"Using the formula of propositon 8 we can compute Delta = "<< Delta<< " and delta = "< >factors(lengthToCheck); for(unsigned int i=0; i< nbletters; i++) factors[0].push_back(string(1,i+'a')); setseenFactors; computefactorsofsizenfromsizep(firstmorphism, 2, factors[0], factors[1], seenFactors); unsigned int templ=0; do{ templ = factors[1].size(); computefactorsofsizenfromsizep(firstmorphism, 2, factors[1], factors[1], seenFactors); }while(templ < factors[1].size()); for(unsigned int i =2; i::iterator it= listOfParents.begin(), ie= listOfParents.end(); it != ie && abeliansquarefree ; ++it) { //if the word realizing the pattern is aubvc int diffLength=0; // = |v|-|u| for(unsigned int i=0; iu(i); int lengthLetters = ((it->c[0]!=-1)?1:0)+((it->c[1]!=-1)?1:0)+((it->c[2]!=-1)?1:0); // lengthLetters = |abc| for(unsigned int i=2; i=0 int p1=l; if(it->c[0]!=-1) p1++; for(unsigned int j=0; jc[0]!=-1 && it->c[0]!=factors[i][j][0]-'a') continue; if(it->c[1]!=-1 && it->c[1]!=factors[i][j][p1]-'a') continue; if(it->c[2]!=-1 && it->c[2]!=factors[i][j][i]-'a') continue; // check if the vector is the same unsigned int lc=0; if(it->c[0]!=-1) lc++; vectxu; for(unsigned int k =lc; kc[1]!=-1) lc++; for(unsigned int k =lc; kc[2]!=-1) lc++; assert(lc==i+1); if(!(xu == it->u)) continue; #ifndef ADDITIVE cout << "There is an abelian square which appears! "< possiblesquares; set seenFactorsh; computefactorsofsizenfromsizep(h, 2*maxL, factors[1], possiblesquares, seenFactorsh); int longestPeriod=0; set squares; for(unsigned int i=0; i::const_iterator it=squares.begin();it!=squares.end();++it) cout<<*it<<" "; cout<