Syntax
Variable declarations
Section titled “Variable declarations”The documentation uses explicit declarations:
let x = 5const y = 7x: intx: int = 5let a, b = 1, 2Shorthand declarations are also accepted:
x = 5a, b = 1, 2Those forms create new variables when the names are not already declared in scope.
The docs prefer explicit let because it is easier to scan quickly.
Type annotation
Section titled “Type annotation”x: intdist: [long]adj: [[pair[int, int]]]Function declarations
Section titled “Function declarations”The documentation uses fn:
fn solve(n: int) -> int: n * 2Shorthand function declarations without fn are also accepted:
solve(n: int) -> int: n * 2When a function declares a return type, the last expression in its body is returned
automatically. return is still available for early exits.
Multiple return values
Section titled “Multiple return values”fn minmax(a: [int]) -> (int, int): (a.min(), a.max())Pass by reference
Section titled “Pass by reference”fn add_one(&a: [int]) -> void: for i in [0, len(a)): a[i] += 1Generic functions
Section titled “Generic functions”fn <T> identity(x: T) -> T: xMemoized functions
Section titled “Memoized functions”@memofn fib(n: int) -> long: if n <= 1: return n fib(n-1) + fib(n-2)Default and named arguments
Section titled “Default and named arguments”fn add(a: int, b: int = 1) -> int: a + b
x = add(4)y = add(b = 2, a = 4)Named arguments work for user functions, struct constructors, and helper builtins with optional knobs. The recommended style is to keep the primary data positional and name the option:
st = segtree(a, op = sum)g = grid_graph(mat, dirs = 8)pre = prefix(a, op = max)prod = prefix(a, init = 1, reducer = |acc: int, x: int| acc * x)Structs
Section titled “Structs”struct Point: x: int y: int
p = Point(y = 2, x = 1)Decorators
Section titled “Decorators”Five decorators are available:
@memofn fib(n: int) -> long: if n <= 1: return n fib(n - 1) + fib(n - 2)
@operator(+)fn add(a: Point, b: Point) -> Point: Point(a.x + b.x, a.y + b.y)
@test(input="5\n", expected="25\n")fn square(x: int) -> int: x * x
@complexity(n: 1..1000)fn solve(n: int) -> int: # used by the complexity analyzer to measure growth rate ...
@discoverfn op(n: int) -> int: # marks a function for automatic test discovery ...| Decorator | Purpose |
|---|---|
@memo | cache return values so repeated calls are O(1) |
@operator(op) | overload an operator (+, -, *, ==, <, etc.) for a struct |
@test(input=..., expected=...) | embed a test case that algo test can check |
@complexity(n: lo..hi) | tell the complexity analyzer the input range to sweep |
@discover | mark a function for automatic hypothesis discovery |
Control flow
Section titled “Control flow”if / elif / else
Section titled “if / elif / else”if x > 0: println "positive"elif x == 0: println "zero"else: println "negative"if as an expression
Section titled “if as an expression”sign = if x > 0 then 1 else if x < 0 then -1 else 0for i in [0, n): # half-open rightfor i in [0, n]: # closedfor i in (0, n): # open bothfor i in [0, n, 2): # explicit stepfor x in a: # iterate collectionfor i, x in a: # index + elementfor (u, v) in edges: # destructuringfor x in a where x > 0: # filterRange notation:
| Syntax | Meaning |
|---|---|
[a, b) | a <= i < b |
[a, b] | a <= i <= b |
(a, b) | a < i < b |
(a, b] | a < i <= b |
Step defaults to +1. If lo > hi with no step specified, the inferred step is -1.
while not q.empty(): # ...while let
Section titled “while let”while let (d, u) = q: # loop ends when q is emptyif let
Section titled “if let”if let (d, u) = pq: println delse: println -1repeat
Section titled “repeat”repeat n: # body runs n timesdefer: vis = filled(n, false)defer runs its body when the current scope exits.
for x in a: guard x > 0 s += x
fn process(x: int) -> int: if x < 0: return -1 x * 2In loops, guard skips to the next iteration. Outside loops, it returns immediately
from the current function or top-level program. Use it only in no-value-return
contexts.
match x: case 0: println "zero" case 1: println "one" default: println "other"
match: case x < 0: println "negative" case x == 0: println "zero" default: println "positive"
sign = match x: case 0: 0 case n where n > 0: 1 else: -1input: n, m: int a: [int][n]
input n: intprintln xprint a, b, cprintln arroutput arroutput arr, sep=" "newlineeach T: println 1Operators
Section titled “Operators”Precedence (high to low)
Section titled “Precedence (high to low)”| Level | Operators | Associativity |
|---|---|---|
| 14 | f(args), a[i], .field, .method(), x++, x-- | left |
| 13 | -x, +x, ~x | right |
| 12 | **, **% | right |
| 11 | *, /, %, //, *% | left |
| 10 | +, -, +%, -% | left |
| 9 | <<, >> | left |
| 8 | ==, !=, <, <=, >, >= | left |
| 7 | & | left |
| 6 | ^ | left |
| 5 | | | left |
| 4 | not | right |
| 3 | and | left |
| 2 | or | left |
| 1 | ? : | right |
Operator semantics
Section titled “Operator semantics”| algo | Meaning |
|---|---|
// | floor division (rounds toward negative infinity) |
% | floor modulo (matches // so that a == (a // b) * b + a % b) |
** | integer exponentiation |
**% | modular exponentiation |
^ | bitwise XOR |
and | logical AND |
or | logical OR |
not | logical NOT |
+% | modular addition |
-% | modular subtraction |
*% | modular multiplication |
x++ | postfix increment |
Assignment operators
Section titled “Assignment operators”=, +=, -=, *=, /=, %=, //=, **=, &=, |=, ^=, <<=, >>=
Chained comparisons
Section titled “Chained comparisons”a < b < c means a < b && b < c. The middle term is evaluated once.
Ternary
Section titled “Ternary”x = a > b ? a : bPipe operator
Section titled “Pipe operator”a |> sort()a |> sort() |> printlng.dijkstra(0).dist[n-1] |> printlnComprehensions and reductions
Section titled “Comprehensions and reductions”Comprehensions
Section titled “Comprehensions”Build a new array from a loop expression:
sq = [x * x for x in a]evens = [x for x in a where x % 2 == 0]indices = [i for i in [0, n) where a[i] > 0]pairs = [(i, a[i]) for i in [0, n)]Reductions
Section titled “Reductions”Collapse a range or collection into a single value:
println max(dp[i] for i in [0, n))println sum(a[i] * b[i] for i in [0, n))ans = min(abs(a[i] - a[j]) for i in [0, n) for j in [0, n) where i != j)Lambdas
Section titled “Lambdas”|x| x * 2|x, y| x + y|x: int| { let r = x * x; return r }Lambdas are used with sort, filter, map, bsearch, and other higher-order builtins.
DP block
Section titled “DP block”dp[dimensions] -> type, strategy: base: dp[...] = val <- candidate <- candidate where condition <- candidate for var in range where conditionDimension syntax is var: lo..hi with inclusive bounds. Multiple dimensions are
comma-separated, for example i: 0..n, j: 0..m.
Supported strategies:
maximizeminimizesumanyassign
Add rolling after the strategy to reduce memory when only adjacent rows matter:
dp[i: 1..n, j: 0..W] -> int, maximize, rolling: base: dp[0][j] = 0 <- dp[i-1][j] <- dp[i-1][j-w[i-1]] + v[i-1] where j >= w[i-1]Special blocks
Section titled “Special blocks”These blocks are used for testing, debugging, and analysis during development. They are not part of the generated C++ output.
stress
Section titled “stress”Run randomized stress tests comparing a slow (correct) solution against a fast one:
stress slow, fast: n in 1..50 gen random_array(n, vals: -100..100) shrink: true max_iterations: 1000| Field | Purpose |
|---|---|
n in lo..hi | range of input sizes to test |
gen name(args) | generator function and its parameters |
shrink: true | minimize failing inputs when a mismatch is found |
max_iterations: N | maximum number of random tests to run |
Search for a counterexample that breaks a target function:
hack solve: target: WA strategy: overflow| Field | Purpose |
|---|---|
target | what kind of failure to look for (WA, etc.) |
strategy | search strategy (overflow, etc.) |
explore
Section titled “explore”Define a function for exploratory testing and hypothesis discovery:
explore op(a: int, b: int) -> int: return a + bThis is used with algo explore to run the function across many inputs and look for patterns.
Pragmas
Section titled “Pragmas”#!int64#!mod 998244353| Pragma | Effect |
|---|---|
#!int64 | source-level int maps to 64-bit integers in generated C++ |
#!mod N | set the modulus for MOD, +%, -%, *%, **%, modinv, and comb |