jeeswg's mathematics tutorial

Helpful script writing tricks and HowTo's
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

jeeswg's mathematics tutorial

14 May 2019, 15:52

==================================================

jeeswg's mathematics tutorial

==================================================

> CONTENTS

> INTRO: MATHEMATICAL OPERATORS
> INTRO: MATHEMATICAL FUNCTIONS

> BASICS: FOUR OPERATIONS
> BASICS: INCREMENT / DECREMENT
> BASICS: TRUE DIVIDE/FLOOR DIVIDE/INTEGER DIVIDE
> BASICS: EXPONENTS: POW
> BASICS: EXPONENTS (INDICES/POWERS/ORDERS)
> BASICS: EXPONENTS: RECIPROCALS
> BASICS: EXPONENTS: ROOTS
> BASICS: EXPONENTS: EXP

> BASICS: OPERATORS: UNARY / BINARY / TERNARY
> BASICS: ORDER OF OPERATIONS / BODMAS / PEMDAS

> BASICS: MULTILINE CALCULATIONS
> BASICS: CONCATENATION
> BASICS: ABS
> BASICS: SIGN (POSITIVE/ZERO/NEGATIVE)
> BASICS: FLOOR / ROUND / CEIL / INTEGER
> BASICS: ROUND TO THE NEAREST MULTIPLE OF N
> BASICS: ROUND UP BY ROUNDING DOWN / ROUND BY ROUNDING DOWN

> BASICS: COMPARISON
> BASICS: COMPARISON (BETWEEN)
> BASICS: COMPARING FLOATING-POINT NUMBERS (WARNING)

> BASICS: LIST NUMBERS (NUMBER SEQUENCES)
> BASICS: MINIMUM / MAXIMUM
> BASICS: COUNT / SUM (TOTAL) / PRODUCT / MEAN AVERAGE / MINIMUM / MAXIMUM / RANGE
> BASICS: FREQUENCY COUNT / FREQUENCY TABLE / MODE
> BASICS: FREQUENCY TABLE TO LIST
> BASICS: MEDIAN

> BASICS: HIGHEST COMMON FACTOR (GREATEST COMMON DIVISOR) (HCF) (GCD)
> BASICS: LOWEST COMMON MULTIPLE (LEAST COMMON MULTIPLE) (LCM)

> BASICS: SORT (ASCENDING / DESCENDING / RANDOM)

> BASICS: TOGGLE
> BASICS: CYCLE

> BASICS: IF (NON-ZERO) (NON-BLANK)
> BASICS: IF / ELSE IF / ELSE (LOGIC)
> BASICS: TERNARY OPERATOR
> BASICS: TERNARY OPERATOR (LOGIC)
> BASICS: SWITCH STATEMENT (CASE) (SELECT)

> BASICS: EXPAND/CONTRACT A LIST OF NUMBERS

> BASICS: LOGICAL OPERATORS: ! && || (NOT/AND/OR)
> BASICS: SHORT-CIRCUIT BOOLEAN EVALUATION

> BASICS: MODULO (MOD AND FLOORMOD)
> BASICS: MODULO (EXAMPLES)
> BASICS: MODULO (CAESAR CIPHER)

> BASICS: RANGE FUNCTION (EXCEL VBA FOR LOOP)

> BASICS: RANDOM
> BASICS: RANDOM-LOOKING LISTS (USING PRIMITIVE ROOTS OF PRIME NUMBERS)
> BASICS: AUTOHOTKEY LIMITS

> BASICS: ASSIGNMENT OPERATORS

> BASICS: TRIGONOMETRY

> BASICS: LOGARITHMS

> BASICS: 1-BASED INDEXES/0-BASED INDEXES

> BASICS: GENERAL TRICKS
> BASICS: GENERAL WARNINGS
> BASICS: AHK V1 ODDITIES

> BITWISE: SIGNED INTEGERS / UNSIGNED INTEGERS
> BITWISE: DLLCALL PARAMETER TYPES
> BITWISE: NUMGET VERSUS NUMPUT
> BITWISE: TWO'S COMPLEMENT
> BITWISE: ~ (NOT)
> BITWISE: ! VERSUS ~
> BITWISE: & | ^ (AND/OR/XOR)
> BITWISE: WINDOW STYLES
> BITWISE: & VERSUS &&, | VERSUS ||
> BITWISE: << >> (BITSHIFT LEFT / BITSHIFT RIGHT)
> BITWISE: NUMGET/NUMPUT AND ENDIANNESS
> BITWISE: UNICODE (UTF-16 LITTLE ENDIAN)
> BITWISE: BINARY DATA VERSUS VARIABLES (GET BYTES)
> BITWISE: NUMGET/NUMPUT AND READING FROM STRUCTS
> BITWISE: BINARY STRUCTURES AND ALIGNMENT (OFFSETS)
> BITWISE: IDENTIFY A FILE TYPE
> BITWISE: BITSHIFT AND NEGATIVE NUMBERS
> BITWISE: HIBYTE/LOBYTE TO/FROM WORD
> BITWISE: HIWORD/LOWORD TO/FROM DWORD
> BITWISE: RGB / BGR

> PRESENTATION: FORMAT FUNCTION
> PRESENTATION: STRING TO NUMBER
> PRESENTATION: IS NUMBER (LOOKS NUMERIC)
> PRESENTATION: TYPES (INTEGER/FLOAT)
> PRESENTATION: BASES: HEXADECIMAL (DEC2HEX) (HEX2DEC)
> PRESENTATION: BASES: BINARY (DEC2BIN) (BIN2DEC)
> PRESENTATION: SCIENTIFIC NOTATION
> PRESENTATION: 0.1 + 0.2 = 0.3
> PRESENTATION: FLOATING POINT / FLOAT / DOUBLE
> PRESENTATION: ROUND TO N DECIMAL PLACES
> PRESENTATION: LEADING/TRAILING ZEROS (INTEGERS)
> PRESENTATION: TRUNCATION
> PRESENTATION: LEADING/TRAILING ZEROS (INTEGER PART AND FRACTIONAL PART)
> PRESENTATION: THOUSANDS SEPARATORS (COMMAS/DOTS)
> PRESENTATION: CONVERT 2-DIGIT YEAR TO 4-DIGIT YEAR
> PRESENTATION: ORDINAL INDICATORS (1ST/2ND/3RD/4TH)
> PRESENTATION: ROMAN NUMERALS

> FURTHER: UNICODE: UTF-8
> FURTHER: UNICODE: UTF-16 LITTLE ENDIAN
> FURTHER: UNIT CONVERSION: BYTES
> FURTHER: UNIT CONVERSION: DISTANCE
> FURTHER: UNIT CONVERSION: TEMPERATURE
> FURTHER: UNIT CONVERSION: TIME
> FURTHER: UNIT CONVERSION: ANGLES
> FURTHER: DISTANCE BETWEEN 2 POINTS ON A SPHERE (HAVERSINE FORMULA)
> FURTHER: PLOT A GRAPH

> LINKS: DOCUMENTATION
> LINKS: AUDIO: MUSICAL NOTES (MIDI) (BEEP)
> LINKS: AUDIO: CREATE WAV FILE
> LINKS: GRAPHICS: RAINBOW COLOURS (ELECTROMAGNETIC SPECTRUM)
> LINKS: GRAPHICS: COLOURS AND TEMPERATURE
> LINKS: GRAPHICS: DISTANCE BETWEEN 2 COLOURS
> LINKS: GRAPHICS: ROTATE IMAGE BY SCALING/SHEARING
> LINKS: GRAPHICS: CREATE BMP FILE
> LINKS: GRAPHICS: SOLAR SYSTEM
> LINKS: GRAPHICS: RESIZE IMAGES (FIT WITHIN RECTANGLE, MAINTAIN RATIO)
> LINKS: CHECK IF WINDOWS/RECTANGLES OVERLAP
> LINKS: EVAL (DYNAMIC CODE)
> LINKS: NORMAL DISTRIBUTION
> LINKS: WXMAXIMA
> LINKS: CUSTOM FUNCTIONS
> LINKS: FURTHER

==================================================

> INTRO: MATHEMATICAL OPERATORS

- This tutorial will cover the following operators:

- Basic mathematical operators:
- add/subtract/multiply/divide: + - * /
- power (exponentiation): ** [note: Excel uses ^]

- Basic mathematical operators (further):
- increment/decrement: ++ --
- floor divide: //

- Comparison operators (relational operators):
- greater-than/less-than: > <
- greater-than-or-equal/less-than-or-equal: >= <=
- case-insensitive equal/case-sensitive equal: = ==
- not-equal: <> != !==

- Logical operators:
- not: ! NOT
- and: && AND
- or: || OR
- ternary operator: ? :, i.e. arg1 ? arg2 : arg3

- Bitwise operators:
- bitwise-not: ~
- bitwise-and: &
- bitwise-exclusive-or: ^
- bitwise-or: |
- bitshift left/bitshift right: << >>

- Assign operator:
- :=

- Assign operators (further):
- += -= *= /= //= |= &= ^= >>= <<=

- Multi-statement:
- , e.g. var1 := 123, var2++, var3 := MyFunc()
- 'Commas may be used to write multiple sub-expressions on a single line.'

- Also covered:
- is [if var is type]

- Some other operators not covered in this tutorial:
- new &(address) *(dereference) . ~= .=

- Links:
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#Operators
Variables and Expressions - Definition & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/Variables.htm#Operators

==================================================

> INTRO: MATHEMATICAL FUNCTIONS

- This tutorial will cover the following functions:
•General Math
◦Abs: Returns the absolute value of a number.
◦Ceil: Returns a number rounded up to the nearest integer.
◦Exp: Returns e raised to the Nth power.
◦Floor: Returns a number rounded down to the nearest integer.
◦Log: Returns the logarithm (base 10) of a number.
◦Ln: Returns the natural logarithm (base e) of a number.
◦Max [v1.1.27+]: Returns the highest value of one or more numbers.
◦Min [v1.1.27+]: Returns the lowest value of one or more numbers.
◦Mod: Returns the remainder of a division.
◦Round: Returns a number rounded to N decimal places.
◦Sqrt: Returns the square root of a number.

•Trigonometry
◦Sin: Returns the trigonometric sine of a number.
◦Cos: Returns the trigonometric cosine of a number.
◦Tan: Returns the trigonometric tangent of a number.
◦ASin: Returns the arcsine (the number whose sine is the specified number) in radians.
◦ACos: Returns the arccosine (the number whose cosine is the specified number) in radians.
◦ATan: Returns the arctangent (the number whose tangent is the specified number) in radians.
- This tutorial will also cover the following commands/functions:
- Format
- Random/RandomSeed
- Integer/Float

- This tutorial will not cover the SetFormat command.
- It is better to avoid this using it, and use the Format function instead.

- Links:
Math functions - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Math.htm
Functions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Functions.htm#Math
Format() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Format.htm
Random - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Random.htm

- Links (AHK v2):
Random - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/Random.htm
Integer - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/Integer.htm
Float - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/Float.htm

==================================================

> BASICS: FOUR OPERATIONS

- The four operations: add, subtract, multiply, divide. + - * /.

Code: Select all

var := 1 + 2
MsgBox, % var ;3

var := 1 - 2
MsgBox, % var ;-1

var := 1 * 2
MsgBox, % var ;2

var := 1 / 2
MsgBox, % var ;0.500000

var := 1 / 1
MsgBox, % var ;1.000000

;AHK v1: dividing by zero gives a blank string:
var := 1/0
MsgBox, % var ;(blank)

;AHK v2: dividing by zero causes an error:
var := 1/0
;MsgBox(var)
- By default, AutoHotkey expresses results to 6dp (6 decimal places).
- There are sections lower down (called 'PRESENTATION') that address changing a number's appearance/format.

==================================================

> BASICS: INCREMENT / DECREMENT

- Different methods of incrementing:

Code: Select all

var := 0
var++
MsgBox, % var ;1

var := 0
++var
MsgBox, % var ;1

var := 0
var += 5
MsgBox, % var ;5

var := 0
var -= -5
MsgBox, % var ;5

var := 0
var := var + 5
MsgBox, % var ;5

var := 0
vOffset := 5
var += vOffset
MsgBox, % var ;5
- Different methods of decrementing:

Code: Select all

var := 0
var--
MsgBox, % var ;-1

var := 0
--var
MsgBox, % var ;-1

var := 0
var -= 5
MsgBox, % var ;-5

var := 0
var += -5
MsgBox, % var ;-5

var := 0
var := var - 5
MsgBox, % var ;-5

var := 0
vOffset := 5 ;-5
var -= vOffset
MsgBox, % var
- Increment/decrement by 5, based on a sign variable:

Code: Select all

vNum := 0
vOffset := 5
vSign := 1
vNum += vSign * vOffset
MsgBox, % vNum ;5

vNum := 0
vOffset := 5
vSign := -1
vNum += vSign * vOffset
MsgBox, % vNum ;-5
- Increment/decrement by 5, based on a boolean (on/off) variable:

Code: Select all

vNum := 0
vOffset := 5
vDoDecrease := 1
vNum += 5*((-1)**vDoDecrease)
MsgBox, % vNum ;-5

vNum := 0
vOffset := 5
vDoDecrease := 0
vNum += 5*((-1)**vDoDecrease)
MsgBox, % vNum ;5
- Increment/decrement works on non-integers as well as integers:

Code: Select all

var := 2.5
var++
MsgBox, % var ;3.500000
- In AHK v1, increment/decrement works on a blank variable:

Code: Select all

var := ""
var++
MsgBox, % var ;1

;note: increment/decrement treats any string (that doesn't start with a numeric part) as 0:
var := "abc"
var++
MsgBox, % var ;1

;note: increment/decrement sees the number in strings that start with a numeric part, and ignores the rest:
var := "123abc"
var++
MsgBox, % var ;124
- For AHK v1/v2, a workaround to increment a number or a blank string:

Code: Select all

var := ""
var := Format("{:01}", var) + 1
MsgBox, % var ;1

var := 0
var := Format("{:01}", var) + 1
MsgBox, % var ;1

var := 0.123
var := Format("{:01}", var) + 1
MsgBox, % var ;1.123000
- Increment/decrement doesn't work on a non-existent object key:

Code: Select all

obj := {}
obj.key++
MsgBox, % obj.key ;(blank)
obj := ""

;some code to handle this:
obj := {}
if obj.HasKey("key")
	obj.key++
else
	obj.key := 1
MsgBox, % obj.key ;1
obj := ""

;a workaround using Format to increment/decrement a non-existent object key:
obj := {}
obj.key := Format("{:01}", obj.key) + 1
MsgBox, % obj.key ;1
obj := ""

;alternatively, we can change the default value for a key from a blank string, to 0
obj := {}
obj := {base:{__Get:Func("Abs").Bind(0)}}
obj.key++
MsgBox, % obj.key ;1
obj := ""
- Increment/decrement doesn't work on a blank object key:

Code: Select all

obj := {}
obj.key := ""
obj.key++
MsgBox, % obj.key ;(blank)
obj := ""

;some code to handle this:
obj := {}
if !(obj.key = "")
	obj.key++
else
	obj.key := 1
MsgBox, % obj.key ;1
obj := ""

;a workaround using Format to increment/decrement a blank object key:
obj := {}
obj.key := ""
obj.key := Format("{:01}", obj.key) + 1
MsgBox, % obj.key ;1
obj := ""

;alternatively, we can change the default value for a key from a blank string, to 0
obj := {}
obj := {base:{__Get:Func("Abs").Bind(0)}}
obj.key++
MsgBox, % obj.key ;1
obj := ""
- Comparing pre-increment (++var) to post-increment (var++), and likewise, pre-decrement (--var) to post-decrement (var--):

Code: Select all

;++var versus var++

var := 3
MsgBox, % "before: " var ;3
MsgBox, % "during: " var++ ;3
MsgBox, % "after: " var ;4

var := 3
MsgBox, % "before: " var ;3
MsgBox, % "during: " ++var ;4
MsgBox, % "after: " var ;4

;==============================

;--var versus var--:

var := 3
MsgBox, % "before: " var ;3
MsgBox, % "during: " var-- ;3
MsgBox, % "after: " var ;2

var := 3
MsgBox, % "before: " var ;3
MsgBox, % "during: " --var ;2
MsgBox, % "after: " var ;2

;==============================

;var2 incremented then var1 assigned
var1 := 0, var2 := 0
var1 := ++var2
MsgBox, % var1 " " var2 ;1 1

;var1 assigned then var2 incremented
var1 := 0, var2 := 0
var1 := var2++
MsgBox, % var1 " " var2 ;0 1
- Differences between increment techniques and storing the values:

Code: Select all

q:: ;test increment
var1 := var2 := var3 := var4 := 0

var5 := var1 + 1
var6 := var2 += 1
var7 := ++var3
var8 := var4++

MsgBox, % Format("{} {} {} {}", var5, var6, var7, var8) ;1 1 1 0

var1 := var2 := var3 := var4 := 0

var1 := var1 + 1
var2 += 1
++var3
var4++

MsgBox, % Format("{} {} {} {}", var1, var2, var3, var4) ;1 1 1 1
return
- A warning re. ++ and --.
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#Operators
Due to backward compatibility, the operators ++ and -- treat blank variables as zero, but only when they are alone on a line; for example, y:=1, ++x and MsgBox % ++x both produce a blank result when x is blank.
- Although note: in AHK v2, if you try to use ++ and -- on a blank variable, it fails.

==================================================

> BASICS: TRUE DIVIDE/FLOOR DIVIDE/INTEGER DIVIDE

- Comparing true/floor/integer division, via pseudocode:

Code: Select all

True divide (normal division):
5 / 2 = 2.5
-5 / 2 = -2.5

Floor divide (divide, then round down):
floor(5 / 2) = floor(2.5) = 2
floor(-5 / 2) = floor(-2.5) = -3

Integer divide (divide, then round towards zero, i.e. discard the fractional part):
int(5 / 2) = int(2.5) = 2
int(-5 / 2) = int(-2.5) = -2

note: AHK does not currently have an Int function
- Note: in AHK, / performs standard division.
- In some programming languages, / performs integer divide if both values have integer type, and / performs true divide if both values have float type.

- In AHK v1, // does integer divide if both values are of integer type, otherwise it does floor divide.
- Note: integer divide and floor divide give the same result when both input values have the same sign.
- In AHK v2, it will probably consistently do one or the other.

- Some // examples:

Code: Select all

MsgBox, % 5 // 2 ;2
MsgBox, % 5 // 2.0 ;2.000000

MsgBox, % 5 // -2 ;-2 ;integer divide
MsgBox, % 5 // -2.0 ;-3.000000 ;floor divide

MsgBox, % -5 // 2 ;-2 ;integer divide
MsgBox, % -5 // 2.0 ;-3.000000 ;floor divide

MsgBox, % -5 // -2 ;2
MsgBox, % -5 // -2.0 ;2.000000
==================================================

> BASICS: EXPONENTS: POW

- AutoHotkey does not currently have a Pow (power) function.
- But one can be created like so:

Code: Select all

MsgBox, % Pow(2, 3) ;2 to the power of 3 = 8

Pow(vNum, vPower)
{
	return vNum ** vPower
}
==================================================

> BASICS: EXPONENTS (INDICES/POWERS/ORDERS)

- Note: in AutoHotkey: '3 squared' is '3**2'.
- Note: in MS Excel: '3 squared' is '3^2'.
- Some examples of using the ** operator:

Code: Select all

;squaring:
MsgBox, % var := 1**2 ;1
MsgBox, % var := 2**2 ;4
MsgBox, % var := 3**2 ;9
MsgBox, % var := 4**2 ;16

;cubing:
MsgBox, % var := 1**3 ;1
MsgBox, % var := 2**3 ;8
MsgBox, % var := 3**3 ;27
MsgBox, % var := 4**3 ;64

;AHK v1: 0**0 = 0
;in most programming languages, e.g. C++: pow(0, 0) = 1
MsgBox, % var := 0**0 ;0
==================================================

> BASICS: EXPONENTS: RECIPROCALS

- The reciprocal of n = 1/n = Pow(n, -1).
- Some examples of obtaining a reciprocal:

Code: Select all

MsgBox, % var := 1**-1 ;1.000000
MsgBox, % var := 2**-1 ;0.500000
MsgBox, % var := 3**-1 ;0.333333
MsgBox, % var := 4**-1 ;0.250000

;equivalent to:
MsgBox, % var := 1/1 ;1.000000
MsgBox, % var := 1/2 ;0.500000
MsgBox, % var := 1/3 ;0.333333
MsgBox, % var := 1/4 ;0.250000
==================================================

> BASICS: EXPONENTS: ROOTS

- The square root of n = Pow(n, 0.5). AHK also has a Sqrt function.
- The cube root of n = Pow(n, 1/3).
- Some examples of squaring/cubing:

Code: Select all

;square roots:
MsgBox, % var := Sqrt(1) ;1.000000
MsgBox, % var := Sqrt(4) ;2.000000
MsgBox, % var := Sqrt(9) ;3.000000
MsgBox, % var := Sqrt(16) ;4.000000

;equivalent to using Sqrt():
MsgBox, % var := 1**0.5 ;1.000000
MsgBox, % var := 4**0.5 ;2.000000
MsgBox, % var := 9**0.5 ;3.000000
MsgBox, % var := 16**0.5 ;4.000000

;cube roots:
MsgBox, % var := 1**(1/3) ;1.000000
MsgBox, % var := 8**(1/3) ;2.000000
MsgBox, % var := 27**(1/3) ;3.000000
MsgBox, % var := 64**(1/3) ;4.000000
==================================================

> BASICS: EXPONENTS: EXP

- Some examples of using the Exp function:

Code: Select all

;Value := Exp(N)
;'Returns e (which is approximately 2.71828182845905) raised to the Nth power.'

MsgBox, % var := Exp(0) ;1.000000
MsgBox, % var := Exp(1) ;2.718282
MsgBox, % var := Exp(2) ;7.389056
MsgBox, % var := Exp(3) ;20.085537

;numerically equivalent to using Exp() (although accuracy can differ):
MsgBox, % var := 2.71828182845905**0 ;1.000000
MsgBox, % var := 2.71828182845905**1 ;2.718282
MsgBox, % var := 2.71828182845905**2 ;7.389056
MsgBox, % var := 2.71828182845905**3 ;20.085537
==================================================

> BASICS: OPERATORS: UNARY / BINARY / TERNARY

- Operators differ by how many arguments they take:
- unary: arg op or op arg e.g. var++ or ++var
- binary: arg1 op arg2 e.g. var1 + var2
- ternary: arg1 op1 arg2 op2 arg3 e.g. (var1 = var2) ? "y" : "n" ('ternary if' is discussed in a later section)

- Note: 'unary minus' (negation) versus 'binary minus'.
- E.g. var := -3 v. var := 0 - 3

==================================================

> BASICS: ORDER OF OPERATIONS / BODMAS / PEMDAS

- In a calculation, you do not simply perform operations from left-to-right.
- Some operations take priority.
- E.g. resolve anything in parentheses first, then handle powers, then any divisions/multiplications, then any additions/subtractions.
- Note: multiplication/division have equal precedence, addition/subtraction have equal precedence.

- 'BODMAS' and 'PEMDAS' summarise this information:
- BODMAS: Brackets, Order, Division/Multiplication, Addition/Subtraction.
- PEMDAS: Parentheses, Exponents, Multiplication/Division, Addition/Subtraction.

- E.g. BODMAS (B/O/DM/AS) / PEMDAS (P/E/MD/AS):

Code: Select all

  1+2-3*4/5 [next: *][the leftmost of * and /]
= 1+2-12/5  [next: /]
= 1+2-2.4   [next: +][the leftmost of + and -]
= 3-2.4     [next: -]
= 0.6

  1+2-3*4/5**6  [next: **]
= 1+2-3*4/15625 [next: *][the leftmost of * and /]
= 1+2-12/15625  [next: /]
= 1+2-0.000768  [next: +][the leftmost of + and -]
= 3-0.000768    [next: -]
= 2.999232
- E.g. parentheses are added to demonstrate the order:

Code: Select all

;the following are equivalent:
MsgBox, % var := 1+2-3*4/5 ;0.600000
MsgBox, % var := (1+2)-((3*4)/5) ;0.600000

;the following are equivalent:
MsgBox, % var := 1+2-3*4/5**6 ;2.999232
MsgBox, % var := (1+2)-((3*4)/(5**6)) ;2.999232
- In programming, however, you typically have many more operators to consider.
- The AHK operators are listed here 'in descending precedence order':
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#Operators
- (If something takes precedence, you do it first.)
- (You can use parentheses to force something to take precedence.)

- Some things to note from the list:
- && (AND) takes precedence over || (OR) [both are introduced lower down].
- ! and NOT have different precedence levels.

- Versus Excel:
- In AHK, exponentiation takes precedence over unary minus (negation).
- So, -3**2 = -(3**2) = -9.
- In Excel, unary minus (negation) takes precedence over exponentiation.
- So, -3^2 = (-3)^2 = 9.

- Links:
Ordering Mathematical Operations, BODMAS | SkillsYouNeed
https://www.skillsyouneed.com/num/bodmas.html
Order of operations - Wikipedia
https://en.wikipedia.org/wiki/Order_of_operations
Calculation operators and precedence in Excel - Office Support
https://support.office.com/en-ie/article/calculation-operators-and-precedence-in-excel-48be406d-4975-4d31-b2b8-7af9e0e2878a

==================================================

> BASICS: MULTILINE CALCULATIONS

- You can split a calculation across multiple lines, if you put operators at the starts of subsequent lines.

Code: Select all

var := 1
+ 2
- 3
* 4
/ 5
MsgBox, % var ;0.600000

var := 1 + 2 - 3
* 4 / 5
MsgBox, % var ;0.600000
==================================================

> BASICS: CONCATENATION

- Concatenation is flexible in AutoHotkey.
- You can essentially just put strings and calculations next to each other.
- You could optionally add parentheses for greater clarity.
- A dot operator is available, for concatenation, but is almost never needed.
- The following are all equivalent:

Code: Select all

MsgBox, % var := "abc" 2*3 "def" ;abc6def
MsgBox, % var := "abc" (2*3) "def" ;abc6def
MsgBox, % var := "abc" . 2*3 . "def" ;abc6def
MsgBox, % var := "abc" . (2*3) . "def" ;abc6def
- Here's an example of a surprise re. the dot operator:
possible issue with GetCapacity? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=14&t=32911

==================================================

> BASICS: ABS

- The Abs function can be used to return the absolute value of a number, it removes any minus signs.
- I.e. it forces a number to be positive(/zero).

Code: Select all

MsgBox, % var := Abs(5) ;5
MsgBox, % var := Abs(5.5) ;5.500000
MsgBox, % var := Abs(-5) ;5
MsgBox, % var := Abs(-5.5) ;5.500000

;for reference:
MsgBox, % var := Sqrt(5**2) ;5.000000
MsgBox, % var := Sqrt(5.5**2) ;5.500000
MsgBox, % var := Sqrt((-5)**2) ;5.000000
MsgBox, % var := Sqrt((-5.5)**2) ;5.500000
==================================================

> BASICS: SIGN (POSITIVE/ZERO/NEGATIVE)

- A Sign function returns 1/0/-1 depending on the sign of a number (positive/zero/negative).
- positive numbers -> 1
- 0 -> 0
- negative numbers -> -1

- AutoHotkey does not currently have a Sign function.
- But one can be created like so:

Code: Select all

MsgBox, % Sign(3) ;1
MsgBox, % Sign(0) ;0
MsgBox, % Sign(-3) ;-1

MsgBox, % Sign(10) ;1
MsgBox, % Sign(1) ;1
MsgBox, % Sign(0.1) ;1
MsgBox, % Sign(0) ;0
MsgBox, % Sign(-0.1) ;-1
MsgBox, % Sign(-1) ;-1
MsgBox, % Sign(-10) ;-1

Sign(vNum)
{
	return (vNum > 0) ? 1 : (vNum < 0) ? -1 : 0
}

;the same logic as above, but presented more simply
SignAlt1(vNum)
{
	if (vNum > 0)
		return 1
	else if (vNum < 0)
		return -1
	else
		return 0
}

SignAlt2(vNum)
{
	return (vNum > 0) - (vNum < 0)
}
==================================================

> BASICS: FLOOR / ROUND / CEIL / INTEGER

- Floor rounds down. Towards negative infinity.
- Ceil (ceiling) rounds up. Towards positive infinity.
- Round rounds to the nearest integer.
- Integer rounds towards zero. It discards the fractional part.

- AHK v1 does not currently have an Integer function. (AHK v2 does.)
- But a custom Integer function is created for AHK v1 below.

- Some examples:

Code: Select all

MsgBox, % var := Floor(5.1) ;5
MsgBox, % var := Round(5.1) ;5
MsgBox, % var := Ceil(5.1) ;6
MsgBox, % var := Integer(5.1) ;5

MsgBox, % var := Floor(5.5) ;5
MsgBox, % var := Round(5.5) ;6
MsgBox, % var := Ceil(5.5) ;6
MsgBox, % var := Integer(5.5) ;5

MsgBox, % var := Floor(5.9) ;5
MsgBox, % var := Round(5.9) ;6
MsgBox, % var := Ceil(5.9) ;6
MsgBox, % var := Integer(5.9) ;5

MsgBox, % var := Floor(-5.1) ;-6
MsgBox, % var := Round(-5.1) ;-5
MsgBox, % var := Ceil(-5.1) ;-5
MsgBox, % var := Integer(-5.1) ;-5

MsgBox, % var := Floor(-5.5) ;-6
MsgBox, % var := Round(-5.5) ;-6
MsgBox, % var := Ceil(-5.5) ;-5
MsgBox, % var := Integer(-5.5) ;-5

MsgBox, % var := Floor(-5.9) ;-6
MsgBox, % var := Round(-5.9) ;-6
MsgBox, % var := Ceil(-5.9) ;-5
MsgBox, % var := Integer(-5.9) ;-5

;warning: in AHK v1, rounding a string (that does not start with a numeric part) returns 0
MsgBox, % var := Floor("abc") ;0
MsgBox, % var := Round("abc") ;0
MsgBox, % var := Ceil("abc") ;0

Integer(vNum)
{
	return (vNum < 0) ? Ceil(vNum) : Floor(vNum)
}
- Round can round to various precisions:

Code: Select all

MsgBox, % Round(123.456, 2) ;123.46
MsgBox, % Round(123.456, 1) ;123.5
MsgBox, % Round(123.456, 0) ;123
MsgBox, % Round(123.456, -1) ;120
MsgBox, % Round(123.456, -2) ;100
==================================================

> BASICS: ROUND TO THE NEAREST MULTIPLE OF N

- Some examples of rounding a number to the nearest multiple.

Code: Select all

;round (to the nearest)

;e.g. round a positive number to the nearest 5
vList := "10,12,12.5,14,15"
vOutput := ""
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vDivisor := 5
	vRemainder := Mod(vNum, vDivisor)
	vRet := vNum - vRemainder + (vRemainder >= (vDivisor/2) ? vDivisor : 0)
	vOutput .= vNum "`t" Round(vRet) "`r`n"
}
MsgBox, % vOutput

