Skip to content

Syntax

The documentation uses explicit declarations:

let x = 5
const y = 7
x: int
x: int = 5
let a, b = 1, 2

Shorthand declarations are also accepted:

x = 5
a, b = 1, 2

Those 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.

x: int
dist: [long]
adj: [[pair[int, int]]]

The documentation uses fn:

fn solve(n: int) -> int:
n * 2

Shorthand function declarations without fn are also accepted:

solve(n: int) -> int:
n * 2

When a function declares a return type, the last expression in its body is returned automatically. return is still available for early exits.

fn minmax(a: [int]) -> (int, int):
(a.min(), a.max())
fn add_one(&a: [int]) -> void:
for i in [0, len(a)):
a[i] += 1
fn <T> identity(x: T) -> T:
x
@memo
fn fib(n: int) -> long:
if n <= 1: return n
fib(n-1) + fib(n-2)
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)
struct Point:
x: int
y: int
p = Point(y = 2, x = 1)

Five decorators are available:

@memo
fn 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
...
@discover
fn op(n: int) -> int:
# marks a function for automatic test discovery
...
DecoratorPurpose
@memocache 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
@discovermark a function for automatic hypothesis discovery
if x > 0:
println "positive"
elif x == 0:
println "zero"
else:
println "negative"
sign = if x > 0 then 1 else if x < 0 then -1 else 0
for i in [0, n): # half-open right
for i in [0, n]: # closed
for i in (0, n): # open both
for i in [0, n, 2): # explicit step
for x in a: # iterate collection
for i, x in a: # index + element
for (u, v) in edges: # destructuring
for x in a where x > 0: # filter

Range notation:

SyntaxMeaning
[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 (d, u) = q:
# loop ends when q is empty
if let (d, u) = pq:
println d
else:
println -1
repeat n:
# body runs n times
defer:
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 * 2

In 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: -1
input:
n, m: int
a: [int][n]
input n: int
println x
print a, b, c
println arr
output arr
output arr, sep=" "
newline
each T:
println 1
LevelOperatorsAssociativity
14f(args), a[i], .field, .method(), x++, x--left
13-x, +x, ~xright
12**, **%right
11*, /, %, //, *%left
10+, -, +%, -%left
9<<, >>left
8==, !=, <, <=, >, >=left
7&left
6^left
5|left
4notright
3andleft
2orleft
1? :right
algoMeaning
//floor division (rounds toward negative infinity)
%floor modulo (matches // so that a == (a // b) * b + a % b)
**integer exponentiation
**%modular exponentiation
^bitwise XOR
andlogical AND
orlogical OR
notlogical NOT
+%modular addition
-%modular subtraction
*%modular multiplication
x++postfix increment

=, +=, -=, *=, /=, %=, //=, **=, &=, |=, ^=, <<=, >>=

a < b < c means a < b && b < c. The middle term is evaluated once.

x = a > b ? a : b
a |> sort()
a |> sort() |> println
g.dijkstra(0).dist[n-1] |> println

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

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)
|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[dimensions] -> type, strategy:
base: dp[...] = val
<- candidate
<- candidate where condition
<- candidate for var in range where condition

Dimension syntax is var: lo..hi with inclusive bounds. Multiple dimensions are comma-separated, for example i: 0..n, j: 0..m.

Supported strategies:

  • maximize
  • minimize
  • sum
  • any
  • assign

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]

These blocks are used for testing, debugging, and analysis during development. They are not part of the generated C++ output.

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
FieldPurpose
n in lo..hirange of input sizes to test
gen name(args)generator function and its parameters
shrink: trueminimize failing inputs when a mismatch is found
max_iterations: Nmaximum number of random tests to run

Search for a counterexample that breaks a target function:

hack solve:
target: WA
strategy: overflow
FieldPurpose
targetwhat kind of failure to look for (WA, etc.)
strategysearch strategy (overflow, etc.)

Define a function for exploratory testing and hypothesis discovery:

explore op(a: int, b: int) -> int:
return a + b

This is used with algo explore to run the function across many inputs and look for patterns.

#!int64
#!mod 998244353
PragmaEffect
#!int64source-level int maps to 64-bit integers in generated C++
#!mod Nset the modulus for MOD, +%, -%, *%, **%, modinv, and comb