Biblioteca estándar
Bytes, matemática y 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 es binario crudo, distinto de text. bytes(...) y decode(...) son inversos:
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) muestra una repr hex como bytes(48656c6c6f) — no decodifica. Un secret nunca se materializa a través de bytes(...). bytes_to_int es exacto a cualquier ancho (un valor de 32 bytes jamás pasa por float), que es lo que piden los enteros de firmas/protocolos — mirá 38-blockchain.
hmac(data, key, algo?) devuelve el MAC como bytes (antes era hmac_sha256, que devolvía texto hex y ahora es un alias deprecado): mostralo con hex(mac) ("0x…") o decode(mac, "hex") (hex pelado). algo es "sha256" (default) o "sha512"; SHA-1 se rechaza. data y key pueden ser texto, bytes (se usan crudos — motor v0.6.44+; antes los bytes se hasheaban como su forma impresa) o un secret; cualquier otra cosa es error. Para verificar una firma que te mandaron (hex o base64, como las mandan Stripe o GitHub), usá verify_hmac(data, signature, key, algo?), que compara en tiempo constante.
Criptografía — ECDH, HKDF, AES-GCM (engine v0.6.20+)§
Nombres y formas estándar (los de WebCrypto), nada exótico — lo justo para correr un acuerdo de claves y un canal con cifrado autenticado desde un programa Synsema.
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)necesitarandom(crea una clave — el mismo gate querandom_bytes); la pública es un punto SEC1 sin comprimir (65 bytes en P-256, 133 en P-521).ecdh_shared_secretes puro.- X25519 (motor v0.6.42+, RFC 7748):
"X25519"anda enecdh_keypairyecdh_shared_secret— claves de 32 bytes, y una clave del par de orden bajo (una que dejaría el valor compartido en ceros) es un error.ecdh_public(private, curve)da la clave pública de una privada que ya tenés (guardada, o recuperada de un secret); es pura. El ejemplo chequea las dos contra los propios vectores de la RFC. hkdf_sha256(ikm, salt, info, length)sigue RFC 5869 (sus vectores de prueba pasan). Con un IKMsecretla salida también essecret, así que una clave derivada nunca se imprime — la usás (como clave AES) en vez de leerla.aes_gcm_encrypt/aes_gcm_decrypt: una clave de 16 bytes es AES-128-GCM, una de 32 es AES-256-GCM (cualquier otra es error); una falla de autenticación (clave, nonce o AAD equivocados, o datos alterados) es un error, nunca un texto plano parcial.
Matemática§
Operadores + - * / // % . / es siempre float; // es división entera hacia abajo, exacta para enteros de cualquier tamaño y redondeando como Python (-7 // 2 → -4); en floats // y % dan la respuesta de CPython calculada sobre los valores binarios reales (7 // 0.1 → 69.0). liga más fuerte que el menos unario: -2 2 es -4. Enteros y floats se comparan exactamente (2 53 + 1 == 9007199254740992.0 es false). Constantes: pi, tau, e, inf, nan. La torre numérica también tiene decimal y complex.
Funciones (puras, sin capability; reales o complex donde se indica):
- magnitud / selección (preservan el tipo):
abs,sign,min,max,clamp(x, lo, hi)—abs(complex)es el módulo. - raíces / potencias:
sqrt,cbrt,hypot,pow. exp / log:exp,ln,log10,log2,log_base(x, base)(no haylogpelado — es una soft keyword). - redondeo:
floor,ceil,round,truncdevuelven un entero (rounddesempata al par:round(2.5)→2);round_to(x, dígitos)redondea el valor binario real de un float como Python (round_to(2.675, 2)→2.67) y deja un decimal como decimal. - enteros desde texto:
int(x)es exacto; la base y el fallback van por nombre —int("ff", base = 16)→255,int("abc", fallback = 7)→7. Un entero 2..36 en segundo lugar (int("ff", 16)) es un error que nombra las dos lecturas. - trigonometría (radianes):
sin,cos,tan,asin,acos,atan,atan2(y, x),radians,degrees; hiperbólicas:sinh,cosh,tanh,asinh,acosh,atanh. - teoría de números (enteros):
gcd,lcm,factorial. - bits (motor v0.6.44+), sobre enteros de 64 bits con signo (complemento a dos):
bit_and(a, b),bit_or(a, b),bit_xor(a, b),bit_not(a),shl(x, n),shr(x, n)(aritmético: conserva el signo,shr(-8, 1)es-4).nva de 0 a 63; un valor que no entra en 64 bits, un float, o unshlcuyo resultado no entra es error — nada se envuelve en silencio.xor_bytes(a, b)hace XOR de dosbytesdel mismo largo (largos distintos, o texto, son error: el texto se convierte conbytes(text)). - introspección:
is_nan,is_infinite,is_finite; predicados de tipois_integer,is_decimal,is_complex,is_array,is_bytes,type_of. - reducciones sobre una lista (o
array):sum,product,mean,median,percentile,quantile,std,var,min,max— ver las reglas de abajo. Tambiénmode(xs)(el valor más frecuente, salteandonothing) ehistogram(x, bins?)→{counts, edges}(CSV, estadística y gráficos). - funciones especiales (sólo reales):
gamma,lgamma,erf,erfc,beta. - complejos:
complex(re, im),real,imag,conj,arg;sqrt/exp/ln/trig aceptan un complejo y devuelven uno (sqrt(complex(-1, 0))→0+1i;complex(0, 1) ** 2→-1+0i). Argumento real → resultado real (sqrt(-1)→ NaN). - enteros ↔ bytes:
bytes_to_int(bytes),int_to_bytes(n, len)(big-endian),int_to_bytes_le(n, len).
Decimales exactos§
Un decimal (1.50d, decimal("1.50")) tiene precisión arbitraria y un tipo estable: una operación decimal siempre da un decimal, nunca un 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
+,-,,//,%,con exponente entero y toda comparación son exactos a cualquier tamaño./ystdredondean a 28 dígitos significativos*, mitad al par, sin cortar nunca la parte entera.- Con exponente, el valor es mantisa × 10^exponente y la escala es la de la mantisa menos el exponente:
decimal("1.50e1")→15.0. Una columna CSV tipadadecimalse lee igual. Más de 4300 dígitos (decimal("1e999999999")) es un error, así un texto corto no puede pedir memoria sin límite. floor,ceil,round,truncde un decimal dan el entero exacto.- Un decimal se cruza con un float solo a propósito:
1d == 1.0es un error (cannot mix decimal and float), también dentro de listas,iny claves de grupo. Convertí antes condecimal(x)ofloat(x). Los enteros se mezclan libremente (1d == 1estrue).
Reducciones — las reglas comunes§
Toda reducción (sum, product, mean, min, max, median, percentile, quantile, std, var, y los agregados de tabla sum_of, mean_of… de Análisis de datos) sigue las mismas reglas — el modelo de polars/SQL:
1. nothing es un dato faltante y se saltea: mean([1, nothing, 3]) → 2.0. Todos los valores faltantes → error; una lista vacía → error, salvo sum([]) → 0 y product([]) → 1. 2. NaN es un resultado inválido y se propaga, en cualquier posición: sum([1, nan]) → nan, min([nan, 3]) → nan, median de datos con un NaN → nan. Limpiá antes con fill_nan(xs, 0) o where(xs, is_finite). 3. Un eje con nombre en arrays: sum(m, axis = 0) (bajando por las columnas), mean(m, axis = -1) (los negativos cuentan desde el final). El posicional sum(m, 1) sigue andando en arrays; axis sobre una lista es un error. 4. std y var son estadísticos muestrales (ddof = 1, como pandas, polars, R y DESVEST.M de Excel): std([1, 2, 3, 4]) → 1.2909944487358056. La cifra poblacional (el default de numpy, DESVEST.P) es std(xs, ddof = 0) → 1.118033988749895. ddof va solo por nombre. Menos de ddof + 1 valores → nan. 5. Los decimales siguen siendo decimales, calculados exacto como el statistics de Python: sum([0.1d, 0.2d]) → 0.3, var([1000000000000000000001d, 1000000000000000000003d]) → 2 (un float da 0), percentile([1d, 2d, 4d], 90) → 3.6.
percentile(x, p) toma p en 0–100; quantile(x, q) toma q en 0–1 (quantile([1], 50) es un error que apunta a percentile). Los dos interpolan linealmente (el default de numpy) y aceptan axis = en arrays: percentile(x, 50) == quantile(x, 0.5) == median(x).
Arrays numéricos y álgebra lineal§
array([...]) construye un array float de n dimensiones. + - / // % son elemento a elemento con broadcasting — no es el producto matricial. Álgebra lineal (2D, vía 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)
dotes el producto interno de dos vectores 1-D solamente; un argumento 2-D es un error que apunta amatmul(a, b).eig(A)→{values, vectors}(autovalores complejos),svd(A)→{u, s, vt},norm(a, kind?),trace(A). Una matriz singular eninv/solvees un error, nunca un NaN silencioso.- Construir:
array(lista_anidada),zeros(forma),ones(forma),full(forma, v),arange(inicio, fin, paso?),linspace(inicio, fin, n),identity(n)(la matriz identidad;eye(n)es su nombre deprecado).formaes un entero o una lista como[2, 3]. - Inspeccionar:
shape(a),ndim(a),size(a)(todos los elementos),length(a)= la primera dimensión, como ellende numpy. Un array 0-d (array(5)) no tiene largo —lengthahí es un error;size(a)es1.a[i]es una fila o un escalar,a[-1]la última fila,slice(a, inicio, fin?)corta filas;enumerate(a)(engine v0.6.46+) recorre ese mismo primer eje →[{index, item}, …], cadaitemun float para un array 1-D o la fila (un array) para uno 2-D o más —each e in enumerate(m)es el loop con índice sobre un array, ya queeach x in aes un error (Cannot iterate over array — …;to_list(a)da una lista para recorrer). Un array 0-d es un 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. - Matemática elemento a elemento:
sqrt,exp,ln,log10,log2, las trigonométricas,abs,floor,ceil,round,trunctoman un array y devuelven uno.apply(a, f)mapea una función (misma forma);where(a, pred)es una máscara booleana (un array 1-D con los elementos que pasan). - Combinar:
concat([a, b], axis = 0)une a lo largo de un eje existente;stack([a, b], axis = 0)agrega uno nuevo. - Ubicar y acumular (listas o arrays):
argmin/argmax(el primero en un empate;axis =; en una lista se salteanothingy el índice sigue siendo la posición original),cumsum(una lista queda exacta y deja unnothingen su lugar),diff.
Estadística y ajuste§
corr(xs, ys)→ correlación de Pearson;cov(xs, ys, ddof = 1)→ covarianza, muestral por defecto comostd(ddof = 0para la poblacional). Listas o arrays 1-D del mismo largo; se saltea un par donde cualquiera de los lados esnothing.lstsq(A, b)→ la solución por mínimos cuadrados deA·x ≈ b, elnp.linalg.lstsqde numpy (SVD): unaAde rango deficiente da la solución de norma mínima, como numpy. Los resultados traen el redondeo de float — compará con una tolerancia.polyfit(xs, ys, grado)→ coeficientes, el de mayor grado primero ([pendiente, ordenada]para grado 1). Un ajuste que los datos no determinan (menos x distintos quegrado + 1) es un error — numpy solo avisa.polyval(coefs, x)evalúa sobre un número, una lista o un array. Unxfaltante en una lista quedanothingen su lugar (polyval([2.0, 1.0], [1, nothing, 3])→[3.0, nothing, 7.0]); un coeficiente faltante es un error, y también unnothingen elbdelstsq—drop_missing(xs)antes.