;e.g. round a positive number to the nearest 5 (alternative)
vList := "10,12,12.5,14,15"
vOutput := ""
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vDivisor := 5
	vRet := Round(vNum / vDivisor) * vDivisor
	vOutput .= vNum "`t" Round(vRet) "`r`n"
}
MsgBox, % vOutput

;==============================

;round down

;e.g. round a positive number down to the nearest 5
vList := "10,12,12.5,14,15"
vOutput := ""
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vDivisor := 5
	vRemainder := Mod(vNum, vDivisor)
	vRet := vNum - vRemainder
	vOutput .= vNum "`t" Round(vRet) "`r`n"
}
MsgBox, % vOutput

;e.g. round a positive number down to the nearest 5 (alternative)
vList := "10,12,12.5,14,15"
vOutput := ""
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vDivisor := 5
	vRet := Floor(vNum / vDivisor) * vDivisor
	vOutput .= vNum "`t" Round(vRet) "`r`n"
}
MsgBox, % vOutput

;==============================

;round up

;e.g. round a positive number up to the nearest 5
vList := "10,12,12.5,14,15"
vOutput := ""
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vDivisor := 5
	vRemainder := Mod(vNum, vDivisor)
	vRet := vNum - vRemainder + (vRemainder = 0 ? 0 : 5)
	vOutput .= vNum "`t" Round(vRet) "`r`n"
}
MsgBox, % vOutput

;e.g. round a positive number up to the nearest 5 (alternative)
vList := "10,12,12.5,14,15"
vOutput := ""
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vDivisor := 5
	vRet := Ceil(vNum / vDivisor) * vDivisor
	vOutput .= vNum "`t" Round(vRet) "`r`n"
}
MsgBox, % vOutput
==================================================

> BASICS: ROUND UP BY ROUNDING DOWN / ROUND BY ROUNDING DOWN

- In some programming languages, if there is a 'Floor' (or 'Int') function, but no 'Ceil' function, you can use the 'Floor' (or 'Int') function to 'round up by rounding down'.
- Similarly you can use the 'Floor' (or 'Int') function, to 'round to the nearest integer by rounding down'.

- Both formulas are listed at different places on this page:
Floor and ceiling functions - Wikipedia
https://en.wikipedia.org/wiki/Floor_and_ceiling_functions

- Here Round and Ceil are recreated by using Floor:
Round(x) := (x>0?1:x<0?-1:0)*Floor(Abs(x)+0.5) [round by rounding down]
Round(x) := Sign(x)*Floor(Abs(x)+0.5) [round by rounding down]
Ceil(x) := -Floor(-x) [round up by rounding down]

- Here is some code to test the principle:

Code: Select all

;'round up by rounding down':
;Ceil(x) := -Floor(-x)
;to confirm it works (a MsgBox would indicate a disparity):
vStart := -10
Loop, 201
{
	vNum := vStart + (A_Index-1) * 0.1
	vNum1 := Ceil(vNum)
	vNum2 := -Floor(-vNum)
	if !(vNum1 = vNum2)
		MsgBox, % vNum " " vNum1 " " vNum2
}
MsgBox, % "done"

;'round (to the nearest integer) by rounding down':
;Round(x) := (x>0?1:x<0?-1:0)*Floor(Abs(x)+0.5)
;Round(x) := Sign(x)*Floor(Abs(x)+0.5)
;to confirm it works (a MsgBox would indicate a disparity):
vStart := -10
Loop, 201
{
	vNum := vStart + (A_Index-1) * 0.1
	vNum1 := Round(vNum)
	vNum2 := (vNum>0?1:vNum<0?-1:0)*Floor(Abs(vNum)+0.5)
	if !(vNum1 = vNum2)
		MsgBox, % vNum " " vNum1 " " vNum2
}
MsgBox, % "done"
==================================================

> BASICS: COMPARISON

- Comparison operators (relational operators):
- > < >= <= = == <> != !==
- > < (greater than, less than)
- >= <= (greater than or equal to, less than or equal to)
- = == (equals) [both are equivalent for numbers, but differ for strings]
- <> != !== (not equal) [both are equivalent for numbers, but differ for strings]

- Note: in AHK, = is *compare*, in many languages it is *assign*.
- Note: although, in AHK v1, in one-liners such as var = value, = is *assign*.
- Note: when these operators are applied to strings: some are always case sensitive e.g. ==, some are always case insensitive e.g. =, some vary based on A_StringCaseSense.

- Some comparison examples:
- Including some notes on forcing a number to be a string.

Code: Select all

MsgBox, % 1 > 2 ;0
MsgBox, % 1 < 2 ;1
MsgBox, % 1 >= 2 ;0
MsgBox, % 1 <= 2 ;1
MsgBox, % 1 = 2 ;0
MsgBox, % 1 == 2 ;0
MsgBox, % 1 <> 2 ;1
MsgBox, % 1 != 2 ;1
;MsgBox(1 !== 2) ;1 ;AHK v2 only

MsgBox, % 2 > 1 ;1
MsgBox, % 2 < 1 ;0
MsgBox, % 2 >= 1 ;1
MsgBox, % 2 <= 1 ;0
MsgBox, % 2 = 1 ;0
MsgBox, % 2 == 1 ;0
MsgBox, % 2 <> 1 ;1
MsgBox, % 2 != 1 ;1
;MsgBox(2 !== 1) ;1 ;AHK v2 only

MsgBox, % 1 > 1 ;0
MsgBox, % 1 < 1 ;0
MsgBox, % 1 >= 1 ;1
MsgBox, % 1 <= 1 ;1
MsgBox, % 1 = 1 ;1
MsgBox, % 1 == 1 ;1
MsgBox, % 1 <> 1 ;0
MsgBox, % 1 != 1 ;0
;MsgBox(1 !== 1) ;0 ;AHK v2 only

;==============================

MsgBox, % (1 = 1.0) ;1

;==============================

;note: if one or both of the values is forced as a string
;the values are compared as strings, not numerically
MsgBox, % ("" 1 = 1.0) ;0
MsgBox, % (1 = "" 1.0) ;0
MsgBox, % ("" 1 = "" 1.0) ;0

;==============================

vNum2 := 3
Loop, 5
{
	vNum1 := A_Index
	if (vNum1 > vNum2)
		MsgBox, % vNum1 " > " vNum2
	else if (vNum1 < vNum2)
		MsgBox, % vNum1 " < " vNum2
	else
		MsgBox, % vNum1 " = " vNum2
}

;same example but shortened by using the ternary operator
vNum2 := 3
Loop, 5
{
	vNum1 := A_Index
	vOp := (vNum1 > vNum2) ? ">" : (vNum1 < vNum2) ? "<" : "="
	MsgBox, % vNum1 " " vOp " " vNum2
}
- When numbers and/or strings are compared in AHK v1 (and probably in AHK v2), the following rules apply:
numbers/strings in expressions - AutoHotkey Community
https://www.autohotkey.com/boards/viewtopic.php?f=37&t=55905
if both items are literal strings e.g. "1" or "a", compare alphabetically
else if either item looks non-numeric, compare alphabetically
else compare numerically
- Note: you can store two literal strings in variables, and compare the variables, and the comparison will be numeric. If two items look numeric, the comparison will be alphabetical only if the items are forced as strings, at the moment they are passed to the operator.
- Note: for most built-in AHK functions, if the parameter accepts a number of Integer/Float type, it will also accept a numeric-looking string. The documentation for the function will specify what types it can accept.

==================================================

> BASICS: COMPARISON (BETWEEN)

- AutoHotkey does not currently have a Between function. (Although AutoHotkey_H does.)
- But one can be created like so:

Code: Select all

MsgBox, % Between(-1, 1, 5) ;0
MsgBox, % Between(3, 1, 5) ;1
MsgBox, % Between(7, 1, 5) ;0

Between(vNum, vLim1, vLim2)
{
	local
	return (vLim1 <= vNum && vNum <= vLim2)
}
==================================================

> BASICS: COMPARING FLOATING-POINT NUMBERS (WARNING)

- Here is an example demonstrating some potential problems when comparing floating-point numbers:

Code: Select all

;this returns 0, even though in theory it should return 1
MsgBox, % (0.3 = 0.1+0.2) ;0

;here it can be seen that the numbers are different:
MsgBox, % Format("{:.17f}", 0.1+0.2) ;0.30000000000000004
MsgBox, % Format("{:.17f}", 0.3) ;0.29999999999999999
==================================================

> BASICS: LIST NUMBERS (NUMBER SEQUENCES)

- Here are some examples of generating lists of numbers.
- E.g. positive integers, odd/even, times tables, triangular, factorial, powers (square/cube/powers of 2/n to the n), Fibonacci, prime.

Code: Select all

;e.g. list numbers 1 to 10
vList := ""
Loop, 10
	vList .= (A_Index=1?"":",") A_Index
MsgBox, % vList

;alternatively (using SubStr instead of the ternary operator):
vList := ""
Loop, 10
	vList .= A_Index ","
vList := SubStr(vList, 1, -1)
MsgBox, % vList

;e.g. list numbers 10 to 1
vList := ""
Loop, 10
	vList .= (A_Index=1?"":",") (11-A_Index)
MsgBox, % vList

;e.g. list first 10 odd numbers
vList := ""
Loop, 10
	vList .= (A_Index=1?"":",") (A_Index*2-1)
MsgBox, % vList

;e.g. list first 10 even numbers
vList := ""
Loop, 10
	vList .= (A_Index=1?"":",") (A_Index*2)
MsgBox, % vList

;e.g. list times tables (a 10x10 grid)
vList := ""
vRowCount := 10
vColCount := 10
Loop, % vRowCount
{
	vNum := A_Index
	Loop, % vColCount
		vList .= (A_Index=1?"":"`t") (vNum*A_Index)
	vList .= "`r`n"
}
MsgBox, % vList

;e.g. list first 10 triangular numbers
vList := ""
vTemp := 0
Loop, 10
{
	vTemp += A_Index
	vList .= (A_Index=1?"":",") vTemp
}
MsgBox, % vList

;alternatively: list first 10 triangular numbers
vList := ""
Loop, 10
	vList .= (A_Index=1?"":",") Round(A_Index * (A_Index + 1) / 2)
MsgBox, % vList

;e.g. list first 10 factorial numbers
;note: AutoHotkey can natively store the first 20 factorial numbers
vList := ""
vTemp := 1
Loop, 10
{
	vTemp *= A_Index
	vList .= (A_Index=1?"":",") vTemp
}
MsgBox, % vList

;e.g. list first 10 square numbers
vList := ""
Loop, 10
	vList .= (A_Index=1?"":",") (A_Index**2)
MsgBox, % vList

;e.g. list first 10 cube numbers
vList := ""
Loop, 10
	vList .= (A_Index=1?"":",") (A_Index**3)
MsgBox, % vList

;e.g. list first 10 powers of 2
vList := ""
Loop, 10
	vList .= (A_Index=1?"":",") (2**A_Index)
MsgBox, % vList

;e.g. list first 10 'n to the n' numbers
vList := ""
Loop, 10
	vList .= (A_Index=1?"":",") (A_Index**A_Index)
MsgBox, % vList

;e.g. list first 10 Fibonacci numbers starting at '1, 1'
vList := "1,1"
vNum1 := 1, vNum2 := 1
Loop, 8
{
	vTemp := vNum1 + vNum2 ;big + small
	vNum2 := vNum1 ;new small
	vNum1 := vTemp ;new big
	vList .= "," vTemp
}
MsgBox, % vList

;alternatively: list first 10 Fibonacci numbers starting at '0, 1'
vList := "0,1"
vNum1 := 0, vNum2 := 1
Loop, 8
{
	vTemp := vNum1 + vNum2
	vNum2 := vNum1
	vNum1 := vTemp
	vList .= "," vTemp
}
MsgBox, % vList

;alternatively: list first 10 Fibonacci numbers starting at '1, 1'
vList := ""
vTemp := 1, vNum1 := 1, vNum2 := 0
Loop, 10
{
	vList .= (A_Index=1?"":",") vTemp
	vTemp := vNum1 + vNum2
	vNum2 := vNum1
	vNum1 := vTemp
}
MsgBox, % vList

;alternatively: list first 10 Fibonacci numbers starting at '0, 1'
vList := ""
vTemp := 0, vNum1 := 1, vNum2 := 0
Loop, 10
{
	vList .= (A_Index=1?"":",") vTemp
	vTemp := vNum1 + vNum2
	vNum2 := vNum1
	vNum1 := vTemp
}
MsgBox, % vList

;e.g. list first 10 prime numbers (note: uses an IsPrime function below)
vCount := 0
vTemp := 0
while (vCount < 10)
{
	vTemp++
	if (IsPrime(vTemp))
	{
		vCount++
		vList .= (vCount=1?"":",") vTemp
	}
}
MsgBox, % vList

IsPrime(vNum)
{
	local
	if (vNum <= 1)
		return 0
	else if (vNum = 2)
		return 1
	else if (Mod(vNum, 2) = 0)
		return 0
	vLim := Floor(Sqrt(vNum)), vTemp := 3
	Loop
	{
		if (vTemp > vLim)
			return 1
		else if (Mod(vNum, vTemp) = 0)
			return 0
		vTemp += 2
	}
}
- Note: to check if a number is odd or even you can use & or Mod.

Code: Select all

;return 1 if number is odd
vNum := -6
vOutput := ""
Loop, 11
{
	vNum++
	vOutput .= (vNum & 1) "`t" vNum "`r`n"
}
MsgBox, % vOutput

;return 1 if number is odd
vNum := -6
vOutput := ""
Loop, 11
{
	vNum++
	vOutput .= !!Mod(vNum, 2) "`t" vNum "`r`n"
}
MsgBox, % vOutput
- Here's a simple script to list a number's factors:

Code: Select all

vOutput := ""
VarSetCapacity(vOutput, 1000000*2)
Loop, 100
	vOutput .= A_Index "`t" ListFactors(A_Index) "`r`n"
Clipboard := vOutput
MsgBox, % "done"
return

ListFactors(vNum)
{
	local
	vNum := Floor(vNum)
	if (vNum < 1)
		return
	vOutput := ""
	Loop, % vNum
	{
		if (Mod(vNum, A_Index) = 0)
			vOutput .= A_Index ","
	}
	return SubStr(vOutput, 1, -1)
}
==================================================

> BASICS: MINIMUM / MAXIMUM

- An example of using the ternary operator to find the max/min of 2 numbers:

Code: Select all

;note: the parentheses aren't needed, but are there for clarity
vNum1 := 5, vNum2 := 10
MsgBox, % vMax := vNum1 > vNum2 ? vNum1 : vNum2
MsgBox, % vMin := vNum1 < vNum2 ? vNum1 : vNum2

MsgBox, % vMax := (vNum1 > vNum2) ? vNum1 : vNum2
MsgBox, % vMin := (vNum1 < vNum2) ? vNum1 : vNum2
- An example of using the Max/Min functions to find the max/min of multiple values:

Code: Select all

;note: each of the 4 examples below are equivalent:

MsgBox, % Max(3, 1, 4, 1, 5) ;5
MsgBox, % Min(3, 1, 4, 1, 5) ;1

vNum1 := 3, vNum2 := 1, vNum3 := 4, vNum4 := 1, vNum5 := 5
MsgBox, % Max(vNum1, vNum2, vNum3, vNum4, vNum5) ;5
MsgBox, % Min(vNum1, vNum2, vNum3, vNum4, vNum5) ;1

;note: you can pass an array as multiple separate parameters by using *

oArray := [3, 1, 4, 1, 5]
MsgBox, % Max(oArray*) ;5
MsgBox, % Min(oArray*) ;1

MsgBox, % Max([3, 1, 4, 1, 5]*) ;5
MsgBox, % Min([3, 1, 4, 1, 5]*) ;1
==================================================

> BASICS: COUNT / SUM (TOTAL) / PRODUCT / MEAN AVERAGE / MINIMUM / MAXIMUM / RANGE

- An example of getting some statistical information for a list of numbers:
- Specifically: count, sum, product, mean average, minimum, maximum, range.

Code: Select all

vList := "1,2,3,4,5"
vCount := 0, vSum := 0, vProduct := 1
Loop, Parse, vList, % ","
{
	vCount++
	;vCount := A_Index ;an alternative
	vSum += A_LoopField
	vProduct *= A_LoopField
	if (A_Index = 1) || (A_LoopField < vMin)
		vMin := A_LoopField
	if (A_Index = 1) || (A_LoopField > vMax)
		vMax := A_LoopField
}
vRange := vMax - vMin
vAverage := vSum / vCount

vOutput := ""
. "count: " vCount "`r`n"
. "sum: " vSum "`r`n"
. "product: " vProduct "`r`n"
. "mean average: " vAverage "`r`n"
. "minimum: " vMin "`r`n"
. "maximum: " vMax "`r`n"
. "range: " vRange
MsgBox, % vOutput
==================================================

> BASICS: FREQUENCY COUNT / FREQUENCY TABLE / MODE

- An example of getting a frequency count, and the mode, for a list of items:

Code: Select all

vList := "a,f,b,b,e,e,c,c,c,d,d,d"
oArray := {}
Loop, Parse, vList, % ","
{
	if oArray.HasKey(A_LoopField)
		oArray[A_LoopField]++
	else
		oArray[A_LoopField] := 1
}
vOutput := ""
vMax := 0, vModeList := ""
for vKey, vValue in oArray
{
	vOutput .= vValue "`t" vKey "`r`n"
	if (vValue > vMax)
		vMax := vValue, vModeList := vKey
	else if (vValue = vMax)
		vModeList .= "," vKey
}
MsgBox, % "frequency count:`r`n" vOutput "`r`n" "mode:`r`n" vModeList
oArray := ""
==================================================

