Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Popup calculator / expression evaluator


  • Please log in to reply
32 replies to this topic
Laszlo
  • 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.

Posted Image
The main calculator window


Posted Image
The help window


Posted Image
Function graph with captured coordinates


Posted Image
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
   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
[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


engunneer
  • 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

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?

engunneer
  • 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.

Laszlo
  • 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
c2f© : 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
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"
GoSub LoadHistory
OnExit CleanUp

Menu Tray, NoStandard                          ; custom tray menu
Menu Tray, Add, Show, #c
Menu Tray, Add, Help, ButtonHelp
Menu Tray, Add, Load History, ButtonLoad
Menu Tray, Add, Save History, ButtonSave
Menu Tray, Add, Exit, CleanUp
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
   FileRead history, %File%
   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

ButtonLoad:                                    ; Alt-L
   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
   Alt-L: Load 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_Index A_IPAddress1 A_IPAddress2 A_IPAddress3 A_IPAddress4 A_IsAdmin
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_ScriptName A_Sec A_Space A_StartMenu A_StartMenuCommon A_Startup
A_StartupCommon A_StringCaseSense A_Tab A_Temp A_ThisFunc A_ThisHotkey
A_ThisLabel A_ThisMenu A_ThisMenuItem A_ThisMenuItemPos A_TickCount A_TimeIdle
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
}
__str(addr,len=-1) {
    if len<0
        return DllCall("MulDiv","uint",addr,"int",1,"int",1,"str")
    VarSetCapacity(str,len), DllCall("lstrcpyn","str",str,"uint",addr,"int",len+1)
    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)
                        gosub __MakeExpressionArg_AddDeref
                    if ! i:=InStr(full_word, "%", 1, j+1)
                        break
                }
                continue
            }
            var_or_func := __getVarInContext(word, pScopeFunc)
        }

        if var_or_func
        {
            word_len := StrLen(word)
            gosub __MakeExpressionArg_AddDeref
        }
    }
    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
    __MakeExpressionArg_AddDeref:
        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
}


Dragonscloud
  • 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.

Laszlo
  • 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

Laszlo
  • 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)

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.

garry
  • 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 ?

Laszlo
  • 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.

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

majkinetor
  • 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.
Posted Image

Dragonscloud
  • 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.

Laszlo
  • 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.