#!/usr/bin/perl # # v 04.07.2010 # Alexey Vikhlinin # http://hea-www.harvard.edu/~alexey/calc.html # some code is taken from Math::Trig; # # This script is provided 'as is' and without any warranty. For example, # I'm not paying if you wrap it in a web form and your files get erased:) # use Term::ReadLine; $log10 = 2.30258509299405; $pi = 3.14159265358979; #----------------------------------------------------------------------------- use constant pi => 3.14159265358979; use constant EE => 2.718281828459045; # exp(1). Not 'e' because e is charge of electron use constant Msun => 1.9891e+33; use constant Lsun => 3.826e+33; use constant Rsun => 6.9599e+10; use constant c => 2.9979245620e+10; # speed of light use constant G => 6.67259e-08; # gravitational constant use constant e => 4.8032068e-10; # charge of electron use constant h => 6.6260755e-27; # erg*s Planck's constant use constant me => 9.1093897e-28; # mass of electron use constant mp => 1.6726231e-24; # mass of proton use constant alpha => 1/(h*c/(2*pi*e**2)); # fine structure constant use constant sigmaT=> 6.6524616e-25; # Thomson cross section=8pi/3*re^2 use constant k => 1.380658e-16; # (erg/K) Boltzman constant use constant NA => 6.0221367e+23; # mol^-1 Avogadro constant use constant sigma => 5.67051e-5; # Stefan-Boltzmann constant use constant keV => 1.602192e-9; use constant eV => 1.602192e-12; # erg use constant pc => 3.085678e+18; use constant kpc => 1000*pc; use constant Mpc => 1000*kpc; use constant AU => 14959787069100; # "astronomical unit" #----------------------------------------------------------------------------- use constant sec => 1; use constant s => sec; # `sec' is used internally use constant hour => 3600; use constant hr => hour; # not 'h' because h is Planck's const use constant day => 24*hour; use constant yr => 365.242*day; use constant year=>yr; use constant lb => 453.5924; use constant oz => lb/16; use constant A => 1e-8; use constant cm => 1; use constant meter=>100; use constant m=>meter; use constant km=>1e5; use constant milimeter=>0.1; # `meter' is used internally use constant inch => 2.54; use constant ft => 12*inch; use constant feet => ft; use constant mile => 1.609344e+05; use constant mph => mile/hour; use constant knot => 5.144444e+01; use constant knots => knot; use constant kg => 1e3; use constant W => 1e7; # Watt use constant Jy => 1e-26*W/meter**2; use constant mJy => 1e-3*Jy; # per Hz use constant deg => pi/180; use constant arcmin => deg/60; use constant arcsec => arcmin/60; sub ln {log($_[0]);} sub lg {log($_[0])/$log10;} sub fact {$s=1; for ($i=2;$i<=$_[0];$i++) {$s*=$i;} return $s;} sub r2d {$_[0]*180.0/$pi} sub atan {atan2($_[0],1)} sub tan {my $z = $_[0]; sin($z)/cos($z)} sub acos {my $z = $_[0]; atan2(sqrt(1-$z*$z), $z)} sub asin {my $z = $_[0]; atan2($z, sqrt(1-$z*$z))} sub C {fact($_[0])/(fact($_[1])*fact(($_[0]-$_[1])))} sub sind {sin($_[0]*pi/180)} sub cosd {cos($_[0]*pi/180)} sub tand {tan($_[0]*pi/180)} sub asind {asin($_[0])*180/pi} sub acosd {acos($_[0])*180/pi} sub atand {atan($_[0])*180/pi} sub sinh {my $x=$_[0]; (exp($x)-exp(-$x))/2.0;} sub cosh {my $x=$_[0]; (exp($x)+exp(-$x))/2.0;} sub asinh {my $x=$_[0]; log($x+sqrt(1+$x**2))} sub acosh {my $x=$_[0]; log($x+sqrt($x**2-1))} sub atanh {my $x=$_[0]; 0.5*(log(($x+1)/(1-$x)))} sub acoth {my $x=$_[0]; atanh(1/$x)} sub nint { my $x = $_[0]; my $n = int($x); if ( $x > 0 ) { if ( $x-$n > 0.5) { return $n+1; } else { return $n; } } else { if ( $n-$x > 0.5) { return $n-1; } else { return $n; } } } sub CtoF { my $x = $_[0]; return 9*$x/5+32; } sub FtoC { my $x = $_[0]; return ($x-32.)*5./9.;} sub lgamma { # per code from numerical recipies my $xx = $_[0]; my $j, $ser, $stp, $tmp, $x, $y; my @cof = (0.0, 76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5); my $stp = 2.5066282746310005; $x = $xx; $y = $x; $tmp = $x + 5.5; $tmp = ($x+0.5)*log($tmp)-$tmp; $ser = 1.000000000190015; foreach $j ( 1 .. 6 ) { $y+=1.0; $ser+=$cof[$j]/$y; } return $tmp + log($stp*$ser/$x); } sub gamma { return exp(&lgamma ($_[0])); } %SHELLCOM=(); if ($ENV{"CALCRC"}) {do $ENV{"CALCRC"};} # personal settings sub doeval { # remove "calc " from the beginning of the line s/^\s*calc\s//; $origexpr = $_; # Apr 17 2010: if nocomma, remove commas between two digits if ( $nocomma ) { s/(\d),(\d)/$1$2/g; } # Jan 19 2007: the shell is likely to put spaces around ( ); # for us this is unnecessary and indeed, harmful # (breaks the 45mp -> (45*mp) subst.) So remove those spaces s/(^| )([()])( |$)/$2/g; # Jan 19 2007: I've seen in somebody's examples calc 6^2 meaning 6**2; # this is wrong! Perl does something else to 6^2, so s/\^/**/g; # Mar 31 2008: atan2 -> ñ s/atan2/\361/g; # Used to be: s/(^|[^\w\+-]).... s/(^|[^\w])([\+-]?(?:\d+\.?\d*|\.\d+))([eE])([\+-]?\d+(?=$|[^\d\.]))/$1$2\371$4/g; # 1.5e5 -> 1.5ù5 so that scientific notations don't mess up with the charge of electron s/(^\'|\'$)//g; # Trim single quotes in the beg/end of string s/(^|[^a-zA-Z])s($|[^a-zA-Z])/$1sec$2/g; # s -> sec s/(^|[^a-zA-Z])mm($|[^a-zA-Z])/$1milimeter$2/g;# mm -> milimeter s/(^|[^a-zA-Z])m($|[^a-zA-Z])/$1meter$2/g;# m -> meter s/(\W)([a-zA-Z]\w*)!/$1fact($2)/g; # $na_3! -> fact($n) # Mon Feb 5 09:54:58 2007: also, 10.(me+mp) -> 10.*(me*mp) s/(\d|\.)\s*\(/$1*\(/g; # 10(me+mp) -> 10*(me+mp) # # s/(^|[^\w\371\+-])([\+-]?(?:\d+\.?\d*|\.\d+)(?:\371[\+-]?\d+)?)([a-zA-Z]+)(?=$|\W)/$1\($2*$3\)/g; # 45mp -> (45*mp) (to be able to 6me/3mp) s/(^|[^\w\371])((?:\d+\.?\d*|\.\d+)(?:\371[\+-]?\d+)?)([a-zA-Z]+)(?=[\$\/\*-\+\s]|$)/$1\($2*$3\)$4/g; # 45mp -> (45*mp) (to be able to 6me/3mp) # Modified prev line on Jan 19 2007; was: # s/(^|[^\w\371])((?:\d+\.?\d*|\.\d+)(?:\371[\+-]?\d+)?)([a-zA-Z]+)(?=$|\W)/$1\($2*$3\)/g; # 45mp -> (45*mp) (to be able to 6me/3mp) # otherwise asind(0.5sqrt(2)) was broken s/([\d\.\)])\s*([a-zA-Z])/$1*$2/g; # 45a -> 45*a; )a-> )*a s/(\w)\s+([a-zA-Z\d])/$1*$2/g; # Msun c**2 -> Msun*c**2 s/(^|\W)([abdfgijln-rt-z])(?=$|\W)/$1\$$2/g; # single letter lower-case # variables s{/([abdfgijln-r-t-z])(?=$|\W)}{/\$$1}g; # preceeded and followed by a # non-word character are replaced # with $variable; second substitution # is needed in cases /n, because $1 # works incorrectly s/(\d+)!/fact($1)/g; # 10! -> fact(10) # factorial after the numbers is done last because otherwise the typo # "calc 3.5!" results (silently) in 3.*fact(5) s/(^|[^\w\.])0+(\d)/$1$2/g; # trim leading zeros in numbers s/\361/atan2/g; # ñ -> atan2 s/\371/e/g; # 1.5ù5 -> 1.5e5 THIS MUST BE THE LAST TRANSFORMATION if ($debug) {print STDERR "Debug: ",$_," format: ",$format,"\n";} if ( !defined($_result_ = eval "$_") ) { print STDERR "wrong expr: $origexpr -> $_\n"; } if ( $commandline || (! /=/) ) { # ignore assignments in the stdin mode if ($format =~ /^%l/ ) { print $_result_,"\n"; } else { printf "$format\n", $_result_; } } } # ENV VARIABLES sub check_env { if ($ENV{"CALCFORMAT"}) {$format = $ENV{"CALCFORMAT"};} else {$format = "%g"} if ($ENV{"CALCDEBUG"}) {$debug=1} else {$debug=0} } if ( $ARGV[0] eq "-nocomma" ) { $nocomma = 1; shift @ARGV; } else { $nocomma = 0; } $_ = "@ARGV"; &check_env; if ( /^-h$|^-help$/ ) { &printconst; exit 0; } if ( /\w/ ) { $commandline = 1; &doeval; } else { $term = new Term::ReadLine 'Perl calc'; # print STDERR $term->ReadLine,"\n"; $prompt = "calc: "; $OUT = $term->OUT || STDOUT; while ( defined ($_ = $term->readline($prompt)) ) { if (/^\s*history\s*$/ ) { @govgov=$term->GetHistory; foreach $gov ( @govgov ) { if (! ($gov =~ /^\s*history\s*$/) ) { print $gov,"\n"; } } next; } if (/^\s*calc\s*$/ ) {exec $0} if (/^\s*setenv CALC/){@gov = split;$ENV{$gov[1]}=$gov[2];&check_env;next;} if (/^\s*unsetenv CALC/){@gov = split;$ENV{$gov[1]}=0;&check_env;next;} if (/^\s*-h\s*$/||/^\s*-help\s*$/||/^\s*help\s*$/){&printconst;next;} @gov=split; if ($SHELLCOM{$gov[0]}) {$gov=shift(@gov); system($SHELLCOM{$gov},@gov); next;} $commandline = 0; &doeval; } # while ( ) { # chop; # $commandline = 0; # &doeval; # } } sub printconst { $scriptname = $0; while ( ) { print; } open (SCR,$scriptname) || die "Can't open $scriptname"; while ( ) { if (/^use constant |^\#------/) { s/use constant //g; s/=>/=/g; print; } } } __END__ Available functions: ------------------- exp, log, abs, sqrt, sin, cos, tan, asin, acos, atan, atan2(x,y)=atan(x/y) sind, cosd, tand, asind, acosd, atand --- degree based trig. functions; you can use the 'deg' constant instead. sinh(x), cosh(x), asinh(x), acosh(x), atanh(x), acoth(x) ln(x),lg(x) -> log_e(x), log_10(x) gamma(x), lgamma(x) - Gamma and log(Gamma) functions r2d(x) = x*180/pi; you can use the 'deg' constant instead fact(n) = n!, C(n,k) = n!/(k!(n-k)!) int, nint - integer portion and nearest integer CtoF,FtoC - convert between degrees centrigrade and Fahrenheit. Examples of usage can be found on http://hea-www.harvard.edu/~alexey/calc.html Available constants (all units are in the cgs system):