Como usar a bc, a calculadora do shell

bc = bench calculator, ou calculadora de bancada. Se você ainda não conhece a calculadora do seu shell, tá na hora de aprender a usá-la, mesmo que seu uso seja muito básico. O uso mais trivial das suas funções já deve suprir grande parte das suas necessidades. Mas não se deixe enganar, esse é um software realmente poderoso que definitivamente deve entrar no seu radar.

Tempo estimado de leitura: 15 minutos

Modos de uso

First things first. Para começar com o bc é importante saber que há 2 formas de utilizá-lo, pelo modo interativo ou não-interativo. Para usar o modo interativo é fácil, basta digitar bc e iniciar os comandos.

$ bc
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
1+1
2

No modo não-interativo, é necessário passar uma string para stdin do bc de alguma maneira. echo, printf, pipe, here strings… escolha a mais conveniente e toque pra frente. Vamos ver alguns exemplos:

$ echo "10*10" | bc  #via pipe
100
$ bc <<< "10-3"  #via here strings
7

Variáveis

O bc tem 4 variáveis importantes que ele utiliza internamente:

ibase   #base dos números de entrada (input) no cálculo
obase   #base do número do resultado (de saída, output) do cálculo
scale   #define a quantidade de dígitos após a vírgula após operações de divisão
last    #repete o último número calculado 

Você também pode definir suas próprias variáveis, mas sempre em letras minúsculas.

$ bc
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.a = 1
b = 2
c = a + b
c
3

O bc não tolera caixas altas nas variáveis, mas exige caixas altas para todas as entradas hexadecimais! Fique atento a isso =)

Conversão hexadecimal, decimal, octal, binário e afins…

Essa calculadora fica ainda mais interessante quando precisamos alterar as bases. Por exemplo, para converter hexadecimal (base 16) para decimal (base 10), basta alterar as variáveis ibase (input) e obase (output). Ambas são 10 por default. Então vamos trocar somente a ibase, já que a saída será decimal por padrão:

$ echo "ibase=16; FF" | bc
255

Ou ainda, para converter da base 16 (hex) para base 2 (binário) com os argumentos ibase (input) e obase (output) declarados juntos:

$ echo "ibase=16; obase=2; FF" | bc
11111111

Operadores

Os básicos:

+ adição
– subtração
* multiplicação
/ divisão
% resto
^ potência

