Features

10
10 passed, 0 failed

Scenarios

431
431 passed, 0 failed

Steps

1117
1117 passed, 0 failed, 0 skipped

The Anzan language itself

48/48 scenarios passed · 0µs
Operator precedence and associativityoutline · 6passed
2 + 3 * 4, 14, # multiplicative over additivepassed2ms
WhenI calculate "2 + 3 * 4"2ms
Thenthe result is "14"64µs
2^3^2, 512, # ^ is right-associativepassed200µs
WhenI calculate "2^3^2"157µs
Thenthe result is "512"39µs
-2^2, -4, # unary minus binds looser than ^passed126µs
WhenI calculate "-2^2"90µs
Thenthe result is "-4"34µs
2^-2, 0.25, # the exponent may carry its own signpassed702µs
WhenI calculate "2^-2"661µs
Thenthe result is "0.25"37µs
[10, 2][0]^2, 100, # postfix indexing binds tighter than ^passed213µs
WhenI calculate "[10, 2][0]^2"177µs
Thenthe result is "100"33µs
√4 + 5, 7, # prefix √ is unary, not greedypassed1ms
WhenI calculate "√4 + 5"965µs
Thenthe result is "7"36µs
Number literalsoutline · 6passed
1_000 + 0, 1000passed145µs
WhenI calculate "1_000 + 0"109µs
Thenthe result is "1000"34µs
2.5e-3, 0.0025passed106µs
WhenI calculate "2.5e-3"71µs
Thenthe result is "0.0025"33µs
.5 + .5, 1passed113µs
WhenI calculate ".5 + .5"77µs
Thenthe result is "1"33µs
0xFF, 255passed87µs
WhenI calculate "0xFF"52µs
Thenthe result is "255"32µs
0b1010, 10passed92µs
WhenI calculate "0b1010"59µs
Thenthe result is "10"31µs
0xDEAD_BEEF, 3735928559passed89µs
WhenI calculate "0xDEAD_BEEF"54µs
Thenthe result is "3735928559"33µs
Pinned cell references evaluate like plain onesoutline · 3passed
$A:$1 * 2passed289µs
Givencell A:1 contains "21"148µs
WhenI calculate "$A:$1 * 2"105µs
Thenthe result is "42"32µs
$A:1 + A:1passed176µs
Givencell A:1 contains "21"57µs
WhenI calculate "$A:1 + A:1"82µs
Thenthe result is "42"33µs
A:$1 * 2passed155µs
Givencell A:1 contains "21"53µs
WhenI calculate "A:$1 * 2"66µs
Thenthe result is "42"33µs
A dangling $ is a loud lex erroroutline · 3passed
$passed93µs
WhenI calculate "$"53µs
Thenthe calculation fails mentioning "pins a cell reference"37µs
$xpassed73µs
WhenI calculate "$x"37µs
Thenthe calculation fails mentioning "pins a cell reference"34µs
2 + $ratepassed94µs
WhenI calculate "2 + $rate"58µs
Thenthe calculation fails mentioning "pins a cell reference"34µs
A comment-only line is a note, not an errorpassed94µs
WhenI calculate "# the calc below confirms our test"47µs
Thenthe result is "# the calc below confirms our test"40µs
A standalone note never touches anspassed268µs
WhenI calculate "21 * 2"107µs
AndI calculate "# just thinking out loud"42µs
AndI calculate "ans"74µs
Thenthe result is "42"37µs
A trailing comment is stripped before evaluationpassed125µs
WhenI calculate "5 + 3 # adds them"87µs
Thenthe result is "8"35µs
A hash inside a string is not a commentpassed113µs
WhenI calculate "len("a # b")"74µs
Thenthe result is "5"36µs
Malformed programmer literals fail loudlyoutline · 4passed
0xFG, malformed hexpassed116µs
WhenI calculate "0xFG"71µs
Thenthe calculation fails mentioning "malformed hex"41µs
0x1.5, malformed hexpassed92µs
WhenI calculate "0x1.5"53µs
Thenthe calculation fails mentioning "malformed hex"36µs
0x, needs digitspassed73µs
WhenI calculate "0x"38µs
Thenthe calculation fails mentioning "needs digits"33µs
0b12, malformed binarypassed81µs
WhenI calculate "0b12"45µs
Thenthe calculation fails mentioning "malformed binary"32µs
Implicit multiplication covers parens, names, and constantspassed311µs
WhenI calculate "x9 = 4"135µs
AndI calculate "(2)(3) + 2x9"134µs
Thenthe result is "14"36µs
Modulo is exactpassed135µs
WhenI calculate "mod(0.3, 0.1)"105µs
Thenthe result is "0"27µs
Percent literalsoutline · 5passed
3%, 0.03passed97µs
WhenI calculate "3%"62µs
Thenthe result is "0.03"32µs
1 * 3%, 0.03passed170µs
WhenI calculate "1 * 3%"130µs
Thenthe result is "0.03"38µs
100 + 5%, 100.05passed173µs
WhenI calculate "100 + 5%"125µs
Thenthe result is "100.05"44µs
50% * 2, 1passed151µs
WhenI calculate "50% * 2"113µs
Thenthe result is "1"33µs
(2 + 3)%, 0.05passed137µs
WhenI calculate "(2 + 3)%"106µs
Thenthe result is "0.05"28µs
A number can't directly follow another valueoutline · 3passed
3 4passed101µs
WhenI calculate "3 4"61µs
Thenthe calculation fails mentioning "operator"37µs
3 % 4passed100µs
WhenI calculate "3 % 4"66µs
Thenthe calculation fails mentioning "operator"33µs
(1) 2passed155µs
WhenI calculate "(1) 2"79µs
Thenthe calculation fails mentioning "operator"72µs
A string is not a conditionpassed221µs
WhenI calculate "if("a", 1, 2)"184µs
Thenthe calculation fails mentioning "number"33µs
Empty reductions yield identitiespassed329µs
WhenI calculate "∑_i=1^0(i) + ∏_i=1^0(i)"207µs
Thenthe result is "1"118µs
A reduction spanning too many terms is refusedpassed253µs
WhenI calculate "∑_i=1^200000(i)"207µs
Thenthe calculation fails mentioning "100,000"42µs
Reserved words refuse assignmentoutline · 3passed
ans = 5passed120µs
WhenI calculate "ans = 5"74µs
Thenthe calculation fails mentioning "cannot assign"42µs
sigma = 1passed105µs
WhenI calculate "sigma = 1"69µs
Thenthe calculation fails mentioning "cannot assign"33µs
true = 0passed89µs
WhenI calculate "true = 0"54µs
Thenthe calculation fails mentioning "cannot assign"33µs
Parameters shadow globals without clobbering thempassed429µs
WhenI calculate "shade = 10"76µs
AndI calculate "twice(shade) = shade * 2"166µs
AndI calculate "twice(3) + shade"148µs
Thenthe result is "16"34µs
Free variables resolve at call timepassed341µs
WhenI calculate "base = 10"67µs
AndI calculate "above(y) = base + y"83µs
AndI calculate "base = 100"72µs
AndI calculate "above(1)"80µs
Thenthe result is "101"33µs
Special forms ship their manualpassed105µs
WhenI calculate "man(if)"66µs
Thendocumentation is shown mentioning "taken branch"36µs
String escapes are honoredpassed97µs
WhenI calculate "len("a\tb")"64µs
Thenthe result is "3"31µs
The word data is still an ordinary namepassed161µs
WhenI calculate "data = 5"59µs
AndI calculate "data * 2"68µs
Thenthe result is "10"31µs
Compact named arguments versus cell referencespassed452µs
GivenI calculate "data Q { a: Number, age: Number }"111µs
WhenI calculate "Q(a: 1, age:36).age"160µs
Thenthe result is "36"29µs
WhenI calculate "sum(a:1)"113µs
Thenthe result is "0"32µs

Calculating in the log

25/25 scenarios passed · 0µs
Everyday calculations are exactoutline · 10passed
0.1 + 0.2, 0.3passed146µs
WhenI calculate "0.1 + 0.2"105µs
Thenthe result is "0.3"38µs
2(3 + 4), 14passed140µs
WhenI calculate "2(3 + 4)"104µs
Thenthe result is "14"34µs
2^10, 1024passed129µs
WhenI calculate "2^10"92µs
Thenthe result is "1024"34µs
∑(1, 2, 3), 6passed158µs
WhenI calculate "∑(1, 2, 3)"124µs
Thenthe result is "6"32µs
∑_i=1^10(i^2), 385passed425µs
WhenI calculate "∑_i=1^10(i^2)"391µs
Thenthe result is "385"31µs
pmt(0.05/12, 360, 200000), -1073.643246024277969656985158225109053609679713701passed1.94s
WhenI calculate "pmt(0.05/12, 360, 200000)"1.94s
Thenthe result is "-1073.643246024277969656985158225109053609679713701"109µs
margin(100, 80), 20passed784µs
WhenI calculate "margin(100, 80)"743µs
Thenthe result is "20"37µs
date(2026, 6, 6) - date(2026, 1, 1), 156passed275µs
WhenI calculate "date(2026, 6, 6) - date(2026, 1, 1)"238µs
Thenthe result is "156"33µs
if(2 > 1, 10, 20), 10passed178µs
WhenI calculate "if(2 > 1, 10, 20)"142µs
Thenthe result is "10"33µs
gcd(48, 36), 12passed212µs
WhenI calculate "gcd(48, 36)"173µs
Thenthe result is "12"34µs
A hundred dimes make exactly a dollar bagpassed1ms
WhenI calculate "∑_i=1^100(0.1)"1ms
Thenthe result is "10"70µs
The previous answer carries forwardpassed227µs
WhenI calculate "6 * 7"108µs
AndI calculate "ans + 8"83µs
Thenthe result is "50"32µs
Variables persist across calculationspassed209µs
WhenI calculate "rate2026 = 0.0825"80µs
AndI calculate "1200 * rate2026"95µs
Thenthe result is "99"31µs
A leading equals sign is toleratedpassed106µs
WhenI calculate "= 1 + 2"74µs
Thenthe result is "3"30µs
A failed calculation never clobbers the previous answerpassed225µs
WhenI calculate "6 * 7"69µs
AndI calculate "1 / 0"61µs
AndI calculate "ans + 0"60µs
Thenthe result is "42"30µs
Dividing by zero explains itselfpassed102µs
WhenI calculate "1 / 0"60µs
Thenthe calculation fails mentioning "division by zero"37µs
Typos are caught, not guessedpassed133µs
WhenI calculate "12 * rte"95µs
Thenthe calculation fails mentioning "unknown variable 'rte'"34µs
Comparisons answer 1 or 0outline · 7passed
2 < 3, 1passed104µs
WhenI calculate "2 < 3"70µs
Thenthe result is "1"32µs
3 < 2, 0passed96µs
WhenI calculate "3 < 2"67µs
Thenthe result is "0"26µs
3 > 2, 1passed120µs
WhenI calculate "3 > 2"83µs
Thenthe result is "1"33µs
2 <= 2, 1passed106µs
WhenI calculate "2 <= 2"72µs
Thenthe result is "1"31µs
2 >= 3, 0passed90µs
WhenI calculate "2 >= 3"63µs
Thenthe result is "0"25µs
2 == 2, 1passed97µs
WhenI calculate "2 == 2"64µs
Thenthe result is "1"30µs
2 != 2, 0passed87µs
WhenI calculate "2 != 2"59µs
Thenthe result is "0"25µs
Comparison chains are rejected, not misreadpassed114µs
WhenI calculate "1 < 2 < 3"77µs
Thenthe calculation fails mentioning "can't be chained"35µs