> BASICS: FREQUENCY TABLE TO LIST

- An example of turning a frequency table into a list of numbers:

Code: Select all

vTable := " ;continuation section
(
1	a
2	b
3	c
2	d
1	e
)"
MsgBox, % vTable

vList := ""
Loop, Parse, vTable, `n
{
	oTemp := StrSplit(A_LoopField, "`t")
	Loop, % oTemp.1
		vList .= oTemp.2 ","
}
vList := SubStr(vList, 1, -1)
oTemp := ""
MsgBox, % vList
==================================================

> BASICS: MEDIAN

- An example of getting the median for a list of numbers:

Code: Select all

;note: not all textbooks define the median the same way,
;this approach takes the middle number in an odd-numbered list,
;and takes the mean average of the middle numbers in an even-numbered list

vList := "1,5,2,4,3"
Sort, vList, D, N
oArray := StrSplit(vList, ",")
vMid := Ceil(oArray.Length()/2)
if (oArray.Length() & 1) ;if odd
	vMedian := oArray[vMid]
else
	vMedian := (oArray[vMid] + oArray[vMid+1]) / 2
MsgBox, % vList "`r`n" vMedian

vList := "1,6,2,5,3,4"
Sort, vList, D, N
oArray := StrSplit(vList, ",")
vMid := Ceil(oArray.Length()/2)
if (oArray.Length() & 1) ;if odd
	vMedian := oArray[vMid]
else
	vMedian := (oArray[vMid] + oArray[vMid+1]) / 2
MsgBox, % vList "`r`n" vMedian
- Link:
Sort - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Sort.htm

==================================================

> BASICS: HIGHEST COMMON FACTOR (GREATEST COMMON DIVISOR) (HCF) (GCD)

- Some code for a custom GCD (HCF) function:

Code: Select all

MsgBox, % GCD(30, 40) ;10
MsgBox, % GCD(60, 64) ;4
MsgBox, % GCD(5, 15) ;5
MsgBox, % GCD(15, 30) ;15

GCD(vNum1, vNum2)
{
	local
	Loop, % vIndex := Min(vNum1, vNum2)
	{
		if (Mod(vNum1, vIndex) = 0) && (Mod(vNum2, vIndex) = 0)
			return vIndex
		vIndex--
	}
	return 0
}
==================================================

> BASICS: LOWEST COMMON MULTIPLE (LEAST COMMON MULTIPLE) (LCM)

- Some code for a custom LCM function:

Code: Select all

MsgBox, % LCM(30, 40) ;120
MsgBox, % LCM(60, 64) ;960
MsgBox, % LCM(5, 15) ;15
MsgBox, % LCM(15, 30) ;30

;adapted from:
;Least common multiple - Rosetta Code
;https://rosettacode.org/wiki/Least_common_multiple#AutoHotkey
LCM(vNum1, vNum2)
{
	local
	if (vNum1 = 0) || (vNum2 = 0)
		return 0
	vTemp := vNum1 * vNum2
	while vNum2
		vNum := vNum2, vNum2 := Mod(vNum1, vNum2), vNum1 := vNum
	return Floor(vTemp / vNum1)
}
==================================================

> BASICS: SORT (ASCENDING / DESCENDING / RANDOM)

- Some examples of sorting numbers in ascending/descending/a random order:

Code: Select all

vList := "1,6,2,5,3,4"

Sort, vList, D, N ;ascending
MsgBox, % vList
Sort, vList, D, N R ;descending
MsgBox, % vList
Sort, vList, D, Random
MsgBox, % vList

;note: to obtain random numbers with no repeats
;create a list of numbers, and randomise them:
;e.g.
vOutput := ""
Loop, 100
	vOutput .= (A_Index=1?"":",") A_Index
Sort, vOutput, D, Random
MsgBox, % vOutput
- Link:
jeeswg's sort algorithms mini-tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=64447

==================================================

> BASICS: TOGGLE

- A toggle switches between two values e.g. 0 and 1.
- Some examples of using ! (not) to toggle variables.
- A blank string or a value of 0, becomes 1, anything else, becomes 0.

Code: Select all

var := ""
MsgBox, % !var ;1
var := 0
MsgBox, % !var ;1
var := 0.0
MsgBox, % !var ;1
var := 0x0
MsgBox, % !var ;1
var := .0
MsgBox, % !var ;1
var := 0.
MsgBox, % !var ;1

var := 1
MsgBox, % !var ;0
var := -1
MsgBox, % !var ;0
var := 5
MsgBox, % !var ;0
var := -5
MsgBox, % !var ;0

;toggling back and forth
var := ""
Loop, 8
{
	var := !var
	MsgBox, % var
}
==================================================

> BASICS: CYCLE

- An example of a cycle would be: 'a, b, c, a, b, c, a, b, c, ...'.
- We have the list: 'a, b, c', and we list the items, and then return to the beginning.

- Some examples of cycling through a list of possible values.
- Modular arithmetic is used.

Code: Select all

vText := "abcde"
vOutput := ""
Loop, 10
{
	vNum := Mod(A_Index-1, 5) + 1
	vChar := SubStr(vText, vNum, 1)
	;MsgBox, % vNum " " vText
	vOutput .= vNum " " vChar "`r`n"
}
MsgBox, % vOutput

oArray := ["a", "b", "c", "d", "e"]
vOutput := ""
Loop, 10
{
	vNum := Mod(A_Index-1, 5) + 1
	;MsgBox, % vNum " " vText
	vOutput .= vNum " " oArray[vNum] "`r`n"
}
MsgBox, % vOutput

oArray := StrSplit("abcde,fghij,klmno,pqrst,uvwxy", ",")
vOutput := ""
Loop, 10
{
	vNum := Mod(A_Index-1, 5) + 1
	vOutput .= vNum " " oArray[vNum] "`r`n"
}
MsgBox, % vOutput
==================================================

> BASICS: IF (NON-ZERO) (NON-BLANK)

- Some examples of what AutoHotkey's if-statement considers true/false:

Code: Select all

;these will all be seen as equal to 0 or blank:
vList1 := ",0, 0,0 , 0 ,0x0,-0,+0,00,000,0.0"
;these will all be seen as non-zero or non-blank:
vList2 := "1,5,-1,-5,abc,0+0,0 0,--0,++0"
vList := vList1 "," vList2
Loop, Parse, vList, % ","
{
	if A_LoopField
		MsgBox, % "y: [" A_LoopField "]"
	else
		MsgBox, % "n: [" A_LoopField "]"
}

;note: strings / expressions will be handled differently
var := "--0"
MsgBox, % !!var ;1
var := "++0"
MsgBox, % !!var ;1
var := --0
MsgBox, % !!var ;0
var := ++0
MsgBox, % !!var ;0

;==============================

;these MsgBoxes will not be triggered:
if 0
	MsgBox, % "y"

if (6 > 9)
	MsgBox, % "y"

var := 0
if var
	MsgBox, % "y"

var := ""
if var
	MsgBox, % "y"

var := 6
if (var > 9)
	MsgBox, % "y"

if (9 < var)
	MsgBox, % "y"

;==============================

;these MsgBoxes will be triggered:
if 1
	MsgBox, % "y"

if (6 > 9)
	MsgBox, % "y"

var := 1
if var
	MsgBox, % "y"

var := "abc"
if var
	MsgBox, % "y"

var := 9
if (var > 6)
	MsgBox, % "y"

if (6 < var)
	MsgBox, % "y"
==================================================

> BASICS: IF / ELSE IF / ELSE (LOGIC)

- Some examples demonstrating the logic of if/else ladders with/without the use of 'else if':

Code: Select all

;note: where 'cond' is short for 'condition'

if cond1
	action1
else
	action2

;==============================

if cond1
	action1
else if cond2
	action2

;equivalent to:

if cond1
	action1
else
{
	if cond2
		action2
}

;==============================

if cond1
	action1
else if cond2
	action2
else
	action 3

;equivalent to:

if cond1
	action1
else
{
	if cond2
		action2
	else
		action 3
}

;==============================

if cond1
	action1
else if cond2
	action2
else if cond3
	action3
else
	action4

;equivalent to:

if cond1
	action1
else
{
	if cond2
		action2
	else
	{
		if cond3
			action3
		else
			action4
	}
}
==================================================

> BASICS: TERNARY OPERATOR

- A ternary operator is an operator that takes 3 arguments.
- It is equivalent to a function that takes 3 arguments.
- E.g. arg1 op1 arg2 op2 arg3
- The 'ternary if' operator ? : is commonly called *the* ternary operator.
- E.g. arg1 ? arg2 : arg3

- Some concrete examples demonstrating the ternary operator:

Code: Select all

;general:
x := 1, a := b := c := 0
(x = 1) ? (a := 1, b := 2, c := 3) : 0 ;if x is 1, set a b c, else, do nothing
MsgBox, % a " " b " " c

x := 0, a := b := c := 0
(x = 1) ? (a := 1, b := 2, c := 3) : 0 ;if x is 1, set a b c, else, do nothing
MsgBox, % a " " b " " c

;maximum / minimum:
vMax := (vNum1 > vNum2) ? vNum1 : vNum2
vMin := (vNum1 < vNum2) ? vNum1 : vNum2

;fonts:
vFontWeight := InStr(vOpt, "b") ? 700 : 400

;window class to program name:
vUtil := ""
(vWinClass = "MediaPlayerClassicW") && (vUtil := "Media Player Classic")
(vWinClass = "Winamp v1.x") && (vUtil := "Winamp")

;address space memory regions:
(vMbiType & 0x20000) ? (vTypeText := "private")
(vMbiType & 0x40000) ? (vTypeText := "mapped")
(vMbiType & 0x1000000) ? (vTypeText := "image")

;Unicode/ANSI:
vEmDash := Chr(A_IsUnicode ? 8212 : 151)
vEnDash := Chr(A_IsUnicode ? 8211 : 150)
vEuroSign := Chr(A_IsUnicode ? 8364 : 128)
vWA := A_IsUnicode ? "W" : "A"

;Ptr type for use with DllCall and NumGet/NumPut:
vUPtrType := (vPBits = 64) ? "UInt64" : "UInt"
vPtrType := (vPBits = 64) ? "Int64" : "Int"

;bitness:
vBitness := (A_PtrSize = 8) ? 64 : 32
- Link:
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#ternary

==================================================

> BASICS: TERNARY OPERATOR (LOGIC)

- Some examples demonstrating the logic of the ternary operator.
- Including chaining multiple ternary operators together.

- Set a variable's contents:

Code: Select all

var := cond1 ? ret1 : ret2
;equivalent to:
if cond1
	var := ret1
else
	var := ret2

var := cond1 ? ret1 : (cond2 ? ret2 : ret3)
;equivalent to:
var := cond1 ? ret1 : cond2 ? ret2 : ret3
;equivalent to:
var := cond1 ? ret1
	: cond2 ? ret2
	: ret3
;equivalent to:
if cond1
	var := ret1
else if cond2
	var := ret2
else
	var := ret3

var := cond1 ? ret1 : (cond2 ? ret2 : (cond3 ? ret3 : ret4))
;equivalent to:
var := cond1 ? ret1 : cond2 ? ret2 : cond3 ? ret3 : ret4
;equivalent to:
var := cond1 ? ret1
	: cond2 ? ret2
	: cond3 ? ret3
	: ret4
;equivalent to:
if cond1
	var := ret1
else if cond2
	var := ret2
else if cond3
	var := ret3
else
	var := ret4
- 'if condition, do action 1, else, do action 2':

Code: Select all

cond1 ? func1(arg1) : func2(arg2)
;equivalent to:
if cond1
	func1(arg1)
else
	func2(arg2)

cond1 ? (var1 := value1) : (var2 := value2)
;equivalent to:
if cond1
	var1 := value1
else
	var2 := value2
- if condition, do action, else, do nothing:

Code: Select all

cond1 ? (var1 := value1) : 0
;equivalent to:
if cond1
	var1 := value1

cond1 ? 0 : (var2 := value2)
;equivalent to:
if !cond1
	var2 := value2

;a similar technique:
(cond1) && (var1 := value1)
(!cond1) && (var2 := value2)
==================================================

> BASICS: SWITCH STATEMENT (CASE) (SELECT)

- An example demonstrating AutoHotkey's switch statement:

Code: Select all

vCol := "red"
switch vCol
{
case "red": vRGB := "FF0000"
case "yellow": vRGB := "FFFF00"
case "lime": vRGB := "00FF00"
case "blue": vRGB := "0000FF"
default: vRGB := "UNKNOWN"
}
MsgBox, % vRGB
- Some alternatives to a switch statement:

Code: Select all

;==============================

;e.g. RGB colour values

;==============================

;via array (if vCol is unknown, oArray returns an empty string):
vCol := "red"
oArray := {red:"FF0000",yellow:"FFFF00",lime:"00FF00",blue:"0000FF"}
MsgBox, % vRGB := oArray[vCol]

;==============================

;via array (if vCol is unknown, oArray returns 'UNKNOWN'):
vCol := "red"
oArray := {red:"FF0000",yellow:"FFFF00",lime:"00FF00",blue:"0000FF"}
if oArray.HasKey(vCol)
	vRGB := oArray[vCol]
else
	vRGB := "UNKNOWN"
MsgBox, % vRGB

;==============================

;via ternary operator:
vCol := "red"
vRGB := (vCol="red") ? "FF0000"
	: (vCol="yellow") ? "FFFF00"
	: (vCol="lime") ? "00FF00"
	: (vCol="blue") ? "0000FF"
	: "UNKNOWN"
MsgBox, % vRGB

;==============================

;via 'condition && action':
vCol := "red"
vRGB := "UNKNOWN"
(vCol = "red") && (vRGB := "FF0000")
(vCol = "yellow") && (vRGB := "FFFF00")
(vCol = "lime") && (vRGB := "00FF00")
(vCol = "blue") && (vRGB := "0000FF")
MsgBox, % vRGB

;==============================

;via if/else ladder:
vCol := "red"
if (vCol = "red")
	vRGB := "FF0000"
else if (vCol = "yellow")
	vRGB := "FFFF00"
else if (vCol = "lime")
	vRGB := "00FF00"
else if (vCol = "blue")
	vRGB := "0000FF"
else
	vRGB := "UNKNOWN"
MsgBox, % vRGB

;==============================
==================================================

> BASICS: EXPAND/CONTRACT A LIST OF NUMBERS

- Expand a list of numbers:

Code: Select all

vList := "1,4-5,9-12,20"
MsgBox, % NumListExpand(vList) ;1,4,5,9,10,11,12,20
NumListExpand(vList)
{
	vOutput := ""
	Loop, Parse, vList, % ","
	{
		oTemp := StrSplit(A_LoopField, "-")
		if (oTemp.Length() = 1)
			vOutput .= oTemp.1 ","
		else if (oTemp.Length() = 2)
		{
			if (oTemp.2 < voTemp.1)
				return ""
			Loop, % oTemp.2 - oTemp.1 + 1
				vOutput .= (oTemp.1-1+A_Index) ","
		}
		else
			return ""
	}
	return SubStr(vOutput, 1, -1)
}
- Contract a list of numbers:

Code: Select all

vList := "1,4,5,9,10,11,12,20"
MsgBox, % NumListContract(vList) ;1,4-5,9-12,20
NumListContract(vList)
{
	vOutput := "", vList .= ","
	Loop, Parse, vList, % ","
	{
		if (A_Index = 1)
			vMem := A_LoopField, vCount := 1
		else if !(A_LoopField = vLast+1)
		{
			if (vCount = 1)
				vOutput .= vMem ","
			else
				vOutput .= vMem "-" vLast ","
			vMem := A_LoopField, vCount := 1
		}
		else
			vCount++
		vLast := A_LoopField
	}
	return SubStr(vOutput, 1, -1)
}
==================================================

> BASICS: LOGICAL OPERATORS: ! && || (NOT/AND/OR)

- Here are the logical operators, listed by precedence.
- not: ! NOT
- and: && AND
- or: || OR

- Note: ! and NOT have different levels of precedence:
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#Operators

- ! (not) was covered in the 'TOGGLE' section above.
- !var returns 1 if var is a blank string or a number equal to 0, otherwise it returns 0.
- Here are a few examples of !:

Code: Select all

var := ""
MsgBox, % !var ;1
var := 0
MsgBox, % !var ;1
var := "abc"
MsgBox, % !var ;0
var := 123
MsgBox, % !var ;0

var := ""
MsgBox, % !!var ;0
var := 0
MsgBox, % !!var ;0
var := "abc"
MsgBox, % !!var ;1
var := 123
MsgBox, % !!var ;1

var := ""
MsgBox, % !!!var ;1
var := 0
MsgBox, % !!!var ;1
var := "abc"
MsgBox, % !!!var ;0
var := 123
MsgBox, % !!!var ;0
- (a || b) is true if either a or b is non-blank/non-zero, it is false if both are non-blank/non-zero.
- (a && b) is true if both a and b are non-blank/non-zero, it is false if either are non-blank/non-zero.
- In AHK v1, var := (a || b || c) would be equal to 1 or 0.
- In AHK v2, var := (a || b || c) would be equal to the first true operand (a/b/c), or the last false operand (c).
- In AHK v1, var := (a && b && c) would be equal to 1 or 0.
- In AHK v2, var := (a && b && c) would be equal to the last true operand (c), or the first false operand (a/b/c).

- Note: short-circuit Boolean evaluation occurs.
Functions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Functions.htm#ShortCircuit
Short-circuiting operates by refusing to evaluate parts of an expression that cannot possibly affect its final result.
- Some examples combining && and ||:

Code: Select all

;these 2 are essentially equivalent:
if !a && !b

if !(a || b)

;these 2 are essentially equivalent:
if (a || b)
	if c
		MsgBox, % "y"

if (a || b)
&& c
	MsgBox, % "y"
==================================================

> BASICS: SHORT-CIRCUIT BOOLEAN EVALUATION

- An explanation of short-circuit Boolean evaluation:

Code: Select all

;the 'short-circuit' refers to not evaluating
;certain things if they are not needed
;e.g. 'a && b && c'
;if a is 0, 'a && b && c' is 0, we don't need to check b or c
;if b is 0, 'a && b && c' is 0, we don't need to check c

;e.g. 'a || b || c'
;if a is 1, 'a || b || c' is 1, we don't need to check b or c
;if a is 0 and b is 1, 'a || b || c' is 1, we don't need to check c

;e.g. cond ? action1 : action2
;if cond is 1, action2 is not executed
;if cond is 0, action1 is not executed

;==============================

;for &&, items will be evaluated until failure:
a := b := c := "_"
if (a := 1) && (b := 1) && (c := 1)
	Sleep, 0
MsgBox, % a " " b " " c ;1 1 1

a := b := c := "_"
if (a := 1) && (b := 0) && (c := 1)
	Sleep, 0
MsgBox, % a " " b " " c ;1 0 _

;for ||, items will be evaluated until success:
a := b := c := "_"
if (a := 1) || (b := 1) || (c := 1)
	Sleep, 0
MsgBox, % a " " b " " c ;1 _ _

a := b := c := "_"
if (a := 0) || (b := 1) || (c := 1)
	Sleep, 0
MsgBox, % a " " b " " c ;0 1 _

;ternary operator, only the winning branch is evaluated:
a := b := "_"
1 ? (a := 1) : (b := 1)
	Sleep, 0
MsgBox, % a " " b ;1 _

a := b := "_"
0 ? (a := 1) : (b := 1)
	Sleep, 0
MsgBox, % a " " b ;_ 1
- Note: short-circuit Boolean evaluation is discussed here:
Functions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Functions.htm#ShortCircuit

==================================================

> BASICS: MODULO (MOD AND FLOORMOD)

- Everyday examples of modulo arithmetic:
- minutes/seconds (0 to 59)
- hours (0 to 23)
- weekdays (1 to 7) (or: 0 to 6)
- days of the month (e.g. 1 to 31)
- months (1 to 12)
- letters A to Z (1 to 26) (or: Chr(65) to Chr(90)) (e.g. used for: Caesar cipher, ROT13)
- letters a to z (1 to 26) (or: Chr(97) to Chr(122)) (e.g. used for: Caesar cipher, ROT13)
- see also: the 'CYCLE' section above

- Here are some examples demonstrating the Mod function.
- The definition is: Mod(Dividend, Divisor).
- The sign of the return value is based on the sign of the dividend.
- This behaviour matches the % operator in C++ and Java.
- This behaviour differs from the % operator in Python, aka FloorMod.

Code: Select all

vOutput := ""
vOutput .= Mod(16, 10) ", " ;6
vOutput .= Mod(16, -10) ", " ;6
vOutput .= Mod(-16, 10) ", " ;-6
vOutput .= Mod(-16, -10) ", " ;-6
MsgBox, % RTrim(vOutput, ",")
- Warning: The Mod function does not necessarily work as you might expect, with regard to numbers with fractional parts.
Mod function returning wrong value? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=14&t=29762

- Here are some examples demonstrating a custom FloorMod function.
- The definition is: FloorMod(Dividend, Divisor).
- The sign of the return value is based on the sign of the divisor.
- This behaviour differs from the % operator in C++ and Java.
- This behaviour matches the % operator in Python. It also matches Java's Math.floorMod.
- Warning: this custom FloorMod function may not handle floating-point numbers as the Mod function would.

Code: Select all

vOutput := ""
vOutput .= FloorMod(16, 10) ", " ;6
vOutput .= FloorMod(16, -10) ", " ;-4
vOutput .= FloorMod(-16, 10) ", " ;4
vOutput .= FloorMod(-16, -10) ", " ;-6
MsgBox, % RTrim(vOutput, ",")

FloorMod(vNum1, vNum2)
{
	return vNum1 - (Floor(vNum1 / vNum2) * vNum2)
}
==================================================

> BASICS: MODULO (EXAMPLES)

- An example: 1,2,3,4,5,6,7,8,9,0 to 1,2,3,4,5,6,7,8,9,10.

Code: Select all

;make the 0 key act like 10 (1 to 9 as normal)
;(0->10, 1->1, 2->2, ... 9->9)

;the following 2 approaches are equivalent:
vNum := (vNum = 0) ? 10 : vNum
vNum := Mod(vNum+9, 10) + 1

;logic:
;num -> num+9 -> mod(num+9,10) -> mod(num+9,10)+1
;0 -> 9 -> 9 -> 10
;1 -> 10 -> 0 ->  1
;2 -> 11 -> 1 ->  2
;...
;8 -> 17 -> 7 -> 8
;9 -> 18 -> 8 -> 9

;logic: range: 0-9 -> 9-18 -> 9,0-8 -> 10,1-9
- Some examples: move forwards/backwards by n.

Code: Select all

;increment/decrement 0-based integers:

;e.g. 0 to 6, move forward by d
vNum := Mod(vNum+d, 7)
;e.g. 0 to b, move forward by d
vNum := Mod(vNum+d, b+1)

;==============================

;increment/decrement 1-based integers:

;convert to 0-based, add difference, apply modulo, convert to 1-based
;e.g. 1 to 7, move forward by d
vNum := Mod(vNum-1+d, 7) + 1
;e.g. 1 to 7, move forward by 1
vNum := Mod(vNum, 7) + 1

;e.g. 1 to b, move forward by d
vNum := Mod(vNum-1+d, b) + 1
;e.g. 1 to b, move forward by 1
vNum := Mod(vNum, b) + 1

;==============================

;increment/decrement integers starting at 65 or 97:

;e.g. 65 to 90, move forward by d (Ord("A") to Ord("Z"))
vNum := Mod(vNum-65+d, 26) + 65
;e.g. 97 to 122, move forward by d (Ord("a") to Ord("z"))
vNum := Mod(vNum-97+d, 26) + 97

;tested where a, b and d are positive integers/zero:
;e.g. a to a+b, move forward by d
vNum := Mod(vNum-a+d, b+1) + a

;==============================

;e.g. numbers in the range 1 to 7, move number forwards by 3
;logic:
;vNum -> vNum-1 -> vNum-1+3 -> Mod(vNum-1+3,7) -> Mod(vNum-1+3,7)+1
;1 -> 0 -> 3 -> 3 -> 4
;2 -> 1 -> 4 -> 4 -> 5
;3 -> 2 -> 5 -> 5 -> 6
;4 -> 3 -> 6 -> 6 -> 7
;5 -> 4 -> 7 -> 0 -> 1
;6 -> 5 -> 8 -> 1 -> 2
;7 -> 6 -> 9 -> 2 -> 3

