#!/usr/local/bin/bc -l ### Output-Formatting.BC - Formatted output # Functions in this library with a name containing "print" return a result # equivalent to what has been output, meaning that to use them properly, # they must be assigned to a dummy variable to prevent bc from inadvertently # printing out the return value! # An example usage might be q=printfrac(0,5,0.25)+newline() # q here is a dummy variable, printfrac will print 0.25 as a fraction and # the newline function prints a carriage return. Net result should be: # 1/4 # Literally and figuratively adding the newline() and printspc/tabs() # (q.v.) is recommended, as these neaten the output. ## Bases < 36 # Set this to non-zero to make output non-standard lowercase output_lcase_=0 # Workhorse functions for the below define letteru__(a) { # expects an integer 0 <= a <= 35 auto oib,oob; oib=ibase;oob=obase;ibase=A;obase=F+1 if(a< 0)print "_"; if(0<=a&&a<16)print a;obase=A if(a==16)print "G";if(a==17)print "H";if(a==18)print "I";if(a==19)print "J" if(a==20)print "K";if(a==21)print "L";if(a==22)print "M";if(a==23)print "N" if(a==24)print "O";if(a==25)print "P";if(a==26)print "Q";if(a==27)print "R" if(a==28)print "S";if(a==29)print "T";if(a==30)print "U";if(a==31)print "V" if(a==32)print "W";if(a==33)print "X";if(a==34)print "Y";if(a==35)print "Z" if(a>=36)print " ",a ibase=oib;obase=oob;return a } define letterl__(a) { # expects an integer 0 <= a <= 35 auto oib,oob; oib=ibase;oob=obase;ibase=obase=A if(a< 0)print "_"; if(0<=a&&a<=9)print a; if(a==10)print "a";if(a==11)print "b" if(a==12)print "c";if(a==13)print "d";if(a==14)print "e";if(a==15)print "f" if(a==16)print "g";if(a==17)print "h";if(a==18)print "i";if(a==19)print "j" if(a==20)print "k";if(a==21)print "l";if(a==22)print "m";if(a==23)print "n" if(a==24)print "o";if(a==25)print "p";if(a==26)print "q";if(a==27)print "r" if(a==28)print "s";if(a==29)print "t";if(a==30)print "u";if(a==31)print "v" if(a==32)print "w";if(a==33)print "x";if(a==34)print "y";if(a==35)print "z" if(a>=36)print " ",a ibase=oib;obase=oob;return a } define letter_(mode,a) { # expects an integer 0 <= a <= 35 auto t; t=a if(mode){if(a==0){print "_";return 0};a+=9} if(output_lcase_){a=letterl__(a)}else{a=letteru__(a)} return t } # By default bc will use decimal groups for 'digits' when outputting # numbers with ibase set above 16. When ibase <= 16 letters are used as # digits. This function outputs using letters right up to base 36 which uses # Z as the base-1 digit. Uses obase, so no base need be specified. # TO DO: allow setting of a sub-base for bases over the new maximum # rather than defaulting to bc's own decimal mode define printbase(x) { auto os,sign,i,ni,f,g,a[],ai,q; if(2^4-6*(!!output_lcase_)>=obase){print x;return x} if(obase>6^2){print x;return x} os=scale;scale=0 sign=1;if(x<0){sign=-1;x=-x} f=x-(i=x/1) ai=0;ni=i while(i){i=ni;ni=i/obase;a[++ai]=i-ni*obase} if(sign<0)print"-" if(ai){for(--ai;ai;ai--)q=letter_(0,a[ai])}else{print 0} if(os==0||f==0){scale=os;return sign*x} print"." g=A^scale(x) for(i=1;i 3^3){return q=printbase(x)} os=scale;scale=0 sign=1;if(x<0){sign=-1;x=-x} f=x-(i=x/1) ai=0;ni=i while(i){i=ni;ni=i/obase;a[++ai]=i-ni*obase} if(sign<0)print"-" if(ai){for(--ai;ai;ai--)q=letter_(1,a[ai])}else{q=letter_(1,0)} if(os==0||f==0){scale=os;return sign*x} print"." g=A^scale(x) for(i=1;i5*7){ print "printbijective: bbase too large, using " if(obase>5*7){print "decimal\n";bbase=A}else{print "obase\n";bbase=obase} } os=scale;scale=0 sign=1;if(x<0){sign=-1;x=-x} bbase/=1 f=x-(i=x/1) ai=0;while(i)i=(i-(a[++ai]=((i-1)%bbase)+1))/bbase if(sign<0)print"-";g=0 if(ai){for(.=.;ai;ai--)q=letter_(0,a[ai])}else{print".";g=1} if(os==0||f==0){scale=os;return sign*x} # Fractional part - not really valid for bijectional so made something up # New terminology .{3}2A2 = 0.000302 {3} representing the shift-right of 3 places if(!g)print ".";if(bbase==1){print "{}";scale=os;return sign*x} g=f;for(i=-1;g<=1;i++)g*=bbase if(i){print "{";i=printbijective(bbase,i);print "}"} g=A^scale(x)-1 for(i=1;i2*D){ print "printbijective_letters: bbase too large, using " if(obase>2*D){print "hexavigintimal\n";bbase=2*D}else{print "obase\n";bbase=obase} } os=scale;scale=0 sign=1;if(x<0){sign=-1;x=-x} f=x-(i=x/1) ai=0;while(i)i=(i-(a[++ai]=((i-1)%bbase)+1))/bbase if(sign<0)print"-";g=0 if(ai){for(.=.;ai;ai--)q=letter_(1,a[ai])}else{print ".";g=1} if(os==0||f==0){scale=os;return sign*x} # Fractional part - not really valid for bijectional so made something up # New terminology .{C}BJB = .___C_B {C} representing the shift-right of 3 places if(!g)print ".";if(bbase==1){print "{}";scale=os;return sign*x} g=f;for(i=-1;g<=1;i++)g*=bbase if(i){print "{";i=printbijective_letters(bbase,i);print "}"} g=A^scale(x)-1 for(i=1;i1)base=-base if(base>-2)base=-obase if(base<-6*6){} b2=base*base;bijective=!!bijective i=x/1;shft=0;if(x!=i)shft=1 if(shft){ d=scale(x);if(bijective)d=os d=A^d shft=-1 for(i=1;i<=d;i*=b2).=shft++ shft+=shft x*=i/b2 } for(i=1;x;i++){ d=((x-bijective)%base)+bijective;if(d=(!pfactb_zero_||bijective);xi--)print " ",x[xi] if(fi<0){scale=os;return 0} for(fi=fi;fi>=0&&!f[fi];fi--){};.=fi++ print "." for(xi=!pfactb_zero_;xi1)eps=1 improper=!!improper sign=1;if(x<0){sign=-1;x=-x} if(x<1)improper=1 if(maxdenom<0)maxdenom=-maxdenom if(maxdenom<5)maxdenom=5 if(maxdenom1)f=1-f if(f1)print"/",sd ibase=oib;scale=os;return x/sd } else { x/=1 fx=(fx*sd+.5)/1 if(sd==1){x+=fx;fx=0} print x if(fx>0){ if(sign<0){print "-"}else{print "+"} print fx,"/",sd } ibase=oib;scale=os;return sign*(x+fx/sd) } } # Time/Degrees output # e.g. q=printdms(54.671) prints 54:40:15.600 define printdms(x){ auto os,ox,h,m; os=scale;scale=0;ox=x h=x/1;x-=h;x*=F*4 m=x/1;x-=m;x*=F*4 print h,":",m,":",x scale=os;return ox } ## Other formatting # Truncate trailing zeroes or nines (in base ten at least) from a scaled number # This function is to counter bc's habit of multiple repeated zeroes or # base-minus-ones to the far right after the 'basimal' point, especially # when the 'scale' was set too high for the calculation which created x # example code: # scale=10;x=1/4;x;trunc(x);x-=10^-scale;x;trunc(x) # .2500000000 # .25 # .2499999999 # .25 define trunc(x) { auto os,ts,s,d,tx; os=scale d=length(x)-scale(x) if(d<5||d>scale)d=5 ts=scale-d if(scale>=d+d){ scale=ts s=1;if(x<0)s=-1 x+=s*A^-scale .=scale--;x/=1 } for(scale=0;scale<=ts;scale++)if(x==(tx=x/1)){x=tx;break} scale=os;return(x) } # Print an integer in a field width define intprint(w, n){ # w is field width auto os,m,i; os=scale;scale=0;n/=1 m=n;w+=(m!=0);if(m<0){m=-m;.=w--} for(.=.;m>0;w--){m/=obase} for(i=1;i=gp){ t+=comma_(x/gp,gp);print "," for(gp/=obase;gp>=obase;gp/=obase)if(t6^2){print "[obase>max]";return n} os=scale;scale=0 leadz=0 if(scale(precision)>0){precision/=1;leadz=1} if(precision<0)precision=-precision align=1 #right width/=1 if(width<0){width=-width;align=-1} #left signn=1 if(n<0){n=-n;signn=-1;.=width--} i=intn=n/1;fracn=n-intn .=width-- for(.=.;i/=obase;width--){} if(precision)width-=precision+1 if( leadz&&signn==-1)print "-" if(align== 1)for(i=0;i0){ print "." fracn*=obase^precision i=printff(precision, 0.0, fracn/1) } if(align==-1)for(i=0;i6^2){print "[obase>max]";return n} op=precision on=n os=scale;scale=0 oib=ibase;ibase=A explen=9/(obase-0.72)+2 engstep=sqrt(135/obase)/1 ibase=oib signn=1 if(n<0){signn=-1;n=-n} align=1 if(width<0){align=-1;width=-width} if(precision<0){precision=-precision}else{engstep=1} exp=0 if(n)while(n/1<1){n*=obase;.=exp--} precision+=precision precision+=engstep-1 while(n/1>=obase){scale=precision;n/=obase;scale=0;.=exp++} for(.=.;exp%engstep;exp--)n*=obase width-=explen+2 if(width<0)width=0 i=printff(align*width, op, signn*n) print "e" if(exp>=0)print "+" i=printff(explen, 0.0, exp) scale=os return n } # Print the specified whitespace characters define printspc(n) { if(n>0)for(i=0;i< n;i++)print " " } define printtabs(n) { if(n>0)for(i=0;i< n;i++)print "\t" } define newline() { print "\n" }