Data types — declared records with named construction

47/47 scenarios passed · 0µs
Declaring a type registers its constructorpassed371µs
WhenI calculate "data Person { name: String, age: Number, active: Boolean }"134µs
Thenthe result is "data Person { name: String, age: Number, active: Boolean }"28µs
WhenI calculate "Person(name: "Ada", age: 36, active: true)"165µs
Thenthe result is "Person(name: "Ada", age: 36, active: true)"38µs
Construction is named fields or one map — never positionalpassed604µs
GivenI calculate "data Pt { x: Number, y: Number }"97µs
WhenI calculate "Pt(y: 4, x: 3)"105µs
Thenthe result is "Pt(x: 3, y: 4)"35µs
WhenI calculate "m = {x: 3, y: 4}"98µs
AndI calculate "Pt(m)"72µs
Thenthe result is "Pt(x: 3, y: 4)"46µs
WhenI calculate "Pt(3, 4)"106µs
Thenthe calculation fails mentioning "takes named fields"34µs
Field types accept any casing and Boolean fields render true/falsepassed255µs
WhenI calculate "data Flag { on: boolean }"85µs
Thenthe result is "data Flag { on: Boolean }"28µs
WhenI calculate "Flag(on: 1 < 2)"109µs
Thenthe result is "Flag(on: true)"28µs
Fields read like map members, by dot or by keypassed2ms
GivenI calculate "data Pt { x: Number, y: Number }"95µs
AndI calculate "p = Pt(x: 3, y: 4)"117µs
WhenI calculate "sqrt(p.x^2 + p.y^2)"1ms
Thenthe result is "5"54µs
WhenI calculate "p["y"]"70µs
Thenthe result is "4"31µs
WhenI calculate "keys(p)"67µs
Thenthe result is "["x", "y"]"60µs
WhenI calculate "len(p)"66µs
Thenthe result is "2"29µs
Records collect into arrays and flow through higher-order functionspassed1ms
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"132µs
AndI calculate "team = [Person(name: "Ada", age: 36, active: true), Person(name: "Grace", age: 30, active: false)]"266µs
WhenI calculate "map(x -> x.age, team)"102µs
Thenthe result is "[36, 30]"37µs
WhenI calculate "sum(map(x -> x.age, team))"126µs
Thenthe result is "66"29µs
WhenI calculate "filter(x -> x.active, team)"93µs
Thenthe result is "[Person(name: "Ada", age: 36, active: true)]"38µs
WhenI calculate "map(Person, [{name: "Bo", age: 1, active: true}])"161µs
Thenthe result is "[Person(name: "Bo", age: 1, active: true)]"36µs
Instances canonicalize to declaration order and compare deeplypassed527µs
GivenI calculate "data Pt { x: Number, y: Number }"95µs
WhenI calculate "Pt(y: 2, x: 1) == Pt(x: 1, y: 2)"178µs
Thenthe result is "1"33µs
WhenI calculate "Pt(x: 1, y: 2) == {x: 1, y: 2}"186µs
Thenthe result is "0"26µs
toJson is pretty by default, compact on request, honest about Booleanspassed628µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"128µs
AndI calculate "p = Person(name: "Ada", age: 36, active: true)"147µs
WhenI calculate "toJson(p)"71µs
Thenthe result is ""{\n \"name\": \"Ada\",\n \"age\": 36,\n \"active\": true\n}""34µs
WhenI calculate "toJson(p, Json.Compact)"87µs
Thenthe result is ""{\"name\":\"Ada\",\"age\":36,\"active\":true}""31µs
WhenI calculate "toJson([1, 2])"95µs
Thenthe result is ""[\n 1,\n 2\n]""28µs
fromJson parses JSON into values, exactlypassed811µs
WhenI calculate "fromJson("[1, 2, 3]")"106µs
Thenthe result is "[1, 2, 3]"41µs
WhenI calculate "fromJson("{\"name\": \"Ada\", \"age\": 36}").age"99µs
Thenthe result is "36"30µs
WhenI calculate "fromJson("0.30000000000000004") == 0.30000000000000004"101µs
Thenthe result is "1"29µs
WhenI calculate "fromJson("123456789012345678901234567890.5") * 2"113µs
Thenthe result is "246913578024691357802469135781"37µs
WhenI calculate "fromJson("true") + fromJson("false")"127µs
Thenthe result is "1"29µs
WhenI calculate "fromJson("\"\\u0041da\"")"62µs
Thenthe result is ""Ada""26µs
fromJson and a constructor re-type a serialized recordpassed707µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"122µs
AndI calculate "p = Person(name: "Ada", age: 36, active: true)"145µs
WhenI calculate "Person(fromJson(toJson(p))) == p"140µs
Thenthe result is "1"29µs
WhenI calculate "map(Person, fromJson(toJson([p, p])))[1].age"232µs
Thenthe result is "36"29µs
fromJson mistakes explain themselvesoutline · 5passed
fromJson("null"), has no Anzan valuepassed112µs
WhenI calculate "fromJson("null")"76µs
Thenthe calculation fails mentioning "has no Anzan value"33µs
fromJson("[1,"), unexpected endpassed191µs
WhenI calculate "fromJson("[1,")"155µs
Thenthe calculation fails mentioning "unexpected end"33µs
fromJson("[1] junk"), trailing contentpassed115µs
WhenI calculate "fromJson("[1] junk")"81µs
Thenthe calculation fails mentioning "trailing content"31µs
fromJson("{\"a\":1,\"a\":2}"), duplicate keypassed121µs
WhenI calculate "fromJson("{\"a\":1,\"a\":2}")"88µs
Thenthe calculation fails mentioning "duplicate key"31µs
fromJson(5), wants JSON textpassed106µs
WhenI calculate "fromJson(5)"74µs
Thenthe calculation fails mentioning "wants JSON text"30µs
Json options are named constants, not magic flagspassed551µs
WhenI calculate "Json.Pretty"58µs
Thenthe result is ""pretty""27µs
WhenI calculate "toJson([1, 2], Json.Compact)"128µs
Thenthe result is ""[1,2]""25µs
WhenI calculate "toJson([1, 2], "compact")"105µs
Thenthe result is ""[1,2]""26µs
WhenI calculate "Json = 5"53µs
Thenthe calculation fails mentioning "cannot assign"31µs
WhenI calculate "man(Json)"57µs
Thendocumentation is shown mentioning "Formatting options"31µs
Construction mistakes explain themselvesoutline · 9passed
Person(name: "Ada", age: 36), missing 'active'passed284µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"135µs
WhenI calculate "Person(name: "Ada", age: 36)"117µs
Thenthe calculation fails mentioning "missing 'active'"29µs
Person(name: 7, age: 36, active: true), 'name' of Person is a Stringpassed393µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"123µs
WhenI calculate "Person(name: 7, age: 36, active: true)"220µs
Thenthe calculation fails mentioning "'name' of Person is a String"44µs
Person(name: "A", age: "x", active: true), 'age' of Person is a Numberpassed318µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"133µs
WhenI calculate "Person(name: "A", age: "x", active: true)"138µs
Thenthe calculation fails mentioning "'age' of Person is a Number"42µs
Person(name: "A", age: 36, active: 7), use true or falsepassed320µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"133µs
WhenI calculate "Person(name: "A", age: 36, active: 7)"150µs
Thenthe calculation fails mentioning "use true or false"32µs
Person(name: "A", age: 36, active: true, pet: 1), no field 'pet'passed343µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"125µs
WhenI calculate "Person(name: "A", age: 36, active: true, pet: 1)"180µs
Thenthe calculation fails mentioning "no field 'pet'"32µs
Person(name: "A", name: "B", age: 1, active: 1), duplicate fieldpassed307µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"124µs
WhenI calculate "Person(name: "A", name: "B", age: 1, active: 1)"147µs
Thenthe calculation fails mentioning "duplicate field"32µs
toJson(sqrt), can't serialize a functionpassed252µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"129µs
WhenI calculate "toJson(sqrt)"72µs
Thenthe calculation fails mentioning "can't serialize a function"40µs
toJson(1, "wat"), unknown toJson optionpassed268µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"136µs
WhenI calculate "toJson(1, "wat")"97µs
Thenthe calculation fails mentioning "unknown toJson option"32µs
toJson(1, true), Json.Pretty or Json.Compactpassed251µs
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"124µs
WhenI calculate "toJson(1, true)"92µs
Thenthe calculation fails mentioning "Json.Pretty or Json.Compact"32µs
Declaration mistakes explain themselvesoutline · 5passed
data person { a: Number }, capital letterpassed105µs
WhenI calculate "data person { a: Number }"71µs
Thenthe calculation fails mentioning "capital letter"31µs
data Bad { a: truthy }, declared data typepassed104µs
WhenI calculate "data Bad { a: truthy }"70µs
Thenthe calculation fails mentioning "declared data type"32µs
data Empty {}, at least one fieldpassed96µs
WhenI calculate "data Empty {}"61µs
Thenthe calculation fails mentioning "at least one field"32µs
data Dup { a: Number, A: Number }, duplicate fieldpassed154µs
WhenI calculate "data Dup { a: Number, A: Number }"109µs
Thenthe calculation fails mentioning "duplicate field"42µs
data Abs { a: Number }, built-inpassed130µs
WhenI calculate "data Abs { a: Number }"94µs
Thenthe calculation fails mentioning "built-in"33µs
Types and functions can't share a name, but redeclaring your own type workspassed725µs
GivenI calculate "f(x) = x + 1"126µs
WhenI calculate "data F { a: Number }"82µs
Thenthe calculation fails mentioning "already a function"34µs
WhenI calculate "data G { a: Number }"83µs
AndI calculate "g(x) = x"82µs
Thenthe calculation fails mentioning "is a data type"43µs
WhenI calculate "data G { a: Number, b: Number }"100µs
AndI calculate "G(a: 1, b: 2).b"123µs
Thenthe result is "2"40µs
A type documents itself through its trailing commentpassed253µs
WhenI calculate "data Invoice { total: Number, paid: Boolean } # one customer invoice"111µs
AndI calculate "man(Invoice)"63µs
Thendocumentation is shown mentioning "one customer invoice"75µs
toJson escapes strings and renders empty containerspassed295µs
WhenI calculate "toJson("a\tb")"81µs
Thenthe result is ""\"a\\tb\"""34µs
WhenI calculate "toJson([])"63µs
Thenthe result is ""[]""26µs
WhenI calculate "toJson({})"57µs
Thenthe result is ""{}""26µs
A constructor call works in tail position of a recursive functionpassed492µs
GivenI calculate "data Box { v: Number }"79µs
AndI calculate "wrap(n) = if(n <= 0, Box(v: n), wrap(n - 1))"196µs
WhenI calculate "wrap(5).v"188µs
Thenthe result is "0"25µs
The explicit formula marker rejects declarations in cellspassed153µs
Givencell A:1 contains "=data Pt { x: Number }"105µs
Thencell A:1 shows an error mentioning "drop the leading '='"45µs
A plain declaration in a cell is a sheet-scoped 𝑫 typepassed522µs
Givencell A:1 contains "data Pt { x: Number, y: Number }"153µs
Andcell B:1 contains "Pt(x: 3, y: 4).x"118µs
Thencell A:1 shows "𝑫 Pt"46µs
Andcell B:1 shows "3"74µs
WhenI calculate "data Pt { x: Number }"79µs
Thenthe calculation fails mentioning "defined in cell Sheet 1!A:1"41µs
Types and record variables survive save and reopenpassed1ms
GivenI calculate "data Person { name: String, age: Number, active: Boolean }"145µs
AndI calculate "p = Person(name: "Ada", age: 36, active: true)"165µs
Whenthe workbook is saved and reopened857µs
AndI calculate "p.age + Person(name: "B", age: 4, active: false).age"207µs
Thenthe result is "40"36µs
A typed parameter dispatches a function by argument typepassed464µs
GivenI calculate "kind(n: Number) = "number""100µs
AndI calculate "kind(s: String) = "string""143µs
WhenI calculate "kind(42)"99µs
Thenthe result is ""number""27µs
WhenI calculate "kind("hi")"62µs
Thenthe result is ""string""26µs
A function can be written for a data typepassed2ms
GivenI calculate "data Point { x: Number, y: Number }"97µs
AndI calculate "midpoint(a: Point, b: Point) = Point(x: (a.x + b.x) / 2, y: (a.y + b.y) / 2)"255µs
WhenI calculate "midpoint(Point(x: 0, y: 0), Point(x: 4, y: 10))"1ms
Thenthe result is "Point(x: 2, y: 5)"56µs
An operator can be overloaded for a data typepassed611µs
GivenI calculate "data Point { x: Number, y: Number }"117µs
AndI calculate "+(a: Point, b: Point) = Point(x: a.x + b.x, y: a.y + b.y)"194µs
WhenI calculate "Point(x: 1, y: 2) + Point(x: 10, y: 20)"254µs
Thenthe result is "Point(x: 11, y: 22)"40µs
An overloaded operator can mix a data type and a scalarpassed493µs
GivenI calculate "data Point { x: Number, y: Number }"104µs
AndI calculate "*(a: Point, s: Number) = Point(x: a.x * s, y: a.y * s)"178µs
WhenI calculate "Point(x: 1, y: 2) * 3"167µs
Thenthe result is "Point(x: 3, y: 6)"38µs
Built-in arithmetic is untouched by an overloadpassed558µs
GivenI calculate "data Point { x: Number, y: Number }"96µs
AndI calculate "+(a: Point, b: Point) = Point(x: a.x + b.x, y: a.y + b.y)"180µs
WhenI calculate "1 + 2"111µs
Thenthe result is "3"74µs
WhenI calculate ""Q" + 1"63µs
Thenthe result is ""Q1""26µs
An operator overload must involve a data typepassed182µs
WhenI calculate "+(a: Number, b: Number) = 5"141µs
Thenthe calculation fails mentioning "must involve a data type"37µs
Records compare equal by all of their statepassed737µs
GivenI calculate "data Point { x: Number, y: Number }"98µs
WhenI calculate "Point(x: 1, y: 2) == Point(x: 1, y: 2)"180µs
Thenthe result is "1"32µs
WhenI calculate "Point(x: 1, y: 2) == Point(x: 9, y: 9)"169µs
Thenthe result is "0"31µs
WhenI calculate "Point(x: 1, y: 2) != Point(x: 9, y: 9)"187µs
Thenthe result is "1"33µs
Operator overloads survive save and reopenpassed2ms
GivenI calculate "data Point { x: Number, y: Number }"105µs
AndI calculate "+(a: Point, b: Point) = Point(x: a.x + b.x, y: a.y + b.y)"194µs
AndI calculate "*(a: Point, s: Number) = Point(x: a.x * s, y: a.y * s)"173µs
Whenthe workbook is saved and reopened557µs
AndI calculate "(Point(x: 1, y: 1) + Point(x: 2, y: 3)).y"275µs
Thenthe result is "4"32µs
AndI calculate "(Point(x: 2, y: 3) * 2).x"158µs
Thenthe result is "4"30µs
A data type can have fields of another data typepassed810µs
GivenI calculate "data Point { x: Number, y: Number }"111µs
AndI calculate "data Line { a: Point, b: Point }"101µs
AndI calculate "l = Line(a: Point(x: 1, y: 2), b: Point(x: 3, y: 4))"246µs
WhenI calculate "l.b.y"56µs
Thenthe result is "4"33µs
WhenI calculate "l == Line(a: Point(x: 1, y: 2), b: Point(x: 3, y: 4))"226µs
Thenthe result is "1"30µs
A nested field rejects the wrong typepassed440µs
GivenI calculate "data Point { x: Number, y: Number }"100µs
AndI calculate "data Line { a: Point, b: Point }"90µs
WhenI calculate "Line(a: 5, b: Point(x: 3, y: 4))"191µs
Thenthe calculation fails mentioning "is a Point"51µs
Nested records survive save and reopenpassed1ms
GivenI calculate "data Point { x: Number, y: Number }"110µs
AndI calculate "data Line { a: Point, b: Point }"93µs
AndI calculate "seg = Line(a: Point(x: 1, y: 1), b: Point(x: 4, y: 5))"240µs
Whenthe workbook is saved and reopened695µs
AndI calculate "seg.b.x - seg.a.x"106µs
Thenthe result is "3"34µs

Number formats are presentation, never value

15/15 scenarios passed · 0µs
A formatted cell displays its value in costumeoutline · 13passed
1234567.5, number, 1,234,567.50passed261µs
Givencell B:1 contains "1234567.5"81µs
Andcell B:1 is formatted as "number"76µs
Thencell B:1 displays "1,234,567.50"99µs
1234.5, dollars, $1,234.50passed150µs
Givencell B:1 contains "1234.5"70µs
Andcell B:1 is formatted as "dollars"34µs
Thencell B:1 displays "$1,234.50"42µs
-1234.5, dollars, -$1,234.50passed169µs
Givencell B:1 contains "-1234.5"73µs
Andcell B:1 is formatted as "dollars"35µs
Thencell B:1 displays "-$1,234.50"56µs
-2, euros, -€2.00passed143µs
Givencell B:1 contains "-2"56µs
Andcell B:1 is formatted as "euros"33µs
Thencell B:1 displays "-€2.00"51µs
0.0825, percent, 8.25%passed137µs
Givencell B:1 contains "0.0825"58µs
Andcell B:1 is formatted as "percent"33µs
Thencell B:1 displays "8.25%"42µs
1, percent, 100.00%passed127µs
Givencell B:1 contains "1"50µs
Andcell B:1 is formatted as "percent"32µs
Thencell B:1 displays "100.00%"42µs
20610, a date, 2026-06-06passed185µs
Givencell B:1 contains "20610"54µs
Andcell B:1 is formatted as "a date"69µs
Thencell B:1 displays "2026-06-06"57µs
0, a date, 1970-01-01passed108µs
Givencell B:1 contains "0"49µs
Andcell B:1 is formatted as "a date"27µs
Thencell B:1 displays "1970-01-01"29µs
195, hex, 0xC3passed175µs
Givencell B:1 contains "195"78µs
Andcell B:1 is formatted as "hex"44µs
Thencell B:1 displays "0xC3"50µs
-255, hex, -0xFFpassed145µs
Givencell B:1 contains "-255"64µs
Andcell B:1 is formatted as "hex"34µs
Thencell B:1 displays "-0xFF"43µs
195, binary, 0b1100_0011passed162µs
Givencell B:1 contains "195"49µs
Andcell B:1 is formatted as "binary"34µs
Thencell B:1 displays "0b1100_0011"75µs
5, binary, 0b101passed131µs
Givencell B:1 contains "5"54µs
Andcell B:1 is formatted as "binary"33µs
Thencell B:1 displays "0b101"41µs
1.5, hex, 1.5passed127µs
Givencell B:1 contains "1.5"55µs
Andcell B:1 is formatted as "hex"31µs
Thencell B:1 displays "1.5"37µs
Formatting never touches the underlying mathpassed338µs
Givencell B:1 contains "0.0825"55µs
Andcell B:1 is formatted as "percent"32µs
Andcell B:2 contains "=B:1 * 200"79µs
Thencell B:1 displays "8.25%"83µs
Andcell B:2 shows "16.5"84µs
Hex format is display-only, like every formatpassed342µs
Givencell A:1 contains "=bitOr(0xC0, 0x03)"99µs
Andcell A:1 is formatted as "hex"32µs
Andcell A:2 contains "=A:1 + 1"62µs
Thencell A:1 displays "0xC3"89µs
Andcell A:2 shows "196"54µs

Defining your own functions

16/16 scenarios passed · 0µs
Define a function and use itpassed274µs
WhenI calculate "tax(x) = x * 1.0825 # TX sales tax"133µs
AndI calculate "tax(100)"102µs
Thenthe result is "108.25"35µs
Functions compose regardless of definition orderpassed311µs
WhenI calculate "g(x) = f(x) + 1"104µs
AndI calculate "f(x) = x * 2"81µs
AndI calculate "g(20)"93µs
Thenthe result is "41"30µs
Recursion workspassed326µs
WhenI calculate "fact(n) = if(n <= 1, 1, n * fact(n - 1))"189µs
AndI calculate "fact(10)"103µs
Thenthe result is "3628800"30µs
Lambdas are valuespassed244µs
WhenI calculate "map(x -> x * 2, [1, 2, 3])"199µs
Thenthe result is "[2, 4, 6]"41µs
Structures carry datapassed318µs
WhenI calculate "people = [{name: "Ada", age: 36}, {name: "Bob", age: 32}]"195µs
AndI calculate "people[0].age"80µs
Thenthe result is "36"37µs
Machin's 1706 formula recovers pipassed12ms
WhenI calculate "arctanInv(x, n) = ∑_k=0^(n)((-1)^k / ((2k + 1) * x^(2k + 1)))"353µs
AndI calculate "16 * arctanInv(5, 40) - 4 * arctanInv(239, 15) - pi"11ms
Thenthe result is within "1e-45" of zero119µs
Comments are for humans and ignored by the mathpassed132µs
WhenI calculate "6 * 7 # the answer"98µs
Thenthe result is "42"32µs
A trailing comment becomes the function's documentationpassed230µs
WhenI calculate "tax(x) = x * 1.0825 # TX sales tax"131µs
AndI calculate "man(tax)"63µs
Thendocumentation is shown mentioning "TX sales tax"32µs
Built-in functions ship their manualpassed97µs
WhenI calculate "man(pmt)"60µs
Thendocumentation is shown mentioning "payment"33µs
Only the taken branch of if() runspassed153µs
WhenI calculate "if(1, 2, 1/0)"119µs
Thenthe result is "2"32µs
Deep recursion is bounded by memory, not a counterpassed57ms
WhenI calculate "countdown(n) = if(n <= 0, 0, countdown(n - 1) + 1)"177µs
AndI calculate "countdown(2000)"57ms
Thenthe result is "2000"76µs
Tail-recursive functions run at constant stack — any depthpassed12.63s
WhenI calculate "sumTo(n, acc) = if(n <= 0, acc, sumTo(n - 1, acc + n))"234µs
AndI calculate "sumTo(500000, 0)"12.63s
Thenthe result is "125000250000"85µs
Forgetting a base case fails politely, with a hintpassed185ms
WhenI calculate "F(n) = F(n - 1) + F(n - 2)"206µs
AndI calculate "f(3)"184ms
Thenthe calculation fails mentioning "base case"72µs
Runaway recursion is cut off cleanly, not a crashpassed11.01s
WhenI calculate "loop(n) = loop(n + 1)"153µs
AndI calculate "loop(1)"11.01s
Thenthe calculation fails mentioning "nested too deeply"91µs
Built-in names are protectedpassed146µs
WhenI calculate "abs(x) = x"107µs
Thenthe calculation fails mentioning "built-in"35µs
Function calls are case-insensitivepassed998µs
WhenI calculate "MIN(1, 2) + Sqrt(16)"957µs
Thenthe result is "5"38µs

The expanded function library

114/114 scenarios passed · 0µs
Exact combinatorics — BigInt keeps every digitoutline · 7passed
choose(5, 2), 10passed150µs
WhenI calculate "choose(5, 2)"114µs
Thenthe result is "10"33µs
choose(52, 5), 2598960passed185µs
WhenI calculate "choose(52, 5)"144µs
Thenthe result is "2598960"38µs
choose(100, 50), 100891344545564193334812497256passed366µs
WhenI calculate "choose(100, 50)"321µs
Thenthe result is "100891344545564193334812497256"42µs
choose(2, 5), 0passed129µs
WhenI calculate "choose(2, 5)"100µs
Thenthe result is "0"27µs
perm(5, 2), 20passed138µs
WhenI calculate "perm(5, 2)"104µs
Thenthe result is "20"32µs
perm(10, 3), 720passed144µs
WhenI calculate "perm(10, 3)"109µs
Thenthe result is "720"32µs
perm(10, 10), 3628800passed162µs
WhenI calculate "perm(10, 10)"127µs
Thenthe result is "3628800"33µs
Array plumbingoutline · 11passed
sort([3, 1, 2]), [1, 2, 3]passed169µs
WhenI calculate "sort([3, 1, 2])"126µs
Thenthe result is "[1, 2, 3]"41µs
sort(["pear", "fig", "kiwi"]), ["fig", "kiwi", "pear"]passed135µs
WhenI calculate "sort(["pear", "fig", "kiwi"])"94µs
Thenthe result is "["fig", "kiwi", "pear"]"38µs
unique([3, 1, 3, 2, 1]), [3, 1, 2]passed186µs
WhenI calculate "unique([3, 1, 3, 2, 1])"145µs
Thenthe result is "[3, 1, 2]"39µs
reverse([1, 2, 3]), [3, 2, 1]passed147µs
WhenI calculate "reverse([1, 2, 3])"107µs
Thenthe result is "[3, 2, 1]"38µs
reverse("abc"), "cba"passed88µs
WhenI calculate "reverse("abc")"59µs
Thenthe result is ""cba""27µs
seq(1, 5), [1, 2, 3, 4, 5]passed165µs
WhenI calculate "seq(1, 5)"118µs
Thenthe result is "[1, 2, 3, 4, 5]"45µs
seq(10, 0, -2), [10, 8, 6, 4, 2, 0]passed255µs
WhenI calculate "seq(10, 0, -2)"156µs
Thenthe result is "[10, 8, 6, 4, 2, 0]"82µs
sum(map(x -> x^2, seq(1, 10))), 385passed504µs
WhenI calculate "sum(map(x -> x^2, seq(1, 10)))"467µs
Thenthe result is "385"35µs
list(1, 2, 3), [1, 2, 3]passed143µs
WhenI calculate "list(1, 2, 3)"100µs
Thenthe result is "[1, 2, 3]"40µs
sumproduct([2, 3], [10, 100]), 320passed259µs
WhenI calculate "sumproduct([2, 3], [10, 100])"223µs
Thenthe result is "320"33µs
sumproduct(1, 2, 3, 4, 5, 6), 32passed223µs
WhenI calculate "sumproduct(1, 2, 3, 4, 5, 6)"188µs
Thenthe result is "32"32µs
list() turns a range into an array — higher-order functions over cellspassed568µs
Giventhe sheet contains:152µs
WhenI calculate "sum(filter(x -> x > 10, list(A:1..A:4)))"200µs
Thenthe result is "42"32µs
WhenI calculate "map(x -> x * 2, list(A:1..A:2))"141µs
Thenthe result is "[10, 24]"37µs
Statistics depth (sample conventions, full precision)outline · 11passed
variance(2, 4, 4, 4, 5, 5, 7, 9), 4.5714285714285714285714285714285714285714285714286passed1ms
WhenI calculate "variance(2, 4, 4, 4, 5, 5, 7, 9)"1ms
Thenthe result is "4.5714285714285714285714285714285714285714285714286"49µs
mode(1, 2, 2, 3, 3, 3), 3passed203µs
WhenI calculate "mode(1, 2, 2, 3, 3, 3)"164µs
Thenthe result is "3"37µs
mode(4, 9, 4, 9), 4passed151µs
WhenI calculate "mode(4, 9, 4, 9)"118µs
Thenthe result is "4"31µs
percentile(15, 20, 35, 40, 50, 0.4), 29passed270µs
WhenI calculate "percentile(15, 20, 35, 40, 50, 0.4)"236µs
Thenthe result is "29"31µs
percentile(1, 2, 3, 4, 0.75), 3.25passed230µs
WhenI calculate "percentile(1, 2, 3, 4, 0.75)"194µs
Thenthe result is "3.25"34µs
percentile(1, 2, 3, 1), 3passed171µs
WhenI calculate "percentile(1, 2, 3, 1)"137µs
Thenthe result is "3"31µs
geomean(4, 9), 6passed898µs
WhenI calculate "geomean(4, 9)"862µs
Thenthe result is "6"34µs
correl(1, 2, 3, 2, 4, 6), 1passed3ms
WhenI calculate "correl(1, 2, 3, 2, 4, 6)"3ms
Thenthe result is "1"34µs
slope(3, 5, 7, 1, 2, 3), 2passed2ms
WhenI calculate "slope(3, 5, 7, 1, 2, 3)"2ms
Thenthe result is "2"35µs
intercept(3, 5, 7, 1, 2, 3), 1passed2ms
WhenI calculate "intercept(3, 5, 7, 1, 2, 3)"2ms
Thenthe result is "1"33µs
forecast(4, 3, 5, 7, 1, 2, 3), 9passed2ms
WhenI calculate "forecast(4, 3, 5, 7, 1, 2, 3)"2ms
Thenthe result is "9"38µs
Amortization and depreciationoutline · 10passed
round(ipmt(0.05/12, 1, 360, 200000), 10), -833.3333333333passed1.80s
WhenI calculate "round(ipmt(0.05/12, 1, 360, 200000), 10)"1.80s
Thenthe result is "-833.3333333333"82µs
round(ipmt(0.05/12, 360, 360, 200000), 10), -4.4549512283passed3.77s
WhenI calculate "round(ipmt(0.05/12, 360, 360, 200000), 10)"3.77s
Thenthe result is "-4.4549512283"83µs
round(cumipmt(0.05/12, 360, 200000, 1, 12), 2), -9932.99passed1.78s
WhenI calculate "round(cumipmt(0.05/12, 360, 200000, 1, 12), 2)"1.78s
Thenthe result is "-9932.99"95µs
round(cumprinc(0.05/12, 360, 200000, 1, 12), 2), -2950.73passed1.78s
WhenI calculate "round(cumprinc(0.05/12, 360, 200000, 1, 12), 2)"1.78s
Thenthe result is "-2950.73"70µs
ipmt(0.05/12, 7, 360, 200000) + ppmt(0.05/12, 7, 360, 200000) == pmt(0.05/12, 360, 200000), 1passed5.32s
WhenI calculate "ipmt(0.05/12, 7, 360, 200000) + ppmt(0.05/12, 7, 360, 200000) == pmt(0.05/12, 360, 200000)"5.32s
Thenthe result is "1"102µs
sln(30000, 7500, 10), 2250passed897µs
WhenI calculate "sln(30000, 7500, 10)"854µs
Thenthe result is "2250"38µs
round(syd(30000, 7500, 10, 1), 2), 4090.91passed897µs
WhenI calculate "round(syd(30000, 7500, 10, 1), 2)"856µs
Thenthe result is "4090.91"38µs
ddb(30000, 7500, 10, 1), 6000passed775µs
WhenI calculate "ddb(30000, 7500, 10, 1)"735µs
Thenthe result is "6000"36µs
ddb(30000, 7500, 10, 10), 0passed1ms
WhenI calculate "ddb(30000, 7500, 10, 10)"1ms
Thenthe result is "0"37µs
round(nominal(effectiveRate(0.06, 12), 12), 10), 0.06passed972µs
WhenI calculate "round(nominal(effectiveRate(0.06, 12), 12), 10)"931µs
Thenthe result is "0.06"38µs
Business days and calendar positionsoutline · 9passed
quarter(date(2026, 6, 6)), 2passed192µs
WhenI calculate "quarter(date(2026, 6, 6))"156µs
Thenthe result is "2"34µs
quarter(date(2026, 11, 1)), 4passed179µs
WhenI calculate "quarter(date(2026, 11, 1))"144µs
Thenthe result is "4"33µs
weeknum(date(2026, 1, 1)), 1passed176µs
WhenI calculate "weeknum(date(2026, 1, 1))"141µs
Thenthe result is "1"33µs
weeknum(date(2026, 1, 4)), 2passed175µs
WhenI calculate "weeknum(date(2026, 1, 4))"139µs
Thenthe result is "2"34µs
workday(date(2026, 6, 5), 1) == date(2026, 6, 8), 1passed268µs
WhenI calculate "workday(date(2026, 6, 5), 1) == date(2026, 6, 8)"233µs
Thenthe result is "1"32µs
workday(date(2026, 6, 8), -1) == date(2026, 6, 5), 1passed271µs
WhenI calculate "workday(date(2026, 6, 8), -1) == date(2026, 6, 5)"237µs
Thenthe result is "1"32µs
networkdays(date(2026, 6, 1), date(2026, 6, 30)), 22passed265µs
WhenI calculate "networkdays(date(2026, 6, 1), date(2026, 6, 30))"231µs
Thenthe result is "22"32µs
networkdays(date(2026, 6, 1), date(2026, 6, 30), date(2026, 6, 19)), 21passed350µs
WhenI calculate "networkdays(date(2026, 6, 1), date(2026, 6, 30), date(2026, 6, 19))"317µs
Thenthe result is "21"31µs
networkdays(date(2026, 6, 5), date(2026, 6, 1)), -5passed263µs
WhenI calculate "networkdays(date(2026, 6, 5), date(2026, 6, 1))"225µs
Thenthe result is "-5"36µs
Scientific completionsoutline · 9passed
deg(pi), 180passed711µs
WhenI calculate "deg(pi)"674µs
Thenthe result is "180"34µs
deg(pi / 4), 45passed807µs
WhenI calculate "deg(pi / 4)"770µs
Thenthe result is "45"34µs
sin(rad(90)), 1passed320µs
WhenI calculate "sin(rad(90))"286µs
Thenthe result is "1"32µs
sinh(0), 0passed110µs
WhenI calculate "sinh(0)"82µs
Thenthe result is "0"26µs
cosh(0), 1passed105µs
WhenI calculate "cosh(0)"72µs
Thenthe result is "1"30µs
tanh(0), 0passed135µs
WhenI calculate "tanh(0)"85µs
Thenthe result is "0"44µs
asinh(0), 0passed144µs
WhenI calculate "asinh(0)"99µs
Thenthe result is "0"40µs
acosh(1), 0passed111µs
WhenI calculate "acosh(1)"81µs
Thenthe result is "0"27µs
atanh(0), 0passed112µs
WhenI calculate "atanh(0)"75µs
Thenthe result is "0"33µs
atan2 knows its quadrantpassed707µs
WhenI calculate "atan2(1, 1) - pi/4"272µs
Thenthe result is within "1e-15" of zero63µs
WhenI calculate "atan2(-1, -1) + 3 * pi/4"298µs
Thenthe result is within "1e-15" of zero70µs
Programmer tools — exact at any widthoutline · 14passed
toBase(255, 16), "FF"passed125µs
WhenI calculate "toBase(255, 16)"96µs
Thenthe result is ""FF""27µs
toBase(10, 2), "1010"passed117µs
WhenI calculate "toBase(10, 2)"93µs
Thenthe result is ""1010""22µs
fromBase("ff", 16), 255passed154µs
WhenI calculate "fromBase("ff", 16)"114µs
Thenthe result is "255"36µs
fromBase("1010", 2), 10passed131µs
WhenI calculate "fromBase("1010", 2)"101µs
Thenthe result is "10"28µs
toBase(-255, 16), "-FF"passed125µs
WhenI calculate "toBase(-255, 16)"98µs
Thenthe result is ""-FF""25µs
fromBase("-ff", 16), -255passed125µs
WhenI calculate "fromBase("-ff", 16)"90µs
Thenthe result is "-255"32µs
fromBase(toBase(123456789012345678901234567890, 36), 36), 123456789012345678901234567890passed233µs
WhenI calculate "fromBase(toBase(123456789012345678901234567890, 36), 36)"188µs
Thenthe result is "123456789012345678901234567890"41µs
bitAnd(12, 10), 8passed198µs
WhenI calculate "bitAnd(12, 10)"152µs
Thenthe result is "8"43µs
bitOr(12, 10), 14passed164µs
WhenI calculate "bitOr(12, 10)"118µs
Thenthe result is "14"43µs
bitXor(12, 10), 6passed149µs
WhenI calculate "bitXor(12, 10)"111µs
Thenthe result is "6"35µs
bitShift(1, 100), 1.267650600228229401496703205376e+30passed164µs
WhenI calculate "bitShift(1, 100)"117µs
Thenthe result is "1.267650600228229401496703205376e+30"44µs
bitShift(256, -4), 16passed140µs
WhenI calculate "bitShift(256, -4)"108µs
Thenthe result is "16"29µs
bitAnd(0xFF, 0x0F), 15passed180µs
WhenI calculate "bitAnd(0xFF, 0x0F)"122µs
Thenthe result is "15"52µs
toBase(0b1111, 16), "F"passed164µs
WhenI calculate "toBase(0b1111, 16)"122µs
Thenthe result is ""F""38µs
solve() is goal seek in a formulapassed253ms
WhenI calculate "solve(x -> x^2, 2) - sqrt(2)"1ms
Thenthe result is within "1e-12" of zero53µs
WhenI calculate "solve(cos, 0, 1) - pi/2"555µs
Thenthe result is within "1e-12" of zero57µs
WhenI calculate "solve(x -> if(x < 1, -1, 1), 0) - 1"1ms
Thenthe result is within "1e-12" of zero50µs
WhenI calculate "solve(r -> fv(r, 120, -100), 20000, 0.01) * 12"250ms
Thenthe result is within "1e-9" of "0.0958092381724"119µs
Derived builtins equal their definitionsoutline · 16passed
avg(2, 4, 9) == sum(2, 4, 9) / count(2, 4, 9)passed1ms
WhenI calculate "avg(2, 4, 9) == sum(2, 4, 9) / count(2, 4, 9)"1ms
Thenthe result is "1"44µs
stdev(2, 4, 4, 7) == sqrt(variance(2, 4, 4, 7))passed3ms
WhenI calculate "stdev(2, 4, 4, 7) == sqrt(variance(2, 4, 4, 7))"3ms
Thenthe result is "1"34µs
variance(2, 4) == ((2 - 3)^2 + (4 - 3)^2) / 1passed2ms
WhenI calculate "variance(2, 4) == ((2 - 3)^2 + (4 - 3)^2) / 1"2ms
Thenthe result is "1"37µs
cbrt(27) == root(27, 3)passed192µs
WhenI calculate "cbrt(27) == root(27, 3)"160µs
Thenthe result is "1"30µs
geomean(2, 4, 8) == root(product(2, 4, 8), 3)passed289µs
WhenI calculate "geomean(2, 4, 8) == root(product(2, 4, 8), 3)"246µs
Thenthe result is "1"37µs
percent(8.25) == 8.25 / 100passed1ms
WhenI calculate "percent(8.25) == 8.25 / 100"1ms
Thenthe result is "1"35µs
deg(2) == 2 * 180 / pipassed384µs
WhenI calculate "deg(2) == 2 * 180 / pi"350µs
Thenthe result is "1"32µs
choose(10, 4) == fact(10) / (fact(4) * fact(6))passed831µs
WhenI calculate "choose(10, 4) == fact(10) / (fact(4) * fact(6))"796µs
Thenthe result is "1"33µs
perm(10, 4) == fact(10) / fact(6)passed759µs
WhenI calculate "perm(10, 4) == fact(10) / fact(6)"726µs
Thenthe result is "1"30µs
lcm(12, 18) == 12 * 18 / gcd(12, 18)passed708µs
WhenI calculate "lcm(12, 18) == 12 * 18 / gcd(12, 18)"675µs
Thenthe result is "1"30µs
median(4, 1, 3) == sort([4, 1, 3])[1]passed246µs
WhenI calculate "median(4, 1, 3) == sort([4, 1, 3])[1]"202µs
Thenthe result is "1"38µs
sumproduct([2, 3], [10, 100]) == 2 * 10 + 3 * 100passed316µs
WhenI calculate "sumproduct([2, 3], [10, 100]) == 2 * 10 + 3 * 100"283µs
Thenthe result is "1"31µs
npv(0.1, 300, 420) == 300 / 1.1 + 420 / 1.1^2passed580µs
WhenI calculate "npv(0.1, 300, 420) == 300 / 1.1 + 420 / 1.1^2"545µs
Thenthe result is "1"32µs
forecast(4, 3, 5, 7, 1, 2, 3) == intercept(3, 5, 7, 1, 2, 3) + slope(3, 5, 7, 1, 2, 3) * 4passed6ms
WhenI calculate "forecast(4, 3, 5, 7, 1, 2, 3) == intercept(3, 5, 7, 1, 2, 3) + slope(3, 5, 7, 1, 2, 3) * 4"6ms
Thenthe result is "1"31µs
quarter(date(2026, 11, 1)) == trunc((month(date(2026, 11, 1)) - 1) / 3) + 1passed431µs
WhenI calculate "quarter(date(2026, 11, 1)) == trunc((month(date(2026, 11, 1)) - 1) / 3) + 1"399µs
Thenthe result is "1"29µs
bitShift(5, 3) == 5 * 2^3passed190µs
WhenI calculate "bitShift(5, 3) == 5 * 2^3"159µs
Thenthe result is "1"29µs
The new functions explain their mistakesoutline · 24passed
choose(-1, 2), non-negativepassed143µs
WhenI calculate "choose(-1, 2)"106µs
Thenthe calculation fails mentioning "non-negative"35µs
sort([1, "a"]), all numbers or all stringspassed118µs
WhenI calculate "sort([1, "a"])"85µs
Thenthe calculation fails mentioning "all numbers or all strings"31µs
seq(1, 10, 0), can't be 0passed152µs
WhenI calculate "seq(1, 10, 0)"121µs
Thenthe calculation fails mentioning "can't be 0"29µs
mode(1, 2, 3), no value repeatspassed149µs
WhenI calculate "mode(1, 2, 3)"113µs
Thenthe calculation fails mentioning "no value repeats"33µs
percentile(1, 2, 3, 1.5), between 0 and 1passed160µs
WhenI calculate "percentile(1, 2, 3, 1.5)"127µs
Thenthe calculation fails mentioning "between 0 and 1"31µs
geomean(4, -9), positivepassed124µs
WhenI calculate "geomean(4, -9)"93µs
Thenthe calculation fails mentioning "positive"29µs
correl(1, 2, 3, 4, 5), equal-lengthpassed161µs
WhenI calculate "correl(1, 2, 3, 4, 5)"129µs
Thenthe calculation fails mentioning "equal-length"30µs
ipmt(0.05, 0, 12, 1000), 1 ≤ per ≤ nperpassed187µs
WhenI calculate "ipmt(0.05, 0, 12, 1000)"136µs
Thenthe calculation fails mentioning "1 ≤ per ≤ nper"49µs
syd(30000, 7500, 10, 11), 1 ≤ per ≤ lifepassed210µs
WhenI calculate "syd(30000, 7500, 10, 11)"175µs
Thenthe calculation fails mentioning "1 ≤ per ≤ life"33µs
toBase(1.5, 16), integerpassed117µs
WhenI calculate "toBase(1.5, 16)"87µs
Thenthe calculation fails mentioning "integer"27µs
fromBase("xyz", 16), not a base-16passed115µs
WhenI calculate "fromBase("xyz", 16)"85µs
Thenthe calculation fails mentioning "not a base-16"27µs
bitAnd(-1, 2), non-negativepassed124µs
WhenI calculate "bitAnd(-1, 2)"93µs
Thenthe calculation fails mentioning "non-negative"28µs
solve(x -> x^2 + 1, 0), did not convergepassed1ms
WhenI calculate "solve(x -> x^2 + 1, 0)"1ms
Thenthe calculation fails mentioning "did not converge"29µs
ipmt(0.05, 1, 0, 1000), 1 ≤ per ≤ nperpassed175µs
WhenI calculate "ipmt(0.05, 1, 0, 1000)"139µs
Thenthe calculation fails mentioning "1 ≤ per ≤ nper"33µs
cumipmt(0.05/12, 12, 1000, 5, 2), 1 ≤ start ≤ endpassed273µs
WhenI calculate "cumipmt(0.05/12, 12, 1000, 5, 2)"238µs
Thenthe calculation fails mentioning "1 ≤ start ≤ end"33µs
nominal(-2, 12), above -100%passed137µs
WhenI calculate "nominal(-2, 12)"102µs
Thenthe calculation fails mentioning "above -100%"32µs
ddb(30000, 7500, 10, 11), 1 ≤ per ≤ lifepassed208µs
WhenI calculate "ddb(30000, 7500, 10, 11)"173µs
Thenthe calculation fails mentioning "1 ≤ per ≤ life"32µs
sln(1, 1, 0), can't be 0passed122µs
WhenI calculate "sln(1, 1, 0)"92µs
Thenthe calculation fails mentioning "can't be 0"27µs
toBase(255, 50), 2–36passed124µs
WhenI calculate "toBase(255, 50)"90µs
Thenthe calculation fails mentioning "2–36"32µs
fromBase("", 16), at least one digitpassed109µs
WhenI calculate "fromBase("", 16)"80µs
Thenthe calculation fails mentioning "at least one digit"27µs
bitShift(1, 1.5), integerpassed116µs
WhenI calculate "bitShift(1, 1.5)"87µs
Thenthe calculation fails mentioning "integer"27µs
seq(1, 1000000), 100,000passed931ms
WhenI calculate "seq(1, 1000000)"930ms
Thenthe calculation fails mentioning "100,000"84µs
workday(date(2026, 1, 1), 200001), too manypassed267µs
WhenI calculate "workday(date(2026, 1, 1), 200001)"221µs
Thenthe calculation fails mentioning "too many"42µs
networkdays(date(1, 1, 1), date(9999, 1, 1)), too manypassed301µs
WhenI calculate "networkdays(date(1, 1, 1), date(9999, 1, 1))"262µs
Thenthe calculation fails mentioning "too many"35µs

The mathematics a user can reach

105/105 scenarios passed · 0µs
Core arithmetic and algebraoutline · 28passed
1 + 2 * 3, 7passed133µs
WhenI calculate "1 + 2 * 3"95µs
Thenthe result is "7"35µs
(1 + 2) * 3, 9passed150µs
WhenI calculate "(1 + 2) * 3"106µs
Thenthe result is "9"40µs
10 / 4, 2.5passed658µs
WhenI calculate "10 / 4"614µs
Thenthe result is "2.5"41µs
mod(7, 3), 1passed137µs
WhenI calculate "mod(7, 3)"99µs
Thenthe result is "1"35µs
-2^2, -4passed136µs
WhenI calculate "-2^2"98µs
Thenthe result is "-4"35µs
2^-1, 0.5passed644µs
WhenI calculate "2^-1"606µs
Thenthe result is "0.5"35µs
abs(-5), 5passed119µs
WhenI calculate "abs(-5)"84µs
Thenthe result is "5"33µs
min(3, 1, 2), 1passed144µs
WhenI calculate "min(3, 1, 2)"109µs
Thenthe result is "1"32µs
max(3, 1, 2), 3passed138µs
WhenI calculate "max(3, 1, 2)"105µs
Thenthe result is "3"31µs
floor(2.7), 2passed121µs
WhenI calculate "floor(2.7)"77µs
Thenthe result is "2"39µs
floor(-1.5), -2passed153µs
WhenI calculate "floor(-1.5)"113µs
Thenthe result is "-2"37µs
ceil(2.1), 3passed119µs
WhenI calculate "ceil(2.1)"84µs
Thenthe result is "3"32µs
ceil(-1.5), -1passed143µs
WhenI calculate "ceil(-1.5)"108µs
Thenthe result is "-1"32µs
trunc(-2.7), -2passed119µs
WhenI calculate "trunc(-2.7)"86µs
Thenthe result is "-2"31µs
round(2.345, 2), 2.34passed140µs
WhenI calculate "round(2.345, 2)"103µs
Thenthe result is "2.34"34µs
round(2.567, 2), 2.57passed143µs
WhenI calculate "round(2.567, 2)"98µs
Thenthe result is "2.57"43µs
round(2.5), 2passed112µs
WhenI calculate "round(2.5)"79µs
Thenthe result is "2"30µs
mod(7, 3), 1passed130µs
WhenI calculate "mod(7, 3)"98µs
Thenthe result is "1"30µs
fact(5), 120passed119µs
WhenI calculate "fact(5)"86µs
Thenthe result is "120"31µs
fact(20), 2432902008176640000passed160µs
WhenI calculate "fact(20)"123µs
Thenthe result is "2432902008176640000"35µs
gcd(12, 18), 6passed153µs
WhenI calculate "gcd(12, 18)"115µs
Thenthe result is "6"35µs
lcm(4, 6), 12passed135µs
WhenI calculate "lcm(4, 6)"99µs
Thenthe result is "12"33µs
percent(8.25), 0.0825passed646µs
WhenI calculate "percent(8.25)"610µs
Thenthe result is "0.0825"33µs
sqrt(144), 12passed878µs
WhenI calculate "sqrt(144)"843µs
Thenthe result is "12"32µs
cbrt(27), 3passed137µs
WhenI calculate "cbrt(27)"103µs
Thenthe result is "3"32µs
cbrt(-27), -3passed131µs
WhenI calculate "cbrt(-27)"97µs
Thenthe result is "-3"31µs
root(32, 5), 2passed133µs
WhenI calculate "root(32, 5)"100µs
Thenthe result is "2"31µs
pow(4, 0.5), 2passed134µs
WhenI calculate "pow(4, 0.5)"100µs
Thenthe result is "2"30µs
Mathematical symbols are first-classoutline · 11passed
√16, 4passed899µs
WhenI calculate "√16"856µs
Thenthe result is "4"39µs
√(2 + 2), 2passed963µs
WhenI calculate "√(2 + 2)"921µs
Thenthe result is "2"40µs
√2^2, 2passed916µs
WhenI calculate "√2^2"862µs
Thenthe result is "2"51µs
6 × 7 ÷ 2, 21passed668µs
WhenI calculate "6 × 7 ÷ 2"630µs
Thenthe result is "21"36µs
6 ÷ 2 − 1, 2passed664µs
WhenI calculate "6 ÷ 2 − 1"628µs
Thenthe result is "2"34µs
3 · 4, 12passed112µs
WhenI calculate "3 · 4"78µs
Thenthe result is "12"32µs
π − pi, 0passed109µs
WhenI calculate "π − pi"79µs
Thenthe result is "0"27µs
τ ÷ π, 2passed761µs
WhenI calculate "τ ÷ π"720µs
Thenthe result is "2"38µs
pi, 3.14159265358979323846264338327950288419716939937510582097494passed104µs
WhenI calculate "pi"46µs
Thenthe result is "3.14159265358979323846264338327950288419716939937510582097494"55µs
3 ≠ 2, 1passed107µs
WhenI calculate "3 ≠ 2"73µs
Thenthe result is "1"32µs
2 ≤ 2, 1passed102µs
WhenI calculate "2 ≤ 2"68µs
Thenthe result is "1"31µs
Constants are protectedpassed123µs
WhenI calculate "π = 3"72µs
Thenthe calculation fails mentioning "cannot assign to 'π'"48µs
Trigonometry (radians)outline · 6passed
sin(pi / 2), 1passed235µs
WhenI calculate "sin(pi / 2)"200µs
Thenthe result is "1"33µs
cos(pi), -1passed154µs
WhenI calculate "cos(pi)"100µs
Thenthe result is "-1"51µs
tan(pi / 4), 0.9999999999999999passed252µs
WhenI calculate "tan(pi / 4)"212µs
Thenthe result is "0.9999999999999999"37µs
asin(1) * 2, 3.1415926535897932passed142µs
WhenI calculate "asin(1) * 2"103µs
Thenthe result is "3.1415926535897932"37µs
acos(0) * 2, 3.1415926535897932passed132µs
WhenI calculate "acos(0) * 2"94µs
Thenthe result is "3.1415926535897932"36µs
atan(1) * 4, 3.1415926535897932passed151µs
WhenI calculate "atan(1) * 4"113µs
Thenthe result is "3.1415926535897932"36µs
Exponentials and logarithmsoutline · 7passed
exp(0), 1passed119µs
WhenI calculate "exp(0)"85µs
Thenthe result is "1"32µs
ln(e), 1passed161µs
WhenI calculate "ln(e)"126µs
Thenthe result is "1"32µs
exp(ln(7)), 6.999999999999999passed172µs
WhenI calculate "exp(ln(7))"129µs
Thenthe result is "6.999999999999999"40µs
log10(1000), 3passed149µs
WhenI calculate "log10(1000)"115µs
Thenthe result is "3"31µs
log(2, 8), 3passed132µs
WhenI calculate "log(2, 8)"100µs
Thenthe result is "3"29µs
sin(0), 0passed111µs
WhenI calculate "sin(0)"68µs
Thenthe result is "0"39µs
cos(0), 1passed118µs
WhenI calculate "cos(0)"83µs
Thenthe result is "1"32µs
Statistics over lists, arrays, and rangesoutline · 8passed
avg(2, 4, 9), 5passed695µs
WhenI calculate "avg(2, 4, 9)"658µs
Thenthe result is "5"34µs
median(1, 9, 5), 5passed146µs
WhenI calculate "median(1, 9, 5)"112µs
Thenthe result is "5"31µs
median(1, 2, 3, 4), 2.5passed695µs
WhenI calculate "median(1, 2, 3, 4)"658µs
Thenthe result is "2.5"35µs
count(1, 2, 3), 3passed135µs
WhenI calculate "count(1, 2, 3)"102µs
Thenthe result is "3"31µs
product(2, 3, 4), 24passed153µs
WhenI calculate "product(2, 3, 4)"112µs
Thenthe result is "24"37µs
stdev(2, 4, 4, 4, 5, 5, 7, 9), 2.1380899352993950774764278470380281724320113187307passed2ms
WhenI calculate "stdev(2, 4, 4, 4, 5, 5, 7, 9)"1ms
Thenthe result is "2.1380899352993950774764278470380281724320113187307"55µs
avg([2, 4, 9]), 5passed703µs
WhenI calculate "avg([2, 4, 9])"665µs
Thenthe result is "5"35µs
stdev(1, 3) - sqrt(2), 0passed2ms
WhenI calculate "stdev(1, 3) - sqrt(2)"2ms
Thenthe result is "0"30µs
Products mirror summationpassed345µs
WhenI calculate "∏(2, 3, 4)"119µs
Thenthe result is "24"32µs
WhenI calculate "∏_i=1^5(i)"158µs
Thenthe result is "120"31µs
Euler's number from its factorial seriespassed10ms
WhenI calculate "∑_k=0^45(1 / fact(k)) - e"9ms
Thenthe result is within "1e-45" of zero61µs
Nicomachus — the sum of cubes is the square of the sumpassed862µs
WhenI calculate "reduce((a, b) -> a + b, map(x -> x^3, [1,2,3,4,5,6,7,8,9,10]), 0) == (∑_i=1^10(i))^2"822µs
Thenthe result is "1"37µs
Three roads to twenty factorial agree exactlypassed1ms
WhenI calculate "rec(n) = if(n <= 1, 1, n * rec(n - 1))"174µs
AndI calculate "∏_i=1^20(i) == fact(20)"400µs
Thenthe result is "1"32µs
WhenI calculate "rec(20) == fact(20)"594µs
Thenthe result is "1"32µs
Compound growth's product form equals its closed form, exactlypassed966µs
WhenI calculate "grow(r, n) = ∏_i=1^(n)(1 + r)"155µs
AndI calculate "grow(0.05, 30) == 1.05^30"769µs
Thenthe result is "1"38µs
A mortgage payment round-trips the principal to 30 decimal placespassed4.35s
WhenI calculate "pv(0.05/12, 360, pmt(0.05/12, 360, 200000)) - 200000"4.35s
Thenthe result is within "1e-30" of zero95µs
Finance agrees with Excel (independent cross-validation)outline · 14passed
pmt(0.05/12, 360, 200000), -1073.64, 0.01passed1.77s
WhenI calculate "pmt(0.05/12, 360, 200000)"1.77s
Thenthe result is within "0.01" of "-1073.64"169µs
pmt(0, 12, 1200), -100, 0.000001passed831µs
WhenI calculate "pmt(0, 12, 1200)"740µs
Thenthe result is within "0.000001" of "-100"87µs
fv(0.06/12, 120, -100), 16387.93, 0.01passed2ms
WhenI calculate "fv(0.06/12, 120, -100)"2ms
Thenthe result is within "0.01" of "16387.93"157µs
fv(0, 12, -100), 1200, 0.000001passed300µs
WhenI calculate "fv(0, 12, -100)"213µs
Thenthe result is within "0.000001" of "1200"83µs
pv(0.04/12, 60, -500), 27149.53, 0.01passed75ms
WhenI calculate "pv(0.04/12, 60, -500)"75ms
Thenthe result is within "0.01" of "27149.53"167µs
nper(0.05/12, -1073.64, 200000), 360, 0.01passed400µs
WhenI calculate "nper(0.05/12, -1073.64, 200000)"317µs
Thenthe result is within "0.01" of "360"78µs
nper(0, -100, 1200), 12, 0.000001passed243µs
WhenI calculate "nper(0, -100, 1200)"178µs
Thenthe result is within "0.000001" of "12"62µs
rate(360, -1073.64, 200000) * 12, 0.05, 0.0001passed309µs
WhenI calculate "rate(360, -1073.64, 200000) * 12"235µs
Thenthe result is within "0.0001" of "0.05"71µs
npv(0.1, 3000, 4200, 6800), 11307.29, 0.01passed517µs
WhenI calculate "npv(0.1, 3000, 4200, 6800)"424µs
Thenthe result is within "0.01" of "11307.29"90µs
irr(-70000, 12000, 15000, 18000, 21000, 26000), 0.0866, 0.0001passed731µs
WhenI calculate "irr(-70000, 12000, 15000, 18000, 21000, 26000)"659µs
Thenthe result is within "0.0001" of "0.0866"69µs
effectiveRate(0.06, 12), 0.061678, 0.000001passed775µs
WhenI calculate "effectiveRate(0.06, 12)"684µs
Thenthe result is within "0.000001" of "0.061678"87µs
markup(80, 25), 100, 0.000001passed745µs
WhenI calculate "markup(80, 25)"666µs
Thenthe result is within "0.000001" of "100"76µs
percentOf(30, 120), 25, 0.000001passed711µs
WhenI calculate "percentOf(30, 120)"647µs
Thenthe result is within "0.000001" of "25"62µs
percentChange(80, 100), 25, 0.000001passed992µs
WhenI calculate "percentChange(80, 100)"869µs
Thenthe result is within "0.000001" of "25"111µs
A mortgage's lifetime cost, built up across linespassed1.77s
WhenI calculate "r = 0.05 / 12"175µs
AndI calculate "payment = pmt(r, 360, 200000)"1.77s
AndI calculate "payment * 360"164µs
Thenthe result is within "0.01" of "-386511.57"100µs
IRR refuses an unsolvable streampassed216µs
WhenI calculate "irr(1000, 2000)"159µs
Thenthe calculation fails mentioning "both positive and negative"52µs
Finance (spreadsheet sign convention)outline · 6passed
fv(0.1, 2, -100), 210passed770µs
WhenI calculate "fv(0.1, 2, -100)"729µs
Thenthe result is "210"38µs
npv(0, 10, 20, 30), 60passed2ms
WhenI calculate "npv(0, 10, 20, 30)"2ms
Thenthe result is "60"44µs
npv(0.1, 110), 100passed719µs
WhenI calculate "npv(0.1, 110)"682µs
Thenthe result is "100"35µs
nper(0, -10, 100), 10passed241µs
WhenI calculate "nper(0, -10, 100)"201µs
Thenthe result is "10"36µs
irr(-100, 110), 0.1passed205µs
WhenI calculate "irr(-100, 110)"167µs
Thenthe result is "0.1"35µs
effectiveRate(0.12, 12), 0.126825030131969720661201passed719µs
WhenI calculate "effectiveRate(0.12, 12)"676µs
Thenthe result is "0.126825030131969720661201"40µs
Accounting shorthandsoutline · 4passed
margin(100, 80), 20passed703µs
WhenI calculate "margin(100, 80)"666µs
Thenthe result is "20"34µs
markup(80, 25), 100passed689µs
WhenI calculate "markup(80, 25)"654µs
Thenthe result is "100"32µs
percentOf(50, 200), 25passed685µs
WhenI calculate "percentOf(50, 200)"650µs
Thenthe result is "25"32µs
percentChange(100, 150), 50passed741µs
WhenI calculate "percentChange(100, 150)"697µs
Thenthe result is "50"41µs
Date arithmetic on exact day serialsoutline · 11passed
weekday(date(2026, 6, 6)), 6passed217µs
WhenI calculate "weekday(date(2026, 6, 6))"165µs
Thenthe result is "6"49µs
weekday(date(2026, 6, 8)), 1passed198µs
WhenI calculate "weekday(date(2026, 6, 8))"160µs
Thenthe result is "1"36µs
year(date(2026, 6, 6)), 2026passed185µs
WhenI calculate "year(date(2026, 6, 6))"150µs
Thenthe result is "2026"32µs
month(date(2026, 6, 6)), 6passed180µs
WhenI calculate "month(date(2026, 6, 6))"146µs
Thenthe result is "6"31µs
day(date(2026, 6, 6)), 6passed176µs
WhenI calculate "day(date(2026, 6, 6))"143µs
Thenthe result is "6"31µs
days(date(2026, 3, 1), date(2026, 2, 1)), 28passed249µs
WhenI calculate "days(date(2026, 3, 1), date(2026, 2, 1))"216µs
Thenthe result is "28"30µs
edate(date(2026, 1, 31), 1) == date(2026, 2, 28), 1passed263µs
WhenI calculate "edate(date(2026, 1, 31), 1) == date(2026, 2, 28)"230µs
Thenthe result is "1"31µs
eomonth(date(2024, 2, 1), 0) == date(2024, 2, 29), 1passed263µs
WhenI calculate "eomonth(date(2024, 2, 1), 0) == date(2024, 2, 29)"228µs
Thenthe result is "1"32µs
day(eomonth(date(1900, 2, 1), 0)), 28passed270µs
WhenI calculate "day(eomonth(date(1900, 2, 1), 0))"230µs
Thenthe result is "28"37µs
day(eomonth(date(2000, 2, 1), 0)), 29passed258µs
WhenI calculate "day(eomonth(date(2000, 2, 1), 0))"221µs
Thenthe result is "29"34µs
days(date(2001, 1, 1), date(2000, 1, 1)), 366passed264µs
WhenI calculate "days(date(2001, 1, 1), date(2000, 1, 1))"224µs
Thenthe result is "366"36µs
Logic composes with everythingpassed191µs
WhenI calculate "and(1 < 2, or(0, not(0)))"152µs
Thenthe result is "1"36µs

Inspecting the workbook

13/13 scenarios passed · 0µs
Workbook reports its sheetspassed956µs
Givena sheet named "Budget"817µs
WhenI calculate "Workbook.count"102µs
Thenthe result is "2"33µs
Worksheet names are readablepassed185µs
Givena sheet named "Budget"40µs
WhenI calculate "Workbook.worksheets[1].name == "Budget""110µs
Thenthe result is "1"31µs
A worksheet is reachable by namepassed161µs
Givena sheet named "Budget"38µs
WhenI calculate "Workbook.worksheets["Budget"].name == "Budget""87µs
Thenthe result is "1"32µs
A negative index counts from the endpassed198µs
Givena sheet named "Budget"38µs
WhenI calculate "Workbook.worksheets[-1].name == "Budget""126µs
Thenthe result is "1"31µs
Reading a cell's value through the object graphpassed404µs
Givena sheet named "Budget"77µs
Andcell B:1 on "Budget" contains "1200"117µs
WhenI calculate "Workbook.worksheets["Budget"].cell("B", 1).value * 2"168µs
Thenthe result is "2400"35µs
A cell value is reachable by positionpassed247µs
Givencell A:1 contains "=2 + 3"74µs
WhenI calculate "Workbook.worksheets[0].cell("A", 1).value"138µs
Thenthe result is "5"31µs
The flat cell() accessor reads the active sheetpassed184µs
Givencell A:1 contains "42"55µs
WhenI calculate "cell("A", 1).value"94µs
Thenthe result is "42"31µs
The flat cell() accessor reaches another sheet by namepassed269µs
Givena sheet named "Budget"46µs
Andcell A:1 on "Budget" contains "99"60µs
WhenI calculate "cell("Budget", "A", 1).value"122µs
Thenthe result is "99"34µs
sheetNames() lists every sheetpassed158µs
Givena sheet named "Budget"40µs
WhenI calculate "len(sheetNames())"83µs
Thenthe result is "2"31µs
rowCount() reports the grid sizepassed113µs
WhenI calculate "rowCount()"78µs
Thenthe result is "1000"32µs
A formula reading a cell through reflection recomputes livepassed236µs
Givencell A:1 contains "10"61µs
Andcell B:1 contains "=cell("A", 1).value + 1"109µs
Thencell B:1 shows "11"63µs
A user's own function shadows a reflection accessorpassed228µs
WhenI calculate "cell(x) = x * 10"114µs
AndI calculate "cell(5)"81µs
Thenthe result is "50"28µs
An unknown sheet is reported clearlypassed179µs
WhenI calculate "cell("Nope", "A", 1).value"139µs
Thenthe calculation fails mentioning "unknown sheet"37µs

Working in the grid

21/21 scenarios passed · 0µs
Cells reference each otherpassed206µs
Givencell B:1 contains "1200"78µs
Andcell B:2 contains "=B:1 * 2"69µs
Thencell B:2 shows "2400"55µs
Labels never become errorspassed110µs
Givencell A:1 contains "Q1 revenue"61µs
Thencell A:1 shows "Q1 revenue"46µs
A comment cell is a note that holds no valuepassed93µs
Givencell A:1 contains "# tentative — revisit in Q4"52µs
Thencell A:1 shows "# tentative — revisit in Q4"39µs
A note cell is skipped in ranges and errors when referencedpassed459µs
Giventhe sheet contains:109µs
Andcell B:4 contains "=sum(B:1..B:3)"72µs
Thencell B:4 shows "150"127µs
Andcell B:5 contains "=B:2 + 1"88µs
Thencell B:5 shows an error mentioning "not a number"55µs
A trailing comment on a formula is kept and ignored by evaluationpassed201µs
Givencell B:1 contains "200"72µs
Andcell B:2 contains "=B:1 * 1.08 # with sales tax"73µs
Thencell B:2 shows "216"52µs
Ranges aggregate a columnpassed305µs
Giventhe sheet contains:115µs
Andcell B:4 contains "=sum(B:1..B:3)"75µs
Thencell B:4 shows "400"111µs
A named cell reads like prosepassed238µs
Givencell B:7 contains "0.08"57µs
Andcell B:7 is named "Projected Rate"48µs
Andcell A:1 contains "='Projected Rate' * 100"77µs
Thencell A:1 shows "8"51µs
Sheet definitions belong to their cellspassed333µs
Givencell A:1 contains "rate = 0.1"72µs
Andcell A:2 contains "=100 * rate"76µs
Thencell A:1 shows "𝑖 rate"35µs
Andcell A:2 shows "10"52µs
WhenI calculate "rate = 0.5"61µs
Thenthe calculation fails mentioning "defined in cell"32µs
A function defined in a cell is callablepassed270µs
Givencell A:1 contains "tax(x) = x * 1.0825"95µs
Andcell A:2 contains "=tax(200)"78µs
Thencell A:1 shows "λ tax(x)"34µs
Andcell A:2 shows "216.5"59µs
A slider is a value with a rangepassed335µs
Givencell A:1 contains "r = slider(0.11, 0, 0.2)"112µs
Andcell A:2 contains "=r * 100"75µs
Thencell A:1 is a slider set to "0.11"88µs
Andcell A:2 shows "11"56µs
The log sees the sheetpassed165µs
Givencell B:1 contains "1200"64µs
WhenI calculate "B:1 * 2"67µs
Thenthe result is "2400"29µs
Explicit markers override auto-detectionpassed220µs
Givencell A:1 contains ""123""37µs
Andcell A:2 contains "=12 * rte"61µs
Thencell A:1 shows "123"36µs
Andcell A:2 shows an error mentioning "unknown variable"78µs
A formula mistake is an error, not a guesspassed203µs
Givencell B:1 contains "100"83µs
Andcell B:2 contains "B:1 / 0"64µs
Thencell B:2 shows an error mentioning "division by zero"52µs
Empty cells read as zeropassed125µs
Givencell A:1 contains "=B:9 + 5"70µs
Thencell A:1 shows "5"53µs
Circular references are caught, not infinitepassed216µs
Givencell A:1 contains "=A:2 + 1"65µs
Andcell A:2 contains "=A:1 + 1"78µs
Thencell A:1 shows an error mentioning "circular reference"67µs
Referencing a text cell is an errorpassed222µs
Givencell A:1 contains "Q1 revenue"64µs
Andcell A:2 contains "=A:1 * 2"82µs
Thencell A:2 shows an error mentioning "not a number"71µs
The controls family holds valuespassed703µs
Givencell A:1 contains "flag = checkbox(true)"87µs
Andcell A:2 contains "n = stepper(5, 1, 20)"139µs
Andcell A:3 contains "region = dropdown("EU", ["EU", "US"])"118µs
Andcell B:1 contains "=if(flag, n * 2, 0)"108µs
Andcell B:2 contains "=if(region == "EU", 1, 2)"100µs
Thencell B:1 shows "10"76µs
Andcell B:2 shows "1"66µs
A column of checkboxes counts its checked onespassed310µs
Giventhe sheet contains:141µs
Andcell C:4 contains "=sum(C:1..C:3)"73µs
Thencell C:4 shows "2"92µs
Formulas reach across worksheets by namepassed419µs
Givena sheet named "Budget"47µs
Andcell B:1 on "Budget" contains "1200"77µs
Andcell A:1 contains "=Budget!B:1 * 2"75µs
Thencell A:1 shows "2400"52µs
WhenI calculate "sum(Budget!B:1..B:1) + 1"129µs
Thenthe result is "1201"32µs
A renamed-away sheet breaks loudly, not silentlypassed138µs
Givencell A:1 contains "=Nowhere!B:1"58µs
Thencell A:1 shows an error mentioning "unknown sheet"77µs
A workbook round-trips through its file formatpassed2ms
Givena sheet named "Loan"44µs
Andcell B:1 on "Loan" contains "350000"91µs
Andcell A:1 contains "=Loan!B:1 / 100"110µs
Andcell B:7 contains "0.08"62µs
Andcell B:7 is named "Projected Rate"47µs
Andcell A:2 contains "='Projected Rate' * 100"91µs
WhenI calculate "growth = 1.5"66µs
Andthe workbook is saved and reopened373µs
Thencell A:1 shows "3500"586µs
Andcell A:2 shows "8"50µs
WhenI calculate "growth * 2"72µs
Thenthe result is "3"32µs

Strings, arrays, maps, and the functions that work them

27/27 scenarios passed · 0µs
Text and structure functionsoutline · 18passed
len([1, 2, 3]), 3passed146µs
WhenI calculate "len([1, 2, 3])"113µs
Thenthe result is "3"31µs
len("hello"), 5passed110µs
WhenI calculate "len("hello")"75µs
Thenthe result is "5"32µs
len({a: 1, b: 2}), 2passed158µs
WhenI calculate "len({a: 1, b: 2})"123µs
Thenthe result is "2"32µs
first([5, 6, 7]), 5passed142µs
WhenI calculate "first([5, 6, 7])"109µs
Thenthe result is "5"31µs
last([5, 6, 7]), 7passed138µs
WhenI calculate "last([5, 6, 7])"105µs
Thenthe result is "7"31µs
keys({name: "Ada", age: 36}), ["name", "age"]passed150µs
WhenI calculate "keys({name: "Ada", age: 36})"107µs
Thenthe result is "["name", "age"]"39µs
values({a: 1, b: 2}), [1, 2]passed183µs
WhenI calculate "values({a: 1, b: 2})"143µs
Thenthe result is "[1, 2]"37µs
sum(values({a: 1, b: 2})), 3passed194µs
WhenI calculate "sum(values({a: 1, b: 2}))"145µs
Thenthe result is "3"46µs
concat("Q", 1, "-", 2026), "Q1-2026"passed151µs
WhenI calculate "concat("Q", 1, "-", 2026)"120µs
Thenthe result is ""Q1-2026""28µs
concat([1, 2], [3]), [1, 2, 3]passed161µs
WhenI calculate "concat([1, 2], [3])"119µs
Thenthe result is "[1, 2, 3]"40µs
"Q" + 1, "Q1"passed89µs
WhenI calculate ""Q" + 1"60µs
Thenthe result is ""Q1""26µs
"a" + "b" + "c", "abc"passed93µs
WhenI calculate ""a" + "b" + "c""63µs
Thenthe result is ""abc""27µs
"abc"[0], "a"passed93µs
WhenI calculate ""abc"[0]"57µs
Thenthe result is ""a""33µs
[[1, 2], [3, 4]][1][0], 3passed203µs
WhenI calculate "[[1, 2], [3, 4]][1][0]"160µs
Thenthe result is "3"41µs
{a: [1, 2]}.a[1], 2passed153µs
WhenI calculate "{a: [1, 2]}.a[1]"120µs
Thenthe result is "2"31µs
[1, 2] == [1, 2], 1passed144µs
WhenI calculate "[1, 2] == [1, 2]"112µs
Thenthe result is "1"30µs
{a: 1, b: 2} == {b: 2, a: 1}, 1passed178µs
WhenI calculate "{a: 1, b: 2} == {b: 2, a: 1}"146µs
Thenthe result is "1"30µs
"x" != 5, 1passed84µs
WhenI calculate ""x" != 5"54µs
Thenthe result is "1"28µs
Structure mistakes explain themselvesoutline · 6passed
[1, 2][5], out of rangepassed141µs
WhenI calculate "[1, 2][5]"100µs
Thenthe calculation fails mentioning "out of range"38µs
{a: 1}.b, no key 'b'passed107µs
WhenI calculate "{a: 1}.b"75µs
Thenthe calculation fails mentioning "no key 'b'"30µs
first([]), empty arraypassed102µs
WhenI calculate "first([])"63µs
Thenthe calculation fails mentioning "empty array"37µs
sum(["a"]), works on numberspassed100µs
WhenI calculate "sum(["a"])"66µs
Thenthe calculation fails mentioning "works on numbers"31µs
"a" * 2, expected a numberpassed90µs
WhenI calculate ""a" * 2"57µs
Thenthe calculation fails mentioning "expected a number"31µs
{a: 1, a: 2}, duplicate keypassed153µs
WhenI calculate "{a: 1, a: 2}"88µs
Thenthe calculation fails mentioning "duplicate key"62µs
Filter and reduce shape data like a user thinkspassed369µs
WhenI calculate "filter(x -> x > 1, [1, 2, 3])"171µs
Thenthe result is "[2, 3]"38µs
WhenI calculate "reduce((a, b) -> a + b, [], 42)"126µs
Thenthe result is "42"31µs
Lambdas live in variables and close over parameterspassed544µs
WhenI calculate "f = x -> x * 2"103µs
AndI calculate "f(21)"76µs
Thenthe result is "42"31µs
WhenI calculate "scale(arr, n) = map(x -> x * n, arr)"127µs
AndI calculate "scale([1, 2, 3], 10)"161µs
Thenthe result is "[10, 20, 30]"40µs
A named function reference follows redefinitionpassed346µs
WhenI calculate "h(x) = x + 1"84µs
AndI calculate "alias = h"51µs
AndI calculate "h(x) = x + 100"96µs
AndI calculate "alias(1)"79µs
Thenthe result is "101"31µs