;logic: range: 1-7 -> 0-6 -> 3-9 -> 3-6,0-2 -> 4-7,1-3

;note: to move a number backwards by 3 (is the same as moving a number forwards by 4)

;==============================

;e.g. a to a+b, move forward by d
vNum := Mod(vNum-a+d, b+1) + a

;e.g. 11 to 20, move forward by 5
;which is 11 to 11+9, move forward by 5
;vNum := Mod(vNum-11+5, 9+1) + 11
;vNum := Mod(vNum-6, 10) + 11

vList := "11,12,13,14,15,16,17,18,19,20"
vOutput := ""
Loop, Parse, vList, % ","
{
	vOutput .= (Mod(A_LoopField-6, 10) + 11) ","
}
MsgBox, % SubStr(vOutput, 1, -1)
==================================================

> BASICS: MODULO (CAESAR CIPHER)

- An example demonstrating a Caesar cipher:

Code: Select all

vText := "AutoHotkey abcdefghijklmnopqrstuvwxyz"
vOutput := ""
Loop, 26
{
	vIndex := A_Index-1
	Loop, Parse, vText
	{
		vNum := Ord(A_LoopField)
		if (vNum >= 65) && (vNum <= 90)
			vOutput .= Chr(Mod(vNum-65+vIndex, 26)+65)
		else if (vNum >= 97) && (vNum <= 122)
			vOutput .= Chr(Mod(vNum-97+vIndex, 26)+97)
		else
			vOutput .= A_LoopField
	}
	vOutput .= "`r`n"
}
MsgBox, % vOutput
==================================================

> BASICS: RANGE FUNCTION (EXCEL VBA FOR LOOP)

- E.g. for loops in Excel VBA (Excel macros) and corresponding values:

Code: Select all

List = ""
For i = 1 To 5
	List = List & i & ","
Next
List = Left(List, Len(List) - 1)
MsgBox List

'For i = 1 To 5 '1,2,3,4,5
'For i = 1 To 1 '1
'For i = 2 To 10 Step 2 '2,4,6,8,10
'For i = 1 To 5 Step 0.9 '1,1.9,2.8,3.7,4.6
- This is also similar to performing a for loop with Python's Range function (which can only handle integers).
- Note: the Python 'Range' function is unintuitive, because the numbers produced stop *before* the end point, not *at* the end point.

Code: Select all

for i in range(1, 6): #1,2,3,4,5
	print(i)
for i in range(1, 2): #1
	print(i)
for i in range(2, 11, 2): #2,4,6,8,10
	print(i)
- Although AutoHotkey has a for loop, it does not work like Excel's for loop.
- However, you can achieve similar results by using a custom function e.g.:
Traditional For loop (i.e., step through a sequence) - Suggestions - AutoHotkey Community
https://autohotkey.com/board/topic/71225-traditional-for-loop-ie-step-through-a-sequence/#entry451619
- Or by using a special object class:
For loop question - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/66916-for-loop-question/#entry423515
- Various ideas are mentioned here (including a custom Range function):
traditional for loop: for i = a to b (step c) possibilities - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=43022

- Here is a simple example:

Code: Select all

;by manipulating A_Index in a standard AutoHotkey loop:

vStart := -5, vEnd := 5, vStep := 0.5
;note: '+ (vStep*0)' ensures that the first number has the same type (Integer/Float) as the later numbers:
vNum := vStart + (vStep*0)
Loop
{
	vOutput .= (A_Index=1?"":",") vNum
	vNum := vStart + (vStep*A_Index)
	if (vNum > vEnd)
		break
}
MsgBox, % vOutput
==================================================

> BASICS: RANDOM

- Some examples of choosing a random number:

Code: Select all

;e.g. choose an integer between 0 and 2147483647
;equivalent:
Random, vRand
Random, vRand, 0, 2147483647
MsgBox, % vRand

;e.g. choose an integer between 1 and 10
Random, vRand, 1, 10
MsgBox, % vRand

;e.g. choose a floating-point number between 0.0 and 1.0
Random, vRand, 0.0, 1.0
MsgBox, % vRand

;e.g. roll a dice ('roll a die')
vOutput := ""
Loop, 10
{
	Random, vNum, 1, 6
	vOutput .= (A_Index=1?"":",") vNum
}
MsgBox, % vOutput

;e.g. roll 2 dice
vOutput := ""
Loop, 10
{
	Random, vNum1, 1, 6
	Random, vNum2, 1, 6
	vOutput .= (A_Index=1?"":"`r`n") vNum1 "+" vNum2 "=" (vNum1+vNum2)
}
MsgBox, % vOutput
- Note: the numbers aren't random, they are pseudorandom. Based on the current time, a seed is chosen, random-looking numbers are generated based on that seed.
- The seed can be manually set via Random (AHK v1) or RandomSeed (AHK v2).

- Random/RandomSeed are recreated here:
generate a 'random' number - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=57482&p=247871#p247871

==================================================

> BASICS: RANDOM-LOOKING LISTS (USING PRIMITIVE ROOTS OF PRIME NUMBERS)

- The basic principle is this:
- We take a times table, but apply the mod function to it.
- For certain values we end up with the integers 1 to n, in a random-looking order.
- Some examples of creating random-looking lists:

Code: Select all

;Primitive root modulo n - Wikipedia
;https://en.wikipedia.org/wiki/Primitive_root_modulo_n
;[note: re. primitive roots of (non-prime) composite numbers]
;Why is 3 a primitive root of 4? : askmath
;https://www.reddit.com/r/askmath/comments/3vmj3d/why_is_3_a_primitive_root_of_4/

;using the primitive roots of prime numbers
;to generate a random-looking list of numbers:
;e.g. the numbers 1 to 10:
;find the smallest prime greater than 10: 11
;(e.g. type 'prime > 10' into wolframalpha.com)
;find the primitive roots of 11: 2,6,7,8
;(e.g. type 'primitive roots 11' into wolframalpha.com)
;we can thus generate lists for each primitive root like so:
;Mod(2,11), Mod(2**2,11), Mod(2**3,11), ..., Mod(2**10)
;2: 2,4,8,5,10,9,7,3,6,1
;6: 6,3,7,9,10,5,8,4,2,1
;7: 7,5,2,3,10,4,6,9,8,1
;8: 8,9,6,4,10,3,2,5,7,1
;note: '2,4,8' in the first example, doesn't look very random,
;it may be better to choose a larger primitive root

vOutput := ""
Loop, 10
	vOutput .= (A_Index=1?"":",") Mod(2**A_Index,11)
MsgBox, % vOutput

;==============================

;to create an image with 16777216 'randomly' coloured pixels

;using the primitive roots of prime numbers
;to generate a random-looking list of numbers:
;e.g. the numbers 0 to 16777215 (0x000000 to 0xFFFFFF):
;(note since we start at 0, 16777215+1=16777216 numbers are needed)
;find the smallest prime greater than 16777216: 16777259
;(e.g. type 'prime > 16777216' into wolframalpha.com)
;find the primitive roots of 16777259: 2,6,7,8,10,11 ...
;(e.g. type 'primitive roots 16777259' into wolframalpha.com)
;we can thus generate lists for each primitive root like so:
;Mod(2,16777259), Mod(2**2,16777259), Mod(2**3,16777259), ..., Mod(2**16777259)
;(note: AutoHotkey cannot cope with powers of 2 beyond a certain point,
;so we: multiply and take the mod, multiply and take the mod etc)
;when looping, as we generate each number,
;since the numbers would be in the range 1-16777258,
;and we want numbers in the range 0-16777215,
;we subtract 1 each time (because we're starting at 0),
;and we ignore any number greater than 16777215

;note: to store the numbers 1 to 16777258
;as a comma-separated list would take:
;139884218 bytes (133.4 MB)
;(16777258*8)-(9+99+999+9999+99999+999999+9999999)+(16777258-1)
;8 digits per number - omitted leading zeros + number of commas
;[see: 'Number of digits of concatenation of first n positive integers.']
;A058183 - OEIS
;https://oeis.org/A058183

;to check that all the numbers will occur are as expected:
;#MaxMem 300
vTemp := vPRoot := 2
vPrime := 16777259
vList1 := ""
VarSetCapacity(vList1, vPrime*StrLen(vPrime ",")*2)
vList2 := ""
VarSetCapacity(vList2, vPrime*StrLen(vPrime ",")*2)
Loop, % vPrime-1
{
	vTemp := Mod(vTemp*vPRoot,vPrime)
	vList1 .= A_Index ","
	vList2 .= vTemp ","
}
Sort, vList2, D, N
MsgBox, % (vList1 = vList2)

/*
;requires the Gdip_All library:
;GDI+ standard library 1.45 by tic - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=6517

;create bitmap
;this should produce a 48MB image
vPIDAhk := DllCall("kernel32\GetCurrentProcessId", "UInt")
Process, Priority, % vPIDAhk, High
vImgW := 4096, vImgH := 4096
pToken := Gdip_Startup()
pBitmap := Gdip_CreateBitmap(vImgW, vImgH)

vTemp := vPRoot := 2
vPrime := 16777259
vIndex := 0
Loop, % vPrime-1
{
	vTemp := Mod(vTemp*vPRoot, vPrime)
	vCol := vTemp-1
	if (vCol > 16777215)
		continue
	vIndex++
	vX := Mod(vIndex-1, 4096)
	vY := Floor((vIndex-1)/4096)
	Gdip_SetPixel(pBitmap, vX, vY, vCol)
}
vPath := A_Desktop "\z all pixel colours " A_Now ".png"
;PixelFormat24bppRGB := 0x00021808
pBitmap2 := Gdip_CloneBitmapArea(pBitmap, 0, 0, vImgW, vImgH, 0x21808)
Gdip_SaveBitmapToFile(pBitmap2, vPath, 100)
Gdip_DisposeImage(pBitmap)
Gdip_DisposeImage(pBitmap2)
Gdip_Shutdown(pToken)
*/
==================================================

> BASICS: AUTOHOTKEY LIMITS

- AutoHotkey stores numbers as Int64 (8-byte signed integers):
Range (hex): -0x8000000000000000 to 0x7FFFFFFFFFFFFFFF
Range (dec): -9223372036854775808 to 9223372036854775807
Max (hex): 0x7FFF FFFF FFFF FFFF
Max (dec): 9,223,372,036,854,775,807
Where: 0x8000000000000000 = (16**15)*8 = 2**63
Where: 0x7FFFFFFFFFFFFFFF = ((16**15)*8)-1 = (2**63)-1

- Nearby numbers for comparison:

Code: Select all

1024**6 = 1152921504606846976 ;within range [1 EiB (exbibyte) = 1024**6 bytes]
    20! = 2432902008176640000 ;within range
          9223372036854775807 ;limit
    21! = 51090942171709440000 ;too big
1024**7 = 1180591620717411303424 ;too big [1 ZiB (zebibyte) = 1024**7 bytes]
- Examples of numbers wrapping around:

Code: Select all

MsgBox, % 0x7FFFFFFFFFFFFFFF+0 ;9223372036854775807 ;max
MsgBox, % 0x7FFFFFFFFFFFFFFF+1 ;-9223372036854775808 ;wraps around

MsgBox, % -0x8000000000000000+0 ;-9223372036854775808 ;min
MsgBox, % -0x8000000000000000-1 ;9223372036854775807 ;wraps around
==================================================

> BASICS: ASSIGNMENT OPERATORS

- AutoHotkey's assignment operator for strings/numbers:
- :=
- Operators formed by appending an = sign:
- += -= *= /= //= |= &= ^= >>= <<=
- Note: the following operators are not operators formed by appending an = sign:
- ~= [RegEx match]
- != [not equals comparison]
- Note: the following operators do not exist:
- **= &&= ||=

- One other (string) assignment operator:
- .= [concatenate]

- Some warnings:
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#Operators
Known limitations caused by backward compatibility (these may be resolved in a future release):
1) When /= is the leftmost operator in an expression and it is not part of a multi-statement expression, it performs floor division unless one of the inputs is floating point (in all other cases, /= performs true division);
2) Date/time math is supported by += and -= only when that operator is the leftmost one on a line;
3) The operators +=, -=, and *= treat blank variables as zero, but only when they are alone on a line; for example, y:=1, x+=1 and MsgBox % x-=3 both produce a blank result when x is blank.
- Warning: In AHK v1, += and -= can be used with dates, but this will not be available in AHK v2.

Code: Select all

;assignment:
var := 2

;multiple assignment (chain the assignment operator):
a := b := c := 0

;equivalent to:
c := 0
b := c
a := b

;each pair of code lines is equivalent:
var += 2
var := var + 2

var -= 2
var := var - 2

var *= 2
var := var * 2

;warning in AHK v1: '/=' sometimes performs floor division
;the code lines are equivalent:
var /= 2
var := var / 2

;floor division (divide then round down):
;warning: in AHK v1: '//' and '//=' sometimes perform integer division (divide then round towards zero)
;the code lines are equivalent:
var //= 2
var := var // 2
var := Floor(var / 2)

;e.g.
MsgBox, % var := 5 // 2 ;2.5 -> 2
MsgBox, % var := Floor(5 / 2) ;2.5 -> 2
MsgBox, % var := 5 // -2 ;-2.5 -> 3
MsgBox, % var := Floor(5 / -2) ;-2.5 -> 3

;bitwise or/and/xor (exclusive or)
var := var | 2
var |= 2

var := var & 2
var &= 2

var := var ^ 2
var ^= 2

;bitshift
var := var >> 2
var >>= 2
var := var << 2
var <<= 2
==================================================

> BASICS: TRIGONOMETRY

- AutoHotkey has the following trigonometry functions available:
- Sin/Cos/Tan ASin/ACos/ATan.

- Here are some examples with Sin/ASin, and custom functions that allow you to specify 'd' for degrees:

Code: Select all

MsgBox, % Sin(30) "`r`n" SinEx(30, "d")
MsgBox, % ASin(0.5) "`r`n" ASinEx(0.5, "d")

SinEx(vAngle, vUnit:="")
{
	if (vUnit = "d")
		return Sin(vAngle * 0.01745329252) ;pi/180
	else
		return Sin(vAngle)
}
ASinEx(vNum, vUnit:="")
{
	if (vUnit = "d")
		return ASin(vNum) * 57.29578 ;180/pi
	else
		return ASin(vNum)
}
- AutoHotkey does not currently have an ATan2 function.
- But one can be created like so:

Code: Select all

;4-quadrant atan
;note: Y first, X second
ATan2(vNumY, vNumX)
{
	local
	return DllCall("msvcrt\atan2", "Double",vNumY, "Double",vNumX, "Cdecl Double")
}
- Some notes re. degrees/radians/pi.
Math functions - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Math.htm
Note: To convert a radians value to degrees, multiply it by 180/pi (approximately 57.29578). To convert a degrees value to radians, multiply it by pi/180 (approximately 0.01745329252). The value of pi (approximately 3.141592653589793) is 4 times the arctangent of 1.
- Here is a way to calculate the value of pi:

Code: Select all

vPi := 4 * ATan(1) ;3.141593

;why this works:
;given tan(y) = opp/adj = x, y = atan(x)
;so, atan(x) will give the size of an angle where opp/adj = x
;so, atan(1) will give the size of an angle where opp/adj = 1
;so, atan(1) = 45 degrees = 360/8 degrees = 2pi/8 radians = pi/4 radians
;since the AutoHotkey ATan function returns a value in radians
;thus ATan(1) = pi/4
;and so 4*ATan(1) = pi
- Some ideas/examples for trigonometry functions (including Radians/Degrees aka DegToRad/RadToDeg):
Math Functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=54366&p=234966#p234966

==================================================

> BASICS: LOGARITHMS

- Note: in AutoHotkey, the Log function uses base 10.
- In some programming languages, the Log function uses base e.

- Some simple Log / Ln examples:

Code: Select all

;10**x = 1000000
;so, x = Log(1000000)
MsgBox, % Log(1000000) ;6.000000 ;so 10**6 = 1000000

;note: e = approx. 2.71828182845905
;e**x = 1000000
;so, x = Ln(1000000)
MsgBox, % Ln(1000000) ;6.000000 ;so e**13.815511 = Exp(13.815511) = 1000000
- An example for base 2:

Code: Select all

;to solve: 2**x = 64

;2**x = 64
;so, x = log_2(64)
;note: in LaTeX, '_2' is notation for subscript 2

;in general:
;where a**b = c:
;b = log_a(c) = Log(c)/Log(a)
;and:
;b = log_a(c) = Ln(c)/Ln(a)

;so:
;where 2**x = 64:
;x = log_2(64) = Log(64)/Log(2)
;and:
;x = log_2(64) = Ln(64)/Ln(2)
MsgBox, % Log(64)/Log(2) ;6.000000
MsgBox, % Ln(64)/Ln(2) ;6.000000

;so, we started with the problem: 2**x = 64
;and we have the solution: x = 6
- Here is an alternate Log function, where you can specify the base:

Code: Select all

;e.g. to calculate x where : 2**x = 64
MsgBox, % LogEx(64, 2)

LogEx(vNum, vBase:=10)
{
	local
	if (vBase = 10) || (vBase = "")
		return Log(vNum)
	else if (vBase = "e")
		return Ln(vNum)
	else
		return Log(vNum)/Log(vBase)
		;return Ln(vNum)/Ln(vBase)
}
- For some more logarithm examples, see:
jeeswg's SoX (Sound eXchange) tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=29234

==================================================

> BASICS: 1-BASED INDEXES/0-BASED INDEXES

- With 1-based indexes, you start counting at 1.
- With 0-based indexes, you start counting at 0.
- 1-based indexes are generally more intuitive.
- If you use 1-based indexes to return a result, you can use 0 to indicate 'not found'/'error'.
- If you use 0-based indexes to return a result, it's typical to use -1 to indicate 'not found'/'error'.

- AHK uses 1-based indexes for string positions and array items:

Code: Select all

vText := "abc"
MsgBox, % InStr(vText, "c") ;3

oArray := ["a", "b", "c"]
MsgBox, % oArray.3 ;c
- Window messages for GUI windows/controls, generally use 0-based indexes for items in a control.
- Whereas AHK ControlXXX commands, generally use 1-based indexes.

Code: Select all

q:: ;Notepad Save As dialog - get index of selected ComboBox item (e.g. Encoding)
ControlGet, hCtl, Hwnd,, ComboBox3, A
SendMessage, 0x147, 0, 0,, % "ahk_id " hCtl ;CB_GETCURSEL := 0x147
vIndex := ErrorLevel
MsgBox, % vIndex
return
- RtlCompareMemory returns the index of the first difference, when comparing binary data.
- It is case-sensitive, and uses 0-based indexes.
- So, when comparing 'abcde' and 'abCde', it reports that the variables differ at byte 2 (the 3rd byte).

Code: Select all

vText1 := "abcde"
vText2 := "abCde"
VarSetCapacity(vData1, 6)
VarSetCapacity(vData2, 6)
StrPut(vText1, &vData1, "CP0")
StrPut(vText2, &vData2, "CP0")
vSize := 5
MsgBox, % DllCall("ntdll\RtlCompareMemory", "Ptr",&vData1, "Ptr",&vData2, "UPtr",vSize, "UPtr")
- Interface method numbers typically use 0-based indexes.
- E.g. the IColumnManager interface, mentioned here:
jeeswg's objects tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=29232

Code: Select all

0 QueryInterface
1 AddRef
2 Release
3 SetColumnInfo
4 GetColumnInfo
5 GetColumnCount
6 GetColumns
7 SetColumns
- Related to indexes is variable offsets.
- When writing to binary data, you write at: an address plus an offset.
- You start off at 0.
- Although this is somewhat 0-based, I regard this as reasonably intuitive, as it relates to position, not item number.

Code: Select all

VarSetCapacity(vData, 16, 0)
NumPut(11, &vData, 0, "Int")
NumPut(22, &vData, 4, "Int")
NumPut(33, &vData, 8, "Int")
NumPut(44, &vData, 12, "Int")
- Related to indexes is text selection.
- For text selection, you don't specify which characters are selected, you specify a position before/in-between/after a character.
- Where 0 is before the first character. 1 is after character 1. 2 is after character 2.
- Although this is somewhat 0-based, I regard this as reasonably intuitive, as it relates to position, not item number.

Code: Select all

q:: ;Notepad - get indexes of anchor/active points
ControlGet, hCtl, Hwnd,, Edit1, A
VarSetCapacity(vPos1, 4, 0), VarSetCapacity(vPos2, 4, 0)
SendMessage, 0xB0, % &vPos1, % &vPos2,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0 ;(left, right)
vPos1 := NumGet(&vPos1, 0, "UInt"), vPos2 := NumGet(&vPos2, 0, "UInt")
MsgBox, % vPos1 " " vPos2
return
==================================================

> BASICS: GENERAL TRICKS

- Note: many of these are covered in the 'BITWISE' sections which follow.
- All are covered in this tutorial.

- num & 1 to check if a number is odd/even.
- num ^ -1 equivalent to using the ~ bitwise-NOT operator.
- The bitwise-XOR operator, ^, to flip the bits of a number.
- num << 1 to double a number, and bitshifting in general to double/halve/multiply by a power of 2.
- The use of the primitive roots of prime numbers to create random-looking lists.
- To apply Mod on a range such as 1-26, shift it to 0-25, apply Mod, then shift it back.
- pi = 4 * ATan(1).
- You can round up by rounding down / round by rounding down.
- The use of masking and bitshifting to isolate parts of a number.
- A tip to multiply matrices: place them adjacently left/right, then move the second one upwards.
- For DllCall/NumGet/NumPut: when writing a number, sign doesn't matter, when reading a number, sign can matter.

==================================================

> BASICS: GENERAL WARNINGS

