function RAT(t,b){  // if infinite, sets  t=1 and b=0  if(!isZ(t))halt("Cant make Q from num:"+t);  if(!isZ(b))halt("cant make Q from den:"+b);  if(b==0){t=(t>0?1:-1)}  else{var g=gcd(t,b);if(g>1){t=Math.round(t/g);b=Math.round(b/g)}};  if(b<0){t=-t;b=-b}  this.top=NB(t);  this.bot=NB(b);  this.isfinite=b!=0;  this.toString=function(){      return (this.isfinite?(this.bot==1?this.top.toString()                                        :this.top.toString()+"/"+this.bot.toString()+"")                           :(this.top<0?"-":"")+"Infinity")};  this.val=NB(t/b);  this.sgnum=(this.top<0?-1:this.top==0?0:1);};function RATmul(a,b){if(typeof a=="number")a=new RAT(a,1);if(typeof b =="number")b=new RAT(b,1);   return new RAT(a.top*b.top,a.bot*b.bot)};function RATdiv(a,b){if(typeof a=="number")a=new RAT(a,1);if(typeof b =="number")b=new RAT(b,1);   return new RAT(a.top*b.bot,a.bot*b.top)};function RATadd(a,b){if(typeof a=="number")a=new RAT(a,1);if(typeof b =="number")b=new RAT(b,1);   return new RAT(a.top*b.bot+b.top*a.bot,a.bot*b.bot)};function RATsub(a,b){if(typeof a=="number")a=new RAT(a,1);if(typeof b=="number")b=new RAT(b,1);   return RATadd(a,RATneg(b))};function RATneg(a){return (typeof a=="number"?new RAT(-a,1):new RAT(-a.top,a.bot))};function RATis(a,op,b){if(typeof a=="number")a=new RAT(a,1);if(typeof b=="number")b=new RAT(b,1);     var L=a.top*b.bot,R=a.bot*b.top;     return eval(isFinite(L)&&isFinite(R)?L+op+R:a.val+op+b.val)};var toosmall=0.0000000001,toolarge=100000;function NB(x){if(Math.abs(Math.round(x)-x)<=toosmall)return Math.round(x)    else if(x>toolarge)return Number.POSITIVE_INFINITY    else if(x<-toolarge)return Number.NEGATIVE_INFINITY    else return x};function norm(x){   //if(isPOLY(x)&&x.deg==0){x=POLYcoeff(x,0)};   //if(isC(x)&&NB(Cim(x))==0){x=Cre(x)};   if(isQ(x)&&x.bot==1){x=x.top};   if(isR(x)&&Math.abs(Math.round(x)-x)<toosmall)x=Math.round(x);   return x};function Z2Q(n){return new RAT(n,1)};//function Q2C(r){return new COMPLX(r,0)};function Q2R(x){return x.val};//function R2C(x){return new COMPLX(x,0)};function isZ(x){return !isNaN(x) && isFinite(x) &&  x.toString().indexOf(".")==-1};function isR(x){return !isNaN(x) && isFinite(x) && x.toString().indexOf(".")!=-1};function isNb(x){return isZ(x)||isR(x)};function isQ(x){return typeof x=="object" && x.top};   //x instanceof RAT }  //function isC(x){return typeof x=="object" && x.im};   //x instanceof COMPLX};function isfinite(n){return isQ(n)? n.isfinite:isFinite(n)};function isInfinite(x){return isQ(x)&&!x.isfinite //|| !isC(x) &&!isfinite(x)};var notyp="?no-type?"function typ(x){return isZ(x)?"Z":isR(x)?"R":isQ(x)?"Q"//:isC(x)?"C":isPOLY(x)?"P"          :notyp};function isString(s){return typeof s == "string"};function toR(x){return //isC(x)?x:      isQ(x)?x.val:isR(x)||isZ(x)?x:halt("cannot convert "+x+" to R")};//function toC(x){if(typ(x)=="C"){return x }else{ return [x,0]}};function toQ(x){switch(typ(x)){   case "Q":return x;  // case "C":halt("Cant convert CtoQ "+C);   case "R":halt("Cant convert RtoQ "+x);   case "Z":return Z2Q(x);}};function toR(x){     if(isQ(x))return x.val;     //if(isC(x)){if(x[1]==0){return x[0]}else{halt("Cant convert CtoR:"+x)}};     if(isNb(x))return x;     };function add(x,y){//document.writeln(">>> ",typeof x,"=",x,"  ",typeof y,"=",y);        if(isInfinite(x) && isInfinite(y)) return (cmp(x,">",0)==cmp(y,">",0)?x:Number.NaN)   else if(isInfinite(x))return x   else if(isInfinite(y))return y   else if(isR(x)||isR(y))return norm(toR(x)+toR(y))   else if(isQ(x)||isQ(y)){x=toQ(x);y=toQ(y);return norm(RATadd(x,y))};   return norm(NB(eval(x)+eval(y))); };function sub(x,y){return add(x,neg(y))};function neg(x){ if(isNb(x))return -x;   if(isInfinite(x))return -x;   if(isQ(x))return RATneg(x);   halt("??? negate "+x)};function mul(a,b){        if(isInfinite(a))return (b==0?Number.NaN:cmp(b,">",0)?a:-a)   else if(isInfinite(b))return (a==0?Number.NaN:cmp(a,">",0)?b:-b) //  else if(isC(a)||isC(b)){a=toC(a);b=toC(b);return norm(Cmul(a,b))}   else if(isR(a)||isR(b))return norm(toR(a)*toR(b));   else if(isQ(a)||isQ(b)){a=toQ(a);b=toQ(b);return norm(RATmul(a,b))}   return norm(a*b)};function mod(a,b){  if(isInfinite(b))return (!isInfinite(a)?a:Number.NaN);  if(isZ(a)&&isZ(b))return a%b  return Number.NaN};function div(n,d){//alert("div "+typ(n)+" "+typ(d));   if(isR(n)||isR(d)){n=toR(n);d=toR(d);return norm(NB(n/d))};   //alert(n+" mod "+d+" = "+mod(n,d)+" flr="+Math.floor(n/d));   if(isZ(n)&&isZ(d))       if(mod(n,d)==0)return norm(Math.round(n/d))       else return norm(new RAT(n,d));   n=toQ(n);d=toQ(d);return norm(RATdiv(n,d))};function pow(a,b){//alert("pow "+typ(a)+"^"+typ(b));  if(isQ(b))b=b.val;  if(isQ(a)){             if(isZ(b)&&b<0)return new RAT(Math.pow(a.bot,-b),Math.pow(a.top,-b))             else           return new RAT(Math.pow(a.top,b),Math.pow(a.bot,b))};  a=toR(a);return norm(NB(Math.pow(a,b)))};function cmp(a,op,b){   if(op=="=")op="==";  if(isQ(a)||isQ(b)){a=toQ(a);b=toQ(b);return RATis(a,op,b)};   return eval(a+op+b)};function abs(x){if(cmp(x,"<",0))x=neg(x);return x};function num(n){ return isQ(n)?n.top:n };function den(n){return isQ(n)?n.bot:1};