diff --git a/contfrac.cc b/contfrac.cc index 50d505a..7f49c9b 100644 --- a/contfrac.cc +++ b/contfrac.cc @@ -47,15 +47,20 @@ static void cf_helper(ContFrac *me, T p, T q, int level) T div=p/q; { T rem=p%q; -if(rem) cf_helper(me,q,rem,level+1); +if(rem) + { + if(rem<0) {--div; rem+=q;} //prevent negative a_i i>0 + cf_helper(me,q,rem,level+1); + } else me->resize(level); } (*me)[level]=div; } template -ContFrac::ContFrac(const T p, const T q) : NRVec() +ContFrac::ContFrac(T p, T q) : NRVec() { +if(q<0) {p= -p; q= -q;} cf_helper(this,p,q,0); } @@ -117,11 +122,33 @@ return x; } +//compare assuming they are canonical +template +T ContFrac::compare(const ContFrac &rhs) const +{ +int l=length(); +if(rhs.length() void ContFrac::canonicalize() { int n=this->length(); if(n==0) return; +if(n>0 && (*this)[1]<0) //handle negative a_i i>0 + { + for(int i=0; i<=n; ++i) (*this[i]) = -(*this[i]); + *this = -(*this); + } this->copyonwrite(); if((*this)[n]==1) {(*this)[n]=0; ++(*this)[n-1];} //avoid deepest 1/1 for(int i=1; i<=n; ++i) //truncate if possible @@ -167,13 +194,18 @@ return hnew; template -bool Homographic::outputready(T &z) const +bool Homographic::outputready(T &z,bool first) const { bool inf=0; T q0,q1; if(v[1][0]==0) inf=1; else q0=v[0][0]/v[1][0]; if(v[1][1]==0) inf=1; else q1=v[0][1]/v[1][1]; -if(!inf && q0==q1) {z=q0; return true;} +if(!inf && q0==q1) + { + z=q0; + if(first && q0<0) --z; //prevent negative a1 etc. + return true; + } return false; } @@ -190,6 +222,7 @@ ContFrac Homographic::value(const ContFrac&x) const Homographic h(*this); std::list l; +bool first=true; for(typename ContFrac::iterator px=x.begin(); px!=x.beyondend(); ++px) { //digest next input term @@ -197,10 +230,11 @@ for(typename ContFrac::iterator px=x.begin(); px!=x.beyondend(); ++px) //output as much as possible T out; - while(h.outputready(out)) + while(h.outputready(out,first)) { l.push_back(out); h=h.output(out); + first=false; } //terminate if exhausted @@ -210,6 +244,11 @@ for(typename ContFrac::iterator px=x.begin(); px!=x.beyondend(); ++px) break; } } +if(l.back()==1) //simplify by removing a trailing 1 + { + l.pop_back(); + l.back()+=1; + } return ContFrac(l); } @@ -273,7 +312,7 @@ return 1; template -bool BiHomographic::outputready(T &z) const +bool BiHomographic::outputready(T &z,bool first) const { T q[2][2]; for(int i=0; i<2; ++i) for(int j=0; j<2; ++j) @@ -283,6 +322,7 @@ for(int i=0; i<2; ++i) for(int j=0; j<2; ++j) if(q[i][j]!=q[0][0]) return false; } z=q[0][0]; +if(first && z<0) --z; return true; } @@ -303,6 +343,7 @@ std::list l; typename ContFrac::iterator px=x.begin(); typename ContFrac::iterator py=y.begin(); +bool first=true; do { //select next input term @@ -316,10 +357,11 @@ do //output as much as possible T out; - while(h.outputready(out)) + while(h.outputready(out,first)) { l.push_back(out); h=h.output(out); + first=false; } //terminate if exhausted @@ -330,6 +372,13 @@ do } } while(px!=x.beyondend() || py!=y.beyondend()); + +if(l.back()==1) //simplify by removing a trailing 1 + { + l.pop_back(); + l.back()+=1; + } + return ContFrac(l); } @@ -344,7 +393,7 @@ if(den<0) den= -den; } T g=gcd(num,den); -if(MYABS(g)>1) +if(g>1) { num/=g; den/=g; @@ -381,6 +430,7 @@ return *this; } +//try avoiding overflows at the cost of speed template Rational Rational::operator+(const Rational &rhs) const { @@ -426,6 +476,45 @@ return *this; } +//unary - +template +ContFrac ContFrac::operator-() const +{ +int l=length(); +if(l==0) + { + ContFrac r(0); + r[0]= -(*this)[0]; + return r; + } +if((*this)[1]!=1) + { + ContFrac r(l+1); + r[0]= -(*this)[0]-1; + r[1]= 1; + r[2]= (*this)[1]-1; + for(int i=2; i<=l; ++i) r[i+1] = (*this)[i]; + return r; + } +else //a_1-1 == 0 + { + if(l==1) //we have trailing 0, actually the input was not canonical + { + ContFrac r(0); + r[0]= -(*this)[0]-1; + return r; + } + else + { + ContFrac r(l-1); + r[0]= -(*this)[0]-1; + r[1]= 1+(*this)[2]; + for(int i=3; i<=l; ++i) r[i-1] = (*this)[i]; + return r; + } + } +} + /***************************************************************************//** diff --git a/contfrac.h b/contfrac.h index 736ebd7..e463121 100644 --- a/contfrac.h +++ b/contfrac.h @@ -25,17 +25,16 @@ namespace LA { -//simple finite continued fraction class +//Support for rationals and a simple finite continued fraction class //NOTE: 0 on any position >0 means actually infinity; simplify() shortens the vector -//presently implements just conversion to/from rationals and floats -//maybe implement arithmetic by Gosper's method cf. https://perl.plover.com/classes/cftalk/TALK -// +//includes Gosper's arithmetics - cf. https://perl.plover.com/classes/cftalk/TALK +//maybe implement the self-feeding Gosper's algorithm for sqrt(int) +//maybe do not interpret a_i=0 i>0 as termination??? template class ContFrac; -//@@@operator== > >= etc. template class Rational { public: @@ -45,7 +44,7 @@ public: Rational() {}; Rational(const T p, const T q) : num(p),den(q) {}; explicit Rational(const T (&a)[2]) :num(a[0]), den(a[1]) {}; - Rational(const ContFrac &cf) {cf.convergent(&num,&den);}; + explicit Rational(const ContFrac &cf) {cf.convergent(&num,&den);}; void simplify(); //basic rational arithmetics @@ -67,7 +66,21 @@ public: Rational & operator+=(const Rational &rhs) {*this = *this+rhs; return *this;}; Rational & operator-=(const Rational &rhs) {*this = *this-rhs; return *this;}; - + //combination with continued fractions + ContFrac operator+(const ContFrac &rhs) const {return rhs + *this;}; + ContFrac operator-(const ContFrac &rhs) const {return -rhs + *this;}; + ContFrac operator*(const ContFrac &rhs) const {return rhs * *this;}; + ContFrac operator/(const ContFrac &rhs) const {return rhs.reciprocal() * *this;}; + + + //relational operators, relying that operator- yields a form with a positive denominator + bool operator==(const Rational &rhs) const {Rational t= *this-rhs; return t.num==0;}; + bool operator!=(const Rational &rhs) const {Rational t= *this-rhs; return t.num!=0;}; + bool operator>=(const Rational &rhs) const {Rational t= *this-rhs; return t.num>=0;}; + bool operator<=(const Rational &rhs) const {Rational t= *this-rhs; return t.num<=0;}; + bool operator>(const Rational &rhs) const {Rational t= *this-rhs; return t.num>0;}; + bool operator<(const Rational &rhs) const {Rational t= *this-rhs; return t.num<0;}; + }; template @@ -96,7 +109,6 @@ class BiHomographic; -//@@@operator== > >= etc. template class ContFrac : public NRVec { private: @@ -106,16 +118,17 @@ public: template ContFrac(const T (&a)[SIZE]) : NRVec(a) {}; ContFrac(const NRVec &v) : NRVec(v) {}; //allow implicit conversion from NRVec ContFrac(const int n) : NRVec(n+1) {}; - ContFrac(double x, const int n, const T thres=0); //might yield a non-canonical form + explicit ContFrac(double x, const int n, const T thres=0); //WARNING: it might yield a non-canonical form //we could make a template for analogous conversion from an arbitrary-precision type - ContFrac(const T p, const T q); //should yield a canonical form - ContFrac(const Rational &r) : ContFrac(r.num,r.den) {}; + ContFrac(T p, T q); //should yield a canonical form + explicit ContFrac(const Rational &r) : ContFrac(r.num,r.den) {}; void canonicalize(); void convergent(T *p, T*q, const int trunc= -1) const; Rational rational(const int trunc= -1) const {T p,q; convergent(&p,&q,trunc); return Rational(p,q);}; double value(const int trunc= -1) const; //we could make also a template usable with an arbitrary-precision type ContFrac reciprocal() const; + ContFrac operator-() const; //unary minus int length() const {return NRVec::size()-1;}; void resize(const int n, const bool preserve=true) { @@ -123,6 +136,7 @@ public: NRVec::resize(n+1,preserve); if(preserve) for(int i=nold+1; i<=n;++i) (*this)[i]=0; } + //arithmetics with a single ContFrac operand ContFrac operator+(const Rational &rhs) const {Homographic h({{rhs.num,rhs.den},{rhs.den,0}}); return h.value(*this);}; ContFrac operator-(const Rational &rhs) const {Homographic h({{-rhs.num,rhs.den},{rhs.den,0}}); return h.value(*this);}; @@ -142,6 +156,16 @@ public: ContFrac operator*(const ContFrac &rhs) const {BiHomographic h({{{0,0},{0,1}},{{1,0},{0,0}}}); return h.value(*this,rhs);}; ContFrac operator/(const ContFrac &rhs) const {BiHomographic h({{{0,1},{0,0}},{{0,0},{1,0}}}); return h.value(*this,rhs);}; + + //relational operators, guaranteed only to work correctly for canonicalized CF! + T compare(const ContFrac &rhs) const; + bool operator==(const ContFrac &rhs) const {return compare(rhs)==0;}; + bool operator>(const ContFrac &rhs) const {return compare(rhs)>0;}; + bool operator<(const ContFrac &rhs) const {return rhs.operator>(*this);}; + bool operator!=(const ContFrac &rhs) const {return ! this->operator==(rhs) ;} + bool operator<=(const ContFrac &rhs) const {return ! this->operator>(rhs) ;} + bool operator>=(const ContFrac &rhs) const {return ! this->operator<(rhs) ;} + //iterator class iterator { private: @@ -176,7 +200,7 @@ T v[2][2]; //{{a,b},{c,d}} for (a+b.x)/(c+d.x) i.e. [denominator][power_x] Homographic input(const T &x, const bool inf) const; Homographic output(const T &x) const; - bool outputready(T &x) const; + bool outputready(T &x, bool first) const; bool terminate() const; }; @@ -195,7 +219,7 @@ T v[2][2][2]; //{{{a,b},{c,d}},{{e,f},{g,h}}} i.e.[denominator][power_y][power_ BiHomographic inputy(const T &y, const bool inf) const; BiHomographic output(const T &z) const; int inputselect() const; - bool outputready(T &x) const; + bool outputready(T &x,bool first) const; bool terminate() const; }; diff --git a/t.cc b/t.cc index 1a21e57..d7ef878 100644 --- a/t.cc +++ b/t.cc @@ -2483,7 +2483,7 @@ ContFrac z= x*Rational({2,3}); cout<(z)< x(11,101); ContFrac v(3,7); @@ -2497,6 +2497,49 @@ cout<(zz)<(x)+3)*(Rational(v)+4)/(Rational(x)-Rational(v))<>x; +ContFrac xx(x,15,100000); +cout < xm= -xx; +cout<< xm< x; +cin >>x; +ContFrac xx(x); +cout< xm= -xx; +cout<< xm; +ContFrac yy(-x); +cout< ym= -yy; +cout<< ym; +cout <<"ZEROs\n"<>x; +ContFrac xx(x,25,100000); +ContFrac xx1(x+1e-4,25,100000); +ContFrac xx2(x-1e-4,25,100000); +xx.canonicalize(); +xx1.canonicalize(); +xx2.canonicalize(); +cout<<"small "<xx2) <