Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

# Popup calculator / expression evaluator

32 replies to this topic
• Moderators
• 4713 posts
• Last active: Mar 31 2012 03:17 AM
• Joined: 14 Feb 2005
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:
```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
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_Programs, A_ProgramsCommon, A_Startup, A_StartupCommon

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, : 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, : 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
```[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```

• Moderators
• 9162 posts
• Last active: Sep 12 2014 10:36 PM
• Joined: 30 Aug 2005
very cool.
user notes:
trig functions use radians
no imaginary numbers

Thanks Laszlo

• Moderators
• 4713 posts
• Last active: Mar 31 2012 03:17 AM
• Joined: 14 Feb 2005
Thanks for adding the missing info. Do you often need complex numbers?

• Moderators
• 9162 posts
• Last active: Sep 12 2014 10:36 PM
• Joined: 30 Aug 2005
no, i just have a habit of testing calculators with e^(i*pi)

I theoretically would need imaginary numbers, since I am an EE, but they really don't come up often, and I have matlab for that.

• Moderators
• 4713 posts
• Last active: Mar 31 2012 03:17 AM
• Joined: 14 Feb 2005
Here is a much enhanced version (1.3). The difference:
- copied over lexikos' lowlevel functions, so there are no external dependencies, which could crash the calculator after a change
- added 6-digit decimal display, which is easier to read
- added negative hex display, to see AHK representations of hex numbers
- added unit conversion constants (US standard to metric with multiplication, back with division)
- command history: the last 99 evaluated expressions are saved and reloaded from a file with the same name as the calculator script, with extension .hst in the working directory.
- Win-C activates/shows calculator window
- Alt-H: Help (list of shortcuts, functions)
- Alt-E: edit the expressions history
- Alt-S: save, Alt-L: load history
- Custom tray menu items, icon
- Tray double click: activate calculator
- More functions. The list is now:

f2c(f) : Fahrenheit -> Centigrade
GCD(a, : Euclidean GCD
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!
sign(x) : the sign of x (-1,0,+1)
fcmp(x,y, tol) : floating point comparison with tolerance
cbrt(x) : signed cubic root
quadratic(ByRef x1, ByRef x2, a,b,c) : -> #real roots {x1,x2} of ax**2+bx+c
cubic(ByRef x1, ByRef x2, ByRef x3, a,b,c,d) : -> #real roots {x1,x2,x3} of ax**3+bx**2+cx+d
min(x,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="") : min of 10 numbers or less
max(x,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="") : max of 10 numbers or less
norm(k, x,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="") : k-th power mean
cot(x) : cotangent
acot(x) : inverse cotangent
atan2(x,y) : 4-quadrant atan
sinh(x) : hyperbolic sine
cosh(x) : hyperbolic cosine
tanh(x) : hyperbolic tangent
coth(x) : hyperbolic cotangent
asinh(x) : inverse hyperbolic sine
acosh(x) : inverse hyperbolic cosine
atanh(x) : inverse hyperbolic tangent
acoth(x) : inverse hyperbolic cotangent
ldexp(x,e) : load exponent -> x * 2**e
frexp(x, ByRef e) : -> scaled x to [0.5,1) or 0; e <- exp: x = frexp(x) * 2**e

The trickiest ones are fcmp and cubic. The later finds the real roots of cubic equations of real coefficients.
```; Popup AHK calculator v.1.3
; Works with AHK 1.0.47.04 and 1.0.47.05
; Includes lexikos' lowlevel functions (http://www.autohotkey.com/forum/viewtopic.php?t=26300)

#SingleInstance Force
#NoEnv
SetBatchLines -1

SetFormat Float, 0.16e                         ; max precise AHK internal format
__init()
pi  = 3.141592653589793238462643383279502884197169399375105820974944592 ; pi
pi_2= 1.570796326794896619231321691639751442098584699687552910487472296 ; pi/2
pi23= 2.094395102393195492308428922186335256131446266250070547316629728 ; 2pi/3
pi43= 4.188790204786390984616857844372670512262892532500141094633259455 ; 4pi/3
pi2 = 9.869604401089358618834490999876151135313699407240790626413349374 ; pi**2
e   = 2.718281828459045235360287471352662497757247093699959574966967628 ; e
ln2 = 0.693147180559945309417232121458176568075500134360255254120680009 ; log(2)
lg2 = 0.301029995663981195213738894724493026768189881462108541310427461 ; log10(2)
lge = 0.434294481903251827651128918916605082294397005803666566114453783 ; log10(e)
ln10= 2.302585092994045684017991454684364207601101488628772976033327901 ; ln(10)

inch_cm = 2.54                                 ; inches to centimeters
foot_cm = 30.48                                ; feet to centimeters
mile_km = 1.609344                             ; miles to Kilometers
oz_l    = 0.02957352956                        ; liquid ounces to liters
pint_l  = 0.4731764730                         ; pints to liters
gallon_l= 3.785411784                          ; gallons to liters
oz_g    = 28.34952312                          ; dry ounces to grams
lb_kg   = 0.45359237                           ; pounds to Kilograms
acre_m2 = 4046.856422                          ; acres to square meters

SplitPath A_ScriptName,,,,File                 ; default history file
File .= ".hst"
OnExit CleanUp

Menu Tray, NoStandard                          ; custom tray menu
Menu Tray, Default, Show
Menu Tray, Icon, %A_WinDir%\system32\calc.exe  ; borrow the icon of calc.exe

Gui font, s10, Verdana
Gui +Delimiter`n
Gui Margin, 5, 5
Gui Add, ComboBox, w530 vCode, %history%`n`n
Gui Add, Edit, -0x200000 vRes w530 r4 ReadOnly ; no scroll bar for results
Gui Add, Edit, w530 r20 vHist                  ; history edit
Gui Add, Button, x0 y0 w0 Default, e&valuate   ; hidden buttons
Gui Add, Button, x0 y0 w0, &Edit               ; activated by Alt-Letter
Gui Add, Button, x0 y0 w0, &Save
Gui Add, Button, x0 y0 w0, &Load
Gui Add, Button, x0 y0 w0, &Help
GuiControl Hide, Hist                          ; hidden history
Gui Show, AutoSize Hide, Evaluate AHK expression {Enter}

#c::Gui Show

ButtonEdit:                                    ; toggle history edit control
GuiControlGet Shown, Visible, Hist
If (Shown) {
GuiControlGet history,,Hist
GuiControl Hide, Hist
}
Else {
GuiControl Show, Hist
GuiControl,,Hist, %history%
GuiControl,,Code, `n%history%`n`n        ; replace all, select last
}
Gui Show, AutoSize                          ; adjust GUI window
Return