- Some general warnings re. maths in programming languages:
- Integers/Floats have size limits. Floats have accuracy limits. If you go beyond the limits, i.e. use big numbers, unexpected behaviour can occur. One possibility is that numbers wraparound from the highest to the lowest number, e.g. going from positive to negative.
- Floating-point numbers can give unexpected results when compared, or when Mod is applied.
- There are two common ways of doing Mod, like the C++ % operator (what AHK does, sign matches dividend), or like the Python % operator (FloorMod, more intuitive, sign matches divisor).
- Some programming languages will perform 5/2 differently depending on the variable types. E.g. integer division for integer types: 5/2 = 2, true division for float types: 5/2 = 2.5.
- There is true division (don't round), floor division (round down towards negative infinity), and integer division (round towards zero).
- Be aware of BODMAS/PEMDAS, and operator precedence in general, including for unary minus, and && v. || (in AHK, && takes precedence over ||).
- Programming languages often define 0**0 as 1.
- If you want to store numbers with exactly two decimal places, it's safer to store those numbers as integers, and add the decimal point only for presentation.
- ATan2(y, x) uses y first, then x.
- In some languages Log() uses base 10, in others, base e.
- Trigonometry functions typically operate using radians, not degrees.
- 'Random' numbers are pseudorandom.
- Short-circuit Boolean evaluation can affect whether certain code is executed, or not.
- If compilers notice certain things they may optimise how certain calculations are performed.
- Little endian. The number 0xAABBCCDD, will commonly be stored as bytes DD CC BB AA.
- Little endian. The bytes DD CC BB AA, will commonly be read as the number 0xAABBCCDD.
- Some programming languages allow chained operators: e.g. (a < b < c), would be interpreted as (a < b) && (b < c), whereas others, including AutoHotkey, interpret it as ((a < b) < c).
- Typically, in UK English, 'brackets' means round brackets/parentheses, in US English, 'brackets' means square brackets.
- The word for factorial in German, looks similar to 'faculty'.
- < > (less than/greater than), are called angle brackets when used in html.
- For screen coordinates: y increases as you go downwards. Cf. Cartesian coordinates.

==================================================

> BASICS: AHK V1 ODDITIES

- In AHK, = is *compare*, in many languages it is *assign*.
- Although, in AHK v1, in one-liners such as var = value, = is *assign*.

- If an error occurs, AHK v1 functions/operators typically return a blank string.

- For AHK v1, 0**0 returns 0. 1 is typically returned in most languages.

- Most AHK v1 mathematical operators/functions handle strings like so:
- They interpret the left bit of the string (or the entire string) as a number.
- It views "123abc" as 123.
- It views "0x10abc" as 0x10 (16).
- It views "abc" and "" as 0.
- E.g. increment:

Code: Select all

var := ""
var++
MsgBox, % var ;1

var := "123abc"
var++
MsgBox, % var ;124
- Of the following mathematical functions:
- (Abs), Ceil, Exp, Floor, Log, Ln, (Max), (Min), (Mod), Round, Sqrt, Sin, Cos, Tan, ASin, ACos, ATan.
- All use the unusual string handling except for: Abs, Max, Min, Mod.
- The most commonly used of the problem functions being Ceil/Floor/Round.
- Also, the functions typically don't show error messages, and return a blank string on failure.
- As demonstrated by this script:

Code: Select all

q:: ;test mathematical functions for string parsing
vList := "Abs,Ceil,Exp,Floor,Log,Ln,Max,Min,Mod,Round,Sqrt,Sin,Cos,Tan,ASin,ACos,ATan"
vOutput := ""
vText := "", vNum := 0
;vText := "1abc", vNum := 1
Loop, Parse, vList, % ","
{
	vTemp := A_LoopField
	if (vTemp = "Mod")
		vOutput .= vTemp " " %vTemp%(vNum, vNum) " " %vTemp%(vText, vText) "`r`n"
	else
		vOutput .= vTemp " " %vTemp%(vNum) " " %vTemp%(vText) "`r`n"
}
;Clipboard := vOutput
MsgBox, % vOutput
return
- The Random command still functions, when passed strings.

Code: Select all

Random, var, abc, abc
MsgBox, % var
- AutoHotkey does not show an error when an incomplete ternary operator is used.

Code: Select all

var := (1 = 1) ? "y" : "n"
MsgBox, % var ;y

var := (1 = 1) ? "y"
MsgBox, % var ;(blank)
- An issue relating to SetFormat:
(var1 = var2) inconsistent on different scripts - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=34103

- var++ applied to a non-numeric string works sometimes (undesirable), and returns blanks other times.

Code: Select all

var1 := ""
var1++
MsgBox, % var1 ;1

var1 := var2 := var3 := ""
var1++, var2++, var3++
MsgBox, % var1 " " var2 " " var3 ;(appears blank)

var1 := 0
var1++
MsgBox, % var1 ;1

var1 := var2 := var3 := 0
var1++, var2++, var3++
MsgBox, % var1 " " var2 " " var3 ;1 1 1
- The '!' and 'not' operators have a different level of precedence (relative to other operators).

- In AHK v1, the '/=' operator (true divide), sometimes performs floor division (divide then round towards negative infinity).
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#AssignOp
When /= is the leftmost operator in an expression and it is not part of a multi-statement expression, it performs floor division unless one of the inputs is floating point (in all other cases, /= performs true division);
- In AHK v1, the floor divide '//' and '//=' operators (divide then round towards negative infinity), sometimes perform integer division (divide then round towards zero).
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#MulDiv
The double-slash operator uses high-performance integer division if the two inputs are integers.
- AutoHotkey stores integers as signed 64-bit integers (Int64).
- There are workarounds to handle unsigned 64-bit integers (UInt64).
- You can store a UInt64 as an Int64.
- You can pass *strings* that look like a UInt64, to functions such as DllCall and NumPut, e.g. "18446744073709551615" and "0xFFFFFFFFFFFFFFFF".
- The Format function can be used to present an Int64 as a UInt64.
- The bitwise operators treat an Int64 as though it were a UInt64.

- Floats are displayed differently in 32-bit/64-bit AHK v1.
AutoHotkey 64-bit/32-bit differences - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=64044

==================================================

> BITWISE: SIGNED INTEGERS / UNSIGNED INTEGERS

- An explanation of signed/unsigned integers:

Code: Select all

;note: number presentation:

;in this section, numbers will be presented as follows:
;dec = hex = bin
;e.g. 255 = 0xFF = 11111111 (bin)
;e.g. 15 = 0xF = 1111 (bin)
;e.g. 1 = 0x1 = 1 (bin)

;==============================

;unsigned integers:

;a bit (binary digit) can store a number 0 or 1 (2**1 = 2 possibilities)
;a byte (8 bits) can store a number between 0 and 255 (2**8 = 256 possibilities)
;e.g. 00000000 (bin) to 11111111 (bin)

;let's say we wanted to be able to store negative numbers as well ...

;signed integers:

;this is *NOT* how signed integers work:
;the first bit indicates positive/negative
;the remaining 7 bits indicate a number between 0 and 127
;giving us -127 to 127 (and 0 twice)
;although this system is simple,
;computers use a different approach ...

;this *IS* how signed integers work:
;bin      dec
;00000000 0
;00000001 1
;00000010 2
;00000011 3
;...
;01111110 126
;01111111 127
;10000000 -128
;10000001 -127
;10000010 -126
;...
;11111101 -3
;11111110 -2
;11111111 -1
;giving us -128 to 127

;==============================

;so depending on whether we are using signed/unsigned integers,
;the byte can be interpreted in one of two ways:
;bin      dec (signed) [dec (unsigned)]
;00000000 0 [0]
;00000001 1 [1]
;00000010 2 [2]
;00000011 3 [3]
;...
;01111110 126 [126]
;01111111 127 [127]
;10000000 -128 [128]
;10000001 -127 [129]
;10000010 -126 [130]
;...
;11111101 -3 [253]
;11111110 -2 [254]
;11111111 -1 [255]

;==============================

;(note: this is really important)
;one handy way to consider signed v. unsigned integers is as follows:
;think of the leftmost bit as having a consistent *size*,
;but a positive value when unsigned,
;and a negative value when signed

;e.g. using a byte as an example:
;the leftmost bit has a consistent size of 128,
;but a value of 128 when unsigned,
;and -128 when signed

;11111111 bin unsigned: 128+64+32+16+8+4+2+1=255
;11111111 bin signed: -128+64+32+16+8+4+2+1=-1

;10000000 bin unsigned: 128
;10000000 bin signed: -128

;note: to write -1 as a signed integer, you fill the bits with a stream of 1s:
;11111111 bin unsigned: 128+64+32+16+8+4+2+1=255
;where 64+32+16+8+4+2+1 = 127
;11111111 bin signed: -128+64+32+16+8+4+2+1=-1
;this works because the size of the leftmost bit is always 1 larger than the sum of the other bits

;when the most significant bit is 0, the signed/unsigned integers are equal e.g.:

;01111111 bin unsigned: 0+64+32+16+8+4+2+1=127
;01111111 bin signed: 0+64+32+16+8+4+2+1=-127

;00000000 bin unsigned: 0
;00000000 bin signed: 0

;==============================

;to convert signed to unsigned, we add 128, and 128 again:
;11111111 bin signed: -128+64+32+16+8+4+2+1=-1
;                       0+64+32+16+8+4+2+1=127
;11111111 bin unsigned: 128+64+32+16+8+4+2+1=255

;to convert unsigned to signed, we subtract 128, and subtract 128 again:
;11111111 bin unsigned: 128+64+32+16+8+4+2+1=255
;                       0+64+32+16+8+4+2+1=127
;11111111 bin signed: -128+64+32+16+8+4+2+1=-1

;==============================

;to convert signed to unsigned:
if (S >= 0)
	U := S
else if (S < 0)
	U := S + 256

;to convert unsigned to signed:
if (U < 128)
	S := U
else if (U >= 128)
	S := U - 256

;==============================

;instead of using 1 byte:
;0 to 255 (or -128 to 127)

;we can apply the same principles to 2 bytes:
;0 to 65535 (or -32768 to 32767)

;we can apply the same principles to 4 bytes:
;0 to 4294967295 (or -2147483648 to 2147483647)

;we can apply the same principles to 8 bytes:
;0 to 18446744073709551615 (or -9223372036854775808 to 9223372036854775807)

;or we can apply the same principles to n bytes:
;0 to ((256**n)-1) (or -((256**n)/2) to (((256**n)/2)-1))

;==============================

;for the 2-byte version, we get the following formulas:
if (S >= 0)
	U := S
else if (S < 0)
	U := S + 65536

if (U < 32768)
	S := U
else if (U >= 32768)
	S := U - 65536

;for the n-byte version, we get the following formulas:
if (S >= 0)
	U := S
else if (S < 0)
	U := S + (256**n)

if (U < (256**n)/2)
	S := U
else if (U >= (256**n)/2)
	S := U - (256**n)

;note: 256**n for n = 1,2,4,8:
;256**1 = 256
;256**2 = 65536
;256**4 = 4294967296
;256**8 = 18446744073709551616
==================================================

> BITWISE: DLLCALL PARAMETER TYPES

- Some info on DllCall parameter types:

Code: Select all

;from:
;DllCall() - Syntax & Usage | AutoHotkey
;https://autohotkey.com/docs/commands/DllCall.htm
;Char: 'An 8-bit integer, whose range is -128 (-0x80) to 127 (0x7F)'
;Short: 'A 16-bit integer, whose range is -32768 (-0x8000) to 32767 (0x7FFF)'
;Int: 'A 32-bit integer (the most common integer type), whose range is -2147483648 (-0x80000000) to 2147483647 (0x7FFFFFFF)'
;Int64: 'A 64-bit integer, whose range is -9223372036854775808 (-0x8000000000000000) to 9223372036854775807 (0x7FFFFFFFFFFFFFFF)'

;UChar: An 8-bit integer, whose range is 0 to 255 (0xFF)
;UShort: A 16-bit integer, whose range is 0 0 to 65535 (0xFFFF)
;UInt: A 32-bit integer, whose range is 0 to 4294967295 (0xFFFFFFFF)
;UInt64: A 64-bit integer, whose range is 0 to 18446744073709551615 (0xFFFFFFFFFFFFFFFF)

;alternative names (signed):
;1 byte: Char
;2 bytes: Short
;4 bytes: Int / Long
;8 bytes: Int64

;alternative names (unsigned):
;1 byte: UChar / Byte
;2 bytes: UShort / ULong / Word
;4 bytes: UInt / DWord
;8 bytes: UInt64 / QWord

;note:
;the 'U' prefix for unsigned integers

;warning:
;AutoHotkey doesn't properly handle unsigned 64-bit integers,
;it stores integers as signed 64-bit integers

;note also:
;Ptr (e.g. LPARAM) and UPtr (e.g. WPARAM):
;Ptr: 'A pointer-sized integer, equivalent to Int or Int64 depending on whether the exe running the script is 32-bit or 64-bit'
;UPtr: A pointer-sized integer, equivalent to UInt or UInt64 (also depending on the script's bitness)

;note also:
;Float and Double, which are covered in a later section
;Float: 'A 32-bit floating point number, which provides 6 digits of precision.'
;Double: 'A 64-bit floating point number, which provides 15 digits of precision.'
==================================================

> BITWISE: NUMGET VERSUS NUMPUT

- Demonstrating the difference between NumGet and NumPut.

Code: Select all

;when writing a number, sign doesn't matter
;when reading a number, sign can matter

;i.e. if you send a number, and you don't know whether
;the receiver expects a signed or an unsigned integer,
;it doesn't matter which you send

;when you receive a number, if it's under the halfway point
;then there is no ambiguity, otherwise if you do not know
;whether the number you received was originally signed or unsigned,
;you may interpret the number incorrectly

;==============================

;writing a number:
VarSetCapacity(vData1, 1)
VarSetCapacity(vData2, 1)
VarSetCapacity(vData3, 1)
VarSetCapacity(vData4, 1)
NumPut(255, &vData1, 0, "UChar")
NumPut(-1, &vData2, 0, "UChar")
NumPut(255, &vData3, 0, "Char")
NumPut(-1, &vData4, 0, "Char")

;to demonstrate that the stored numbers are all identical,
;we retrieve all the values as though they are unsigned
;and they give the same results:
vOutput := NumGet(&vData1, 0, "UChar") " " NumGet(&vData2, 0, "UChar")
. " " NumGet(&vData3, 0, "UChar") " " NumGet(&vData4, 0, "UChar")
MsgBox, % vOutput

;to demonstrate that the stored numbers are all identical,
;we retrieve all the values as though they are signed
;and they give the same results:
vOutput := NumGet(&vData1, 0, "Char") " " NumGet(&vData2, 0, "Char")
. " " NumGet(&vData3, 0, "Char") " " NumGet(&vData4, 0, "Char")
MsgBox, % vOutput
==================================================

> BITWISE: TWO'S COMPLEMENT

- An explanation of two's complement:

Code: Select all

;in binary (base 2):
;any number can have a 1's complement and a 2's complement

;in decimal (base 10):
;any number can have a 9's complement and a 10's complement

;in base n:
;any number can have an (n-1)'s complement and an n's complement

;==============================

;e.g. in decimal (base 10):

;to find the 9's complement of 43210:
;work out '9 - d' for each digit,
;so the 9's complement of 43210 is 56789

;to find the 10's complement of 43210:
;add 1 to the 9's complement:
;so the 10's complement of 43210 is 56790

;==============================

;e.g. in binary (base 2):

;to find the 1's complement of 101 (bin):
;work out '1 - d' for each digit,
;so the 1's complement of 101 (bin) is 010 (bin) = 10 (bin)
;note: we flip the bits (0's to 1's, 1's to 0's)

;to find the 2's complement of 101 (bin):
;add 1 to the 1's complement:
;so the 2's complement of 101 (bin) is 11 (bin)

;==============================

;one reason that the two's complement (binary) is useful
;is that it allows us to convert negative signed integers to unsigned integers

;note: it is important to use the appropriate number of
;leading zeros, if we are working with 8-bit integers,
;the number should be 8 digits long:

;note: the following technique works for *negative* numbers only:

;e.g. signed 8-bit integer, -1, to unsigned 8-bit integer
;the absolute value of -1: 00000001 (bin) = 1
;1's complement: 11111110 (bin) = 254
;2's complement: 11111111 (bin) = 255
;so -1 (signed) = 255 (unsigned)

;e.g. signed 8-bit integer, -128, to unsigned 8-bit integer
;the absolute value of -128: 10000000 (bin) = 128
;1's complement: 01111111 (bin) = 127
;2's complement: 10000000 (bin) = 128
;so -128 (signed) = 128 (unsigned)
- See also:
algorithm - How to find N's complement of a number? - Stack Overflow
http://stackoverflow.com/questions/2303210/how-to-find-ns-complement-of-a-number
Two's complement - Wikipedia
https://en.wikipedia.org/wiki/Two%27s_complement

==================================================

> BITWISE: ~ (NOT)

- An explanation of the ~ (bitwise-not) operator:

Code: Select all

;in AutoHotkey:
;bitwise NOT is done by the operator ~

;~ returns the one's complement of a number, it flips the bits (0's to 1's, 1's to 0's)
;e.g. for 32-bit (4-byte) integers:
;e.g. 0 = 00000000000000000000000000000000 (bin)
;so, ~0 = 11111111111111111111111111111111 (bin) = 4294967295
;and, if you flip the bits again, thus the opposite is true:
;~4294967295 = 0

;note: the number of bits used is important, e.g. 32 or 64,
;it determines how many leading zeros to add

;for AHK v1:
;'If the operand is between 0 and 4294967295 (0xffffffff),
;it will be treated as an unsigned 32-bit value.'
MsgBox, % var := ~(-1) ;0
MsgBox, % var := ~0 ;4294967295 (0xFFFFFFFF) (-1) (11111111111111111111111111111111 (bin))
MsgBox, % var := ~1 ;4294967294 (0xFFFFFFFE) (-2) (11111111111111111111111111111110 (bin))
MsgBox, % var := ~0x40000000 ;3221225471 (0xBFFFFFFF)
MsgBox, % var := ~0x80000000 ;2147483647 (0x7FFFFFFF)
MsgBox, % var := ~0xC0000000 ;1073741823 (0x3FFFFFFF)
MsgBox, % var := ~0xFFFFFFFE ;1
MsgBox, % var := ~0xFFFFFFFF ;0

;for AHK v1:
;'Otherwise, it is treated as a signed 64-bit value.'
MsgBox, % var := ~0x100000000 ;-4294967297

;==============================

;often True is defined as 1, and False is defined as 0,
;note however that ~(-1) is 0, and ~0 is -1
;this is why sometimes True is defined as -1, rather than 1
- Note: ~ differs in AHK v1 and AHK v2.
- A two-way compatible alternative, that works like AHK v2 does, is: var := var ^ -1.

- See also:
Classic VB - Why is TRUE equal to -1 and not 1?-VBForums
http://www.vbforums.com/showthread.php?405047-Classic-VB-Why-is-TRUE-equal-to-1-and-not-1

==================================================

> BITWISE: ! VERSUS ~

- A comparison of logical not and bitwise not:

Code: Select all

;there two types of 'not':
;~ (bitwise-not) as explained above,
;and the more straightforward ! (logical not)

MsgBox, % var := !0 ;1
MsgBox, % var := !!0 ;0
MsgBox, % var := !!!0 ;1
MsgBox, % var := !!!!0 ;0

MsgBox, % var := !!0 ;0
MsgBox, % var := !!"" ;0
MsgBox, % var := !!0x0 ;0
MsgBox, % var := !!0.0 ;0

MsgBox, % var := !!1 ;1
MsgBox, % var := !!5 ;1
MsgBox, % var := !!-1 ;1
MsgBox, % var := !!(-1) ;1
MsgBox, % var := !!"abc" ;1
- See also: 'IF (NON-ZERO) (NON-BLANK)', for more details about what is considered zero/blank.

==================================================

> BITWISE: & | ^ (AND/OR/XOR)

- An explanation of the & | ^ (bitwise and/or/xor) operators:

Code: Select all

;in AutoHotkey:
;- bitwise AND is done by the operator &
;- bitwise OR is done by the operator |
;- bitwise XOR (exclusive or) is done by the operator ^

;e.g.
MsgBox, % 44 & 38 ;36
MsgBox, % 44 | 38 ;46
MsgBox, % 44 ^ 38 ;10

;for &, bitwise AND, if both bits in a column are 1, write 1, else 0
;   44: 101100
;   38: 100110
;44&38: 100100 ;36 in decimal

;for |, bitwise OR, if at least one bit in a column is 1, write 1, else 0
;   44: 101100
;   38: 100110
;44|38: 101110 ;46 in decimal

;note: in MS Excel ^ is used for exponents e.g. 2^3 = 8
;for ^, bitwise XOR, if exactly one bit in a column is 1, write 1, else 0
;   44: 101100
;   38: 100110
;44^38: 001010 ;10 in decimal

;==============================

;examples of masking using bitwise AND/bitwise OR:
;         0xFF0000: 11111111 00000000 00000000
;         0xABCDEF: 10101011 11001101 11101111
;0xFF0000&0xABCDEF: 10101011 00000000 00000000 ;0xAB0000
;0xFF0000|0xABCDEF: 11111111 11001101 11101111 ;0xFFCDEF

;==============================

;similar to BODMAS (B/O/DM/AS) / PEMDAS (P/E/MD/AS)
;you can use the phrase 'and/or' as a mnemonic:
;(and, exclusive or, or)
;(where the '/' is like a slash in the X of XOR)
;or you can just use round brackets for clarity:

;these are equivalent:
MsgBox, % 1 | 2 ^ 3 & 4 ;3
MsgBox, % 1 | (2 ^ (3 & 4)) ;3

;this is different:
MsgBox, % ((1 | 2) ^ 3) & 4 ;0
- Some links (bitwise operations):
Bitwise-and Bitwise-or - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/74552-bitwise-and-bitwise-or/
Bitwise Operators - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=1744
Bitwise operation - Wikipedia
https://en.wikipedia.org/wiki/Bitwise_operation
Low Level Bit Hacks You Absolutely Must Know - good coders code, great coders reuse
http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/
bithacks.h - bit hacks header file - good coders code, great coders reuse
http://www.catonmat.net/blog/bit-hacks-header-file/
Mask (computing) - Wikipedia
https://en.wikipedia.org/wiki/Mask_(computing)

==================================================

> BITWISE: WINDOW STYLES

- Here are some bitwise-and examples for use with window styles:

Code: Select all

;here are some examples of checking for the presence
;of a particular window style:

WinGet, vWinStyle, Style, A
vOutput := ""
;styles:
(vWinStyle & 0x10000) && (vOutput .= "WS_MAXIMIZEBOX`r`n")
(vWinStyle & 0x20000) && (vOutput .= "WS_MINIMIZEBOX`r`n")
(vWinStyle & 0x80000) && (vOutput .= "WS_SYSMENU`r`n")
(vWinStyle & 0x10000000) && (vOutput .= "WS_VISIBLE`r`n")
;composite styles:
(vWinStyle & 0xC00000 = 0xC00000) && (vOutput .= "WS_CAPTION`r`n")
(vWinStyle & 0xCF0000 = 0xCF0000) && (vOutput .= "WS_OVERLAPPEDWINDOW`r`n")
MsgBox, % SubStr(vOutput, 1, -2)

;==============================

;note: if we observe the styles in binary:
;- for the first 4 examples, since only one switch is set to on,
;to confirm the style is present we can either do:
;'if styles & style > 0' or 'if styles & style = style'
;- for the last 2 examples, since multiple switches are set to on,
;to confirm the style is present we must do:
;'if styles & style = style'

;00000000000000010000000000000000 WS_MAXIMIZEBOX
;00000000000000100000000000000000 WS_MINIMIZEBOX
;00000000000010000000000000000000 WS_SYSMENU
;00010000000000000000000000000000 WS_VISIBLE
;00000000110000000000000000000000 WS_CAPTION
;00000000110011110000000000000000 WS_OVERLAPPEDWINDOW
==================================================

> BITWISE: & VERSUS &&, | VERSUS ||

- Comparing & versus &&, | versus ||:

Code: Select all

MsgBox, % 44 & 38 ;36
MsgBox, % 44 | 38 ;46
MsgBox, % 44 && 38 ;1 (AHK v1) ;38 (AHK v2)
MsgBox, % 44 || 38 ;1 (AHK v1) ;44 (AHK v2)
MsgBox, % 44 AND 38 ;1 (AHK v1) ;38 (AHK v2) ;same as &&
MsgBox, % 44 OR 38 ;1 (AHK v1) ;44 (AHK v2) ;same as ||

;& and | do bitwise AND/OR comparisons
;they look at the binary representation of each number
;and compare bits

;&&
;in AHK v1:
;e.g. a && b, will return 1 if both variables
;are non-zero/non-blank or 0 otherwise
;e.g. a && b && c, will return 1 if all 3 variables
;are non-zero/non-blank or 0 otherwise
;in AHK v2:
;e.g. a && b, will return the value of b if both variables
;are non-zero/non-blank, or the first false operand (a/b) otherwise
;e.g. a && b && c, will return the value of c if all 3 variables
;are non-zero/non-blank, or the first false operand (a/b/c) otherwise

;||
;in AHK v1:
;e.g. a || b, will return 1 if at least one variable
;is non-zero/non-blank, or 0 otherwise
;e.g. a || b || c, will return 1 if at least one variable
;is non-zero/non-blank, or 0 otherwise
;in AHK v2:
;e.g. a || b, will return the value of of the first variable
;that is non-zero/non-blank, or b otherwise
;e.g. a || b || c, will return the value of of the first variable
;that is non-zero/non-blank, or c otherwise

;==============================

;according to this link which lists all operators,
;'in descending precedence order':
;Variables and Expressions - Definition & Usage | AutoHotkey
;https://autohotkey.com/docs/Variables.htm#Operators

;&& has precedence over ||, so:

;the following are equivalent:
a:=1, b:=0, c:=0
MsgBox, % a || b && c ;1
MsgBox, % a || (b && c) ;1

;this is different:
a:=1, b:=0, c:=0
MsgBox, % (a || b) && c ;0
==================================================

> BITWISE: << >> (BITSHIFT LEFT / BITSHIFT RIGHT)

- An explanation of bitshifting:

Code: Select all

;think of a positive integer written in binary:
;bitshift right: move each bit right and discard the rightmost bit
;bitshift right: divide by 2 and discard the remainder
;bitshift left: move each bit left and append a zero
;bitshift left: multiply by 2

;note: if there is a maximum number of bits, a bitshift left,
;can result in the leftmost bit being discarded

;a bitshift of 8 or 16 is often used, to prepare a struct
;with a certain number of bytes,
;see the section: 'BITWISE: RGB / BGR'

;bitshift right:
;   15: 1111
;15>>1:  111 ;Floor(15/(2**1)) = Floor(15/2)  = 7
;15>>2:   11 ;Floor(15/(2**2)) = Floor(15/4)  = 3
;15>>3:    1 ;Floor(15/(2**3)) = Floor(15/8)  = 1
;15>>4:    0 ;Floor(15/(2**4)) = Floor(15/16) = 0

;0xAB0000    : 101010110000000000000000
;0xAB0000>>8 :         1010101100000000 ;discard 8 bits (1 byte)
;0xAB0000>>16:                 10101011 ;discard 16 bits (2 bytes)

;bitshift left:
;   15:     1111
;15<<1:    11110 ;15*(2**1) = 15*2  = 30
;15<<2:   111100 ;15*(2**2) = 15*4  = 60
;15<<3:  1111000 ;15*(2**3) = 15*8  = 120
;15<<4: 11110000 ;15*(2**4) = 15*16 = 240

;0xAB    :                 10101011
;0xAB<<8 :         1010101100000000 ;double it 8 times (multiply by 256) (move up 1 byte)
;0xAB<<16: 101010110000000000000000 ;double it 16 times (multiply by 65536) (move up 2 bytes)

;==============================

;bitshift left by 1 bit, i.e. multiply by 2
var := var << 1
;e.g. 0x1000 -> 0x2000
;e.g. 0x2000 -> 0x4000
;e.g. 0x4000 -> 0x8000
;e.g. 0x8000 -> 0x10000

;bitshift left by 1 byte (8 bits), i.e. multiply by 2**8
var := var << 8
;e.g. 0x1234 -> 0x123400

;bitshift left by 2 bytes (16 bits), i.e. multiply by 2**16
var := var << 16
;e.g. 0x1234 -> 0x12340000

;bitshift right by 1 bit,
;i.e. divide by 2 (and round down to nearest integer)
var := var >> 1
;e.g. 5 -> 2
;e.g. 2 -> 1
;e.g. 1 -> 0

;bitshift right by 1 byte,
;i.e. divide by 2**8 (and round down to nearest integer)
var := var >> 8
;e.g. 0x123400 -> 0x1234

;bitshift right 2 bytes,
;i.e. divide by 2**16 (and round down to nearest integer)
var := var >> 16
;e.g. 0x12340000 -> 0x1234
==================================================

> BITWISE: NUMGET/NUMPUT AND ENDIANNESS

- An explanation of endianness with respect to NumGet/NumPut:

Code: Select all

;endianness:
;in basic mathematics:
;usually '123' means:
;1 hundred, 2 tens, 3 units
;this interpretation is 'big endian',
;'big' (more significant) numbers appear at the start

;in basic mathematics:
;this is *never* usually done, but we could interpret '123' as:
;1 unit, 2 tens, 3 hundreds
;this interpretation is 'little endian',
;'little' (less significant) numbers appear at the start

;with (U)Short/(U)Int/(U)Int64 integers,
;the 8 bits within a byte are listed as big endian,
;however the bytes themselves are listed as little endian

;e.g. 0x1234:
;0x12 = 00010010 (bin) = 18
;0x34 = 00110100 (bin) = 52
;0x1234 = 00010010 00110100 (bin) (bytes big endian) [stored as: 12 34 (hex)]
;0x1234 = 00110100 00010010 (bin) (bytes little endian) [stored as: 34 12 (hex)]

;==============================

;let's say I create an 8-byte structure:
VarSetCapacity(vData, 8)
NumPut(0x12, &vData, 0, "UChar")
NumPut(0x34, &vData, 1, "UChar")
NumPut(0x56, &vData, 2, "UChar")
NumPut(0x78, &vData, 3, "UChar")
NumPut(0x9A, &vData, 4, "UChar")
NumPut(0xBC, &vData, 5, "UChar")
NumPut(0xDE, &vData, 6, "UChar")
NumPut(0xF0, &vData, 7, "UChar")

;the data in Hex will appear like this:
;from first byte to last byte:
;12 34 56 78 9A BC DE F0

;but watch what happens when I retrieve
;the first 1/2/4/8 bytes using NumGet:
vByte := NumGet(&vData, 0, "UChar") ;0x12
vWord := NumGet(&vData, 0, "UShort") ;0x3412
vDWord := NumGet(&vData, 0, "UInt") ;0x78563412
vQWord := NumGet(&vData, 0, "UInt64") ;0xF0DEBC9A78563412
MsgBox, % Format("0x{1:X} 0x{2:X} 0x{3:X} 0x{4:X}", vByte, vWord, vDWord, vQWord)

;for UShort/UInt/UInt64,
;it looks as though it is retrieving the bytes in the wrong order,
;this is because those integers use bytes in little endian order,
;so the binary data '12 34' is viewed as the UShort integer 0x3412,
;and the UShort integer 0x3412 is stored as binary data '12 34'

;==============================

;if we want to read binary data as a hex string,
;i.e. from left-to-right,
;we can instead use CryptBinaryToString:
VarSetCapacity(vData, 8)
NumPut(0xF0DEBC9A78563412, &vData, 0, "UInt64")
vSize := 8
;CRYPT_STRING_HEX := 0x4 ;to return space/CRLF-separated text
;CRYPT_STRING_HEXRAW := 0xC ;to return raw hex (not supported by Windows XP)
DllCall("crypt32\CryptBinaryToString", "Ptr",&vData, "UInt",vSize, "UInt",0x4, "Ptr",0, "UInt*",vChars)
VarSetCapacity(vHex, vChars*2, 0)
DllCall("crypt32\CryptBinaryToString", "Ptr",&vData, "UInt",vSize, "UInt",0x4, "Str",vHex, "UInt*",vChars)
MsgBox, % vHex

;if we want to write bytes using (U)Short/(U)Int/(U)Int64,
;we must remember to swap the bytes:
;so to write '12 34 56 78 9A BC DE F0', we do the following:
VarSetCapacity(vData, 8)
NumPut(0xF0DEBC9A78563412, &vData, 0, "UInt64")
vQWord := NumGet(&vData, 0, "UInt64") ;0xF0DEBC9A78563412
MsgBox, % Format("0x{:X}", vQWord)

;or alternatively to convert a hex string to binary:
;we can instead use CryptStringToBinary:
vHex := "12 34 56 78 9A BC DE F0"
vChars := StrLen(vHex)
;CRYPT_STRING_HEX := 0x4
;CRYPT_STRING_HEXRAW := 0xC ;(not supported by Windows XP)
DllCall("crypt32\CryptStringToBinary", "Ptr",&vHex, "UInt",vChars, "UInt",0x4, "Ptr",0, "UInt*",vSize, "Ptr",0, "Ptr",0)
VarSetCapacity(vData, vSize, 0)
DllCall("crypt32\CryptStringToBinary", "Ptr",&vHex, "UInt",vChars, "UInt",0x4, "Ptr",&vData, "UInt*",vSize, "Ptr",0, "Ptr",0)
vQWord := NumGet(&vData, 0, "UInt64") ;0xF0DEBC9A78563412
MsgBox, % Format("0x{:X}", vQWord)
==================================================

> BITWISE: UNICODE (UTF-16 LITTLE ENDIAN)

- An explanation of how characters are stored in UTF-16:

Code: Select all

;there are two common Unicode formats:
;UTF-8, and UTF-16 (little endian),
;Notepad describes UTF-16 little endian simply as 'Unicode',
;Unicode versions of AutoHotkey store strings as UTF-16 little endian

;we saw in the section 'BITWISE: NUMGET/NUMPUT AND ENDIANNESS',
;that NumGet/NumPut reverse the order of bytes,
;however, if the bytes are already 'reversed'
;because they are in little endian order,
;then the fact NumGet/NumPut reverses the order of bytes
;can actually save effort in this case

;UTF-16 little endian stores each character between 0 and 65535,
;as a UShort, taking up two bytes, with the bytes in little endian order
;e.g. the square root symbol is character 8730 (0x221A)
;since the encoding is little endian it is stored as '1A 22',
;this is convenient for use with NumGet and NumPut:

vText := Chr(8730) ;square root sign
MsgBox, % NumGet(&vText, 0, "UShort") ;8730 ;first Unicode character
MsgBox, % NumGet(&vText, 0, "UChar") ;26 (0x1A) ;first byte
MsgBox, % NumGet(&vText, 1, "UChar") ;34 (0x22) ;second byte

;if the encoding was big endian, we would have to get
;each byte separately and perform bitshifts,
;we couldn't get the number using NumGet and UShort directly,
;to store the square root in big endian encoding
;and retrieve it via NumGet we would do this:
vText := Chr(0x1A22)
MsgBox, % vByte1 := NumGet(&vText, 0, "UChar") ;34 (0x22)
MsgBox, % vByte2 := NumGet(&vText, 1, "UChar") ;26 (0x1A)
MsgBox, % (vByte1 << 8)|vByte2 ;8730

;e.g. 'abc' is Chr(97), Chr(98), Chr(99)
;or in hex: Chr(0x0061), Chr(0x0062), Chr(0x0063)
;so it will be stored as '61 00 62 00 63 00'

;this example will only work in Unicode versions of AutoHotkey:
vText := "abc"
MsgBox, % NumGet(&vText, 0, "UShort") ;97 ;first Unicode character
MsgBox, % NumGet(&vText, 0, "UChar") ;97 ;first byte
MsgBox, % NumGet(&vText, 1, "UChar") ;0 ;second byte

;this version will work in Unicode and ANSI versions:
VarSetCapacity(vText, 6)
StrPut("abc", &vText, "UTF-16")
MsgBox, % NumGet(&vText, 0, "UShort") ;97 ;first Unicode character
MsgBox, % NumGet(&vText, 0, "UChar") ;97 ;first byte
MsgBox, % NumGet(&vText, 1, "UChar") ;0 ;second byte
- See these sections also:
- 'FURTHER: UNICODE: UTF-8'
- 'FURTHER: UNICODE: UTF-16 LITTLE ENDIAN'

==================================================

> BITWISE: BINARY DATA VERSUS VARIABLES (GET BYTES)

- Some notes on storing numbers as an integer type versus as binary data, and retrieving the bytes.

Code: Select all

;numbers can be stored as binary data or as a value

;binary data:
VarSetCapacity(vData, 4)
NumPut(0x78, &vData, 0, "UChar")
NumPut(0x56, &vData, 1, "UChar")
NumPut(0x34, &vData, 2, "UChar")
NumPut(0x12, &vData, 3, "UChar")
vByte1 := NumGet(&vData, 0, "UChar")
vByte2 := NumGet(&vData, 1, "UChar")
vByte3 := NumGet(&vData, 2, "UChar")
vByte4 := NumGet(&vData, 3, "UChar")
vNum := NumGet(&vData, 0, "UInt")
MsgBox, % Format("0x{:X}", vNum) "`r`n" Format("0x{1:X} 0x{2:X} 0x{3:X} 0x{4:X}", vByte1, vByte2, vByte3, vByte4)

;value:
vNum := 0x12345678
vByte1 := vNum & 0xFF ;gets '78'
vByte2 := (vNum >> 8) & 0xFF ;get '56'
vByte3 := (vNum >> 16) & 0xFF ;gets '34'
vByte4 := (vNum >> 24) & 0xFF ;gets '12'
MsgBox, % Format("0x{:X}", vNum) "`r`n" Format("0x{1:X} 0x{2:X} 0x{3:X} 0x{4:X}", vByte1, vByte2, vByte3, vByte4)
==================================================

> BITWISE: NUMGET/NUMPUT AND READING FROM STRUCTS

- Some examples of using NumGet/NumPut with binary data structs:

Code: Select all

;binary data types such as Short/UShort/Int/UInt
;were discussed in previous sections

;we can have a struct, a binary structure,
;which is a like a big data type,
;with lots of smaller data types inside it

;two common structs are:
;- the 8-byte POINT struct (2 4-byte Int types)
;used to specify the (x,y) coordinates of a point
;- the 16-byte RECT struct (4 4-bytes Int types)
;used to specify the 4 dimensions of a rectangle

;this script will get the position of the cursor:
VarSetCapacity(POINT, 8)
DllCall("user32\GetCursorPos", "Ptr",&POINT)
vCurX := NumGet(&POINT, 0, "Int") ;x-coordinate
vCurY := NumGet(&POINT, 4, "Int") ;y-coordinate
MsgBox, % vCurX " " vCurY

;this script will get the position/size of the active window:
WinGet, hWnd, ID, A
VarSetCapacity(RECT, 16)
DllCall("user32\GetWindowRect", "Ptr",hWnd, "Ptr",&RECT)
vWinX := NumGet(&RECT, 0, "Int") ;top-left corner x-coordinate
vWinY := NumGet(&RECT, 4, "Int") ;top-left corner y-coordinate
vWinR := NumGet(&RECT, 8, "Int") ;bottom-right corner x-coordinate
vWinB := NumGet(&RECT, 12, "Int") ;bottom-right corner y-coordinate
vWinW := vWinR - vWinX ;width
vWinH := vWinB - vWinY ;height
MsgBox, % vWinX " " vWinY " " vWinR " " vWinB " " vWinW " " vWinH
==================================================

> BITWISE: BINARY STRUCTURES AND ALIGNMENT (OFFSETS)

- Some notes on binary structs and the alignment (offsets) of struct members:

Code: Select all

;e.g. MEMORY_BASIC_INFORMATION
;for a 64-bit application (x64), its size is 48 bytes
;for a 32-bit application (x32), its size is 28 bytes

;typedef struct _MEMORY_BASIC_INFORMATION {
;  PVOID  BaseAddress;
;  PVOID  AllocationBase;
;  DWORD  AllocationProtect;
;  SIZE_T RegionSize;
;  DWORD  State;
;  DWORD  Protect;
;  DWORD  Type;
;} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

;first we get the parameter types:
;Ptr, Ptr, UInt, UPtr, UInt, UInt, UInt

;then we get the parameter sizes:
;for x64: 8, 8, 4, 8, 4, 4, 4 (sum: 40)
;for x32: 4, 4, 4, 4, 4, 4, 4 (sum: 28)

;however for both x64 and x32 applications:
;an n-byte parameter must start an n-byte offset,
;i.e. at an offset that is a multiple of n,
;we add padding to the x64 struct:
;for x64: 8, 8, 4(+4), 8, 4, 4, 4 (sum: 44)
;for x32: 4, 4, 4,     4, 4, 4, 4 (sum: 28)

;also, the size of a structure, must be a multiple
;of the size of the largest parameter,
;we add padding at the end of the x64 struct:
;for x64: 8, 8, 4(+4), 8, 4, 4, 4(+4) (sum: 48)
;for x32: 4, 4, 4,     4, 4, 4, 4     (sum: 28)

;offsets:
;for x64: 0, 8, 16, 24, 32, 36, 40 (sum: 48)
;for x32: 0, 4, 8,  12, 16, 20, 24 (sum: 28)

;==============================

;the two rules about multiples only apply to raw parameters
;of size: 1, 2, 4, 8 bytes

;e.g. an ACCEL struct (1(+1), 2, 2)
;if an ACCEL struct (6 bytes) appears within another struct
;its biggest raw parameter is a 2-byte WORD,
;so it must start at a multiple of 2 bytes
;and the overall struct size must be a multiple of 2 bytes

;e.g. a PANOSE struct (1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
;if a PANOSE struct (10 bytes) appears within another struct
;its biggest raw parameter is a 1-byte BYTE,
;so it must start at a multiple of 1 bytes (at any position)
;and the overall struct size must be a multiple of 1 byte (any size)

;e.g. an RGBQUAD struct (1, 1, 1, 1)
;if an RGBQUAD struct (4 bytes) appears within another struct
;its biggest raw parameter is a 1-byte BYTE,
;so it must start at a multiple of 1 bytes (at any position)
;and the overall struct size must be a multiple of 1 byte (any size)
;(note: a 4-byte RGBQUAD struct is not a raw 4-byte parameter)
- For more information about binary structures (structs):
key dll issues: calls and structs - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=26406

- For examples of structs:
list of structs with parameters (sizes and types) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=30497

==================================================

> BITWISE: IDENTIFY A FILE TYPE

- Reading the initial bytes of a file, to identify the file type:

Code: Select all

;for many filetypes, the first few bytes of the file
;identify the file type:
;e.g. bmp files start with 'BM' ('42 4D'),
;and gif files start with 'GIF' ('47 49 46')

;text files often have a BOM (byte order mark),
;to identify the encoding of the file:
;- UTF-8 text files start with bytes: 239,187,191 'EF BB BF'
;- UTF-16 little endian text files start with bytes: 255,254 'FF FE'

;e.g. to check a file is UTF-8/UTF-16
;note: ReadXXX reverses the order of bytes
;note: to check the first 3 bytes, we read 4 bytes and ignore the 4th byte
vPath := A_ScriptFullPath
;vPath = A_Desktop "\utf8.txt"
oFile := FileOpen(vPath, "r")
oFile.Seek(0, 0)
vNum := oFile.ReadUInt()
if (oFile.ReadUShort() = 0xFEFF)
	MsgBox, % "UTF-16 LE"
else if (vNum & 0xFFFFFF = 0xBFBBEF)
	MsgBox, % "UTF-8"
else
	MsgBox, % "other"
oFile.Close()
==================================================

> BITWISE: BITSHIFT AND NEGATIVE NUMBERS

- When bitshifting numbers, in order to combine them, you have to be careful when handling negative numbers.

Code: Select all

;note: masking is using bitwise-and with a number

;an example of HiByte and LoByte to Word
;to show when masking is/isn't necessary
;e.g. for HiByte = LoByte = -10, we should get Word = 0xF6F6
;see below: 'BITWISE: HIBYTE/LOBYTE TO/FROM WORD'
vNum := -10
vOutput := ""

vNum1 := vNum << 8
vNum2 := vNum
vNum3 := (vNum1|vNum2) & 0xFFFF
vOutput .= Format("0x{:X}", vNum3) "`r`n" ;0xFFF6

vNum1 := (vNum & 0xFF) << 8 ;this masking isn't needed
vNum2 := vNum
vNum3 := (vNum1|vNum2) & 0xFFFF
vOutput .= Format("0x{:X}", vNum3) "`r`n" ;0xFFF6

vNum1 := vNum << 8
vNum2 := (vNum & 0xFF) ;this masking is needed
vNum3 := (vNum1|vNum2) & 0xFFFF
vOutput .= Format("0x{:X}", vNum3) "`r`n" ;0xF6F6

MsgBox, % SubStr(vOutput, 1, -2)

;==============================

;an example of HiWord and LoWord to DWord
;to show when masking is/isn't necessary
;e.g. for HiWord = LoWord = -10, we should get DWord = 0xFFF6FFF6
;see below: 'BITWISE: HIWORD/LOWORD TO/FROM DWORD'
vNum := -10
vOutput := ""

vNum1 := vNum << 16
vNum2 := vNum
vNum3 := (vNum1|vNum2) & 0xFFFFFFFF
vOutput .= Format("0x{:X}", vNum3) "`r`n" ;0xFFFFFFF6

vNum1 := (vNum & 0xFFFF) << 16 ;this masking isn't needed
vNum2 := vNum
vNum3 := (vNum1|vNum2) & 0xFFFFFFFF
vOutput .= Format("0x{:X}", vNum3) "`r`n" ;0xFFFFFFF6

vNum1 := vNum << 16
vNum2 := (vNum & 0xFFFF)
vNum3 := (vNum1|vNum2) & 0xFFFFFFFF ;this masking is needed
vOutput .= Format("0x{:X}", vNum3) "`r`n" ;0xFFF6FFF6

MsgBox, % SubStr(vOutput, 1, -2)
==================================================

> BITWISE: HIBYTE/LOBYTE TO/FROM WORD

- Some code re. high/low bytes to/from a two-byte word.
- The logic would be used to create HiByte/LoByte/MakeWord functions.

Code: Select all

;Word: 0xHHLL (HH: HiByte, LL: LoByte)

;Word to HiByte/LoByte
vWord := 0x1234
vHiByte := (vWord>>8) & 0xFF ;0x12
vLoByte := vWord & 0xFF ;0x34
MsgBox, % Format("0x{1:X} 0x{2:X}", vHiByte, vLoByte)

;alternatively:
;vHiByte := (vWord & 0xFF00) >> 8

;HiByte/LoByte to Word
vHiByte := 0x12, vLoByte := 0x34
vWord := (vHiByte<<8)|(vLoByte & 0xFF);0x1234
MsgBox, % Format("0x{:X}", vWord)
==================================================

> BITWISE: HIWORD/LOWORD TO/FROM DWORD

- Some code re. high/low words to/from a four-byte DWord (double word).
- The logic would be used to create HiWord/LoWord/MakeDWord functions.
- Note: 'MakeDWord' is typically called 'MakeLong'. 'MakeLong' is confusingly named since it doesn't return a Long (signed) it returns a ULong (unsigned), a ULong is equivalent to a DWord.

Code: Select all

;DWord: 0xHHHHLLLL (HHHH: HiWord, LLLL: LoWord)

;DWord to HiWord/LoWord
vDWord := 0x12345678
vHiWord := (vDWord>>16) & 0xFFFF ;0x1234
vLoWord := vDWord & 0xFFFF ;0x5678
MsgBox, % Format("0x{1:X} 0x{2:X}", vHiWord, vLoWord)

;alternatively:
;vHiWord := (vDWord & 0xFFFF0000) >> 16

;HiWord/LoWord to DWord
vHiWord := 0x1234, vLoWord := 0x5678
vDWord := (vHiWord<<16)|(vLoWord & 0xFFFF) ;0x12345678
MsgBox, % Format("0x{:X}", vDWord)

;examples of HiWord/LoWord to DWord:
PostMessage, 0x200, 0, % (vCurX & 0xFFFF)|(vCurY<<16),, % "ahk_id " hWnd ;WM_MOUSEMOVE := 0x200
PostMessage, 0x201, 0, % (vCurX & 0xFFFF)|(vCurY<<16),, % "ahk_id " hWnd ;WM_LBUTTONDOWN := 0x201
PostMessage, 0x202, 0, % (vCurX & 0xFFFF)|(vCurY<<16),, % "ahk_id " hWnd ;WM_LBUTTONUP := 0x202
- Links:
[MakeLong is really MakeULong or MakeDWord]
c++ - Packaging values in DWORD - Stack Overflow
https://stackoverflow.com/questions/15973349/packaging-values-in-dword
[AutoHotkey_H has various HiXXX/LoXXX/MakeXXX functions.]
AutoHotkey_H New Features
https://hotkeyit.github.io/v2/docs/AHKH_Features.htm

==================================================

> BITWISE: RGB / BGR

- An example of rearranging bytes to convert to/form RGB/BGR:

Code: Select all

;RGB --> R, G, B
vColRGB := 0x123456
vColR := (vColRGB >> 16) & 0xFF
vColG := (vColRGB >> 8) & 0xFF
vColB := vColRGB & 0xFF
MsgBox, % Format("0x{:02X} 0x{:02X} 0x{:02X}", vColR, vColG, vColB)

;BGR --> B, G, R
vColBGR := 0x563412
vColB := (vColBGR >> 16) & 0xFF
vColG := (vColBGR >> 8) & 0xFF
vColR := vColBGR & 0xFF
MsgBox, % Format("0x{:02X} 0x{:02X} 0x{:02X}", vColB, vColG, vColR)

;R, G, B --> RGB
vColR := 0x12, vColG := 0x34, vColB := 0x56
MsgBox, % Format("0x{:02X}{:02X}{:02X}", vColR, vColG, vColB)

;B, G, R --> BGR
vColB := 0x56, vColG := 0x34, vColR := 0x12
MsgBox, % Format("0x{:02X}{:02X}{:02X}", vColB, vColG, vColR)

;R, G, B --> RGB (alternative)
vColR := 0x12, vColG := 0x34, vColB := 0x56
vColRGB := (vColR<<16)|(vColG<<8)|vColB
MsgBox, % Format("0x{:06X}", vColRGB)

;B, G, R --> BGR (alternative)
vColB := 0x56, vColG := 0x34, vColR := 0x12
vColBGR := (vColB<<16)|(vColG<<8)|vColR
MsgBox, % Format("0x{:06X}", vColBGR)

;RGB --> BGR
vColRGB := 0x123456
vColBGR := ((vColRGB & 0xFF)<<16)|(vColRGB & 0xFF00)|((vColRGB>>16) & 0xFF)
MsgBox, % Format("0x{:06X}", vColBGR)

;BGR --> RGB
vColBGR := 0x563412
vColRGB := ((vColBGR & 0xFF)<<16)|(vColBGR & 0xFF00)|((vColBGR>>16) & 0xFF)
MsgBox, % Format("0x{:06X}", vColRGB)

;RGB --> BGR (alternative)
vColRGB := 0x123456
vColRGB := Format("0x{:06X}", vColRGB)  ;ensure format: 0xRRGGBB
MsgBox, % "0x" SubStr(vColRGB,7,2) SubStr(vColRGB,5,2) SubStr(vColRGB,3,2)

;BGR --> RGB (alternative)
vColBGR := 0x563412
vColBGR := Format("0x{:06X}", vColBGR) ;ensure format: 0xBBGGRR
MsgBox, % "0x" SubStr(vColBGR,7,2) SubStr(vColBGR,5,2) SubStr(vColBGR,3,2)
- For more information on swapping bytes, and reversing bits within bytes, see:
reverse bits / reverse bytes - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=35707
swap bytes - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=47081

==================================================

> PRESENTATION: FORMAT FUNCTION

- See 'FORMAT FUNCTION EXAMPLES', here:
jeeswg's characters tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=26486

- See also:
Format() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Format.htm#Example

- And see various examples in the 'PRESENTATION' sections below.

==================================================

> PRESENTATION: STRING TO NUMBER

- Some examples of converting a string to a number:

Code: Select all

var := "3"
MsgBox, % var+1 ;4

MsgBox, % Format("3")+1 ;4

var := 0xF
MsgBox, % var+0 ;15
var := "0xF"
MsgBox, % var+0 ;15
var := "F"
MsgBox, % ("0x" var)+0 ;blank
var := "F"
MsgBox, % Format("0x" var)+0 ;15 ;workaround for line above

MsgBox, % 0xF+0 ;15
MsgBox, % (0xF)+0 ;15
MsgBox, % ("0xF")+0 ;blank
MsgBox, % Format("0xF")+0 ;15 ;workaround for line above
==================================================

> PRESENTATION: IS NUMBER (LOOKS NUMERIC)

- See:
- STRING/NUMERIC: COMPARE NUMERIC AS TEXT
- STRING/NUMERIC: CHECK IF VARIABLE LOOKS LIKE STRING/NUMERIC
- STRING/NUMERIC: CHECK IF VARIABLE IS STRING/NUMERIC
- STRING/NUMERIC: IF VAR IS TYPE
- Here:
jeeswg's strings tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=32985

- The AHK v2 'is' operator can be used to determine if a string looks numeric:
- It checks a variable for one of these types:
- integer, float, number, object, byref, digit, xdigit, alpha, upper, lower, alnum, space, time, date
- AHK v1 has somewhat similar functionality.

- Some code re. checking if a number looks numeric (IsNum function):
Num / IsNum / Int / Float / Str - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=13&t=64339

- Links:
If Var is Type - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/IfIs.htm
Value is Type - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/is.htm

==================================================

> PRESENTATION: TYPES (INTEGER/FLOAT)

- AHK v2 has a Type function, that retrieves the type of a variable e.g. Integer/Float/String.
- AHK v2 has Float/Integer functions which convert variables to a Float/Integer type.
- The AHK v1/v2 'Float' type is a 64-bit (8-byte) floating-point number, a Double.
- The AHK v1/v2 'Integer' type is a signed 64-bit (8-byte) integer, an Int64.
- Note: the Format function can be used to display a (signed) Int64, as an (unsigned) UInt64.

- Some code re. converting the type of a number:
Num / IsNum / Int / Float / Str - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=13&t=64339

- Links:
Type - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/Type.htm
Float - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/Float.htm
Integer - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/Integer.htm

==================================================

> PRESENTATION: BASES: HEXADECIMAL (DEC2HEX) (HEX2DEC)

- Note: some common prefixes:
- 0b (binary) (base 2)
- 0o (octal) (base 8)
- 0x (hexadecimal) (base 16)

- Some notes on hex notation:

Code: Select all

0x notation:
e.g. 0x4 = 4, 0xA = 10, 0xF = 15, 0x10 = 16,
0xFF = 255, 0x100 = 256
note: '0x4' does *not* mean '0 multiplied by 4'

from:
Hexadecimal - Wikipedia
https://en.wikipedia.org/wiki/Hexadecimal
'The prefix "0x" is used in C and related languages'

hexadecimal digits, decomposed for understanding binary switches:
0xF = 15 = 8 + 4 + 2 + 1
0xE = 14 = 8 + 4 + 2
0xD = 13 = 8 + 4 + 1
0xC = 12 = 8 + 4
0xB = 11 = 8 + 2 + 1
0xA = 10 = 8 + 2
0x9 = 8 + 1
0x8 = 8
0x7 = 4 + 2 + 1
0x6 = 4 + 2
0x5 = 4 + 1
0x4 = 4
0x3 = 2 + 1
0x2 = 2
0x1 = 1

;note: a binary switch is a power of 2,
;e.g. 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, ...,
;that indicates whether something is on/off,
;e.g. they are commonly used with window styles
- Some examples of converting between dec and hex:

Code: Select all

;hex2dec (with 0x prefix):
vNum := 0xFF
vNum := vNum + 0
MsgBox % vNum ;255

;hex2dec (without 0x prefix):
vNum := "FF"
vNum := Format("{:i}", "0x" vNum)
MsgBox % vNum ;255

;dec2hex (with 0x prefix):
vNum := 255
vNum := Format("0x{:X}", vNum)
MsgBox % vNum ;0xFF

;dec2hex (without 0x prefix):
vNum := 255
vNum := Format("{:X}", vNum)
MsgBox % vNum ;FF
- Some examples of splitting/joining hex numbers.

Code: Select all

;splitting hex (by character position) (to dec):
vNum := 0xABCDEF
vNum1 := Format("{:d}", "0x" SubStr(vNum, 3, 2))
vNum2 := Format("{:d}", "0x" SubStr(vNum, 5, 2))
vNum3 := Format("{:d}", "0x" SubStr(vNum, 7, 2))
MsgBox % vNum1 " " vNum2 " " vNum3 ;171 205 239

;splitting hex (by character position) (to hex):
vNum := 0xABCDEF
vNum1 := Format("0x{:X}", "0x" SubStr(vNum, 3, 2))
vNum2 := Format("0x{:X}", "0x" SubStr(vNum, 5, 2))
vNum3 := Format("0x{:X}", "0x" SubStr(vNum, 7, 2))
MsgBox % vNum1 " " vNum2 " " vNum3 ;0xAB 0xCD 0xEF

;joining hex (multiple dec numbers to one hex number)
vNum1 := 171, vNum2 := 205, vNum3 := 239 ;0xAB 0xCD 0xEF
vNum := "0x" Format("{:02X}", vNum1) Format("{:02X}", vNum2) Format("{:02X}", vNum3)
MsgBox % vNum ;0xABCDEF
- Links:
Binary number - Wikipedia
https://en.wikipedia.org/wiki/Binary_number
Octal - Wikipedia
https://en.wikipedia.org/wiki/Octal
Hexadecimal - Wikipedia
https://en.wikipedia.org/wiki/Hexadecimal

==================================================

> PRESENTATION: BASES: BINARY (DEC2BIN) (BIN2DEC)

- Some examples of converting between dec and bin:

Code: Select all

;e.g. dec2bin (decimal zero/positive integer to binary)
vNum := 657, vBin := ""
while vNum
	vBin := (vNum & 1) vBin, vNum >>= 1
vBin := Format("{:01}", vBin) ;to handle vNum := 0
MsgBox, % vBin ;1010010001

;e.g. bin2dec (binary zero/positive integer to decimal)
vBin := "1010010001", vNum := 0
Loop, Parse, vBin
	vNum := (vNum*2) + A_LoopField
MsgBox, % vNum ;657
==================================================

> PRESENTATION: SCIENTIFIC NOTATION

- Some scientific notation examples:

Code: Select all

;e.g. one million, we can write this as: 1.0 * 10**6
;e.g. 1234 = 1.234 * 10**3
;e.g. 123456789 = 1.23456789 * 10**8
;e.g. 0.001234 = 1.234 * 10**(-3)

;in AutoHotkey this would be:
MsgBox, % 1.0 * 10**6 ;1000000
MsgBox, % 1.234 * 10**3 ;1234
MsgBox, % 1.23456789 * 10**8 ;123456789
MsgBox, % 1.234 * 10**(-3) ;0.001234

;==============================

;in AutoHotkey you can store numbers like so:
MsgBox, % 1.0e6 * 1 ;1000000.000000
MsgBox, % 1.234e3 * 1 ;1234.000000
MsgBox, % 1.23456789e8 * 1 ;123456789.000000
MsgBox, % 1.234e-3 * 1 ;0.001234

;although the integer part does not have to be a 1-digit number
MsgBox, % 1000.0e3 * 1 ;1000000.000000
MsgBox, % 12.34e2 * 1 ;1234.000000
MsgBox, % 0.00123456789e11 * 1 ;123456789.000000
MsgBox, % 12.34e-4 * 1 ;0.001234

;==============================

;here are some examples using the Format function,
;in dec and hex, with (presumably) e for exponent, and p for power
;(hex uses abcdef as digits, hence e is not a good choice for exponent)
MsgBox, % Format("{:e}", 0) ;0.000000e+000
MsgBox, % Format("{:a}", 0) ;0x0.000000p+0

MsgBox, % Format("{:e}", 1) ;1.000000e+000
MsgBox, % Format("{:a}", 1) ;0x1.000000p+0

MsgBox, % Format("{:e}", 2) ;2.000000e+000
MsgBox, % Format("{:a}", 2) ;0x1.000000p+1

MsgBox, % Format("{:e}", 3) ;3.000000e+000
MsgBox, % Format("{:a}", 3) ;0x1.800000p+1

MsgBox, % Format("{:e}", 1000000) ;1.000000e+006
MsgBox, % Format("{:a}", 1000000) ;0x1.e84800p+19

MsgBox, % Format("{:e}", 1234) ;1.234000e+003
MsgBox, % Format("{:a}", 1234) ;0x1.348000p+10

MsgBox, % Format("{:e}", 123456789) ;1.234568e+008
MsgBox, % Format("{:a}", 123456789) ;0x1.d6f345p+26

MsgBox, % Format("{:e}", 0.001234) ;1.234000e-003
MsgBox, % Format("{:a}", 0.001234) ;0x1.437c57p-10
==================================================

> PRESENTATION: 0.1 + 0.2 = 0.3

- Investigating 0.1 + 0.2 = 0.3:

Code: Select all

;based on:
;Concepts and Conventions | AutoHotkey
;https://autohotkey.com/docs/Concepts.htm

MsgBox, % Format("{:.17f}", 0.1) ;0.10000000000000001
MsgBox, % Format("{:.17f}", 0.2) ;0.20000000000000001
MsgBox, % Format("{:.17f}", 0.3) ;0.29999999999999999
MsgBox, % Format("{:.17f}", 0.1+0.2) ;0.30000000000000004
MsgBox, % 0.1 + 0.2 = 0.3 ;0 (not equal)

MsgBox, % Format("{:.17f}", 1.1) ;1.10000000000000010
MsgBox, % Format("{:.17f}", 1.2) ;1.20000000000000000
MsgBox, % Format("{:.17f}", 1.3) ;1.30000000000000000
MsgBox, % Format("{:.17f}", 1.1+1.2) ;2.29999999999999980
MsgBox, % Format("{:.17f}", 2.3) ;2.29999999999999980
MsgBox, % 1.1 + 1.2 = 2.3 ;1 (equal)

;'One strategy for dealing with this is to avoid direct comparison, instead comparing the difference. For example:'
MsgBox % Abs((0.1 + 0.2) - (0.3)) < 0.0000000000000001

;'Another strategy is to always convert to string (thereby applying rounding) before comparison. There are generally two ways to do this while specifying the precision, and both are shown below:'
MsgBox % Round(0.1 + 0.2, 15) = Format("{:.15f}", 0.3)

MsgBox, % Round(0.1+0.2,6) = Round(0.3,6) ;1
MsgBox, % Format("{:0.6f}", 0.1+0.2) = Format("{:0.6f}", 0.3) ;1
==================================================

> PRESENTATION: FLOATING POINT / FLOAT / DOUBLE

- Float: 'A 32-bit floating point number, which provides 6 digits of precision.'
- Double: 'A 64-bit floating point number, which provides 15 digits of precision.'

- Some links:
SetFormat - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/SetFormat.htm
Format() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Format.htm
Floating point comparison - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/24203-floating-point-comparison/
What are the limits of a Float? min, max, NAN, -infinity, +infinity - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=31554#p147160

Floating-point arithmetic - Wikipedia
https://en.wikipedia.org/wiki/Floating-point_arithmetic
Single-precision floating-point format - Wikipedia
https://en.wikipedia.org/wiki/Single-precision_floating-point_format
Double-precision floating-point format - Wikipedia
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
c++ - Difference between float and double - Stack Overflow
https://stackoverflow.com/questions/2386772/difference-between-float-and-double
numeric precision - What causes floating point rounding errors? - Software Engineering Stack Exchange
https://softwareengineering.stackexchange.com/questions/101163/what-causes-floating-point-rounding-errors

jeeswg's floats and doubles mini-tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=63650

==================================================

> PRESENTATION: ROUND TO N DECIMAL PLACES

- Some examples of rounding:

Code: Select all

vNum := 123
MsgBox,  % Round(vNum, -1) ;120
MsgBox,  % Round(vNum, -2) ;100

vNum := 5
MsgBox, % Round(vNum) ;5
MsgBox, % Round(vNum, 1) ;5.0
MsgBox, % Round(vNum, 2) ;5.00
MsgBox, % Format("{:0.1f}", vNum) ;5.0
MsgBox, % Format("{:0.2f}", vNum) ;5.00

vNum := 5.999999
MsgBox, % Round(vNum) ;6
MsgBox,  % Round(vNum, 1) ;6.0
MsgBox,  % Round(vNum, 2) ;6.00
MsgBox, % Format("{:0.1f}", vNum) ;6.0
MsgBox, % Format("{:0.2f}", vNum) ;6.00

vNum := 5.111111
MsgBox, % Round(vNum) ;5
MsgBox,  % Round(vNum, 1) ;5.1
MsgBox,  % Round(vNum, 2) ;5.11
MsgBox, % Format("{:0.1f}", vNum) ;5.1
MsgBox, % Format("{:0.2f}", vNum) ;5.11

;by default AHK, certain operations show numbers to 6 decimal places:
MsgBox, % 5/1.0 ;5.000000
- See also: 'BASICS: ROUND TO THE NEAREST MULTIPLE OF N'.

==================================================

> PRESENTATION: LEADING/TRAILING ZEROS (INTEGERS)

- Some examples for handling leading/trailing zeros, and cropping numbers:

Code: Select all

;a maximum number of characters:
;e.g. get the first 3 characters:
MsgBox, % SubStr(12345, 1, 3) ;123
;e.g. get the last 3 characters:
vIsV1 := !!SubStr(1, 0)
MsgBox, % SubStr(12345, vIsV1-3) ;345

;an exact number of characters:
;e.g. pad with trailing zeros, get the first 3 characters:
MsgBox, % SubStr(1 "000", 1, 3) ;100
;e.g. pad with trailing zeros, get the first 10 characters:
MsgBox, % SubStr(123 Format("{:010}", 0), 1, 10) ;1230000000
;e.g. pad with leading zeros, get the last 3 characters:
vIsV1 := !!SubStr(1, 0)
MsgBox, % SubStr("000" 1, vIsV1-3) ;001
;e.g. pad with leading zeros, get the last 10 characters:
vIsV1 := !!SubStr(1, 0)
MsgBox, % SubStr(Format("{:010}", 123), vIsV1-10) ;0000000123

;a minimum number of characters:
;e.g. pad with trailing zeros, ensure a minimum number of characters:
vNum := 1, vLimit := 3
MsgBox, % (StrLen(vNum) >= vLimit) ? vNum : SubStr(vNum "000", 1, vLimit) ;100
vNum := 1234, vLimit := 3
MsgBox, % (StrLen(vNum) >= vLimit) ? vNum : SubStr(vNum "000", 1, vLimit) ;1234
vNum := 123, vLimit := 10
MsgBox, % (StrLen(vNum) >= vLimit) ? vNum : SubStr(vNum Format("{:0" vLimit "}", 0), 1, vLimit) ;1230000000
;e.g. pad with leading zeros, ensure a minimum number of characters:
MsgBox, % Format("{:03}", 1) ;001
MsgBox, % Format("{:03}", 1234) ;1234 ;(no leading zeros needed)
MsgBox, % Format("{:010}", 123) ;0000000123
==================================================

> PRESENTATION: TRUNCATION

- Some notes/examples re. truncation:

Code: Select all

;regarding cropping a number, or padding it with zeros,
;there are six possibilities:
;- integer part has maximum n digits (leave / crop)
;- integer part has exactly n digits (leave / crop / pad)
;- integer part has minimum n digits (leave / pad)
;- fractional part has maximum n digits (leave / crop)
;- fractional part has exactly n digits (leave / crop / pad)
;- fractional part has minimum n digits (leave / pad)

;e.g. split a number into its sign/integer part/fractional part
RegExMatch("", "", o), vPfx := IsObject(o) ? "" : "O)" ;two-way compatibility
vList := "123.456,-123.456,-123,123"
Loop, Parse, vList, % ","
{
	RegExMatch(A_LoopField, vPfx "(-?)(\d*)(\.?)(\d*)", oNum)
	MsgBox, % oNum.1 "|" oNum.2 "|" oNum.3 "|" oNum.4
}
oNum := ""

;e.g. truncation: fractional part has maximum n digits:
MsgBox, % RegExReplace("-001.100000", ".*\.\d{2}\K.*", "")
MsgBox, % RegExReplace("-001.100000", ".*\.\d{3}\K.*", "")
MsgBox, % RegExReplace("-001.555555", ".*\.\d{3}\K.*", "")
MsgBox, % RegExReplace("-001555555", ".*\.\d{3}\K.*", "")

;e.g. an alternative way to truncate a number:
RegExMatch("", "", o), vPfx := IsObject(o) ? "" : "O)" ;two-way compatibility
RegExMatch(-12345.12345, vPfx "^-?\d+.\d{1,2}|^-?\d+$", oMatch)
vNum := oMatch.0
MsgBox, % vNum

;e.g. the integer part has a maximum n digits:
MsgBox, % RegExReplace("123456789.12345", "[^-.]*(?=\d{3})")
MsgBox, % RegExReplace("-123456789.12345", "[^-.]*(?=\d{3})")
==================================================

> PRESENTATION: LEADING/TRAILING ZEROS (INTEGER PART AND FRACTIONAL PART)

- Some examples re. cropping leading/trailing zeros:

Code: Select all

;crop leading zeros (maintain minus sign, keep a 0 if required)
vList := "1000,-1000,000,-000,00.00,-00.00,0.0,-0.0,00123,-00123,00123.123,-00123.123,00.123,-00.123,2.00500,-2.00500,2.500,-2.500,2.000,-2.000,0002000.0002000,-0002000.0002000"
vOutput := ""
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vOutput .= vNum "`t`t" RegExReplace(vNum, "^-?\K0+(?=[^0.]|0\.|0$)") "`r`n"
	;vOutput .= vNum "`t`t" RegExReplace(vNum, "^-?\K0+") "`r`n" ;never keeps a leading zero
}
;Clipboard := vOutput
MsgBox, % vOutput

;crop trailing zeros after decimal point (remove decimal point if required)
vOutput := ""
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vOutput .= vNum "`t`t" RegExReplace(vNum, "\.0+$|\..*?\K0+$") "`r`n"
}
;Clipboard := vOutput
MsgBox, % vOutput

;crop leading and trailing zeros
vOutput := ""
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vOutput .= vNum "`t`t" RegExReplace(vNum, "^-?\K0+(?=[^0.]|0\.|0$)|\.0+$|\..*?\K0+$") "`r`n"
}
;Clipboard := vOutput
MsgBox, % vOutput
==================================================

