Skripti toimii naurettavan hitaasti jopa BASH skriptiksi johtuen siitä että mitään skriptiä ei edes ole vaan pääte itse toimii laskimena. Lisäksi BASH:in on pakko käyttää laskentaan jotain laskin-ohjelmaa. Ja laskinohjelmat ovat matemaatikoille tarkoitettuja ja niiden merkintätavat senmukaisia. Tämä skripti käyttääkin ajastaan valtaosan muuttaakseen merkintätavat inhimillisiksi. Jos haluaa skriptiinsä BASH:in normaalia nopeutta kannattaa tästä skriptistä kopioida omaan skriptiin tarvittava osa - siis tämä skripti vain osoittaa mikä on mahdollista.
Käyttääkseesi päätettä laskimena kopioi seuraaava koodi johonkin tiedostoon koneessasi. Kun avattuasi päätteen annat käskyn: . polku_tiedoston_nimeen (siis: piste välilyönti polku) niin kehote tulee takaisin semmoisenaan ja tuntuu siltä ettei mitään tapahtunut. Mutta vaikka pääte toimii niinkuin ennenkin niin tämä pääte osaa nyt normaalin pääte-toiminnan lisäksi toimia myös laskimena - jos esimerkiksi kirjoitat päätteeseen: sin30-cos60 niin enteriä painettuasi saat vastauksen: -.000000000000000000000000000000000000000000000000000000000000000003 . Listan tunnetuista laskuista saat käskyllä: laske. Skripti ei tee koneeseen mitään pysyviä muutoksia ja kun suljet päätteen niin matematiikka-taidotkin unohtuvat.
Laskimella on hitautensa korvauksena rajoittamaton tarkkuus:
- jos haluat laskustasi tuhansia oikeita desimaaleja niin se onnistuu.
- kokonaisosan numeromäärää ei rajoiteta koskaan. Esimerkiksi 10000!:n kaikki 36000 numeroa lasketaan 3:ssa sekunnissa.
- myös samassa päätteessä toimivat skriptit perivät matemaattiset taidot.
- toiminta alkaa siitä että kirjoitetaan päätteeseen "laskukaava" ja painetaan enter. Mikäli funktiot eivät osaa laskukaavaa toteuttaa siirretään "laskukaava" päätteen komento-tulkille joten normaali päätetoiminta on mahdollista.
#!/bin/bash
# petteriIII 2.7.2014 ~/OMATSKRIPTIT/FUNKTIOKIRJASTO/matemaattiset_funktiot
set +H # jos tämä puuttuisi niin kertoman merkki: ! aiheuttaisi virheen
unset command_not_found_handle # poistaa virheviestit mallia: tarkoititko käskyä xxx paketissa yyy
trap 'laske "$BASH_COMMAND"' 1 ERR
function laske () { # Tuloparametrina on laskukaava. Tulostaa näytölle ja asettaa kutsuttujen funktioiden nimiset muuttujat ja tuloksen muuttujaan: tulos.
[[ ! $1 ]] && echo "- laskuissa on 66 desimaalia jollei toisin määrätä. Desimaalipisteen jälkeisiä peränollia ei tulosteta.
- kokonaisosan numeromäärä on aina rajoittamaton ja desimaalien määrän voi määrätä ihan miksi haluaa.
- sallitut operaattorit: +, -, *, / ja ^ Näissä laskuissa ^ on potenssiinkorotus, esimerkiksi 3^0.5 tai: 10000000000^-0.1.
- funktiot: sin, asin, cos, tan, atan, sqrt, log, ln, exp, abs, int, e, pi, ! (=kertoma), kahteen, kymmeneen (kantaluvun muutokset), des (määrää desimaalien
luvun), rad (=määrää että geometriset laskut suoritetaan radiaaneissa), annuiteettilaina lainanmäärä korkoprosentti lainaaika_vuosissa -> vuosilyhennys
- merkitätavat noudattavat normaaleja matematiikan sääntöjä. Sulkuina on kuitenkin helpointa käyttää hakasulkuja.
- syötettävien esitysmuoto voi olla tieteellinenkin, esimerkiksi: 1.23e6 . Vaikka se olisi samassa lauseessa kuin joku luonnollisen e:n funktio.
- desimaalien ja radiaanien määräämis-esimerkki: des256radsin0.511 . Asetukset säilyvät kunnes muutetaan tai vaihdetaan päätettä.
- testilaskuja: -1-[2*6!+1]/1441+[[sin210-cos60]*tan60*2]/[3^.5]*ln[e^-pi]/pi -> 0 ; ln[e^ln[e^ln[e^ln[e^ln[e^ln[e^ln[e^ln[e^1.25]]]]]]]] -> 1.25 ;
2+1.1e2+e-2.7 -> 110.018....; sin[asin.5]-asin[sin.5] -> 0
- edellisen laskun tulokseen voi viitata sanalla: tulos (esimerkiksi laskun: 3^-.5 tuloksen -oikeellisuustesti- on: 1/tulos joka on neliöjuuri 3)"
trap "" 1 ERR
tulos=$1
# testi yritetäänkö jotain tuntematonta:
[[ $(echo $tulos | sed 's/asin//g;s/sin//g;s/cos//g;s/atan//g;s/tan//g;s/log//g;s/ln//g;s/exp//g;s/sqrt//g;s/int//g;s/abs//g;s/kymppiin//g;s/kahteen//g;s/des//g;s/pi//g;s/annuiteetti//g;s/e//g;s/rad//g;s/ast//g;s/\!//g;s/tulos//g' | grep [a-zäöåA-ZÄÖÅ] ) ]] && exec $tulos
lasketaan_radiaaneissa=${lasketaan_radiaaneissa:-0} # toimintamuotona on asteet ellei toimintamuodoksi peritä radiaaneja.
desimaaleja=${desimaaleja:-66} # laskujen desimaaliluku on 66 ellei peritä toista arvoa.
tulos=${tulos#+} # poistetaan + edestä
tulos=${tulos%%#*} # poistetaan kommentit
tulos=${tulos//tulos/$tulos} # muutetaan annetussa laskussa sana:tulos edellisen laskun muuttujaan tulos
tulos=$(echo $tulos | tr -d \' | tr -d \" | tr "(" '[' | tr ")" ']' ) # poistetaan heittomerkit ja muunnetaan kaarisulut hakasuluiksi.
tulos=$(echo "$tulos" | sed 's/--/+/g;s/++/+/g;s/-+/-/g;s/\^+/\^/g;s/\/+/\//g;s/\*+/\*/g') # -- muuntuu +:ksi ja /+ muuntuu /:ksi ja *+ muuntuu *:ksi
[[ $(echo $tulos | grep des) ]] && maaraa_laskujen_desimaaliluku
[[ $(echo $tulos | grep rad) ]] && lasketaan_radiaaneissa=1
# muutetaan luonnollisen järjestelmän kantaluku e lukuarvoonsa kaikkialla. Siis luvut kuten tuo e kaavassa: 1+e^2
e_numeroina=$(exp 1)
tulos=$(echo $tulos | sed "s/e$/$(exp 1)/g;s/^e/$(exp 1)/g" ) # muuttaa e:n rivin alusta ja lopusta
apu=$(echo $tulos | grep -ob [^0-9a-z]e[^a-z0-9] | tr "\n" " " | awk '{print $1}' )
while [[ $apu != "" ]] ; do e_paikka=${apu%%\:*}; tulos=${tulos:0:e_paikka+1}$e_numeroina${tulos:e_paikka+2}; apu=$(echo $tulos | grep -ob [^0-9a-z]e[^a-z0-9] | tr "\n" " " | awk '{print $1}' ); done
# muutetaan pi (ympyrän kehä/halkaisija) lukuarvoonsa kaikkialla
pi_numeroina=$(echo "scale=$desimaaleja; 4*a(1)" | bc -l | tr -d '\\' | tr -d "\n")
tulos=$(echo "$tulos" | sed "s/pi$/$pi_numeroina/g" ) # muuttaa pi:n kun se on rivin lopussa #[[ $(echo "$tulos" | grep -o pi[^a-z0-9] ) ]] &&
tulos=$(echo $tulos | sed "s/pi\^/$pi_numeroina\^/g;s/pi\*/$pi_numeroina\*/g;s/pi\-/$pi_numeroina\-/g;s/pi\//$pi_numeroina\//g;s/pi[+]/$pi_numeroina\+/g" )
# ratkaistaan kaikki tieteelliset esitysmuodot (niinkuin: 1.23e17)
apu=$(echo $tulos | grep -o [0-9]e.[0-9]* )
while [[ $apu != "" ]] ; do muuta_e_esitys_numeroiksi; apu=$(echo $tulos | grep -o [0-9]e.[0-9]* ); done
tput cuu1; tput el # kursori ylös rivinverran ja rivin tyhjennys
echo #"$tulos"
#korkeintaso=0; [[ ${tulos#*\[} ]] &&
etsi_laskukaavan_korkeimman_tason_vasemmanpuoleisimman_sulun_teksti
[[ $korkeintaso -gt 0 ]] && laita_se_laskentaan_ja_korvaa_sulun_teksti_lasketulla_arvolla #&& return
for funktiolaji in asin sin cos atan tan log ln exp sqrt int abs kymppiin kahteen annuiteetti ! ; do
korvauksia_tekematta=1
while [ $korvauksia_tekematta -eq 1 ]; do
korvauksia_tekematta=0
[[ $(echo $tulos | grep $funktiolaji ) ]] && korvauksia_tekematta=1 && erota_parametrit_ja_kutsu_funktiota $funktiolaji ; done
done
# ratkaise x^y kaikkialla, sillä bc ei yksinään ymmärrä desimaalisia eksponentteja
apu=$(echo $tulos | grep -o [0-9.][0-9.]*^[0-9.+-][0-9.]* | tr "\n" " " | awk '{print $1}')
while [[ $apu != "" ]] ; do ratkaise_eksponentti $apu; apu=$(echo $tulos | grep -o [0-9.][0-9.]*^[0-9.+-][0-9.]* | tr "\n" " " | awk '{print $1}'); done
tulos=$(echo $tulos | sed 's/--/+/g') && tulos=$(echo "obase=10;ibase=10;scale=$desimaaleja ; $tulos" | bc -l | tr -d '\\' | tr -d "\n" ) && tulosta $tulos
}
function erota_parametrit_ja_kutsu_funktiota () {
[[ $funktiolaji = ! ]] && apu=$(echo ${tulos%%\!*} | sed 's/\^/ /g;s/\*/ /g;s/\// /g;s/\+/ /g;s/\-/ /g') && numero=${apu##* }
[[ $funktiolaji != ! ]] && apu=$(echo ${tulos#*$funktiolaji} | sed 's/\^/ /g;s/\*/ /g;s/\// /g;s/\+/ /g;s/\-/ /g') && numero=${apu%% *}
[[ $numero = "" ]] && apuu=$(echo ${tulos#*$funktiolaji}) && apu=${apuu:0:1}${apu:1} && numero=${apu%% *} # palautetaan operaattoreita poistettaessa hävinnyt merkki
kutsu="$funktiolaji $numero"; $kutsu; arvo=$($kutsu); korvattava=$funktiolaji$numero;[[ $funktiolaji = ! ]] && korvattava=$numero$funktiolaji;tulos=$(echo $tulos | sed "s/$korvattava/$arvo/g")
}
function tulosta () { # mikäli luvussa on desimaalipiste niin poistetaan peränollat ja pistekin jos se jää loppuun yksikseen
[[ ${1%%\.*} != $1 ]] && echo $1 | sed 's/0*$//g' | sed 's/\.$//g' || echo $1
}
function ratkaise_eksponentti () { # ratkaistaan: x^y sillä ilman apua bc ei ymmärrä desimaalisia eksponentteja
[[ $(echo $tulos | grep '\^' ) = "" ]] && return
lukuedessa=${tulos%%^*}
etukaannettyna=$(echo $lukuedessa | rev)
kantaluku=$(echo ${etukaannettyna%%[!\.0-9]*} | rev)
exponentti=$(echo ${tulos##*\^} | sed 's/\^/ /g;s/\*/ /g;s/\// /g;s/\+/ /g;s/[0-9]\-/ /g') # 420
valitulos=$(echo "scale=$desimaaleja; e($exponentti*l($kantaluku))" | bc -l | tr -d '\\' | tr -d "\n")
korvattava=$kantaluku"\^"$exponentti; tulos=$(echo $tulos | sed "s/$korvattava/$valitulos/g")
echo $kantaluku'^'$exponentti' '$valitulos
}
function etsi_laskukaavan_korkeimman_tason_vasemmanpuoleisimman_sulun_teksti () { # echo "etsitään tasoa"
raja=112\101\116\116\101\114\105\73\73\73
korkeintaso=0; olintaso=0
for (( n=0; n<=${#tulos}; n++ )); do
[[ ${tulos:$n:1} = "[" ]] && let olintaso++
[[ $olintaso -gt $korkeintaso ]] && korkeintaso=$olintaso && tekstinalku=$n
[[ ${tulos:$n:1} = "]" ]] && let olintaso--
done
[[ $korkeintaso -eq 0 ]] && return
for (( m=tekstinalku; m<=${#tulos}; m++ )); do
[[ ${tulos:$m:1} = "]" ]] && tekstinloppu=$m && break
done
}
function laita_se_laskentaan_ja_korvaa_sulun_teksti_lasketulla_arvolla () {
tulos_alussa="$tulos"
suluissa_oleva="${tulos:$tekstinalku:$(($tekstinloppu-$tekstinalku))}" # oli: -$tekstinalku+1))}"
ilman_sulkuja=$(echo $suluissa_oleva | sed 's/^\[*//g;s/\]*$//g');
laske $ilman_sulkuja
uusi_tulos=${tulos_alussa:0:$tekstinalku}$tulos${tulos_alussa:$tekstinloppu+1}; tulos=$uusi_tulos; echo $tulos
etsi_laskukaavan_korkeimman_tason_vasemmanpuoleisimman_sulun_teksti $tulos
[[ $korkeintaso -gt 0 ]] && laita_se_laskentaan_ja_korvaa_sulun_teksti_lasketulla_arvolla # koe [[ $korkeintaso -gt 0 ]] && laske $tulos
}
function muuta_e_esitys_numeroiksi () { # ratkaistaan tieteellinen esitystapa; luvut mallia 1.23e18
korvattava=$(echo $tulos | grep -o [.0-9]*e[-+0-9][0-9]* )
[[ $korvattava = "" ]] && return
arvo=$(echo $korvattava | sed "s/e/\*10\^/g")
tulos=$(echo $tulos | sed "s/$korvattava/"\["$arvo"\]"/g")
}
function maaraa_laskujen_desimaaliluku () {
desimaaleja=$(echo ${tulos##*des} | sed 's/[[:alpha:]]//g')
korvattava="des"$desimaaleja; tulos=$(echo $tulos | sed "s/$korvattava//g")
}
function tan () {
asteluku=$(echo ${1#+}) # jos asteluvun edessä on + niin poistetaan se
[[ $lasketaan_radiaaneissa -eq 1 ]] && tan=$(echo "scale=$desimaaleja; s($asteluku)/c($asteluku)" | bc -l | tr -d '\\' | tr -d "\n") || tan=$(echo "scale=$desimaaleja; s($asteluku*$pi_numeroina/180)/c($asteluku*$pi_numeroina/180)" | bc -l | tr -d '\\' | tr -d "\n") ; tulosta $tan
}
function kahteen () {
kahteen=$(echo "obase=2;ibase=10;$1" | bc -l | tr -d '\\' | tr -d "\n" ) ; tulosta $kahteen
}
function kymppiin () {
kymppiin=$(echo "obase=10;ibase=2;$1" | bc -l | tr -d '\\' | tr -d "\n" ) ; tulosta $kymppiin
}
function cos () {
asteluku=$(echo ${1#+}) # jos asteluvun edessä on + niin poistetaan se
[[ $lasketaan_radiaaneissa -eq 1 ]] && cos=$(echo "scale=$desimaaleja; c($asteluku)" | bc -l | tr -d '\\' | tr -d "\n") || cos=$(echo "scale=$desimaaleja; c($asteluku*$pi_numeroina/180)" | bc -l | tr -d '\\' | tr -d "\n") ; tulosta $cos
}
function log () { # briggsin logaritmi
log=$(echo "scale=$desimaaleja; l("$(echo $1 | sed "s/e/$e_numeroina/g")")/l(10)" | bc -l | tr -d '\\' | tr -d "\n"); tulosta $log
}
function ln () { # e-kantainen logaritmi
ln=$(echo "scale=$desimaaleja; l($1)" | bc -l | tr -d '\\' | tr -d "\n"); tulosta $ln
}
function exp () { # e^x
exp=$(echo "scale=$desimaaleja; e($1)" | bc -l | tr -d '\\' | tr -d "\n"); tulosta $exp
}
function sqrt () { # neliöjuuri
sqrt=$(echo "scale=$desimaaleja; sqrt ($1)" | bc -l | tr -d '\\' | tr -d "\n"); tulosta $sqrt
}
function ! () {
[[ $1 -eq 0 ]] && kertoma=1 || kertoma=$( eval echo {1..$1} | tr " " \* | bc | tr -d '\\' | tr -d "\n"); tulosta $kertoma
}
function atan () { # arctangentti
[[ $lasketaan_radiaaneissa -eq 1 ]] && atan=$(echo "scale=$desimaaleja; a($1)" | bc -l | tr -d '\\' | tr -d "\n") || atan=$(echo "scale=$desimaaleja; a($1*$pi_numeroina/180)" | bc -l | tr -d '\\' | tr -d "\n") ; tulosta $atan
}
function sin () {
asteluku=$(echo ${1#+}) # jos asteluvun edessä on + niin poistetaan se
[[ $lasketaan_radiaaneissa -eq 1 ]] && sin=$(echo "scale=$desimaaleja; s($asteluku)" | bc -l | tr -d '\\' | tr -d "\n") || sin=$(echo "scale=$desimaaleja; s($asteluku*$pi_numeroina/180)" | bc -l | tr -d '\\' | tr -d "\n") ; tulosta $sin
}
function asin () { # arcsini
[[ $lasketaan_radiaaneissa -eq 1 ]] && asin=$(echo "scale=${desimaaleja:-66}; a($1/sqrt(1-($1*$1)))" | bc -l | tr -d '\\' | tr -d "\n") || asin=$(echo "scale=${desimaaleja:-66}; a($1/sqrt(1-($1*$1)))*180/$pi_numeroina" | bc -l | tr -d '\\' | tr -d "\n")
tulos=$(echo $tulos | sed "s/asin$1/$asin/g");tulosta $asin
}
function abs () { # tämä poistaa etumerkin, myös plussan
abs=$1; abs=$(echo ${abs#[-+]}); tulosta $abs
}
function int () { # muuttujan kokonaisluku-osa
int=$(echo 0$1 | cut -d. -f1 ); tulosta $int
}
function annuiteetti () {
laske [[1+$korkoprosentti/100]^$lainaaika*$korkoprosentti/100]/[[1+$korkoprosentti/100]^$lainaaika-1]*$lainamaara
annuiteetti=$tulos
}
function ratkaise_annuiteetti () { # lainanmäärä korkoprosentti lainaaika(vuosissa)
set -- $(echo $tulos | sed 's/annuiteetti//g')
lainamaara=$1
korkoprosentti=$2
lainaaika=$3
echo 'annuiteetti '$lainamaara' '$korkoprosentti' '$lainaaika
annuiteetti #kutsu="annuiteetti";arvo=$("$kutsu"); korvattava="annuiteetti $lainamaara $korkoprosentti $lainaaika"; tulos=$(echo $tulos | sed "s/$korvattava/$arvo/g"); echo $annuiteetti
}