Mathematical calculator and AHK expression evaluator with real (double precision) decimal/integer numbers, hexadecimal and binary integers of 64 bit precision, and vectors of these, with function plots and a vector editor. Version 3.1.
The main calculator window
The help window
Function graph with captured coordinates
Edit/View vectors
Lexikos’
low level AHK functions make it possible to dynamically execute certain AHK commands, and with tat we can program a simple but powerful AHK popup calculator. When a second AHK instance was started to evaluate a user supplied expression, it would have to have all user functions and variables included, which would blow up the dynamic expression and make the calling script complicated. With the new dynamic expressions functions and variables are automatically accessible, so the old
Minimalist Calculator can be vastly improved.
The user entered expressions are stored in a history file (calc.hst), excluding repetitions, which you can Edit, Save or Load with hotkeys or via the tray menu with all the actions: Show, Help, Edit/Load/Save history. A single tray click activates/shows the calculator window, like its hotkey.
The size of the calculator window is calculated to 66 chars for the current font, DPI. The script remembers (calc.ini) the last position of each window it creates, and they will pop up in there the next time.
If an expression contains an assignment (:=, +=…), the variable on the left hand side becomes global, that is, subsequent dynamic expressions could use its value. Comma delimited lists of dynamic expressions are not fully supported, therefore the calculator does some processing of the commands: when semicolons “;” separate expressions, they get evaluated one-by-one. Entering “x := 1; y := 2; x+y” will evaluate to 3.
The input expression can contain decimal or hex numbers or strings, so we have instant conversions between number systems. Binary input is allowed via the b() function: b("0101") -> 5, b("1000") -> -8, where the first bit is always the sign.
To avoid specifying the output format, the result is shown in all of the most often used formats: decimal, hexadecimal and binary. In the top row of the results field the AHK representation of the result is shown (which can be any string), in the second line the C printf version in short and long format. It shows reasonable sized integers w/o decimal point, normal decimal numbers w/o exponents. Only very small or very large numbers are printed in engineering form (with an exponent).
The integer part of the output is shown also in signed and unsigned decimal and hexadecimal formats, and its complement as negative hex.
Binary numbers are shown truncated from the left: the leftmost bit is the sign, further bits to the left (which would be the same as the sign bit) are omitted: –8-->1000; –1-->1; 0-->0; 8-->01000... This often makes the binary digit sequence much shorter and easier to handle.
The calculator script can access every AHK built in function, like sqrt, ln, sin, atan... The missing ones, found on scientific calculators are included as user functions. Here is the content of the Help screen with all the functions in the current version:
Code:
Calculator Shortcuts:
Esc: Clear expression
Home/End/BS/Del... Edit expression
Up/Dn: Next entry in history, which starts with current exp
PgUp/PgDn: Move in exp history
Ctrl-Home/End: 1st/Last history entry
Alt-V/Enter: eValuate expression
Alt-H/F1: Help
Alt-E: Edit history Start/Stop
Alt-S: Save history
Alt-L: Load history
Ctrl-F1: Find function under cursor highlighted in Help
Array Lists:
Delayed double click: edit value
Context menu: More, Less, refresh
Enter, Alt-M: Moore (add new entry, Enter accepts)
Alt-L: Less (delete last etry)
Alt-R: Refresh (take over array changes)
Graph:
Mouse cursor: crosshair with ToolTip (x,y)
Right-click: MsgBox with current (x,y), ClipBoard copy: Ctrl-C
Basic constructs:
; : separator between expressions
'..', "..", `word : quoated strings
:=, +=, -=, *=, /=, //=, .=, |=, &=, ^=, >>=, <<= : assignments
+, -, /, //, *, ** : arithemtic operators
~, &, |, ^, <<, >> : bitwise operators (shift is 64-bit signed)
!, &&, || : logical operators
. : string concatenation
"c ? a : b" : ternary operator
<, >, =, ==, <=, >=, <> (or !=) : relational operators
++, -- : pre- and post-increments
AHK Built-in variables:
A_WorkingDir, A_ScriptDir, A_ScriptName, A_ScriptFullPath
A_AhkVersion, A_AhkPath,
A_YYYY, A_MM, A_DD, A_MMMM, A_MMM, A_DDDD, A_DDD, A_WDay, A_YDay
A_YWeek, A_Hour, A_Min, A_Sec, A_MSec, A_Now, A_NowUTC, A_TickCount
ComSpec, A_Temp, A_OSType, A_OSVersion, A_Language, A_ComputerName
A_UserName, A_WinDir, A_ProgramFiles, A_AppData, A_AppDataCommon
A_Desktop, A_DesktopCommon, A_StartMenu, A_StartMenuCommon
A_Programs, A_ProgramsCommon, A_Startup, A_StartupCommon
A_MyDocuments, A_IsAdmin, A_ScreenWidth, A_ScreenHeight, A_IPAddress1..4
AHK functions:
InStr(Haystack, Needle [, CaseSensitive = false, StartingPos = 1])
SubStr(String, StartingPos [, Length])
StrLen(String)
RegExMatch(Haystack, NeedleRegEx [, OutputVar = "", StartingPos = 1])
RegExReplace(Hstck,Ndle[,Rpmnt="",OutVarCnt="",Limit=-1,StartPos=1])
FileExist(FilePattern)
WinExist([WinTitle, WinText, ExcludeTitle, ExcludeText])
DllCall()
NumGet(VarOrAddress [, Offset = 0, Type = "UInt"])
NumPut(Number, VarOrAddress [, Offset = 0, Type = "UInt"])
VarSetCapacity(UnquotedVarName [, RequestedCapacity, FillByte])
Asc(String), Chr(Number)
Abs(), Ceil(), Exp(), Floor(), Log(), Ln(), Mod(x,m), Round(x[,N]), Sqrt()
Sin(), Cos(), Tan(), ASin(), ACos(), ATan()
General Functions:
b("bits") : string of bits to number, Sign = MS_bit
list(vector) : Edit/Sort ListView of elements. Build: Enter-value-Enter
RightClick menu: More (Enter, Alt-M: add new); Less (Alt-L: del last)
msg(x) : MsgBox `% x
sign(x) : the sign of x (-1,0,+1)
rand(x,y) : random number in [x,y]; rand(x) new seed=x.
Float Functions:
f2c(f) : Fahrenheit -> Centigrade
c2f(c) : Centigrade -> Fahrenheit
fcmp(x,y, tol) : floating point comparison with tolerance
ldexp(x,e) : load exponent -> x * 2**e
frexp(x, e) : -> scaled x to 0 or [0.5,1); e <- exp: x = frexp(x) * 2**e
cot(x), sec(x), cosec(x) : trigonometric functions
acot(x), asec(x), acosec(x) : arcus (inverse) trigonometric functions
atan2(x,y) : 4-quadrant atan
sinh(x), cosh(x), tanh(x), coth(x) : hyperbolic functions
asinh(x),acosh(x),atanh(x),acoth(x) : inverse hyperbolics
cbrt(x) : signed cubic root
quadratic(x1, x2, a,b,c) : -> #real roots {x1,x2} of ax²+bx+c
cubic(x1, x2, x3, a,b,c,d) :->#real roots {x1,x2,x3} of ax³+bx²+cx+d
Integer functions:
LCM(a,b) : Least Common Multiple
GCD(a,b) : Euclidean Greatest Common Divisor
xGCD(c,d, x,y) : eXtended GCD (returned), compute c,d: GCD = c*x + d*y
Choose(n,k) : Binomial coefficient. "n.0" force floating point arithmetic
Fib(n) : n-th Fibonacci number (n < 0 OK, iterative to avoid globals)
fac(n) : n!
IsPrime(p) : primality test 0/1
ModMul(a,b, m) : unsigned a*b mod m, no overflow till a*b < 2**127
ModPow(a,e, m) : unsigned a**e mod m, no overflow
uMod(x,m) : unsigned x mod m
uCmp(a,b) : unsigned 64-bit compare a <,=,> b: -1,0,1
MsMul(a,b) : Most Significant UInt64 of a*b
Reci64(m [, ByRef ms]) : Int64 2**ms/m: normalized (negative); unsigned m
MSb(x) : Most Significant bit: 1..64, (0 if x=0)
LSb(x) : Least Significant bit: 1..64, (0 if x=0)
Iterators/Evaluators
eval(expr) : evaluate ";" separated expressions, [.] index OK
call(FName,p1=""...,p10="") : evaluate expression in FName,
sets variables FName1:=p1, FName2:=p2...
solve('x',x0,x1,'expr'[,tol]) : find 'x' where 'expr' = 0
fmax('x',x0,x1,x2,'expr'[,tol])) : 'x' where 'expr' = max
for(Var,i0,i1,d,expr) : evaluate all, return result of last
{expr(Var:=i0),expr(Var:=i0+d)...}, until i1
while(cond,expr) : evaluate expr while cond is true; -> last result
until(expr,cond) : evaluate expr until cond gets true (>= once); -> last
Array creating functions (-> X="X", X_0=length, X_1,X_2... entries):
copy("X",Y) : duplicates Y
seq("X",i0,i1[,d=1]) : set up linear sequence X = {i0,i0+d..,i1}
array("X","i",i0,i1,d, "expr") : X = {expr(i:=i0),expr(i:=i0+d)..,expr(i~=i1)}
assign("X",entry1...) : assign (<=30) new entries to the of array X
more("X",entry1...) : add (<=30) new entries to the of array X
part("Y",X,i0,i1[,d=1]) : Y (!= X) <- {X[i0],X[i0+d]..,X[~i1]}
join("Z",X,Y) : Z <- join arrays {X,Y}, (Z != X or Y)
@("Z",X,"op|func",Y="") : elementwise ops, 0-pad; Y or X can be scalar
plmul("z",y,x) : z <- polynomial y*x (convolution, FIR filter)
pldiv("q","r",n,d) : polynomial division-mod: q <- n/d; r <- mod(n,d)
sort("y",x[.opt]) : y <- sorted array x
opt = 0: random, 1: no duplicates, 2: reverse, 3: reverse+unique
primes("p",n) : p <- primes till n (Sieve of Eratosthenes)
pDivs("d",n) : d <- prime divisors of n (increasing)
Vector -> scalar functions
mean(X), std(X), moment(k,X) : statistics functions
sum(X), prod(X), sumpow(k,X), dot(X,Y)
pleval(p,x): evaluate polynomial, <- p(x), (Horner's method)
min(x,x1=""...,x9="") : min of numbers or one vector
max(x,x1=""...,x9="") : max of numbers or one vector
pmean(p, x,x1=""...,x9="") : p-th power mean of numbers or one vector
Graphing functions
graph(x0,x1,y0,y1,width=400,height=300,BGcolor=white) :
create/change graph window to plot in, graph() destroys
Xtick(Array=10,LineColor=gray,LineWidth=1) : add | lines at x positions
can be called multiple times, BEFORE plot
Array=integer : equidistant ticks, Array="" : 11 ticks
Array=float : single line
Ytick(Array=10,LineColor=gray,LineWidth=1) : add - lines at y positions
plot(Y,color=blue,LineWidth=2) : add plot of Y with X = {1..len(Y)}
if no graph paper: create default one
plot() : erase function graphs
plotXY(X,Y...): add XY plot to last graph
if no graph, auto created with graph(min(X),max(X),min(Y),max(Y))
Special constructs:
[expr] : "_" . eval(expr). x[i] = x_%i%; x[i+1]:=1 OK
_ : last output
_1, _2,..._9: earlier outputs (list() shows them)
Math Constants:
pi = pi `te = e
pi_2 = pi/2 `tln2 = log(2)
pi23 = 2pi/3 `tlg2 = log10(2)
pi43 = 4pi/3 `tlge = log10(e)
pi2 = pi**2 `tln10= ln(10)
rad = pi/180 `tdeg = 180/pi
Unit conversion constants (150*lb_kg -> 150 pounds = 68.0389 KG)
inch_cm, foot_cm, mile_km
oz_l, pint_l, gallon_l
oz_g, lb_kg
acre_m2
There are a few mathematical constants included, too: pi, pi/2, pi**2, ln(2), lg(2), lg(e), ln(10)... If you enter their corresponding names (pi, pi_2, pi2, e, ln2, lg2, lge, ln10) to the input field of the calculator, the first line of the output field shows them with 63 decimal places. It could be useful for references.
The calculator window pops up for the Win-C hotkey. Enter the desired expressions in the input field. Upon hitting Enter the results appear in the output field, below the input. They can be selected by pressing the Tab key twice, or with the mouse, and copied to the Clipboard with Ctrl-C.
It is very easy to add further user functions or constants, so you can tune this calculator to specific needs, like unit conversion, geometric computation, statistics... If you write a general purpose special function, please post (or request) it here.
Vectors (arrays) are also supported. There are functions, which create arrays. Elements of them can be accessed as x_1 or x[i-1] type of indexing (x[0] containing the length). The list() function opens up a ListView, where the array can be seen sorted and its elements can be edited, new ones added, the last one removed. A right click allows adding and removing elements. "More" is the default, so Enter adds a new element, which you can type in, and the second Enter finishes its creation. This provides a very convenient way of creating long vectors of user data.
There are functions operating on arrays, like statistics: mean, standard deviation…, sums, products of elements, dot-product of vectors, etc. You can apply any 1 or 2 argument function or AHK expression to each element of an array with the apply-to function: @("Z",X,"op/func",Y="").
The most general way of creating an array is with array("X","i",i0,i1,d, "expr"). It assigns i0, i0+d,…up to i1 to the variable i, evaluates "expr" and assigns its value to the next array element. For variable names there is a shorthand: `var == "var", where var is terminated by "," or ")", "]", eol, space. (Be careful with expressions: `sin(x) becomes "sin(x"), `round(x,2) --> "round(x",2)). You can sort arrays: sort('y',x[.opt]): y ← sorted array x. (opt = 0: random, 1: no duplicates, 2: reverse, 3: reverse unique.)
Examples with different quotes `x, "x" and 'x':
more(`X,1,-2,3); min(X)
more(X,5,6,-7); std(X)
array('Y',`i, 0,9,1, "i*i"); Y_5+Y_4
dot(X,Y)
Basic polynomial functions:
- polynomials are representated by vectors: p[0] = LEN = degree+1, p[1] + p[2]*x + ... p[LEN]*x**(LEN-1)
- @() pad shorter vector with 0's ( @(`z,y,'+',x) ~ polynomial addition )
- pleval(p,x): evaluate polynomial, <- p(x), (Horner's method)
- plmul('z',y,x): z <- polynomial y*x (~ convolution, FIR filter)
- pldiv('q','r',y,x): polynomial division,mod: q <- y/x; r <- mod(y,x)
There are functions for iterative computations:
- for(Var,i0,i1,d,expr) : evaluate {expr(i0),expr(i0+d)..,expr(i1)}; returns the last result
x:=0; for("i",1,10,1,"x+=i*i") -> the sum of the 1st 10 squares
- while(cond,expr) : evaluate expr while cond is true; returns the last result
n:=1; x:=1; while("n++<10","x+=1/n") -> the sum of the 1st 10 reciprocals
- until(expr,cond) : evaluate expr until cond gets true (at least once); returns the last result
n:=1;x:=1;until("x*=++n","n=10") -> 10!
Dynamic expression evaluation is generally supported: eval(expr):
f:="1/x+1/y+1/z"
x:=1;y:=2;z:=0.5; eval(f)
Simple dynamic functions: call(FName, Param1,Param2...)
f:="1/f1+1/f2+1/f3"
call("f",1,2,3)
~Assign the function body (as a string) to a variable, like “f”.
~The function parameters are numbered, prefixed with the name of the variable “f”: f1, f2,...
This way the chance of variable naming conflict is smaller. In the Popup Calculator every variable is global, so if you have used f2 earlier, it will be overwritten by the call("f",1,2,3).
[expr]: evaluate expr -> v, return “_v”; it behaves like a computed index.
e.g. X[1]:=2 is the same as X_1:=2
e.g. i:=2; 3-X[i-1] is the same as i:=2; __temp:=i-1; 3-X_%__temp%, or 3-X[1]. Here __temp is not global, there is no conflict with multiple brackets in one expression.
B[1][2] is the same as B_1_2, so we can mix one-, and two dimensional arrays: B[1][2] is different from B[12]. The elements of an automatic array X are accessed as X_0=X[0]: length, X_1=X[1], X_2=X[2]... which reduces the possibility of conflicts with explicitly defined variables x1, x2...:
GRAPHING FUNCTIONS- graph(x0,x1,y0,y1,width=400,height=300,BGcolor=white) : create/change graph window to plot in
---- (margin=10) deleted when closed[x] or GuiEscape
---- graph is copied with Alt-PrtScrn to the clipboard
---- graph() destroys
- Xtick(Array=10,LineColor=gray,LineWidth=1,Label=true) : add | lines to graph at X positions
---- (Labels are not yet implemented)
---- can be called multiple times, BEFORE plot
---- Array=integer : equidistant ticks, Array="" : 10 stripes
---- Array=float : single line
- Ytick(Array=10,LineColor=gray,LineWidth=1,Label=true) : add - lines at y positions...
- plot(Y,color=blue,LineWidth=2) add plot of straight lines with X = {1..len(Y)}
---- if no graph: create default one with y0 = min(Y), y1 = max(Y)
---- plot() erase function graphs
- plotXY(X,Y...): add XY plot to last graph
---- if no graph, create default one with graph(min(X),max(X),min(Y),max(Y))
In the graph window the mouse cursor is crosshair + x/y coordinates (not pixels) in ToolTip, so you can easily find interesting points on function (inflection point, min or max…). A right click shows the coordinates in message boxes (as many as you want), from where you can copy them with the usual Ctrl-C.
Example:
array("X","i",1,100,1,"sin(i/pi)"); plot(X)
array("Y","i",1,100,1,"atan((i-50)/25)/pi_2"); plot(Y,0xFF)
Here are some not numeric applications:
- fast conversion between char and its ANSI code: asc('a')->97, chr(0x44)->D
- quick test of RegEx Match/Replace (not all works):
--- RegExReplace("C:\Windows\system32", "Windows", "WinNT")
- get file attributes: FileExist("C:\Windows\system32\systeminfo.exe") -> A
There are functions for finding the zero or the maximum of an arbitrary expression (function):
- solve('x',x0,,x1,'expr'[,tol]): find 'x' where 'expr' = 0
- fmax('x',x0,x1,x2,'expr'[,tol])): find 'x' where 'expr' = max (at flat curve lower precision)
There are also functions related to prime numbers:
- primes('p',n): {primes <= n} → array 'p' using the sieve Eratosthenes
--- list(primes(`x,999999)) shows all primes less than a million
--- m <= primes <= n: list(array(`x,`i,1,299,1,'isprime(2**32+i)'))
- IsPrime(p) added: machine code prime test (IsPrimeA is pure AHK, slow)
--- isprime(2**31+11), isprime(2**31-1)
--- list(array(`x,`i,1,999,1,'isprime(2**62+i)')) find all 32 primes in (2**62,2**62+1000) (Dell Inspiron 9300: 15 minutes)
--- isprime(9223372036854775783) → 1 (34 sec), it is the largest prime, 2**63-25, AHK can show
- pDivs('d',n) added: {prime divisors of n} → d (pDivsA is pure AHK, slow)
--- list(pdivs(`x,9973*9967)), list(pdivs(`x,(2**31+45)*(2**31+11))) – (Dell Inspiron 9300: 23 sec)
Some integer modular arithmetic functions are also included:
- ModMul(a,b, m) : unsigned a*b mod m, no overflow if a*b < 2**127
- ModPow(a,e, m) : unsigned a**e mod m, no overflow even when a**e is too large to fit to the memory
- MsMul(a,b) : Most Significant Int64 of unsigned a*b (which could be 128 bits)
- Reci64(m [, ByRef ms]) : 2**ms/m: Int64 normalized (negative), m: UNSIGNED written with sign
- uMod(x,m) : unsigned mod (umod((2**32-1)*(2**32-1),9) -> 0)
- uCmp(a,b) : unsigned 64-bit compare. a <|=|> b: -1|0|1
- MSb(x) : Most Significant bit: 1..64, (0 if x=0)
- LSb(x) : Least Significant bit: 1..64, (0 if x=0)
The file grew too large to be directly copied here, but you can download the
script or the compiled
executable from AutoHotkey.net. No installation is required, just copy it somewhere and run. You can rename it (scriptname.ahk/exe). It will create two files scriptname.hst and scriptname.ini in the working directory (could be set differently for different users). The scriptname.ini file is used (if present in the working directory) to store the last positions of windows (where they will pop up the next time) and define defaults, like hotkeys, fonts, colors, graph sizes, etc. Here is an example
Code:
[Window Positions]
CalcX=706
CalcY=505
HelpX=4
HelpY=4
ListX=5
ListY=692
GraphX=1023
GraphY=214
[Hotkeys]
Calc=#c
Vars=#d
[Font]
size=11
font=Verdana
[Graph Defaults]
width=600
height=400
xTicks=10
yTicks=10
BgColor=0xFFFFFF ; white, black=0
LnColor=0xFF0000 ; blue, red = 0xFF
LnWidth=2