Standard library / primitives
Bytes, math & arrays
-- Doc example: bytes, math, and numeric arrays / linear algebra (all pure).
intent: "doc example: bytes, math and arrays"
require random -- only for ecdh_keypair (fresh key material); the rest is pure
print("decode(bytes(\"Hi\")) = " + decode(bytes("Hi")) + ", norm([3,4]) = " + text(norm(array([3, 4]))))
test "bytes <-> text are inverses; hex encoding"
let b be bytes("Hi")
assert_eq(decode(b), "Hi")
assert_eq(decode(bytes("Hi"), "hex"), "4869")
assert_eq(decode(bytes("4869", "hex")), "Hi")
test "base58/base32 round-trip; bytes <-> exact integers"
assert_eq(decode(bytes(decode(bytes("Hi"), "base58"), "base58")), "Hi")
assert_eq(decode(bytes("Hi"), "base32"), "JBUQ")
-- bytes_to_int/int_to_bytes: big-endian, exact (protocol/signature integers)
assert_eq(bytes_to_int(bytes("0400", "hex")), 1024)
assert_eq(int_to_bytes(1024), bytes("0400", "hex"))
assert_eq(int_to_bytes(7, 4), bytes("00000007", "hex"))
test "hkdf_sha256 matches RFC 5869 test case 1; aes_gcm round-trips and refuses tampering (engine v0.6.20+)"
let okm be hkdf_sha256(bytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "hex"), bytes("000102030405060708090a0b0c", "hex"), bytes("f0f1f2f3f4f5f6f7f8f9", "hex"), 42)
assert_eq(decode(okm, "hex"), "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865")
let key be bytes("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "hex") -- 32 bytes → AES-256-GCM
let nonce be bytes("000000000000000000000001", "hex") -- 12 bytes, never reused with a key
let ct be aes_gcm_encrypt(key, nonce, bytes("hola"), bytes("aad"))
assert_eq(length(ct), 4 + 16) -- ciphertext ‖ 16-byte tag
assert_eq(decode(aes_gcm_decrypt(key, nonce, ct, bytes("aad"))), "hola")
assert_error(() => aes_gcm_decrypt(key, nonce, ct, bytes("other-aad"))) -- wrong aad → error, never partial bytes
test "ecdh_keypair + ecdh_shared_secret: both sides derive the same key (engine v0.6.20+)"
let a be ecdh_keypair("P-256") -- {private: secret, public: bytes} — needs `random`
let b be ecdh_keypair("P-256")
assert_eq(length(public of a), 65) -- SEC1 uncompressed point
let k_ab be hkdf_sha256(ecdh_shared_secret(private of a, public of b, "P-256"), bytes("salt"), bytes("ctx"), 32)
let k_ba be hkdf_sha256(ecdh_shared_secret(private of b, public of a, "P-256"), bytes("salt"), bytes("ctx"), 32)
-- a shared secret is a `secret` (never printed); derived keys stay sealed too — prove equality by USING them
let nonce be bytes("000000000000000000000002", "hex")
assert_eq(decode(aes_gcm_decrypt(k_ba, nonce, aes_gcm_encrypt(k_ab, nonce, bytes("hi")))), "hi")
test "X25519 (RFC 7748 §6.1 vectors): ecdh_public and the same key on both sides (engine v0.6.42+)"
let alice be bytes("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", "hex")
let bob be bytes("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", "hex")
let alice_pub be ecdh_public(alice, "X25519")
let bob_pub be ecdh_public(bob, "X25519")
assert_eq(decode(alice_pub, "hex"), "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")
assert_eq(decode(bob_pub, "hex"), "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f")
let k_ab be hkdf_sha256(ecdh_shared_secret(alice, bob_pub, "X25519"), bytes("salt"), bytes("ctx"), 32)
let k_ba be hkdf_sha256(ecdh_shared_secret(bob, alice_pub, "X25519"), bytes("salt"), bytes("ctx"), 32)
let nonce be bytes("000000000000000000000003", "hex")
assert_eq(decode(aes_gcm_decrypt(k_ba, nonce, aes_gcm_encrypt(k_ab, nonce, bytes("hi")))), "hi")
assert_eq(length(public of ecdh_keypair("X25519")), 32) -- a fresh key: 32 bytes
test "hmac returns bytes: hex(mac) or decode(mac, 'hex') shows it; verify_hmac is constant-time"
let mac be hmac("data", "key")
assert_eq(type_of(mac), "bytes")
assert_eq(length(mac), 32)
assert_eq(hex(mac), "0x5031fe3d989c6d1537a013fa6e739da23463fdaec3b70137d828e36ace221bd0")
assert_eq(decode(mac, "hex"), "5031fe3d989c6d1537a013fa6e739da23463fdaec3b70137d828e36ace221bd0")
assert_eq(length(hmac("data", "key", "sha512")), 64)
assert_error(() => hmac("data", "key", "sha1")) -- SHA-1 is rejected
assert_eq(verify_hmac("data", decode(mac, "hex"), "key"), true)
test "hmac takes bytes as raw bytes: the same MAC as text, and binary keys work (engine v0.6.44+)"
assert_eq(hex(hmac(bytes("data"), bytes("key"))), "0x5031fe3d989c6d1537a013fa6e739da23463fdaec3b70137d828e36ace221bd0")
-- RFC 4231 test case 1: a binary key (0x0b × 20).
let key be bytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "hex")
assert_eq(hex(hmac("Hi There", key)), "0xb0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7")
-- Chaining keys, as AWS SigV4 does, with the key as a secret (never revealed): each MAC
-- (bytes) is the next key. AWS's own example key and its documented signing key.
let secret_key be as_secret("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY")
let k_signing be hmac("aws4_request", hmac("iam", hmac("us-east-1", hmac("20150830", "AWS4" + secret_key))))
assert_eq(hex(k_signing), "0xc4afb1cc5771d871763a393e44b703571b55cc28424d1a5e86da6ed3c154a4b9")
assert_eq(verify_hmac("data", nothing, key), false) -- a missing signature is false
assert_eq(verify_hmac("data", hmac("data", key), key), true) -- the signature can be the raw bytes
assert_error(() => hmac(123, "key")) -- not text, bytes or a secret: error
test "bits on 64-bit integers and xor_bytes (engine v0.6.44+)"
assert_eq(bit_xor(0x36, 0x5c), 0x6a)
assert_eq(bit_and(0xff, 0x0f), 0x0f)
assert_eq(bit_or(1, 6), 7)
assert_eq(bit_not(0), -1)
assert_eq(shl(1, 10), 1024)
assert_eq(shr(-8, 1), -4) -- arithmetic: keeps the sign
assert_error(() => shl(1, 64)) -- shifts are 0..63
assert_error(() => shl(1, 63)) -- does not fit in 64 bits: error, never wraps
assert_eq(hex(xor_bytes(bytes("3636", "hex"), bytes("5c5c", "hex"))), "0x6a6a")
assert_error(() => xor_bytes(bytes("ab"), bytes("a"))) -- lengths must match
test "math operators: / is float, // floors like Python, ** binds tighter than unary minus"
assert_eq(2 ** 10, 1024)
assert_eq(10 % 3, 1)
assert_eq(7 / 2, 3.5)
assert_eq(7 // 2, 3)
assert_eq(-7 // 2, -4)
assert_eq(7 // 0.1, 69.0) -- CPython's answer (0.1 is a bit more than 0.1)
assert_eq(-2 ** 2, -4)
assert_eq(2 ** 53 + 1 == 9007199254740992.0, false) -- int vs float compare exactly
assert_eq(round(2.5), 2) -- ties to even
assert_eq(round_to(2.675, 2), 2.67) -- a float rounds its real binary value
assert_eq(int("ff", base = 16), 255)
assert_eq(int("abc", fallback = 7), 7)
assert_error(() => int("ff", 16)) -- ambiguous: name base = or fallback =
test "decimals are exact at any size, and can be written with an exponent"
assert_eq(0.1d + 0.2d, 0.3d)
assert_eq(text(1d + 10 ** 30), "1000000000000000000000000000001")
assert_eq(type_of(1d + 10 ** 30), "decimal")
assert_eq(text(1d / 3d), "0.3333333333333333333333333333") -- / rounds to 28 significant digits
assert_eq(floor(2.5d), 2)
assert_eq(round_to(1.005d, 2), 1.00d) -- half to even, stays decimal
assert_eq(round_to(2.675d, 2), 2.68d)
assert_eq(decimal("1.5e-3"), 0.0015d)
assert_eq(decimal("1e5"), 100000d)
assert_eq(2e3d, 2000d)
assert_eq(text(decimal("1.50e1")), "15.0") -- scale = mantissa's minus the exponent
assert_error(() => decimal("1e999999999")) -- more than 4300 digits
assert_error(() => 1d == 1.0) -- decimal vs float: convert on purpose
assert_eq(1d == 1, true) -- integers mix freely
test "reductions: nothing is skipped, NaN propagates, std/var are sample (ddof = 1)"
assert_eq(mean([1, nothing, 3]), 2.0)
assert_eq(median([3, nothing, 1]), 2.0)
assert_eq(min([3, nothing, 1]), 1)
assert(is_nan(sum([1, nan])))
assert(is_nan(min([nan, 3])))
assert(is_nan(median([1, nan, 3])))
assert_eq(sum([]), 0)
assert_eq(product([]), 1)
assert_error(() => mean([]))
assert_error(() => mean([nothing, nothing])) -- every value missing
assert_eq(std([1, 2, 3, 4]), 1.2909944487358056) -- sample, like pandas / Excel STDEV.S
assert_eq(var([1, 2, 3, 4]), 1.6666666666666667)
assert_eq(std([1, 2, 3, 4], ddof = 0), 1.118033988749895) -- population, numpy's default
assert(is_nan(std([5]))) -- fewer than ddof + 1 values
assert_eq(sum(fill_nan([1, nan], 0)), 1)
test "reductions keep decimals exact"
assert_eq(sum([0.1d, 0.2d]), 0.3d)
assert_eq(type_of(mean([1.5d, 2.5d])), "decimal")
assert_eq(median([1d, 2d, 4d, 8d]), 3d)
assert_eq(var([1000000000000000000001d, 1000000000000000000003d]), 2d) -- a float would give 0
assert_eq(percentile([1d, 2d, 4d], 90), 3.6d)
test "percentile takes 0..100, quantile takes 0..1"
assert_eq(percentile([1, 2, 3, 4], 50), 2.5)
assert_eq(quantile([1, 2, 3, 4], 0.5), 2.5)
assert_eq(percentile([1, 2, 3, 4], 25), 1.75)
assert_error(() => quantile([1], 50))
assert_error(() => percentile([1], 101))
assert_eq(mode([1, 2, 2, nothing]), 2)
test "arrays: length is the first dimension, size counts elements; a 0-d array has no length"
let m be array([[1, 2], [3, 4]])
assert_eq(length(m), 2)
assert_eq(size(m), 4)
assert_eq(shape(m), [2, 2])
assert_eq(ndim(m), 2)
assert_eq(to_list(m[-1]), [3, 4])
assert_eq(to_list(slice(m, -1)), [[3, 4]])
assert_error(() => length(array(5)))
assert_eq(size(array(5)), 1)
assert_eq(to_list(identity(2)), [[1, 0], [0, 1]])
test "enumerate walks an array's first axis, like length and a[i] (engine v0.6.46+)"
let v be array([10, 20, 30])
assert_eq(enumerate(v), [{"index": 0, "item": 10.0}, {"index": 1, "item": 20.0}, {"index": 2, "item": 30.0}])
let m be array([[1, 2], [3, 4]])
let rows be enumerate(m)
assert_eq(length(rows), length(m))
assert_eq(rows[1].index, 1)
assert_eq(to_list(rows[1].item), [3, 4]) -- a 2-D array gives its rows
assert_eq(shape(enumerate(zeros([2, 2, 3]))[0].item), [2, 3])
assert_eq(enumerate(array([])), [])
let seen be []
each x in to_list(v) -- `each x in v` is an error: walk a list
set seen to append(seen, x)
assert_eq(seen, [10, 20, 30])
assert_error(() => enumerate(array(5))) -- 0-d: "it is a scalar and has no rows"
test "arrays: named axis, elementwise math, masks, combine, locate"
let m be array([[1, 2], [3, 4]])
assert_eq(to_list(sum(m, axis = 0)), [4, 6])
assert_eq(to_list(mean(m, axis = -1)), [1.5, 3.5])
assert_eq(to_list(std(m, axis = 0, ddof = 0)), [1, 1])
assert_eq(to_list(sum(m, 1)), [3, 7]) -- positional axis still works on arrays
assert_error(() => sum([1, 2], axis = 0)) -- a list has one axis
assert_eq(to_list(m * m), [[1, 4], [9, 16]]) -- * is elementwise, not matmul
assert_eq(to_list(m ** 2), [[1, 4], [9, 16]])
assert_eq(to_list(m // 3), [[0, 0], [1, 1]])
assert_eq(to_list(m % 3), [[1, 2], [0, 1]])
assert_eq(to_list(sqrt(array([4, 9]))), [2, 3])
assert_eq(to_list(floor(array([1.5, -1.5]))), [1, -2])
assert_eq(to_list(apply(array([1, 2, 3]), (x) => x * 10)), [10, 20, 30])
assert_eq(to_list(where(array([1, 5, 2, 8]), (x) => x > 2)), [5, 8])
assert_eq(to_list(concat([array([1, 2]), array([3])])), [1, 2, 3])
assert_eq(to_list(concat([array([[1], [2]]), array([[3], [4]])], axis = 1)), [[1, 3], [2, 4]])
assert_eq(to_list(stack([array([1, 2]), array([3, 4])])), [[1, 2], [3, 4]])
assert_eq(argmax([3, 9, 2]), 1)
assert_eq(argmin([nothing, 3, 1]), 2) -- nothing skipped, position kept
assert_eq(to_list(argmax(m, axis = 1)), [1, 1])
assert_eq(cumsum([1, nothing, 3]), [1, nothing, 4])
assert_eq(diff([1, 4, 9]), [3, 5])
test "linear algebra: dot is for vectors, matmul for matrices"
assert_eq(norm(array([3, 4])), 5)
assert_eq(dot(array([1, 2, 3]), array([4, 5, 6])), 32)
let m be array([[1, 2], [3, 4]])
assert_error(() => dot(m, m)) -- error points to matmul
assert_eq(to_list(matmul(m, identity(2))), [[1, 2], [3, 4]])
assert_eq(det(array([[2, 0], [0, 3]])), 6)
assert_eq(to_list(solve(array([[2, 0], [0, 4]]), array([2, 8]))), [1, 2])
assert_error(() => inv(array([[1, 2], [2, 4]]))) -- singular: an error, never NaN
test "statistics and fitting: corr, cov, lstsq, polyfit, polyval"
assert_eq(cov([1, 2, 3], [1, 2, 3]), 1.0) -- sample, like std
assert_eq(cov([1, 2, 3], [1, 2, 3], ddof = 0), 0.6666666666666666)
assert_eq(corr([1, nothing, 3, 4], [2, 5, 6, 8]), 1.0) -- a pair with nothing is skipped
let x be lstsq(array([[1, 0], [0, 1], [1, 1]]), array([1, 2, 3]))
assert(abs(x[0] - 1) < 1e-9 and abs(x[1] - 2) < 1e-9) -- float round-off: compare with a tolerance
let rank_def be lstsq(array([[1, 1], [2, 2]]), array([2, 4])) -- rank-deficient: minimum-norm, like numpy
assert(abs(rank_def[0] - 1) < 1e-9 and abs(rank_def[1] - 1) < 1e-9)
let fit be polyfit([0, 1, 2, 3], [1, 3, 5, 7], 1) -- [slope, intercept], highest degree first
assert(abs(fit[0] - 2) < 1e-9 and abs(fit[1] - 1) < 1e-9)
assert_error(() => polyfit([1, 1, 1], [1, 2, 3], 1)) -- not determined by the data
assert_eq(polyval([2.0, 1.0], 10), 21.0)
assert_eq(polyval([2.0, 1.0], [1, nothing, 3]), [3.0, nothing, 7.0]) -- a missing x stays missing
assert_error(() => polyval([2.0, nothing], 1)) -- a missing coefficient is an error
assert_error(() => lstsq(array([[1, 0], [0, 1]]), [1, nothing]))
test "array constructors and inspection"
assert_eq(to_list(zeros(2)), [0, 0])
assert_eq(to_list(ones([2, 2])), [[1, 1], [1, 1]])
assert_eq(to_list(full(2, 7)), [7, 7])
assert_eq(to_list(arange(0, 3)), [0, 1, 2])
assert_eq(to_list(arange(0, 1, 0.5)), [0, 0.5])
assert_eq(to_list(linspace(0, 1, 3)), [0, 0.5, 1])
let m be reshape(arange(0, 4), [2, 2])
assert_eq(to_list(m), [[0, 1], [2, 3]])
assert_eq(to_list(transpose(m)), [[0, 2], [1, 3]])
assert_eq(to_list(flatten(m)), [0, 1, 2, 3])
assert_eq(at(m, [1, 0]), 2)
assert_eq(trace(m), 3)
assert_eq(is_array(m), true)
assert(contains(eig(array([[2, 0], [0, 3]])), "values"))
assert_eq(sort(keys(svd(m))), ["s", "u", "vt"])
assert_eq(argmax([1, 3, 3]), 1) -- first on a tie
assert_eq(to_list(stack([array([1, 2]), array([3, 4])], axis = 1)), [[1, 3], [2, 4]])
assert_eq(is_integer(1), true)
assert_eq(is_integer(1.0), false)
assert_eq(sum(where([1, nan, 2], is_finite)), 3)
assert_error(() => 1.5 in [1.5d]) -- decimal vs float, also inside `in`Bytes§
bytes is raw binary, distinct from text. bytes(...) and decode(...) are inverses:
bytes("Hi") -- UTF-8 bytes
bytes("4869", "hex") -- decode hex → bytes
bytes("SGk=", "base64") -- decode base64 → bytes
bytes("SGk", "base64url") -- decode base64url (URL-safe -_, padding optional; JWT/tokens) → bytes
bytes("StV1DL6", "base58") -- decode base58 (Bitcoin/Solana alphabet) → bytes
bytes("JBSWY3DP", "base32") -- decode base32 RFC 4648 (Algorand convention) → bytes
decode(b) -- bytes → text (UTF-8 strict)
decode(b, "hex") -- bytes → hex text (also base64 / base64url / base58 / base32)
sha256(x) -- raw digest (bytes); hex via decode(sha256(x), "hex")
bytes_to_int(b) -- big-endian bytes → exact non-negative integer (empty → 0)
int_to_bytes(n, size?) -- integer → big-endian bytes: minimal, or zero-padded to size
int_to_bytes_le(n, size) -- integer → little-endian bytes of exactly size (binary structs, e.g. Solana u32/u64 LE)
text(b) shows a hex repr like bytes(48656c6c6f) — it does not decode. A secret never materializes through bytes(...). bytes_to_int is exact at any width (a 32-byte value never touches float), which is what signature/protocol integers need — see 38-blockchain.
hmac(data, key, algo?) returns the MAC as bytes (it used to be hmac_sha256, which returned hex text and is now a deprecated alias): show it with hex(mac) ("0x…") or decode(mac, "hex") (bare hex). algo is "sha256" (default) or "sha512"; SHA-1 is rejected. data and key may be text, bytes (used raw — engine v0.6.44+; before, bytes were hashed as their printed form) or a secret; anything else is an error. To check a signature someone sent (hex or base64, as Stripe or GitHub send them), use verify_hmac(data, signature, key, algo?), which compares in constant time.
Cryptography — ECDH, HKDF, AES-GCM (engine v0.6.20+)§
Standard names and shapes (WebCrypto's), nothing exotic — enough to run a key agreement and an authenticated-encryption channel from a Synsema program.
require random -- ecdh_keypair generates key material
let me be ecdh_keypair("P-256") -- {private: secret, public: bytes}; also "P-521"
let shared be ecdh_shared_secret(private of me, their_public, "P-256") -- a secret (the raw X coordinate)
let key be hkdf_sha256(shared, salt, bytes("app v1"), 32) -- 32 bytes → AES-256; stays a secret when the IKM is one
let nonce be random_bytes(12) -- 12 bytes, never reused with the same key
let ct be aes_gcm_encrypt(key, nonce, plaintext, aad) -- ciphertext ‖ 16-byte tag (aad optional)
aes_gcm_decrypt(key, nonce, ct, aad) -- the plaintext, or an error — never partial bytes
ecdh_keypair(curve)needsrandom(it creates a key — the same gate asrandom_bytes); the public key is a SEC1 uncompressed point (65 bytes for P-256, 133 for P-521).ecdh_shared_secretis pure.- X25519 (engine v0.6.42+, RFC 7748):
"X25519"works inecdh_keypairandecdh_shared_secret— 32-byte keys, and a peer key of low order (one that would make the shared value all zeros) is an error.ecdh_public(private, curve)gives the public key of a private key you already hold (stored, or restored from a secret); it is pure. The example checks both against the RFC's own vectors. hkdf_sha256(ikm, salt, info, length)follows RFC 5869 (its test vectors pass). With asecretIKM the output is asecrettoo, so a derived key never prints — you use it (as the AES key) instead of reading it.aes_gcm_encrypt/aes_gcm_decrypt: a 16-byte key is AES-128-GCM, a 32-byte key AES-256-GCM (anything else is an error); an authentication failure (wrong key, nonce, AAD, or tampered data) is an error, never a partial plaintext.
Math§
Operators + - * / // % . / is always float; // is floor division, exact for integers of any size and flooring like Python (-7 // 2 → -4); on floats // and % give CPython's answer computed from the real binary values (7 // 0.1 → 69.0). binds tighter than unary minus: -2 2 is -4. Integers and floats compare exactly (2 53 + 1 == 9007199254740992.0 is false). Constants: pi, tau, e, inf, nan. The numeric tower also has decimal and complex.
Functions (pure, no capability; real or complex where noted):
- magnitude / selection (type-preserving):
abs,sign,min,max,clamp(x, lo, hi)—abs(complex)is the modulus. - roots / powers:
sqrt,cbrt,hypot,pow. exp / log:exp,ln,log10,log2,log_base(x, base)(no barelog— it is a soft keyword). - rounding:
floor,ceil,round,truncreturn an integer (roundties to even:round(2.5)→2);round_to(x, digits)rounds a float's real binary value like Python (round_to(2.675, 2)→2.67) and keeps a decimal decimal. - integers from text:
int(x)is exact; the base and the fallback go by name —int("ff", base = 16)→255,int("abc", fallback = 7)→7. An integer 2..36 in second place (int("ff", 16)) is an error that names both readings. - trig (radians):
sin,cos,tan,asin,acos,atan,atan2(y, x),radians,degrees; hyperbolic:sinh,cosh,tanh,asinh,acosh,atanh. - number theory (integers):
gcd,lcm,factorial. - bits (engine v0.6.44+), on 64-bit signed integers (two's complement):
bit_and(a, b),bit_or(a, b),bit_xor(a, b),bit_not(a),shl(x, n),shr(x, n)(arithmetic: keeps the sign,shr(-8, 1)is-4).nis 0..63; a value outside 64 bits, a float, or ashlwhose result does not fit is an error — nothing wraps around silently.xor_bytes(a, b)XORs twobytesof the same length (different lengths, or text, are an error: convert text withbytes(text)). - introspection:
is_nan,is_infinite,is_finite; type predicatesis_integer,is_decimal,is_complex,is_array,is_bytes,type_of. - reductions over a list (or
array):sum,product,mean,median,percentile,quantile,std,var,min,max— see the rules below. Alsomode(xs)(the most frequent value,nothingskipped) andhistogram(x, bins?)→{counts, edges}(CSV, stats & charts). - special functions (real only):
gamma,lgamma,erf,erfc,beta. - complex:
complex(re, im),real,imag,conj,arg;sqrt/exp/ln/trig accept a complex and return one (sqrt(complex(-1, 0))→0+1i;complex(0, 1) ** 2→-1+0i). Real argument → real result (sqrt(-1)→ NaN). - integers ↔ bytes:
bytes_to_int(bytes),int_to_bytes(n, len)(big-endian),int_to_bytes_le(n, len).
Exact decimals§
A decimal (1.50d, decimal("1.50")) has arbitrary precision and a stable type: a decimal operation always gives a decimal, never a float.
0.1d + 0.2d == 0.3d -- true, exactly
1d + 10 ** 30 -- 1000000000000000000000000000001 (a decimal)
1d / 3d -- 0.3333333333333333333333333333 — / rounds to 28 significant digits
decimal("1.5e-3") -- 0.0015: an exponent is allowed (engine v0.6.30+)
2e3d -- 2000
round_to(2.675d, 2) -- 2.68: decimals round in decimal, half to even
+,-,,//,%,with an integer exponent and every comparison are exact at any size./andstdround to 28 significant* digits, half to even, never cutting the integer part.- With an exponent the value is mantissa × 10^exponent and the scale is the mantissa's minus the exponent:
decimal("1.50e1")→15.0. A CSV column typeddecimalreads the same way. More than 4300 digits (decimal("1e999999999")) is an error, so a short text cannot ask for unbounded memory. floor,ceil,round,truncof a decimal give the exact integer.- A decimal meets a float only on purpose:
1d == 1.0is an error (cannot mix decimal and float), also inside lists,inand group keys. Convert first withdecimal(x)orfloat(x). Integers mix freely (1d == 1istrue).
Reductions — the common rules§
Every reduction (sum, product, mean, min, max, median, percentile, quantile, std, var, and the table aggregates sum_of, mean_of… of Data analysis) follows the same rules — the polars/SQL model:
1. nothing is missing data and is skipped: mean([1, nothing, 3]) → 2.0. Every value missing → an error; an empty list → an error, except sum([]) → 0 and product([]) → 1. 2. NaN is an invalid result and propagates, in any position: sum([1, nan]) → nan, min([nan, 3]) → nan, median of data with a NaN → nan. Clean first with fill_nan(xs, 0) or where(xs, is_finite). 3. A named axis on arrays: sum(m, axis = 0) (down the columns), mean(m, axis = -1) (negatives count from the end). The positional sum(m, 1) still works on arrays; axis on a list is an error. 4. std and var are sample statistics (ddof = 1, like pandas, polars, R and Excel STDEV.S): std([1, 2, 3, 4]) → 1.2909944487358056. The population figure (numpy's default, STDEV.P) is std(xs, ddof = 0) → 1.118033988749895. ddof goes by name only. Fewer than ddof + 1 values → nan. 5. Decimals stay decimal, computed exactly like Python's statistics: sum([0.1d, 0.2d]) → 0.3, var([1000000000000000000001d, 1000000000000000000003d]) → 2 (a float gives 0), percentile([1d, 2d, 4d], 90) → 3.6.
percentile(x, p) takes p in 0–100; quantile(x, q) takes q in 0–1 (quantile([1], 50) is an error that points at percentile). Both interpolate linearly (numpy's default) and take axis = on arrays: percentile(x, 50) == quantile(x, 0.5) == median(x).
Numeric arrays & linear algebra§
array([...]) builds an n-dimensional float array. + - / // % are elementwise with broadcasting — is not the matrix product. Linear algebra (2D, via faer):
norm(array([3, 4])) -- 5
dot(array([1, 2, 3]), array([4, 5, 6])) -- 32
matmul(a, b) -- matrix product
solve(A, b) det(A) inv(A) eig(A) svd(A)
dotis the inner product of two 1-D vectors only; a 2-D argument is an error that points tomatmul(a, b).eig(A)→{values, vectors}(complex eigenvalues),svd(A)→{u, s, vt},norm(a, kind?),trace(A). A singular matrix ininv/solveis an error, never a silent NaN.- Construct:
array(nested_list),zeros(shape),ones(shape),full(shape, v),arange(start, stop, step?),linspace(start, stop, n),identity(n)(the identity matrix;eye(n)is its deprecated name).shapeis an integer or a list like[2, 3]. - Inspect:
shape(a),ndim(a),size(a)(every element),length(a)= the first dimension, like numpy'slen. A 0-d array (array(5)) has no length —lengthis an error there;size(a)is1.a[i]is a row or a scalar,a[-1]the last row,slice(a, start, end?)slices rows;enumerate(a)(engine v0.6.46+) walks the same first axis →[{index, item}, …], eachitema float for a 1-D array or the row (an array) for a 2-D or deeper one —each e in enumerate(m)is the indexed loop over an array, sinceeach x in ais an error (Cannot iterate over array — …;to_list(a)gives a list to walk). A 0-d array is an error:enumerate() of a 0-d array: it is a scalar and has no rows — use to_list(a) or the value itself;at(a, [i, j]),to_list,reshape,transpose,flatten. - Elementwise math:
sqrt,exp,ln,log10,log2, the trig functions,abs,floor,ceil,round,trunctake an array and return one.apply(a, f)maps a function (same shape);where(a, pred)is a boolean mask (a 1-D array of the elements that pass). - Combine:
concat([a, b], axis = 0)joins along an existing axis;stack([a, b], axis = 0)adds a new one. - Locate and accumulate (lists or arrays):
argmin/argmax(first on a tie;axis =; on a listnothingis skipped and the index is still the original position),cumsum(a list stays exact and keeps anothingin its place),diff.
Statistics and fitting§
corr(xs, ys)→ Pearson correlation;cov(xs, ys, ddof = 1)→ covariance, sample by default likestd(ddof = 0for the population). Lists or 1-D arrays of equal length; a pair where either side isnothingis skipped.lstsq(A, b)→ the least-squares solution ofA·x ≈ b, numpy'snp.linalg.lstsq(SVD): a rank-deficientAgives the minimum-norm solution, like numpy. Results carry float round-off — compare with a tolerance.polyfit(xs, ys, degree)→ coefficients, highest degree first ([slope, intercept]for degree 1). A fit the data does not determine (fewer distinct x thandegree + 1) is an error — numpy only warns.polyval(coefs, x)evaluates on a number, a list or an array. A missingxin a list staysnothingin its place (polyval([2.0, 1.0], [1, nothing, 3])→[3.0, nothing, 7.0]); a missing coefficient is an error, and so is anothinginlstsq'sb—drop_missing(xs)first.