ButtonEvaluate:                                ; Alt-V or Enter: evaluate expression
Gui Submit, NoHide
GuiControl,,Res, % Eval(code)
StringGetPos pos, history, `n, R99          ; append expression to history
If ErrorLevel
pos = 0
history := SubStr(history,pos+1) . "`n" . Code
GuiControl,,Code, `n%history%`n`n           ; replace all, select last
return

GuiClose:
GuiEscape:
Gui Hide                                    ; keep it alive for fast activation
Return

SaveHistory:                                   ; overwrite file with history data
FileDelete %File%
StringReplace history, history, `r, `n, ALL
StringReplace history, history, `n`n, `n, ALL
StringReplace history, history, `n`n, `n, ALL
FileAppend %history%, %File%
Return

LoadHistory:                                   ; gets earlier saved history data
StringReplace history, history, `r, `n, ALL
StringReplace history, history, `n`n, `n, ALL
StringReplace history, history, `n`n, `n, ALL
Return

ButtonSave:                                    ; Alt-S
FileSelectFile File, S, %A_WorkingDir%\%File%, Save the history to..., *.hst
IfEqual ErrorLevel,0, GoSub SaveHistory
Return

FileSelectFile File,, %A_WorkingDir%\%File%, Load the history from..., *.hst
IfEqual ErrorLevel,0, GoSub LoadHistory
GuiControl,,Code, `n%history%`n`n           ; replace all, select last
Return