> PRESENTATION: THOUSANDS SEPARATORS (COMMAS/DOTS)

- Some examples re. adding thousands separators to numbers:

Code: Select all

;e.g. '123456789' to '123,456,789'
;e.g. '-123456789.123456' to '-123,456,789.123456'

;based on:
;Add Thousands Separator - Scripts and Functions - AutoHotkey Community
;https://autohotkey.com/board/topic/50019-add-thousands-separator/

vList := "123456789,-123456789,123456789.123456,-123456789.123456"
Loop, Parse, vList, % ","
{
	vNum := A_LoopField
	vOutput := vNum "`r`n"
	vSep := ","
	vOutput .= RegExReplace(vNum, "\G(?:-?)\d+?(?=(\d{3})+(?:\D|$))", "$0" vSep) "`r`n"
	vSep := ".", vNum := StrReplace(vNum, ".", ",")
	vOutput .= RegExReplace(vNum, "\G(?:-?)\d+?(?=(\d{3})+(?:\D|$))", "$0" vSep)
	MsgBox, % vOutput
}
- Link:
Add Thousands Separator - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/50019-add-thousands-separator/

==================================================

> PRESENTATION: CONVERT 2-DIGIT YEAR TO 4-DIGIT YEAR

- Some examples re. converting a 2-digit year to a 4-digit year.
- Note: an assumption must be made regarding which century to use.