Veja o bc em ação com alguns comandos e meus comentários ao lado (sim, ele aceita comentários usando # depois de cada comando). Para entrar no modo interativo, apenas chame pelo comando bc.

$ bc -q
10*10 #cálculo com base 10, default
100
ibase=16 #alteração do input para base 16 (hex)
FF
255 #output de FF
FF*2
510 #output de FF*2
obase=2 #troca a base do output para 2 (binário)
FF
11111111 #resultado de FF
FF*2
111111110 #resultado de FF*2
ibase=2 #troca base do input para 2
obase=A #troca base do output para 10 (escrito em hex). Aqui tem uma pegadinha, se colocar 10 o BC irá se perder.
obase #chama a variável obase
10 #exibe o valor que consta na variável obase, igual no python 🙂 
11111111
255 #resultado de 11111111 em decimal
last #exibe o último resultado
255
scale=3 #define os dígitos após vírgula
23/3
7.666
quit #sai do bc interativo

Outras funções

Precisa de mais? Logarítimo, seno, cosseno, tangente etc? Utilize a biblioteca matemática do bc com a opção –mathlib

$ bc --mathlib
s(1)
.84147098480789650665
c(1)
.54030230586813971740
a(1)
.78539816339744830961

E o bc ainda consegue fazer programas! Veja esse exemplo do manual do bc, o check book. Salve o código abaixo num arquivo qualquer, por exemplo checkbook.bc

scale=2
print "\nCheck book program!\n"
print "  Remember, deposits are negative transactions.\n"
print "  Exit by a 0 transaction.\n\n"
print "Initial balance? "; bal = read()
bal /= 1
print "\n"
while (1) {
   "current balance = "; bal
   "transaction? "; trans = read()
   if (trans == 0) break;
   bal -= trans
   bal /= 1
}
quit

Agora, execute-o.

$ bc -q checkbook.bc


Check book program!
  Remember, deposits are negative transactions.

  Exit by a 0 transaction.

Initial balance? 2

current balance = 2.00
transaction? 1
current balance = 1.00
transaction? 1
current balance = 0
transaction? 1
current balance = -1.00
transaction? 0

quit

Tem muitas outras funções no bc, vale a pena explorar mais se vc quiser mergulhar no assunto. O manual do bc é bem interessante, certamente vale dar uma olhada.

Mas antes de encerrar o artigo, dê uma olhada nesse arquivo gigante de funções para o bc!

#!/usr/local/bin/bc -l

### Funcs.BC - a large number of functions for use with GNU BC

  ## Not to be regarded as suitable for any purpose
  ## Not guaranteed to return correct answers

scale=50;
define pi() {
  auto s;
  if(scale==(s=scale(pi_)))return pi_
  if(scale<s)return pi_/1
  scale+=5;pi_=a(1)*4;scale-=5
  return pi_/1
}
e = e(1);
define phi(){return((1+sqrt(5))/2)} ; phi = phi()
define psi(){return((1-sqrt(5))/2)} ; psi = psi()

# Reset base to ten
obase=ibase=A;

## Integer and Rounding

# Round to next integer nearest 0:  -1.99 -> 1, 0.99 -> 0
define int(x)   { auto os;os=scale;scale=0;x/=1;scale=os;return(x) } 

# Round down to integer below x
define floor(x) {
  auto os,xx;os=scale;scale=0
  xx=x/1;if(xx>x).=xx--
  scale=os;return(xx)
}

# Round up to integer above x
define ceil(x) {
  auto os,xx;x=-x;os=scale;scale=0
  xx=x/1;if(xx>x).=xx--
  scale=os;return(-xx)
}

# Fractional part of x:  12.345 -> 0.345
define frac(x) {
  auto os,xx;os=scale;scale=0
  xx=x/1;if(xx>x).=xx--
  scale=os;return(x-xx)
}

# Absolute value of x
define abs(x) { if(x<0)return(-x)else return(x) }

# Sign of x
define sgn(x) { if(x<0)return(-1)else if(x>0)return(1);return(0) }

# Round x up to next multiple of y
define round_up(  x,y) { return(y*ceil( x/y )) }

# Round x down to previous multiple of y
define round_down(x,y) { return(y*floor(x/y )) }

# Round x to the nearest multiple of y
define round(     x,y) {
  auto os,oib;
  os=scale;oib=ibase
  .=scale++;ibase=A
    y*=floor(x/y+.5)
  ibase=oib;scale=os
  return y
}

# Find the remainder of x/y
define int_remainder(x,y) {
  auto os;
  os=scale;scale=0
   x/=1;y/=1;x%=y
  scale=os
  return(x)
}
define remainder(x,y) {
  os=scale;scale=0
   if(x==x/1&&y==y/1){scale=os;return int_remainder(x,y)}
  scale=os
  return(x-round_down(x,y))
}

# Greatest common divisor of x and y
define int_gcd(x,y) {
  auto r,os;
  os=scale;scale=0
  x/=1;y/=1
  while(y>0){r=x%y;x=y;y=r}
  scale=os
  return(x)
}
define gcd(x,y) {
  auto r,os;
  os=scale;scale=0
   if(x==x/1&&y==y/1){scale=os;return int_gcd(x,y)}
  scale=os
  while(y>0){r=remainder(x,y);x=y;y=r}
  return(x)
}

# Lowest common multiple of x and y
define int_lcm(x,y) {
  auto r,m,os;
  os=scale;scale=0
  x/=1;y/=1
  m=x*y
  while(y>0){r=x%y;x=y;y=r}
  m/=x
  scale=os
  return(m)
}
define lcm(x,y) { return (x*y/gcd(x,y)) }

# Remove largest possible power of 2 from x
define oddpart(x){
  auto os;
  os=scale;scale=0;x/=1
  if(x==0){scale=os;return 1}
  while(!x%2)x/=2
  scale=os;return x
}

# Largest power of 2 in x
define evenpart(x) {
  auto os;
  os=scale;scale=0
  x/=oddpart(x/1)
  scale=os;return x
}

## Trig / Hyperbolic Trig

# Sine
define sin(x) { return s(x) } # alias for standard library
# Cosine
define c(x)   { return s(x+pi()/2) } # as fast or faster than
define cos(x) { return c(x)        } # . standard library
# Tangent
define tan(x)   { auto c;c=c(x);if(c==0)c=A^-scale;return(s(x)/c) }

# Secant
define sec(x)   { auto c;c=c(x);if(c==0)c=A^-scale;return(   1/c) }
# Cosecant
define cosec(x) { auto s;s=s(x);if(s==0)s=A^-scale;return(   1/s) }
# Cotangent
define cotan(x) { auto s;s=s(x);if(s==0)s=A^-scale;return(c(x)/s) }

# Arcsine
define arcsin(x) { if(x==-1||x==1)return(pi()/2*x);return( a(x/sqrt(1-x*x)) ) } 
# Arccosine
define arccos(x) { if(x==0)return(0);return pi()/2-arcsin(x) }

# Arctangent (one argument)
define arctan(x)  { return a(x) } # alias for standard library

# Arctangent (two arguments)
define arctan2(x,y) { 
  auto p;
  if(x==0&&y==0)return(0)
  p=(1-sgn(y))*pi()*(2*(x>=0)-1)/2
  if(x==0||y==0)return(p)
  return(p+a(x/y))
}

# Arcsecant
define arcsec(x)      { return( a(x/sqrt(x*x-1)) ) }
# Arccosecant
define arccosec(x)    { return( a(x/sqrt(x*x-1))+pi()*(sgn(x)-1)/2 ) }
# Arccotangent (one argument)
define arccotan(x)    { return( a(x)+pi()/2 ) }
# Arccotangent (two arguments)
define arccotan2(x,y) { return( arctan(x,y)+pi()/2 ) }

# Hyperbolic Sine
define sinh(x) { auto t;t=e(x);return((t-1/t)/2) }
# Hyperbolic Cosine
define cosh(x) { auto t;t=e(x);return((t+1/t)/2) }
# Hyperbolic Tangent
define tanh(x) { auto t;t=e(x+x)-1;return(t/(t+2)) }

# Hyperbolic Secant
define sech(x)   { auto t;t=e(x);return(2/(t+1/t)) }
# Hyperbolic Cosecant
define cosech(x) { auto t;t=e(x);return(2/(t-1/t)) }
# Hyperbolic Cotangent
define coth(x)   { auto t;t=e(x+x)-1;return((t+2)/t) }

# Hyperbolic Arcsine
define arcsinh(x) { return( l(x+sqrt(x*x+1)) ) }
# Hyperbolic Arccosine
define arccosh(x) { return( l(x+sqrt(x*x-1)) ) }
# Hyperbolic Arctangent
define arctanh(x) { return( l((1+x)/(1-x))/2 ) }

# Hyperbolic Arcsecant
define arcsech(x)   { return( l((sqrt(1-x*x)+1)/x) ) }
# Hyperbolic Arccosecant
define arccosech(x) { return( l((sqrt(1+x*x)*sgn(x)+1)/x) ) }
# Hyperbolic Arccotangent
define arccoth(x)   { return( l((x+1)/(x-1))/2 ) }

# Length of the diagonal vector (0,0)-(x,y) [pythagoras]
define pyth(x,y) { return(sqrt(x*x+y*y)) }
define pyth3(x,y,z) { return(sqrt(x*x+y*y+z*z)) }

# Gudermannian Function
define gudermann(x)    { return 2*(a(e(x))-a(1)) }
# Inverse Gudermannian Function
define arcgudermann(x) {
  return arctanh(s(x))
}

# Bessel function
define besselj(n,x) { return j(n,x) } # alias for standard library

## Exponential / Logs

# Exponential e^x
define exp(x) { return e(x) } # alias for standard library

# Natural Logarithm (base e)
define ln(x) {
  auto os,len,ln;
  if(x< 0){print "ln error: logarithm of a negative number\n";return 0}
  if(x==0)print "ln error: logarithm of zero; negative infinity\n"
  len=length(x)-scale(x)-1
  if(len<A)return l(x);
  os=scale;scale+=length(len)+1
  ln=l(x/A^len)+len*l(A)
  scale=os
  return ln/1
} # speed improvement on standard library

# workhorse function for pow and log - new, less clever version
# Helps determine whether a fractional power is legitimate for a negative number
# . expects to be fed a positive value
# . returns -odd for even/odd; odd2 for odd1/odd2;
#           even for odd/even;   -2 for irrational
# . note that the return value is the denominator of the fraction if the
#   fraction is rational, and the sign of the return value states whether
#   the numerator is odd (positive) or even (negative)
# . since even/even is not possible, -2 is used to signify irrational
define id_frac2_(y){
  auto os,oib,es,eps,lim,max,p,max2,i,cf[],f[],n,d,t;
  os=scale
  if(cf_max){
    # cf.bc is present!
    .=cf_new(cf[],y);if(scale(cf[0]))return -2;
    .=frac_from_cf(f[],cf[],1)
    d=f[0];scale=0;if(f[1]%2==0)d=-d;scale=os
   return d
  }
  oib=ibase;ibase=A
  scale=0
   es=3*os/4
  scale=os
   eps=A^-es
   y+=eps/A
  scale=es
   y/=1
  scale=0
  if(y<0)y=-y
  d=y-(n=y/1)
  if(d<eps){t=2*(n%2)-1;scale=os;ibase=oib;return t}#integers are x/1
  t=y/2;t=y-t-t
  # Find numerator and denominator of fraction, if any
  lim=A*A;max2=A^5*(max=A^int(os/2));p=1
  i=0;y=t
  while(1) {
    scale=es;y=1/y;scale=0
    y-=(t=cf[++i]=y/1);p*=1+t
    if(i>lim||(max<p&&p<max2)){cf[i=1]=-2;break}#escape if number seems irrational    
    if((p>max2||3*length(t)>es+es)&&i>1){cf[i--]=0;break}#cheat: assume rational
    if(y==0)break;#completely rational
  }
  n=1;d=cf[i]
  if(i==0){print "id_frac2_: something is wrong; y=",y,", d=",d,"\n"}
  if(d!=-2&&i)while(--i){d=n+cf[i]*(t=d);n=t}
  if(d<A^os){d*=2*(n%2)-1}else{d=-2}
  scale=os;ibase=oib
  return d;
}

# raise x to integer power y faster than bc's x^y
# . it seems bc (at time of writing) uses
# . an O(n) repeated multiplication algorithm
# . for the ^ operator, which is inefficient given
# . that there is a simple O(log n) alternative:
define fastintpow__(x,y) {
  auto r,hy;
  if(y==0)return(1)
  if(y==1)return(x)
  r=fastintpow__(x,hy=y/2)
  r*=r;if(hy+hy<y)r*=x
  return( r )
}
define fastintpow_(x,y) {
  auto ix,os;
  if(y<0)return fastintpow_(1/x,-y)
  if(y==0)return(1)
  if(y==1)return(x)
  if(x==1)return(1)
  os=scale;scale=0
  if(x==-1){y%=2;y+=y;scale=os;return 1-y}
  # bc is still faster for integers
  if(x==(ix=x/1)){scale=os;return ix^y}
  # ...and small no. of d.p.s, but not for values <= 2
  if(scale(x)<3&&x>2){scale=os;return x^y}
  scale=os;x/=1;scale=0
  x=fastintpow__(x,y);
  scale=os;return x;
}

# Raise x to a fractional power faster than e^(y*l(x))
define fastfracpow_(x,y) {
  auto f,yy,inv;
  inv=0;if(y<0){y=-y;inv=1}
  y-=int(y)
  if(y==0)return 1;
  if((yy=y*2^C)!=int(yy)){x=l(x);if(inv)x=-x;return e(y/1*x)}
  # faster using square roots for rational binary fractions
  # where denominator <= 8192
  x=sqrt(x)
  for(f=1;y&&x!=1;x=sqrt(x))if(y+=y>=1){.=y--;f*=x}
  if(inv)f=1/f;
  return f;
}

# Find the yth root of x where y is integer
define fastintroot_(x,y) {
  auto os,d,r,ys,eps;
  os=scale;scale=0;y/=1;scale=os
  if(y<0){x=1/x;y=-y}
  if(y==1){return x}
  if(y>=x-1){return fastfracpow_(x,1/y)}
  if(y*int((d=2^F)/y)==d){
    r=1;while(r+=r<=y)x=sqrt(x)
    return x
  }
  scale=length(y)-scale(y);if(scale<5)scale=5;r=e(ln(x)/y)
  scale=os+5;if(scale<5)scale=5
  d=1;eps=A^(3-scale)
  ys=y-1
  while(d>eps){
    d=r;r=(ys*r+x/fastintpow_(r,ys))/y
    d-=r;if(d<0)d=-d
  }
  scale=os
  return r/1
}

# Raise x to the y-th power
define pow(x,y) {
 auto os,p,ix,iy,fy,dn,s;
 if(y==0) return 1
 if(x==0) return 0
 if(0<x&&x<1){x=1/x;y=-y}
 os=scale;scale=0
  ix=x/1;iy=y/1;fy=y-iy;dn=0
 scale=os;#scale=length(x/1)
 if(y!=iy&&x<0){
   dn=id_frac2_(y)# -ve implies even numerator
   scale=0;if(dn%2){# odd denominator
     scale=os
     if(dn<0)return  pow(-x,y) # even/odd
     /*else*/return -pow(-x,y) #  odd/odd
   }
   print "pow error: "
   if(dn>0) print "even root"
   if(dn<0) print "irrational power"
   print " of a negative number\n"
   scale=os;return 0
 }
 if(y==iy) {
   if(x==ix){p=fastintpow_(ix,iy);if(iy>0){scale=0;p/=1};scale=os;return p/1}
   scale+=scale;p=fastintpow_(x,iy);scale=os;return p/1
 }
 if((dn=id_frac2_(y))!=-2){ #accurate rational roots (sometimes slower)
   if(dn<0)dn=-dn
   s=1;if(y<0){y=-y;s=-1}
   p=y*dn+1/2;scale=0;p/=1;scale=os
   if(p<A^3)x=fastintpow_(x,p)
   x=fastintroot_(x,dn)
   if(p>=A^3)x=fastintpow_(x,p)
   if(s<0)x=1/x
   return x
 }
 p=fastintpow_(ix,iy)*fastfracpow_(x,fy);
 scale=os+os
 if(ix)p*=fastintpow_(x/ix,iy)
 scale=os
 return p/1
 #The above is usually faster and more accurate than
 # return( e(y*l(x)) );
}

# y-th root of x [ x^(1/y) ]
define root(x,y) {
  return pow(x,1/y)
}

# Specific cube root function
# = stripped down version of fastintroot_(x,3)
define cbrt(x) {
  auto os,d,r,eps;
  if(x<0)return -cbrt(-x)
  if(x==0)return 0
  os=scale;scale=0;eps=A^(scale/3)
  if(x<eps){scale=os;return 1/cbrt(1/x)}
  scale=5;r=e(ln(x)/3)
  scale=os+5;if(scale<5)scale=5
  d=1;eps=A^(3-scale)
  while(d>eps){
    d=r;r=(r+r+x/(r*r))/3
    d-=r;if(d<0)d=-d
  }
  scale=os
  return r/1
}

# Logarithm of x in given base:  log(2, 32) = 5 because 2^5 = 32
#  tries to return a real answer where possible when given negative numbers
#  e.g.     log(-2,  64) = 6 because (-2)^6 =   64
#  likewise log(-2,-128) = 7 because (-2)^7 = -128
define log(base,x) {
  auto os,i,l,sx,dn,dnm2;
  if(base==x)return 1;
  if(x==0){print "log error: logarithm of zero; negative infinity\n";     return  l(0)}
  if(x==1)return 0;
  if(base==0){print "log error: zero-based logarithm\n";                  return    0 }
  if(base==1){print "log error: one-based logarithm; positive infinity\n";return -l(0)}
  scale+=6
  if((-1<base&&base<0)||(0<base&&base<1)){x=-log(1/base,x);scale-=6;return x/1}
  if((-1<x   &&   x<0)||(0<x   &&   x<1)){x=-log(base,1/x);scale-=6;return x/1}
  if(base<0){
    sx=1;if(x<0){x=-x;sx=-1}
    l=log(-base,x)
    dn=id_frac2_(l)
    os=scale;scale=0;dnm2=dn%2;scale=os
    if(dnm2&&dn*sx<0){scale-=6;return l/1}
    print "log error: -ve base: "
    if(dnm2)print "wrong sign for "
    print "implied "
    if(dnm2)print "odd root/integer power\n"
    if(!dnm2){
      if(dn!=-2)print "even root\n"
      if(dn==-2)print "irrational power\n"
    }
    scale-=6;return 0;
  }
  if(x<0){
    print "log error: +ve base: logarithm of a negative number\n"
    scale-=6;return 0;
  }
  x=ln(x)/ln(base);scale-=6;return x/1
}

# Integer-only logarithm of x in given base
# (compare digits function in digits.bc)
define int_log(base,x) { 
 auto os,p,c;
 if(0<x&&x<1) {return -int_log(base,1/x)}
 os=scale;scale=0;base/=1;x/=1
  if(base<2)base=ibase;
  if(x==0)    {scale=os;return  1-base*A^os}
  if(x<base)  {scale=os;return  0    }
  c=length(x) # cheat and use what bc knows about decimal length
  if(base==A){scale=os;return c-1}
  if(base<A){if(x>A){c*=int_log(base,A);c-=2*(base<4)}else{c=0}}else{c/=length(base)+1}
  p=base^c;while(p<=x){.=c++;p*=base}
  scale=os;return(c-1)
}

# Lambert's W function 0 branch; Numerically solves w*e(w) = x for w
# * is slow to converge near -1/e at high scales
define lambertw0(x) {
  auto oib, a, b, w, ow, lx, ew, e1, eps;
  if(x==0) return 0;
  oib=ibase;ibase=A
  ew = -e(-1)
  if (x<ew) {
    print "lambertw0: expected argument in range [-1/e,oo)\n"
    ibase=oib
    return -1
  }
  if (x==ew) {ibase=oib;return -1}
  # First approximation from :
  #   http://www.desy.de/~t00fri/qcdins/texhtml/lambertw/
  #   (A. Ringwald and F. Schrempp)
  # via Wikipedia
  if(x < 0){
    w = x/ew
  } else if(x < 500){
    lx=l(x+1);w=0.665*(1+0.0195*lx)*lx+0.04
  } else if((lx=length(x)-scale(x))>5000) {
    lx*=l(A);w=lx-(1-1/lx)*l(lx)
  } else {
    lx=l(x);w=l(x-4)-(1-1/lx)*l(lx)
  } 
  # Iteration adapted from code found on Wikipedia
  #   apparently by an anonymous user at 147.142.207.26
  #   and later another at 87.68.32.52
  ow = 0
  eps = A^-scale
  scale += 5
  e1 = e(1)
  while(abs(ow-w)>eps&&w>-1){
    ow = w
    if(x>0){ew=pow(e1,w)}else{ew=e(w)}
    a = w*ew
    b = a+ew
    a -= x;
    if(a==0)break
    b = b/a - 1 + 1/(w+1)
    w -= 1/b
    if(x<-0.367)w-=eps
  }
  scale -= 5
  ibase=oib
  return w/1
}

# Lambert's W function -1 branch; Numerically solves w*e(w) = x for w
# * is slow to converge near -1/e at high scales
define lambertw_1(x) {
  auto oib,os,oow,ow,w,ew,eps,d,iters;
  oib=ibase;ibase=A
  ew = -e(-1)
  if(ew>x||x>=0) {
    print "lambertw_1: expected argument in [-1/e,0)\n"
    ibase=oib
    if(x==0)return 1-A^scale
    if(x>0)return 0
    return -1
  }
  if(x==ew) return -1;
  os=scale
  eps=A^-os
  scale+=3
  oow=ow=0
  w=x
  w=l(-w)
  w-=l(-w)
  w+=sqrt(eps)
  iters=0
  while(abs(ow-w)>eps){
    oow=ow;ow=w
    if(w==-1)break
    w=(x*e(-w)+w*w)/(w+1)
    if(iters++==A+A||oow==w){iters=0;w-=A^-scale;scale+=2}
  }
  scale=os;ibase=oib
  return w/1
}

# LambertW wrapper; takes most useful branch based on x
# to pick a branch manually, use lambertw_1 or lambertw0 directly
define w(x) {
  if(x<0)return lambertw_1(x)
  return lambertw0(x)
}

# Faster calculation of lambertw0(exp(x))
# . avoids large intermediate value and associated slowness
# . numerically solves x = y+ln(y) for y
define lambertw0_exp(x) {
  auto oy,y,eps;
  # Actual calculation is faster for x < 160 or thereabouts
  if(x<C*D)return lambertw0(e(x));
  oy=0;y=l(x);y=x-y+y/x;eps=A^-scale
  while(abs(oy-y)>eps)y=x-l(oy=y)
  return y
}

# Shorthand alias for the above
define w_e(x){ return lambertw0_exp(x) }

# Numerically solve pow(y,y) = x for y
define powroot(x) {
  auto r;
  if(x==0) {
    print "powroot error: attempt to solve for zero\n"
    return 0
  }
  if(x==1||x==-1) {return x}
  if(x<=r=e(-e(-1))){
    print "powroot error: unimplemented for values\n  <0";r
    return 0
  }
  r = ln(x)
  r /= w(r)
  return r
}

## Triangular numbers

# xth triangular number
define tri(x) {
  auto xx
  x=x*(x+1)/2;xx=int(x)
  if(x==xx)return(xx)
  return(x)
}

# 'triangular root' of x
define trirt(x) {
  auto xx
  x=(sqrt(1+8*x)-1)/2;xx=int(x)
  if(x==xx)x=xx
  return(x)
}

# Workhorse for following 2 functions
define tri_step_(t,s) {
  auto tt
  t=t+(1+s*sqrt(1+8*t))/2;tt=int(t)
  if(tt==t)return(tt)
  return(t)
}

# Turn tri(x) into tri(x+1) without knowing x
define tri_succ(t) {
  return(tri_step_(t,0+1))
}

# Turn tri(x) into tri(x-1) without knowing x
define tri_pred(t) {
  return(tri_step_(t,0-1))
}

## Polygonal Numbers

# the xth s-gonal number:
#   e.g. poly(3, 4) = tri(4) = 1+2+3+4 = 10; poly(4, x) = x*x, etc
define poly(s, x) {
  auto xx
  x*=(s/2-1)*(x-1)+1;xx=int(x);if(x==xx)x=xx
  return x
}

# inverse of the above = polygonal root:
#   e.g. inverse_poly(3,x)=trirt(x); inverse_poly(4,x)=sqrt(x), etc
define inverse_poly(s, r) {
  auto t,xx
  t=(s-=2)-2
  r=(sqrt(8*s*r+t*t)+t)/s/2;xx=int(r);if(r==xx)r=xx
  return r
}

# converse of poly(); solves poly(s,x)=r for s
#   i.e. if the xth polygonal number is r, how many sides has the polygon?
#   e.g. if the 5th polygonal number is 15, converse_poly(5,15) = 3
#     so the polygon must have 3 sides! (15 is the 5th triangular number)
define converse_poly(x,r) {
  auto xx
  x=2*((r/x-1)/(x-1)+1);xx=int(x);if(x==xx)x=xx
  return x
}

## Tetrahedral numbers

# nth tetrahedral number
define tet(n) { return n*(n+1)*(n+2)/6 }

# tetrahedral root = inverse of the above
define tetrt(t) {
  auto k,c3,w;
  if(t==0)return 0
  if(t<0)return -2-tetrt(-t)
  k=3^5*t*t-1
  if(k<0){print "tetrt: unimplemented for 0<|t|<sqrt(3^-5)\n"; return 0}
  c3=cbrt(3)
  k=cbrt(sqrt(3*k)+3^3*t)
  return k/c3^2+1/(c3*k)-1
}

## Arithmetic-Geometric mean

define arigeomean(a,b) {
  auto c,s;
  if(a==b)return a;
  s=1;if(a<0&&b<0){s=-1;a=-a;b=-b}
  if(a<0||b<0){print "arigeomean: mismatched signs\n";return 0}
  while(a!=b){c=(a+b)/2;a=sqrt(a*b);b=c}
  return s*a
}

# solve n = arigeomean(x,y)
define inv_arigeomean(n, y){
  auto ns,ox,x,b,c,d,i,s,eps;
  if(n==y)return n;
  s=1;if(n<0&&y<0){s=-1;n=-n;y=-y}
  if(n<0||y<0){print "inv_arigeomean: mismatched signs\n";return 0}  
  if(n<y){x=y;y=n;n=x}
  n/=y
  scale+=2;eps=A^-scale;scale+=4
  ns=scale
  x=n*(1+ln(n));ox=-1
  for(i=0;i<A;i++){
    # try to force quadratic convergence
    if(abs(x-ox)<eps){i=-1;break}
    ox=x;scale+=scale
    b=x+x/n*(n-arigeomean(1,x));
    c=b+b/n*(n-arigeomean(1,b));
    d=b+b-c-x
    if(d){x=(b*b-c*x)/d}else{x=b;i=-1;break}
    scale=ns
  }
  if(i!=-1){
    # give up and converge linearly
    x=(x+ox)/2
    while(abs(x-ox)>eps){ox=x;x+=x/n*(n-arigeomean(1,x))}
  }
  x+=5*eps
  scale-=6;return x*y/s
}

Mantive o script original, mas recomendo que substitua a primeira linha por

#!/usr/bin/env bc -l

Isso vai evitar que o shell se perca e não encontre o bc no caminho certo!

Para utilizá-lo, salve com um nome qualquer, como funcs.bc, torne-o executável com chmod +x e rode como ./funcs.bc. Escolha uma das funções do script e preencha com os números, como a raiz cúbica de 27, do exemplo abaixo. Digite quit para sair do modo interativo do bc.

./funcs.bc
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
root(27,3)
3.00000000000000000000000000000000000000000000000000
quit

Configurar .bcrc

Para carregar automaticamente as suas funções preferidas, crie um arquivo em ~/.bcrc contendo todas elas. Uma boa sugestão para iniciar o seu .bcrc está em https://github.com/idealvin/bc/blob/master/bcrc

pi=4*a(1);
e=e(1);

define sin(x) {
    if (x == pi/2) {
        return 1;
    }

    return s(x);
}

define cos(x) {
    if (x == pi/2) {
        return 0;
    }

    return c(x);
}

define tan(x) {
    if (x == pi/4) {
        return 1;
    }

    if (x == -pi/4) {
        return -1;
    }

    return s(x)/c(x);
}

define cot(x) {
    if (x == pi/4) {
        return 1;
    }

    if (x == -pi/4) {
        return -1;
    }

    return c(x)/s(x);
}

define sec(x) {
    return 1/cos(x);
}

define csc(x) {
    return 1/sin(x);
}

define asin(x) {
    if (x == 1) {
        return pi/2;
    }

    if (x == -1) {
        return -pi/2;
    }

    return a(x/sqrt(1-x^2));
}

define arcsin(x) {
    return asin(x);
}

define acos(x) {
    if (x == 0) {
        return pi/2;
    }

    if (x == 1) {
        return 0;
    }

    if (x == -1) {
        return pi/1;
    }

    if (x > 0) {
        return a(sqrt(1-x^2)/x);
    }

    return pi/1 - acos(-x);
}

define arccos(x) {
    return acos(x);
}

define atan(x) {
    if (x == 1) {
        return pi/4;
    }

    if (x == -1) {
        return -pi/4;
    }

    return a(x);
}

define arctan(x) {
    return a(x);
}

define acot(x) {
    return pi/2-atan(x);
}

define arccot(x) {
    return acot(x);
}

define asec(x) {
    if (x >= 1) {
        return a(sqrt(x^2-1));
    }

    return pi/1 - a(sqrt(x^2-1));
}

define arcsec(x) {
    return asec(x);
}

define acsc(x) {
    if (x == 1) {
        return pi/2;
    }

    if (x == -1) {
        return -pi/2;
    }

    if (x > 1) {
        return a(1/sqrt(x^2-1));
    }

    return -a(1/sqrt(x^2-1));
}

define arccsc(x) {
    return acsc(x);
}

define ln(x) {
    return l(x);
}

define log(a, b) {
    return l(b)/l(a);
}

define lg(x) {
    return log(10, x);
}

define log10(x) {
    return log(10, x);
}

define log2(x) {
    return log(2, x);
}

define pow(a, b) {
    if (scale(b) == 0) {
        return a ^ b;
    }
    return e(b*l(a));
}

define exp(x) {
    return e(x);
}

define cbrt(x) {
    return pow(x, 1/3);
}

define abs(x) {
    if (x < 0) {
        return -x;
    }
    return x;
}

define bessel(n, x) {
    return j(n,x);
}

define a(m, n) {
    if (n < 0) {
        return 0;
    }

    v = 1;
    for (i = 0; i < n; i++) {
        v *= (m - i);
    }
    return v;
}

define fac(n) {
    return a(n, n);
}

define c(m, n) {
    auto v

    if (n < 0) {
        return 0;
    }

    s = scale;
    scale = 0;
    v = a(m, n) / a(n, n);
    scale = s;
    return v;
}

define rad(deg) {
    return deg/180*pi;
}

define deg(rad) {
    return rad/pi*180;
}

Depois, insira o caminho do .bcrc no seu ~/.bashrc. Utilize a variável BC_ENV_ARGS que é carregada pelo bc toda vez que ele inicia. Também sugiro a opção -l, que carrega a biblioteca matemática padrão do bc.

export BC_ENV_ARGS="-l $HOME/.bcrc"

E por hora isso é tudo. Esse e muitos outros scripts com funções incríveis estão no site do phodd.net. Recomendo visitar os seguintes links:

E mais funções interessantes para inspirar idéias:

E, não menos importante, no manual do bc tem outras informações muito importantes para dominar esta ferramenta.

man bc

Até a próxima!

Uma resposta

Comentários

Mais artigos

URL encoding/decoding com sed

Existem várias formas de fazer encoding/decoding de urls. Geralmente os programadores já utilizam funções prontas para isso. Mas você realmente sabe o que essas funções estão fazendo? Para este artigo, escolhi o sed como ferramenta para substituir os códigos e aponto as RFCs que discorrem sobre o assunto.

Leia o artigo »

Entenda como customizar o VIM no MacOS

Se você já está começando a ficar fera no VIM, já é hora de dar os próximos passos. Nesse artigo, explico como configurar o VIM para MacOS da maneira que eu considero mais limpa (você pode querer instalar de outro jeito e tudo bem) e como configurar os plugins NerdTree, para acessar a árvore de diretórios; Status Tab para colocar na tela algumas ferramentas adicionais (e deixam o VIM muito bacana visualmente); e o plugin do Git, pra facilitar o controle de versionamento sem sair da aplicação. Boa leitura!

Leia o artigo »

Como exibir cores no terminal

Quer exibir os textos com cores, negritos, itálicos, underline etc? Entendendo algumas regras e códigos, fica bem mais fácil do que parece. Entenda como exibir cores no seu terminal com a clareza de quem sabe o que está fazendo.

Leia o artigo »
bureau-it.com