ButtonHelp:                                    ; Alt-H
MsgBox,                                     ; list of shortcuts, functions
(
Shortcut commands:
Alt-V, Enter: evaluate expression
Alt-E: Edit history Start/Stop
Alt-S: Save history

Functions (AHK's and the following):
f2c(f) : Fahrenheit -> Centigrade
c2f(c) : Centigrade -> Fahrenheit

GCD(a,b) : Euclidean GCD
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!

sign(x) : the sign of x (-1,0,+1)
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

cbrt(x) : signed cubic root
quadratic(x1, x2, a,b,c) : -> #real roots {x1,x2} of ax**2+bx+c
cubic(x1, x2, x3, a,b,c,d) : -> #real roots {x1,x2,x3} of ax**3+bx**2+cx+d

min(x,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="") : min of 10 numbers or less
max(x,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="") : max of 10 numbers or less
norm(k, x,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="") : k-th power mean

cot(x) : cotangent
acot(x) : inverse cotangent
atan2(x,y) : 4-quadrant atan

sinh(x) : hyperbolic sine
cosh(x) : hyperbolic cosine
tanh(x) : hyperbolic tangent
coth(x) : hyperbolic cotangent
asinh(x) : inverse hyperbolic sine
acosh(x) : inverse hyperbolic cosine
atanh(x) : inverse hyperbolic tangent
acoth(x) : inverse hyperbolic cotangent

Math Constants:
pi  = pi       e   = e
pi_2= pi/2     ln2 = log(2)
pi23= 2pi/3    lg2 = log10(2)
pi43= 4pi/3    lge = log10(e)
pi2 = pi**2    ln10= ln(10)

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
)
Return

CleanUp:
GoSub SaveHistory
ExitApp

Eval(x) {
Static B:="1234567890123456789012345678901234567890123456789012345678901234"    ; 64 chars
,H:="0x1234567890123456",N:="0x1234567890123456"                                ; allocate
,D:="1234567890123456789012345678901234567890123456789012345678901234"          ; ..memory
Loop Parse, x, `;
y := __expr(A_LoopField)        ; evaluate each term separated by ";", keep last result
DllCall("msvcrt\sprintf", "Str",D, "Str","%.6g  /  %.17g","double",y,"double",y)
DllCall("msvcrt\sprintf", "Str",H, "Str","0x%I64X","Int64",y+0) ;+0 to handle large floats
DllCall("msvcrt\sprintf", "Str",N, "Str","0x%I64X","Int64",-y)              ; negative hex
B := ToBin(StrLen(H)<18 ? H : SubStr(H,1,17)*16+abs("0x" SubStr(H,0))) ; force wrap around
Return y "`n" D "`n" H "  /  -" N "`n" B
}

ToBin(n) { ; Binary form of n. 1st bit = SIGN, to be extended left: -8:1000; -1:1; 0:0; 8:01000
m := -(n<0)
Loop  {
IfEqual m,%n%, Return -m . b
b := n&1 . b, n >>= 1
}
}

f2c(f) {        ; Fahrenheit -> Centigrade
Return (f-32)*0.55555555555555556
}

c2f(c) {        ; Centigrade -> Fahrenheit
Return c*1.8+32
}

GCD(a,b) {      ; Euclidean GCD
Return b=0 ? Abs(a) : GCD(b, mod(a,b))
}

Choose(n,k) {   ; Binomial coefficient. "n.0" force floating point arithmetic
p := 1, i := 0, k := k < n-k ? k : n-k
Loop %k%
p *= n-i, p //= ++i
Return p
}

Fib(n) {        ; n-th Fibonacci number (n < 0 OK, iterative to avoid globals)
a := 0, b := 1
Loop % abs(n)-1
c := b, b += a, a := c
Return n=0 ? 0 : n>0 || n&1 ? b : -b
}

fac(n) {        ; n!
Return n<2 ? 1 : n*fac(n-1)
}

sign(x) {       ; the sign of x (-1,0,+1)
Return (x>0) - (x<0)
}

fcmp(x,y,tol) { ; floating point comparison with tolerance
Static f
If (f = "") {
VarSetCapacity(f,162)
Loop 324
NumPut("0x"
. SubStr("558bec83e4f883ec148b5510538b5d0c85db568b7508578b7d148974241889542410b9000000807f"
. "137c0485f6730d33c02bc68bd91b5d0c89442418837d14007f137c0485d2730d33c02bc28bf91b7d1489442410"
. "8b7424182b7424108b45188bcb1bcff7d8993bd17f187c043bc677128b4518993bca7f0a7c043bf0770433c0eb"
. "183bdf7f117c0a8b44241039442418730583c8ffeb0333c0405f5e5b8be55dc3"
, 2*A_Index-1,2), f, A_Index-1, "Char")
}
Return DllCall(&f, "double",x, "double",y, "Int",tol, "CDECL Int")
}

cbrt(x) {       ; signed cubic root
Static e := 0.333333333333333333
Return x < 0 ? -(-x)**e : x**e
}

quadratic(ByRef x1, ByRef x2, a,b,c) { ; -> #real roots {x1,x2} of ax**2+bx+c
i := fcmp(b*b,4*a*c,63) ; 6 LS bit tolerance
If (i = -1) {
x1 := x2 := ""
Return 0
}
If (i = 0) {
x1 := x2 := -b/2/a
Return 1
}
d := sqrt(b*b - 4*a*c)
x1 := (-b-d)/2/a
x2 := x1 + d/a
Return 2
}
cubic(ByRef x1, ByRef x2, ByRef x3, a,b,c,d) { ; -> #real roots {x1,x2,x3} of ax**3+bx**2+cx+d
Static pi23:=2.094395102393195492, pi43:=4.188790204786390985
x := -b/3/a                                 ; Nickalls method
y := ((a*x+b)*x+c)*x+d
E2:= (b*b-3*a*c)/9/a/a
H2:= 4*a*a*E2*E2*E2

i := fcmp(y*y,H2, 63)

If (i = 1) { ; 1 real root
q := sqrt(y*y-H2)
x1 := x + cbrt((-y+q)/2/a) + cbrt((-y-q)/2/a)
x2 := x3 := ""
Return 1
}
If (i = 0) { ; 3 real roots (1 or 2 different)
If (fcmp(H2,0, 63) = 0) { ; root1 is triple...
x1 := x2 := x3 := x
Return 1
} ; h <> 0                : root2 is double...
E := cbrt(y/2/a) ; with correct sign
x1 := x - E - E
x2 := x3 := x + E
Return 2
} ; i = -1   : 3 real roots (different)...
t := acos(-y/sqrt(H2)) / 3
E := 2*sqrt(E2)
x1 := x + E*cos(t)
x2 := x + E*cos(t+pi23)
x3 := x + E*cos(t+pi43)
Return 3
}

min(x,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="") { ; min of 10 numbers or less
Loop 9
If (x%A_Index% <> "" && x > x%A_Index%) ; ignore blanks
x:= x%A_Index%
Return x
}
max(x,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="") { ; max of 10 numbers or less
Loop 9
If (x < x%A_Index%)
x:= x%A_Index%
Return x
}

norm(k, x,x1="",x2="",x3="",x4="",x5="",x6="",x7="",x8="",x9="") { ; k-th power mean
x := x**k
Loop 9 {
IfEqual x%A_Index%,, break ; stop at omitted/blank parameter
x += x%A_Index% ** k
n := A_Index
}
Return (x/(n+1))**(1/k)
}

cot(x) {        ; cotangent
Return 1/tan(x)
}
acot(x) {       ; inverse cotangent
Return 1.57079632679489662 - atan(x)
}
atan2(x,y) {    ; 4-quadrant atan
Return dllcall("msvcrt\atan2","Double",y, "Double",x, "CDECL Double")
}

sinh(x) {       ; hyperbolic sine
Return dllcall("msvcrt\sinh", "Double",x, "CDECL Double")
}
cosh(x) {       ; hyperbolic cosine
Return dllcall("msvcrt\cosh", "Double",x, "CDECL Double")
}
tanh(x) {       ; hyperbolic tangent
Return dllcall("msvcrt\tanh", "Double",x, "CDECL Double")
}
coth(x) {       ; hyperbolic cotangent
Return 1/dllcall("msvcrt\tanh", "Double",x, "CDECL Double")
}

asinh(x) {      ; inverse hyperbolic sine
Return ln(x + sqrt(x*x+1))
}
acosh(x) {      ; inverse hyperbolic cosine
Return ln(x + sqrt(x*x-1))
}
atanh(x) {      ; inverse hyperbolic tangent
Return 0.5*ln((1+x)/(1-x))
}
acoth(x) {      ; inverse hyperbolic cotangent
Return 0.5*ln((x+1)/(x-1))
}

ldexp(x,e) {    ; load exponent -> x * 2**e
Return dllcall("msvcrt\ldexp","Double",x, "Int",e, "CDECL Double")
}

frexp(x, ByRef e) { ; -> scaled x to [0.5,1) or 0; e <- exp: x = frexp(x) * 2**e
Return dllcall("msvcrt\frexp","Double",x, "Int*",e, "CDECL Double")
}

;------------------------------------------------------------------------------------
; lexikos' lowlevel functions (http://www.autohotkey.com/forum/viewtopic.php?t=26300)
;------------------------------------------------------------------------------------
__init() {
__getFirstFunc()
__mcode("__getVar","8B4C24088B0933C08379080375028B018B4C2404998901895104C3")
__mcode("__static","8B4424088B008378080375068B0080480D04C3")
}

__mcode(FuncName, Hex) {
if !(pFunc := __getFuncUDF(FuncName)) or !(pbin := DllCall("GlobalAlloc","uint",0,"uint",StrLen(Hex)//2))
return 0
Loop % StrLen(Hex)//2
NumPut("0x" . SubStr(Hex,2*A_Index-1,2), pbin-1, A_Index, "char")
NumPut(pbin,pFunc+4), NumPut(1,pFunc+49,0,"char")
return pbin
}

__static(var) {
}

__getVar(var) {
}

__getGlobalVar(__gGV_sVarName) {
global
return __getVar(%__gGV_sVarName%)
}

__getBuiltInVar(sVarName) {
static pDerefs, DerefCount
if !pDerefs ;= label->mJumpToLine->mArg[0]->deref
{
pDerefs := NumGet(NumGet(NumGet(__findLabel("__gBIV_marker"),4),4),8)
Loop
if ! NumGet(pDerefs+(A_Index-1)*12) {
DerefCount := A_Index-1
break
}
}
low := 0
high := DerefCount - 1
Loop {
if (low > high)
break
mid := (low+high)//2
i := DllCall("shlwapi\StrCmpNIA","uint",NumGet(pDerefs+mid*12),"str",sVarName,"int",NumGet(pDerefs+mid*12,10,"ushort"))
if i > 0
high := mid - 1
else if i < 0
low := mid + 1
else
return NumGet(pDerefs+mid*12,4)
}
return 0
__gBIV_marker:
return % 0,
( Join`s
A_AhkPath A_AhkVersion A_AppData A_AppDataCommon A_AutoTrim A_BatchLines
A_CaretX A_CaretY A_ComputerName A_ControlDelay A_Cursor A_DD A_DDD A_DDDD
A_DefaultMouseSpeed A_Desktop A_DesktopCommon A_DetectHiddenText
A_DetectHiddenWindows A_EndChar A_EventInfo A_ExitReason A_FormatFloat
A_FormatInteger A_Gui A_GuiControl A_GuiControlEvent A_GuiEvent A_GuiHeight
A_GuiWidth A_GuiX A_GuiY A_Hour A_IconFile A_IconHidden A_IconNumber A_IconTip
A_IsSuspended A_KeyDelay A_Language A_LastError A_LineFile A_LineNumber
A_LoopField A_LoopFileAttrib A_LoopFileDir A_LoopFileExt A_LoopFileFullPath
A_LoopFileLongPath A_LoopFileName A_LoopFileShortName A_LoopFileShortPath
A_LoopFileSize A_LoopFileSizeKb A_LoopFileSizeMb A_LoopFileTimeAccessed
A_LoopFileTimeCreated A_LoopFileTimeModified A_LoopReadLine A_MDay A_Min A_MM
A_MMM A_MMMM A_Mon A_MouseDelay A_MSec A_MyDocuments A_Now A_NowUtc
A_NumBatchLines A_OSType A_OSVersion A_PriorHotkey A_ProgramFiles A_Programs
A_ProgramsCommon A_ScreenHeight A_ScreenWidth A_ScriptDir A_ScriptFullPath
A_StartupCommon A_StringCaseSense A_Tab A_Temp A_ThisFunc A_ThisHotkey
A_TimeIdlePhysical A_TimeSincePriorHotkey A_TimeSinceThisHotkey
A_TitleMatchMode A_TitleMatchModeSpeed A_UserName A_WDay A_WinDelay A_WinDir
A_WorkingDir A_YDay A_Year A_YWeek A_YYYY Clipboard ClipboardAll ComSpec false
ProgramFiles true
)
}
__getVarInContext(sVarName, pScopeFunc=0) {
static pThisFunc
if pVar:=__getBuiltInVar(sVarName)
return pVar
if !pScopeFunc
return __getGlobalVar(sVarName)
if !pThisFunc && !(pThisFunc := __getFuncUDF(A_ThisFunc))
return
NumPut(NumGet(pScopeFunc+48,0,"char"),pThisFunc+48,0,"char")
VarSetCapacity(ThisFuncProps, 20)
, DllCall("RtlMoveMemory","uint",&ThisFuncProps,"uint",pThisFunc+20,"uint",20)
, DllCall("RtlMoveMemory","uint",pThisFunc+20,"uint",pScopeFunc+20,"uint",20)
return pVar
}
__listFuncs(UserFunctionsOnly=False) {
this_file_index := NumGet(NumGet(__getFuncUDF(A_ThisFunc)+4),2,"ushort")
if !(pFunc := __getFirstFunc())
return
Loop {
if !UserFunctionsOnly || !NumGet(pFunc+49,0,"char") && NumGet(NumGet(pFunc+4),2,"ushort") != this_file_index
list .= __str(NumGet(pFunc+0)) "`n"
if !(pFunc := NumGet(pFunc+44)) ; pFunc->mNextFunc
break
}
return SubStr(list,1,-1)
}
__findFunc(FuncName, BuiltIn="") {
if ((pFunc:=__getFuncUDF(FuncName)) && !BuiltIn)
return pFunc
if !(pFunc:=__getFirstFunc())
return 0
Loop {  ; Note: ! is used here to ensure both values are true boolean 1 or 0.
if (BuiltIn = "" or (!NumGet(pFunc+49,0,"uchar") = !BuiltIn))
if (__str(NumGet(pFunc+0)) = FuncName) ; pFunc->mName
return pFunc
if !(pFunc := NumGet(pFunc+44)) ; pFunc->mNextFunc
break
}
return 0
}
__getFirstFunc() {
static pFirstFunc
if !pFirstFunc {
if !(pLine := __getFirstLine())
return 0
Loop {
Loop % NumGet(pLine+1,0,"uchar") { ; pLine->mArgc
pArg := NumGet(pLine+4) + (A_Index-1)*12  ; pLine->mArg[A_Index-1]
if (NumGet(pArg+0,0,"uchar") != 0) ; pArg->type != ARG_TYPE_NORMAL
continue ; arg has no derefs (only a Var*)
Loop {
pDeref := NumGet(pArg+8) + (A_Index-1)*12  ; pArg->deref[A_Index-1]
if (!NumGet(pDeref+0)) ; pDeref->marker (NULL terminates list)
break
if (NumGet(pDeref+8,0,"uchar")) ; pDeref->is_function
{
pFunc := NumGet(pDeref+4)
if (NumGet(pFunc+49,0,"uchar")) { ; pFunc->mIsBuiltIn
if !pFirstBIF
pFirstBIF := pFunc
} else { ; UDF
pFuncLine := NumGet(pFunc+4)
FuncLine := NumGet(pFuncLine+8)
FuncFile := NumGet(pFuncLine+2,0,"ushort")
if !pFirstFunc or (FuncFile < FirstFuncFile || (FuncFile = FirstFuncFile && FuncLine < FirstFuncLine))
pFirstFunc:=pFunc, FirstFuncLine:=FuncLine, FirstFuncFile:=FuncFile
}
}
}
}
if !(pLine:=NumGet(pLine+20)) ; pLine->mNextLine
break
}
if pFirstBIF
{
if pFirstFunc
{   ; Look for the BIF using the UDF as a starting point.
pFunc := pFirstFunc
Loop {
if !(pFunc := NumGet(pFunc+44)) ; pFunc->mNextFunc
break
; If the BIF is found, the UDF must precede it in the list.
if (pFunc = pFirstBIF)
return pFirstFunc
}
}
pFirstFunc := pFirstBIF
return pFirstFunc
}
}
return pFirstFunc
}
__listLabels(UserLabelsOnly=False) {
this_file_index := NumGet(NumGet(__getFuncUDF(A_ThisFunc)+4),2,"ushort")
if pLabel := __getFirstLabel()
Loop {
if !UserLabelsOnly || NumGet(NumGet(pLabel+4),2,"ushort") != this_file_index
list .= __str(NumGet(pLabel+0)) "`n"
if ! pLabel := NumGet(pLabel+12)
break
}
return SubStr(list,1,-1)
}
__findLabel(sLabel) {
if pLabel := __getFirstLabel()
Loop {
if __str(NumGet(pLabel+0)) = sLabel
return pLabel
if ! pLabel := NumGet(pLabel+12)
return 0
}
}
__getFirstLabel() {
static pFirstLabel
if !pFirstLabel {
if !(pLine := NumGet(__getFuncUDF(A_ThisFunc)+4))
return 0
Loop {
act := NumGet(pLine+0,0,"char")
if (act = 96 || act = 95) ; ACT_GOSUB || ACT_GOTO
break
if !(pLine:=NumGet(pLine+20)) ; pLine->mNextLine
return 0
}
pFirstLabel := NumGet(pLine+24)
Loop {
if ! pPrevLabel:=NumGet(pFirstLabel+8)
break
pFirstLabel := pPrevLabel
}
}
return pFirstLabel
__getFirstLabel_HookLabel:
goto __getFirstLabel_HookLabel
}
__getFirstLine() {
static pFirstLine
if (pFirstLine = "") {
if pThisFunc := __getFuncUDF(A_ThisFunc) {
if pFirstLine := NumGet(pThisFunc+4) ; mJumpToLine
Loop {
if !(pLine:=NumGet(pFirstLine+16)) ; mPrevLine
break
pFirstLine := pLine
}
}
}
return pFirstLine
}
__getFuncUDF(FuncName) {
if pCb := RegisterCallback(FuncName) {
func := NumGet(pCb+28)
DllCall("GlobalFree","uint",pCb)
}
return func
}
if len<0
return str
}
__expr(expr, pScopeFunc=0) {
static pFunc, pThisFunc
if !pFunc
pFunc:=__getFuncUDF("__expr_sub"), pThisFunc:=__getFuncUDF(A_ThisFunc)

if ! pArg:=__MakeExpressionArg(expr, pScopeFunc)
return

nInst := NumGet(pThisFunc+40)
VarSetCapacity(Line%nInst%,32,0), __static(Line%nInst%), pLine:=&Line%nInst%

NumPut(pArg,NumPut(1,NumPut(102,pLine+0,0,"char"),0,"char"),2), NumPut(pLine,NumPut(pLine,pLine+16))
NumPut(pLine,pFunc+4)
, ret := __expr_sub()
, NumPut(0,pLine+1,0,"char"), DllCall("GlobalFree","uint",pArg)
return ret
}
__expr_sub() {
global
}
__MakeExpressionArg(expr, pScopeFunc=0) {
static OPERAND_TERMINATORS = "<>=/|^,:"" `t*&~!()+-"
i = 0
open_parens = 0
Loop {
if (c:=SubStr(expr,++i,1)) = ""
break
if c = "
{
if ! i:=InStr(expr,"""",1,i+1)
return 0, ErrorLevel:="Missing close-quote ("")"
}
else if c = '
{
if ! j:=InStr(expr,"'",1,i+1)
return 0, ErrorLevel:="Missing close-quote (')"
literal := SubStr(expr,i+1,j-i-1)
StringReplace, literal, literal, ", "", UseErrorLevel
expr := SubStr(expr,1,i-1) . """" . literal . """" . SubStr(expr,j+1)
i := j + ErrorLevel
}
else if c = (
open_parens += 1
else if c = )
open_parens -= 1
}
if open_parens > 0
return 0, ErrorLevel:="Missing "")"""
if open_parens < 0
return 0, ErrorLevel:="Missing ""("""
num_derefs = 0
pos = 1
Loop {
if ! pos:=RegExMatch(expr, """.*?""|[\w#@\$?\[\]\.%]+", word, pos)
break
marker := pos-1
pos += StrLen(word)
if SubStr(word,1,1)="""" ; skip quoted literal strings
continue
if InStr(word,".") or word+0!="" ; number or error
continue
if word = ?
continue
param_count = 0
if is_func:=SubStr(expr,pos,1)="("
{
if InStr(word, "%")
return 0, Errorlevel:="Dynamic function calls are not supported."
if ! var_or_func:=__findFunc(word)
return 0, ErrorLevel:="Call to nonexistent function """ word """."
i := pos
open_parens = 1
Loop {
if (c:=SubStr(expr,++i,1))=""
break
if (param_count=0 && !(c=" "||c="`t"||c=")"))
param_count = 1
if c = ,
param_count += open_parens=1 ? 1 : 0
else if c = (
open_parens += 1
else if c = )
if --open_parens = 0
break
}
if (param_count < NumGet(var_or_func+16))
return 0, ErrorLevel:="Too few parameters passed to function """ word """."
if (param_count > NumGet(var_or_func+12))
return 0, ErrorLevel:="Too many parameters passed to function """ word """."
}
else
{
if i:=InStr(full_word:=word, "%") {
original_marker := marker
Loop {
if !(j:=InStr(full_word, "%", 1, i+1)) or j-i<2
return 0, ErrorLevel:="Invalid deref """ full_word """."
word_len := StrLen( word := SubStr(full_word, i, j-i+1) )
marker := original_marker + i-1
if var_or_func:=__getVarInContext(SubStr(word,2,-1), pScopeFunc)
if ! i:=InStr(full_word, "%", 1, j+1)
break
}
continue
}
var_or_func := __getVarInContext(word, pScopeFunc)
}

if var_or_func
{
word_len := StrLen(word)
}
}
pArg := DllCall("GlobalAlloc","uint",0x40,"uint",25+StrLen(expr)+num_derefs*12)
DllCall("lstrcpy","uint",pArg+12,"str",expr)
NumPut(pDerefs:=pArg+13+StrLen(expr),NumPut(pArg+12,NumPut(StrLen(expr),NumPut(1,NumPut(0,pArg+0,0,"char"),0,"char"),0,"short")))
Loop, %num_derefs% {
DllCall("RtlMoveMemory","uint",pDeref:=pDerefs+A_Index*12-12,"uint",&deref%A_Index%,"uint",12)
NumPut(pArg+12+NumGet(pDeref+0),pDeref+0) ; deref.marker += base_address_of_text
}
return pArg, ErrorLevel=0
num_derefs += 1
VarSetCapacity(deref%num_derefs%,12)
NumPut(word_len,NumPut(param_count,NumPut(is_func,NumPut(var_or_func,NumPut(marker,deref%num_derefs%)),0,"char"),0,"char"),0,"short")
return
}```

• Members
• 96 posts
• Last active: Jul 21 2010 08:33 PM
• Joined: 16 Jul 2005
8) Excellent. I won't be needing Windows Calc anymore.
In fact, some of the calculations that I've been using Excel for can be replaced with this.
Very useful. Thanks.

I should have taken a closer look at your last post. I posted some metric conversion functions, but you already beat me to it.
“yields falsehood when preceded by its quotation” yields falsehood when preceded by its quotation.

• Moderators
• 4713 posts
• Last active: Mar 31 2012 03:17 AM
• Joined: 14 Feb 2005
I updated the ehanced calculator to version 1.2. The changes are:

- added 6-digit decimal display
- 99 last expressions in history
- Win-C activates/shows the calculator window
- Alt-H: Help (list of shortcuts, functions)
- Alt-E: edit the expressions history
- Alt-S: save, Alt-L: load history
- Custom tray menu items, icon

• Moderators
• 4713 posts
• Last active: Mar 31 2012 03:17 AM
• Joined: 14 Feb 2005
The enhanced version is updated to v.1.3. The changes:

- New tray menu item: Show (the calculator window)
- Tray double click: activate calculator
- Added few more constants
- Help now lists constants, too
- Functions f2c, c2f: Fahrenheit <-> Centigrade conversion (borrowed from Dragonscloud)

• Members
• 96 posts
• Last active: Jul 21 2010 08:33 PM
• Joined: 16 Jul 2005
I had not realized that AHK supports the equivalent of ParamArray from VB functions.

Here's a max function that accepts an arbitray number of values. If you want to assign the values to a variable, simply enclose the string in quotes.

x:="1 11 99999.9 87 2 21.3";max(x)
Returns 99999.9

For ease of typing, I'm using the space character as a delimiter.

Code:
```max(ParamArray) {
Loop, Parse, ParamArray, %A_Space%
if (p < A_LoopField)
p:= A_LoopField
Return p
}```

A few more functions:
```sum(ParamArray){ ; Returns sum of ParamArray.
i= 0
s=0
Loop, Parse, ParamArray, %A_Space%
{
s:=s + A_LoopField
i++
}
Return s
}

ave(ParamArray){ ; Returns average of Vals.
i= 0
Sum = 0
Loop, Parse, ParamArray, %A_Space%
{
Sum:=Sum + A_LoopField
i++
}
Return Sum / i
}

product(ParamArray){ ;Returns product of ParamArray.
Loop, Parse, ParamArray, %A_Space%
{
p:= A_Index =1 ? A_LoopField : p * A_LoopField
}
Return p
}
```

If I get the time, I'll try porting my VBA slope ,Y-intercept, standard deviation, and correlation coefficient UDFs to AHK.

For those who wonder why I reinvented the ball bearing: The former two I had to hand-code because apparently Office doesn't use the "Rise Over The Run" method to calculate the slope and that didn't work with our laboratory calculations. The latter two I coded because I needed the formulae for calculated controls on Access forms due to a clash with aggregate functions.
“yields falsehood when preceded by its quotation” yields falsehood when preceded by its quotation.

• Spam Officer
• 3219 posts
• Last active: Sep 20 2018 02:47 PM
• Joined: 19 Apr 2005
compute means calculate,
I run computer with 130W and never used this MS calc.exe
thank you for the program, very useful
how can I calculate ( now it's RADIAN ) :
DEGREE sin(45)=0.7071067811865475244 ?

• Moderators
• 4713 posts
• Last active: Mar 31 2012 03:17 AM
• Joined: 14 Feb 2005
@Dragonscloud: Almost 3 years ago I posted the min and max functions for variable number of arguments: here. They work, but their use is less intuitive: min("1 -3") is -3, but one could think the result would be -2 = 1 - 3, so commas are better separators. Also, using variables is cumbersome: min("1" . "," . x . "," . y) was needed instead of min(1,x,y). In a calculator I never needed more than 3 arguments.

If you want to do statistics, it is a different story. We have to think about the easiest syntax. Maybe building an AHK array, X, with X0 = number of entries, X1, X2... the entries. One could define array functions, like attach(X,10), to attach a new entry 10 to the array X, and mean(X), std(X), moment(k,X), sum(X), prod(X), powersum(k,X) could handle the arrays. The functions min(X) and max(X) could work on arrays, when they are called with only one argument. If you use statistics often, some shortcuts could be introduced, like X # 10 instead of attach(X,10). For more complex stats we probably need an optional include module.

@garry: degree * pi/180 makes it radian, radian *180/pi makes it degrees. In the next version I will add two more constants: rad = pi/180, deg = 180/pi, so you can write 45*rad or pi/6*deg.

• Spam Officer
• 3219 posts
• Last active: Sep 20 2018 02:47 PM
• Joined: 19 Apr 2005
thank you very much Laszlo, wishing nice weekend

• Moderators
• 4512 posts
• Last active: May 20 2019 07:41 AM
• Joined: 24 May 2006
This is all nice, but I wait for lex to create dynamic code methods.

Then calc can have option for defining custom functions and that will rock.
It can be done now though using Plugin framework but script needs restart.

• Members
• 96 posts
• Last active: Jul 21 2010 08:33 PM
• Joined: 16 Jul 2005

@Dragonscloud: Almost 3 years ago I posted the min and max functions for variable number of arguments: here. They work, but their use is less intuitive: min("1 -3") is -3, but one could think the result would be -2 = 1 - 3, so commas are better separators. Also, using variables is cumbersome: min("1" . "," . x . "," . y) was needed instead of min(1,x,y). In a calculator I never needed more than 3 arguments.

Personally, I find it easier and more intuitive to type max("11.2 -3 9 7 13.4") than max("11.2","-3", "9.7", "13.4"). I can see your point, however, in how certain applications of the more typist-friendly approach might not look right to someone who has a background in mathematics (as you demonstrated).

I just realized another flaw in my approach. It works fine with a simple list of numbers inside the parentheses or with a named variable, but if someone wanted to mix and match variables and numbers inside the argument the function breaks. There's probably a way to parse the ParamArray to differentiate between named variables and straight numbers, but I don't see it as really being worth the trouble. Then again, I might work on it simply as a learning exercise.

If you want to do statistics, it is a different story. We have to think about the easiest syntax. Maybe building an AHK array, X, with X0 = number of entries, X1, X2... the entries. One could define array functions, like attach(X,10), to attach a new entry 10 to the array X, and mean(X), std(X), moment(k,X), sum(X), prod(X), powersum(k,X) could handle the arrays. The functions min(X) and max(X) could work on arrays, when they are called with only one argument. If you use statistics often, some shortcuts could be introduced, like X # 10 instead of attach(X,10). For more complex stats we probably need an optional include module.

The syntax I used in VBA was simply " StdDev(ParamArray a()) ".

StdDev(x1,x2,x3,x4,y1,y2,y3,y4)

Might not be as practical in AHK.
“yields falsehood when preceded by its quotation” yields falsehood when preceded by its quotation.

• Moderators
• 4713 posts
• Last active: Mar 31 2012 03:17 AM
• Joined: 14 Feb 2005

I wait for lex to create dynamic code methods. Then calc can have option for defining custom functions and that will rock.

It has been done in Monster, without restart. You just save the string form of the function body and re-evaluate it at each call, doing parameter passing as assignments. It is slow and has some ugly side effects, so it would truly be better if we could dynamically define and un-define functions. Hope, we will live long enough to see that happen.