Code: Select all

;specify the range values as required
;convert 2-digit year to 4-digit year
vRange1 := 0, vRange2 := 30
vOutput := ""
Loop, 100
{
	vNum := Mod(A_Index-1, 100)
	if (vNum >= vRange1) && (vNum <= vRange2)
		vOutput .= 2000 + vNum ","
	else
		vOutput .= 1900 + vNum ","
}
vOutput := SubStr(vOutput, 1, -1)
MsgBox, % vOutput
==================================================

> PRESENTATION: ORDINAL INDICATORS (1ST/2ND/3RD/4TH)

- Some code re. adding an ordinal indicator to a number:

Code: Select all

;adding 'st/nd/rd/th' suffixes to numbers:
;e.g. 1st/2nd/3rd/4th
vIsV1 := !!SubStr(1, 0)
vOutput := ""
Loop, 100
{
	vNum := A_Index
	if RegExMatch(vNum, "[4567890]$|1[123]$") ;11th/12th/13th
		vNum .= "th"
	else if (SubStr(vNum, vIsV1-1) = 1)
		vNum .= "st"
	else if (SubStr(vNum, vIsV1-1) = 2)
		vNum .= "nd"
	else ;if (SubStr(vNum, vIsV1-1) = 3)
		vNum .= "rd"
	vOutput .= vNum ","
}
vOutput := SubStr(vOutput, 1, -1)
;Clipboard := vOutput
MsgBox, % vOutput
- See also:
Ordinal indicator - Wikipedia
https://en.wikipedia.org/wiki/Ordinal_indicator

==================================================

> PRESENTATION: ROMAN NUMERALS

- Some links:
Convert Decimal to Roman Numerals - Scripts - AutoHotkey Community
https://autohotkey.com/board/topic/53615-convert-decimal-to-roman-numerals/
Roman numerals/Encode - Rosetta Code
http://rosettacode.org/wiki/Roman_numerals/Encode#AutoHotkey
Roman numerals/Decode - Rosetta Code
http://rosettacode.org/wiki/Roman_numerals/Decode#AutoHotkey

==================================================

> FURTHER: UNICODE: UTF-8

- A description of how UTF-8 stores characters:

Code: Select all

note: within the square brackets, all numbers start at their minimum,
when A completes a cycle, and returns to its minimum,
B increases by 1 etc
e.g. time: [0-23 C][0-59 B][0-59 A]

UTF-8:
[no. bytes: start-end (count): [bytes]]
1 byte: 0-127 (128): [0-127 A]
2 bytes: 128-2047 (1920): [194-223 B][128-191 A]
3 bytes: 2048-4095 (2048): [224 C][160-191 B][128-191 A]
3 bytes: 4096-65535 (61440): [225-239 C][128-191 B][128-191 A]
4 bytes: 65536-262143 (196608): [240 D][144-191 C][128-191 B][128-191 A]
4 bytes: 262144-1048575 (786432): [241-243 D][128-191 C][128-191 B][128-191 A]
4 bytes: 1048576-1114111 (65535): [244 D][128-143 C][128-191 B][128-191 A]

note: 55296-56319 (1024: high surrogates)
note: 56320-57343 (1024: low surrogates)

==============================

some characters and their representation as bytes in UTF-8:

0	0
127	127
128	194 128
2047	223 191
2048	224 160 128
4095	224 191 191
4096	225 128 128
65535	239 191 191
65536	240 144 128 128
262143	240 191 191 191
262144	241 128 128 128
1048575	243 191 191 191
1048576	244 128 128 128
1114111	244 143 191 191
- An example of the mathematics behind the UTF-8 encoding:

Code: Select all

;UTF-8: character number to bytes
;StrPut writes the correct bytes to &vData1
;the if/else ladder writes the correct bytes to &vData2
;a MsgBox is shown if there is a mismatch (which there shouldn't be)
VarSetCapacity(vData1, 4, 0)
VarSetCapacity(vData2, 4, 0)
Loop, 1114112
{
	vNum := A_Index - 1
	StrPut(Chr(vNum), &vData2, "UTF-8")
	if (vNum <= 127)
	{
		NumPut(vNum, &vData1, 0, "UChar")
	}
	else if (vNum <= 2047)
	{
		NumPut(192+Floor(vNum/64), &vData1, 0, "UChar")
		NumPut(128+Mod(vNum,64), &vData1, 1, "UChar")
	}
	else if (vNum >= 55296) && (vNum <= 57343)
	{
		;bytes '239 191 189' give Chr(65533) 'REPLACEMENT CHARACTER'
		NumPut(239, &vData1, 0, "UChar")
		NumPut(191, &vData1, 1, "UChar")
		NumPut(189, &vData1, 2, "UChar")
	}
	else if (vNum <= 65535)
	{
		NumPut(224+Floor(vNum/4096), &vData1, 0, "UChar")
		NumPut(128+Mod(Floor(vNum/64),64), &vData1, 1, "UChar")
		NumPut(128+Mod(vNum,64), &vData1, 2, "UChar")
	}
	else if (vNum <= 1114111)
	{
		NumPut(240+Floor(vNum/262144), &vData1, 0, "UChar")
		NumPut(128+Mod(Floor(vNum/4096),64), &vData1, 1, "UChar")
		NumPut(128+Mod(Floor(vNum/64),64), &vData1, 2, "UChar")
		NumPut(128+Mod(vNum,64), &vData1, 3, "UChar")
	}
	vNum1 := NumGet(&vData1, 0, "UInt")
	vNum2 := NumGet(&vData2, 0, "UInt")
	vPrompt := A_Index " " Format("0x{:X}", vNum1) " " Format("0x{:X}", vNum2)
	if !(vNum1 = vNum2)
		MsgBox, % vPrompt
}
MsgBox, % "done"
- Note: UTF-8 uses Chr(65279) as the BOM, bytes: 239, 187, 191.
- The BOM (byte order mark), at the start of a text file, identifies its encoding.

==================================================

> FURTHER: UNICODE: UTF-16 LITTLE ENDIAN

- A description of how UTF-16 LE stores characters:

Code: Select all

note: within the square brackets, all numbers start at their minimum,
when A completes a cycle, and returns to its minimum,
B increases by 1 etc
e.g. time: [0-23 C][0-59 B][0-59 A]

UTF-16 (little endian):
2 byte: 0-65535 (65536): [0-255 A][0-255 B]
4 bytes: 65536-1114111 (1048576): [0-255 C][216-219 D][0-255 A][220-223 B]
4 bytes: 65536-1114111 (1048576): [char 55296-56319 B][char 56320-57343 A]
- Two examples of the mathematics behind the UTF-16 LE encoding:

Code: Select all

;UTF-16: character number to surrogate pair
;a MsgBox is shown if there is a mismatch (which there shouldn't be)
Loop, 1114111
{
	if (A_Index <= 65535)
		continue
	vNum := A_Index-65536
	vChar1 := Chr(55296+Floor(vNum/1024))
	vChar2 := Chr(56320+Mod(vNum,1024))
	if !(Chr(A_Index) = vChar1 vChar2)
		MsgBox, % A_Index
}
MsgBox, % "done"

;UTF-16: surrogate pair to character number
;a MsgBox is shown if there is a mismatch (which there shouldn't be)
vNum1 := 55296
Loop, % 1024
{
	vNum2 := 56320
	Loop, 1024
	{
		vNum := (vNum1-55296)*1024+(vNum2-56320)+65536
		if !(Chr(vNum) = Chr(vNum1) Chr(vNum2))
			MsgBox, % vNum1 " " vNum2 " " vNum
		vNum2++
	}
	vNum1++
}
MsgBox, % "done"
- Note: UTF-16 BE (big endian), uses the same system, except that the bytes, in each pair of bytes, are swapped.

- Note: UTF-16 LE uses Chr(65279) as the BOM, bytes: 255, 254.
- Note: UTF-16 BE uses Chr(65279) as the BOM, bytes: 254, 255.
- The BOM (byte order mark), at the start of a text file, identifies its encoding.

- See also, a few comments in this section:
- 'BITWISE: UNICODE (UTF-16 LITTLE ENDIAN)'

==================================================

> FURTHER: UNIT CONVERSION: BYTES

- Some examples of displaying file sizes in different friendly formats:

Code: Select all

;powers of 1000:
;byte/kilobyte/megabyte/gigabyte/terabyte/petabyte

;powers of 1024 ('bi' for binary):
;byte/kibibyte/mebibyte/gibibyte/tebibyte/pebibyte

;['Multiples of bytes' chart]
;Terabyte - Wikipedia
;https://en.wikipedia.org/wiki/Terabyte

;display file size in a particular format/unit:
vSize := 123456789
Loop, 4
{
	(A_Index = 1) && (vFormat := "2dp", vUnit := "m")
	(A_Index = 2) && (vFormat := "c", vUnit := "m")
	(A_Index = 3) && (vFormat := "2dp", vUnit := "k")
	(A_Index = 4) && (vFormat := "c", vUnit := "k")

	oArray1 := {b:"bytes", k:"KiB", m:"MiB", g:"GiB", t:"TiB", p:"PiB"}
	oArray2 := {b:0, k:1, m:2, g:3, t:4, p:5}
	vUnit := SubStr(vUnit, 1, 1)
	if (vUnit = "b")
		vDesc := vSize " " oArray1[vUnit]
	else if (vFormat = "2dp")
		vDesc := Format("{:0.2f}", vSize/1024**oArray2[vUnit]) " " oArray1[vUnit]
	else if (vFormat = "c")
		vDesc := Ceil(vSize/1024**oArray2[vUnit]) " " oArray1[vUnit]
	else if (vFormat = "f")
		vDesc := Floor(vSize/1024**oArray2[vUnit]) " " oArray1[vUnit]
	MsgBox, % vDesc
}
oArray1 := oArray2 := ""

;display file size using the nearest unit:
vSize := 123456789
vPower := (vSize = 0) ? 0 : Floor(Log(vSize)/Log(1024))
Loop, 2
{
	(A_Index = 1) && (vFormat := "2dp")
	(A_Index = 2) && (vFormat := "c")

	oArray := {0:"bytes", 1:"KiB", 2:"MiB", 3:"GiB", 4:"TiB", 5:"PiB"}
	vUnit := oArray[vPower]
	if (vPower = 0)
		vDesc := vSize " " vUnit
	else if (vFormat = "2dp")
		vDesc := Format("{:0.2f}", vSize/1024**vPower) " " vUnit
	else if (vFormat = "c")
		vDesc := Ceil(vSize/1024**vPower) " " vUnit
	else if (vFormat = "f")
		vDesc := Floor(vSize/1024**vPower) " " vUnit
	MsgBox, % vDesc
}
oArray := ""

;friendly display size:
vText := ""
vSizeFile := 123456789
VarSetCapacity(vText, vSize := 260*2, 0) ;(no suggested size in MSDN)
DllCall("shlwapi\StrFormatByteSize", "Int64",vSizeFile, "Str",vText, "UInt",vSize, "Ptr")
MsgBox, % vText ;117 MB (tested on Windows 7)
==================================================

> FURTHER: UNIT CONVERSION: DISTANCE

- Some distance conversion examples:

Code: Select all

;kilometres to miles:
vNum := 42
MsgBox, % vNum*0.621371192 ;26.097590

;miles to kilometres:
vNum := 26
MsgBox, % vNum*1.609344 ;41.842944

;metres to feet:
vNum := 1.8
MsgBox, % vNum*3.2808399 ;5.905512

;metres to inches:
vNum := 1.8
MsgBox, % vNum*3.2808399*12 ;70.866142

;feet to metres:
vNum := 5.9
MsgBox, % vNum*0.3048 ;1.798320

;inches to metres:
vNum := 71
MsgBox, % vNum*0.0254 ;1.803400

;metres to feet and inches:
vNum := 1.8
vNum := vNum*3.2808399
vFeet := Floor(vNum)
vInches := (vNum-vFeet)*12
MsgBox, % vFeet "'" vInches Chr(34) ;5'10.866142"

;feet to feet and inches:
vNum := 5.9
vFeet := Floor(vNum)
vInches := (vNum-vFeet)*12
MsgBox, % vFeet "'" vInches Chr(34) ;5'10.800000"

;inches to feet and inches:
vNum := 71
vNum := vNum/12
vFeet := Floor(vNum)
vInches := (vNum-vFeet)*12
MsgBox, % vFeet "'" vInches Chr(34) ;5'11.000000"
==================================================

> FURTHER: UNIT CONVERSION: TEMPERATURE

- Some temperature conversion examples:

Code: Select all

;Fahrenheit to Celsius
vDegF := 212
MsgBox, % vDegC := (5/9)*(vDegF-32) ;100.000000
vDegF := 32
MsgBox, % vDegC := (5/9)*(vDegF-32) ;0.000000

;Celsius to Fahrenheit
vDegC := 100
MsgBox, % vDegF := ((9/5)*vDegC)+32 ;212.000000
vDegC := 0
MsgBox, % vDegF := ((9/5)*vDegC)+32 ;32.000000

;Celsius to Kelvin
vDegC := 100
MsgBox, % vK := vDegC + 273.15 ;373.150000

;Kelvin to Celsius
vK := 0
MsgBox, % vDegC := vK - 273.15 ;-273.150000
==================================================

> FURTHER: UNIT CONVERSION: TIME

- Some time conversion examples:

Code: Select all

;h:m:s to seconds
vHMS := "01:02:03"
oTemp := StrSplit(vHMS, ":")
MsgBox, % oTemp.1*3600 + oTemp.2*60 + oTemp.3 ;3723
oTemp := ""

;h:m:s to seconds (alternative)
vHMS := "01:02:03"
vSec := "19990101" StrReplace(vHMS, ":")
;EnvSub, vSec, % 199901, Seconds ;AHK v1
vSec := DateDiff(vSec, 199901, "Seconds") ;AHK v2
MsgBox, % vSec ;3723

;seconds to h:m:s
vSec := 3723
MsgBox, % Format("{:02}:{:02}:{:02}", Floor(vSec/3600), Floor(Mod(vSec,3600)/60), Mod(vSec,60)) ;01:02:03

;seconds to h:m:s (alternative)
vSec := 3723
vHMS := "19990101"
;EnvAdd, vHMS, vSec, Seconds ;AHK v1
vHMS := DateAdd(vHMS, vSec, "Seconds") ;AHK v2
FormatTime, vHMS, % vHMS, HH:mm:ss
MsgBox, % vHMS ;01:02:03
==================================================

> FURTHER: UNIT CONVERSION: ANGLES

- Some angle conversion examples:

Code: Select all

;degrees to radians:
;r = d * (pi/180)
vDeg := 360
MsgBox, % vDeg * 0.01745329252 ;6.283185

;radians to degrees:
;d = r * (180/pi)
vRad := 6.283185
MsgBox, % vRad * 57.29578 ;359.999985

vPi := 4 * ATan(1)
vRad := 2 * vPi
MsgBox, % vRad * 57.29578 ;360.000003
==================================================

> FURTHER: DISTANCE BETWEEN 2 POINTS ON A SPHERE (HAVERSINE FORMULA)

- Some example code for calculating the great-circle distance between 2 points on a sphere:

Code: Select all

;Calculate distance and bearing between two Latitude/Longitude points using haversine formula in JavaScript
;http://www.movable-type.co.uk/scripts/latlong.html
;algorithm - Calculate distance between two latitude-longitude points? (Haversine formula) - Stack Overflow
;https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula
;Haversine formula - Wikipedia
;https://en.wikipedia.org/wiki/Haversine_formula
;Haversine formula - Rosetta Code
;https://rosettacode.org/wiki/Haversine_formula
;Wolfram|Alpha Examples: Geodesy & Navigation
;https://www.wolframalpha.com/examples/science-and-technology/earth-sciences/geodesy-and-navigation/
;51.51N 0.13W to 41.29S 174.78E km - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=51.51N+0.13W+to+41.29S+174.78E+km
;51.51N 0.13W to 35.28S 149.13E km - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=51.51N+0.13W+to+35.28S+149.13E+km
;41.29S 174.78E to 35.28S 149.13E km - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=41.29S+174.78E+to+35.28S+149.13E+km

oCoordA := [51.51, -0.13] ;London
oCoordB := [-41.29, -174.78] ;Wellington
oCoordC := [-35.28, -149.13] ;Canberra
MsgBox % LatLonGetDist(oCoordA.1, oCoordA.2, oCoordB.1, oCoordB.2) ;18807.714928
MsgBox % LatLonGetDist(oCoordA.1, oCoordA.2, oCoordC.1, oCoordC.2) ;16965.064315
MsgBox % LatLonGetDist(oCoordB.1, oCoordB.2, oCoordC.1, oCoordC.2) ;2326.586058

;distance between two points on a sphere (e.g. Earth, the globe)
;note: radians = degrees * (pi/180) = degrees * 0.01745329252
;note: radius of Earth is 6371 km = 3958.8 miles
LatLonGetDist(vLat1, vLon1, vLat2, vLon2, vRadius:=6371)
{
	local
	vLat1R := vLat1*0.01745329252
	vLat2R := vLat2*0.01745329252
	vLatDiffR := (vLat2-vLat1)*0.01745329252
	vLonDiffR := (vLon2-vLon1)*0.01745329252
	vA := Sin(vLatDiffR/2) * Sin(vLatDiffR/2) + Cos(vLat1R) * Cos(vLat2R) * Sin(vLonDiffR/2) * Sin(vLonDiffR/2)
	return vRadius * 2 * DllCall("msvcrt\atan2", "Double",Sqrt(vA), "Double",Sqrt(1-vA), "Cdecl Double")
}
==================================================

> FURTHER: PLOT A GRAPH

- A note about Cartesian v. screen coordinates:

Code: Select all

be aware of Cartesian coordinates: y decreases as you go downwards
     y
     |
-x — + — x
     |
    -y

v. screen coordinates: y increases as you go downwards
     + — x
     |
     y
- Note also: for bitmaps/icons, in some systems, the pixel rows are interpreted as bottom up. So, the first row of pixel data refers to the bottom row, and the last row of pixel data refers to the top row.

- Note also: a possible off-by-one error when plotting points.
- To plot a graph at integers, -10 to 10, is 21 points.
- To plot a graph in 0.5 steps, -10 to 10, is 41 points.
- To plot a graph in 0.25 steps, -10 to 10, is 81 points.

- Note: when dealing with graphs, or anything 2-dimensional e.g. a spreadsheet/table or a 2D matrix; when looping through data, you typically handle rows, and then retrieve items within rows. So, it's typical to process data 'by rows', rather than 'by columns'.

==================================================

> LINKS: DOCUMENTATION

Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#Operators
Variables and Expressions - Definition & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/Variables.htm#Operators

[ternary operator]
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#ternary

[short-circuit Boolean evaluation]
Functions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Functions.htm#ShortCircuit

[Abs Ceil Exp Floor Ln Log Max/Min Mod Round Sqrt]
[Sin/Cos/Tan ASin/ACos/ATan]
Math functions - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Math.htm
Functions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Functions.htm#Math

[types][Integer: similar to Ceil/Round/Floor]
Integer - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/Integer.htm
Float - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/Float.htm

Random - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Random.htm
Random - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/Random.htm

[Sort for numeric/random sorting]
Sort - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Sort.htm

[number formats]
Format() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Format.htm
Format() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Format.htm#Example
If Var is Type - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/IfIs.htm
RegExMatch() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/RegExMatch.htm
RegExReplace() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/RegExReplace.htm

[dates][note: DateAdd/DateDiff are AHK v2]
FormatTime - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/FormatTime.htm
DateAdd - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/DateAdd.htm
DateDiff - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/DateDiff.htm

[deprecated]
SetFormat - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/SetFormat.htm
EnvAdd - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/EnvAdd.htm
EnvSub - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/EnvSub.htm

==================================================

> LINKS: AUDIO: MUSICAL NOTES (MIDI) (BEEP)

[SoundBeep examples and conversion between pitch (frequency)/MIDI note numbers/scientific pitch notation]
musical SoundBeeps, MIDI note numbers, scientific pitch notation - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=34619

MIDI Output from AHK - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/17212-midi-output-from-ahk/

[octaves]
Musical note - Wikipedia
https://en.wikipedia.org/wiki/Musical_note
[MIDI note number/pitch formulae]
MIDI tuning standard - Wikipedia
https://en.wikipedia.org/wiki/MIDI_tuning_standard

==================================================

> LINKS: AUDIO: CREATE WAV FILE

create wav files from scratch (draw a wav file) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=34584

==================================================

> LINKS: GRAPHICS: RAINBOW COLOURS (ELECTROMAGNETIC SPECTRUM)

graphics: rainbow colours - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=34837
efg's Computer Lab: Spectra Lab Report
http://www.efg2.com/Lab/ScienceAndEngineering/Spectra.htm

==================================================

> LINKS: GRAPHICS: COLOURS AND TEMPERATURE

graphics: rainbow colours - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=34837
Class Monitor (Brightness, ColorTemperature) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=7854
How to Convert Temperature (K) to RGB: Algorithm and Sample Code – Tanner Helland (dot) com
http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/

==================================================

> LINKS: GRAPHICS: DISTANCE BETWEEN 2 COLOURS

find nearest colour in list (nearest RGB/BGR value) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=34623
Color difference - Wikipedia
https://en.wikipedia.org/wiki/Color_difference
Function within function possible ? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=32416
[Library] HSL & HSV v0.1 (Preview) - Convert RGB To/From HSL or HSV - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=30908

==================================================

> LINKS: GRAPHICS: ROTATE IMAGE BY SCALING/SHEARING

MS Paint: rotate by scaling/shearing (stretching/skewing) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=64481

==================================================

> LINKS: GRAPHICS: CREATE BMP FILE

graphics: create bmp files from scratch - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=34952

==================================================

> LINKS: GRAPHICS: SOLAR SYSTEM

graphics: Solar System - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=34790

==================================================

> LINKS: GRAPHICS: RESIZE IMAGES (FIT WITHIN RECTANGLE, MAINTAIN RATIO)

[maths to work out how image should be resized]
How to perfectly fit any image on a fixed size picture control - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=34785&p=162741

==================================================

> LINKS: CHECK IF WINDOWS/RECTANGLES OVERLAP

check if rectangles (windows) overlap - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=30809

==================================================

> LINKS: EVAL (DYNAMIC CODE)

[eval][dynamic code][calculation based on a string][via JavaScript]
Math - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=29602&p=139131#p139131
eval (using JS/COM) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=15389
Math - JavaScript | MDN
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math

[also:]
in-line calculator - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=27431

[also:]
Run / RunWait - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Run.htm#ExecScript

==================================================

> LINKS: NORMAL DISTRIBUTION

[calc.ahk by Laszlo][e.g. gamma/erf functions]
AutoHotkey-scripts-by-others/calc.ahk at master · jeeswg/AutoHotkey-scripts-by-others
https://github.com/jeeswg/AutoHotkey-scripts-by-others/blob/master/calc.ahk
Popup calculator / expression evaluator - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/24114-popup-calculator-expression-evaluator/

[bell curve and CDF]
Excel NORM.S.DIST Function
https://www.excelfunctions.net/excel-norm-s-dist-function.html
Excel Statistical Functions
https://www.excelfunctions.net/excel-statistical-functions.html

[bell curve]
[equivalent to numbers obtained by rolling n dice and summing]
[an inverse ERF function, ErfInv9]
Bell curve? - Page 2 - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/28475-bell-curve/page-2#entry182636

[CDF][a cdf function can be created by using the gamma and erf functions from calc.ahk]

Code: Select all

cdf(x)
{
	return (1 + erf(x/Sqrt(2))) / 2
}
[ERF and CDF]
cdf - How are the Error Function and Standard Normal distribution function related? - Cross Validated
https://stats.stackexchange.com/questions/187828/how-are-the-error-function-and-standard-normal-distribution-function-related

==================================================

> LINKS: WXMAXIMA

[Maxima CAS (freeware computer algebra system) (cf. Mathematica, MATLAB)]
[highly-recommended freeware for anyone doing secondary school or university maths (high school or college math)]

wxMaxima
http://andrejv.github.io/wxmaxima/
Maxima CAS Tutorials - YouTube
https://www.youtube.com/playlist?list=PLEDEE2F7C6750729F
Maxima CAS (wxMaxima): run via the command line - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=56032&p=239502#p239502

==================================================

> LINKS: CUSTOM FUNCTIONS

[factorial]
jeeswg's functions tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=41823

FloorDiv / FloorMod (and an 'always positive' Mod function) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=55751

BitGet/BitPut (NumGet/NumPut for bits) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=59736

quantiles: list first number, last number, and numbers in-between - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=55844

BaseToDec / DecToBase - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=57772

[Range function]
traditional for loop: for i = a to b (step c) possibilities - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=43022

[big integer functions][mathematics by string manipulation][handle big numbers]
Decimal to Base64 conversion + Custom character list - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=37465&p=174697#p174697
Scientific Maths - infinite precision Mathematics Library - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/93516-scientific-maths-infinite-precision-mathematics-library/

[big integer functions][further]
BigInteger-Calculation with AHK - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/3474-biginteger-calculation-with-ahk/
big number math - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/13403-big-number-math/

[Combin/Permut][combinatorics][combinations/permutations (with/without repetitions)]
[e.g. aaaa, aaab, ... zzzz]
[e.g. Excel: A to Z, AA to ZZ, ...]
combinations and permutations (and anagrams) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=34244&p=158871#p158871

[complex numbers]
complex numbers - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=35809

[matrices][linear algebra][matrix multiplication, matrix transposition]
matrix functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=64480
MS Paint: rotate by scaling/shearing (stretching/skewing) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=64481

[graphs][Mandelbrot set]
graphics: plot a graph / Mandelbrot set - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=35807

[hashes]
CRC-32 hash tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=35671

==================================================

> LINKS: FURTHER

multiline mathematical calculations - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=27106

[tutorials]
jeeswg's base64 tutorial: size calculations - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=50571

Pascal's triangle - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=59635

[explaining bitwise and/or]
Inequality If-Else expressions for colors - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=29958

[dec2hex][hex2dec]
Use Gdip to split a single image into multiple images - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=29101

[issues with SetFormat]
(var1 = var2) inconsistent on different scripts - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=34103

[truncate numbers]
compare numbers with different decimal point - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=29400

[trigonometry]
How to find angle? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=32347
Compass - Measure angles and scale distances with your mouse - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=7225
Measure the angle between horizontal x axis and a line joining mouse pointer position and center of the monitor - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=24014

[calculate window position/size relative to another window]
cover window region - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=35259
Distinguishing Between Multiple Windows of Same Application - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=35670

==================================================
Last edited by jeeswg on 01 Nov 2019, 22:33, edited 12 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
just me
Posts: 9464
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: jeeswg's mathematics tutorial

15 May 2019, 05:12

You stuffed the whole contents into one post without internal links! :facepalm:

Edit: Dear moderators, if jeeswg wants to restructure this thread, feel free to delete this post.
Last edited by just me on 16 May 2019, 03:25, edited 1 time in total.
User avatar
Cuadrix
Posts: 236
Joined: 07 May 2017, 08:26

Re: jeeswg's mathematics tutorial

15 May 2019, 19:52

What the hell is this sorcery
sree2022
Posts: 3
Joined: 28 Aug 2022, 11:51

Re: jeeswg's mathematics tutorial

01 Sep 2022, 15:58

Quite informative. Excellent presentation all in one place
peter_ahk
Posts: 100
Joined: 13 Feb 2024, 14:49

Re: jeeswg's mathematics tutorial

25 Feb 2024, 20:57

thank you thats very usefull to me

Return to “Tutorials (v1)”

Who is online

Users browsing this forum: No registered users and 144 guests