;; ==========================================================================
;; primitives.sx — Irreducible primitive set
;;
;; These are the functions that CANNOT be written in SX because they
;; require host-native capabilities: native arithmetic, type inspection,
;; host string library, host math, host I/O, host data structures.
;;
;; Everything else lives in spec/stdlib.sx as library functions.
;;
;; The primitive set is the out-of-band floor. The fewer primitives,
;; the tighter the strange loop and the more of the system is auditable,
;; verifiable, portable SX.
;;
;; Format:
;;   (define-primitive "name"
;;     :params (param1 param2 &rest rest)
;;     :returns "type"
;;     :doc "description")
;;
;; Typed params use (name :as type) syntax.
;; Modules: (define-module :name) scopes subsequent entries.
;;
;; Functions moved to stdlib.sx (no longer primitives):
;;   Comparison:  != <= >= eq? eqv? equal?
;;   Predicates:  nil? boolean? number? string? list? dict?
;;                continuation? empty? odd? even? zero? contains?
;;   Arithmetic:  inc dec abs ceil round min max clamp
;;   Collections: first last rest nth cons append reverse flatten
;;                range chunk-every zip-pairs vals has-key? merge
;;                assoc dissoc into
;;   Strings:     upcase downcase string-length substring
;;                string-contains? starts-with? ends-with?
;;                split join replace
;;   Logic:       not
;;   Text:        pluralize escape assert parse-datetime
;; ==========================================================================
;; --------------------------------------------------------------------------
;; Core — Arithmetic
;; --------------------------------------------------------------------------
(define-module :core.arithmetic)

(define-primitive
  "+"
  :params (&rest (args :as number))
  :returns "number"
  :doc "Sum all arguments. Returns integer iff all args are exact integers (float contagion)."
  :body (reduce (fn (a b) (native-add a b)) 0 args))

(define-primitive
  "-"
  :params ((a :as number) &rest (b :as number))
  :returns "number"
  :doc "Subtract. Unary: negate. Binary: a - b. Float contagion: returns integer iff all args are integers."
  :body (if (empty? b) (native-neg a) (native-sub a (first b))))

(define-primitive
  "*"
  :params (&rest (args :as number))
  :returns "number"
  :doc "Multiply all arguments. Float contagion: integer result iff all args are exact integers."
  :body (reduce (fn (a b) (native-mul a b)) 1 args))

(define-primitive
  "/"
  :params ((a :as number) (b :as number))
  :returns "float"
  :doc "Divide a by b. Always returns inexact float."
  :body (native-div a b))

(define-primitive
  "mod"
  :params ((a :as number) (b :as number))
  :returns "number"
  :doc "Modulo a % b. Returns integer iff both args are integers."
  :body (native-mod a b))

(define-primitive
  "random-int"
  :params ((low :as number) (high :as number))
  :returns "number"
  :doc "Random integer in [low, high] inclusive."
  :body (native-random-int low high))

(define-primitive
  "json-encode"
  :params (value)
  :returns "string"
  :doc "Encode value as JSON string with indentation.")

(define-primitive
  "sqrt"
  :params ((x :as number))
  :returns "number"
  :doc "Square root.")

(define-primitive
  "pow"
  :params ((x :as number) (n :as number))
  :returns "number"
  :doc "x raised to power n.")

(define-primitive
  "abs"
  :params ((x :as number))
  :returns "number"
  :doc "Absolute value.")

(define-primitive
  "floor"
  :params ((x :as number))
  :returns "integer"
  :doc "Floor toward negative infinity — returns exact integer.")

(define-primitive
  "ceil"
  :params ((x :as number))
  :returns "integer"
  :doc "Ceiling toward positive infinity — returns exact integer.")

(define-primitive
  "round"
  :params ((x :as number) &rest (ndigits :as number))
  :returns "number"
  :doc "Round to ndigits decimal places (default 0). Returns integer when ndigits is 0.")

(define-primitive
  "truncate"
  :params ((x :as number))
  :returns "integer"
  :doc "Truncate toward zero — returns exact integer.")

(define-primitive
  "remainder"
  :params (((a :as number) (b :as number)))
  :returns "number"
  :doc "Remainder — sign follows dividend.")

(define-primitive
  "modulo"
  :params (((a :as number) (b :as number)))
  :returns "number"
  :doc "Modulo — sign follows divisor.")

(define-primitive
  "exact?"
  :params ((x :as number))
  :returns "boolean"
  :doc "True if x is an exact integer (not an inexact float).")

(define-primitive
  "inexact?"
  :params ((x :as number))
  :returns "boolean"
  :doc "True if x is an inexact float (not an exact integer).")

;; --------------------------------------------------------------------------
;; Core — Comparison
;; --------------------------------------------------------------------------
(define-primitive
  "exact->inexact"
  :params ((x :as number))
  :returns "float"
  :doc "Convert exact integer to inexact float. Floats pass through unchanged.")

(define-primitive
  "inexact->exact"
  :params ((x :as number))
  :returns "integer"
  :doc "Convert inexact float to nearest exact integer (truncates). Integers pass through unchanged.")

(define-primitive
  "make-vector"
  :params ((n :as number) (fill :as any :optional true))
  :returns "vector"
  :doc "Create vector of length n, each element initialised to fill (default nil).")

(define-primitive
  "vector"
  :params (:rest (elts :as any))
  :returns "vector"
  :doc "Construct a vector from its arguments.")

(define-primitive
  "vector?"
  :params ((x :as any))
  :returns "boolean"
  :doc "True if x is a vector.")

(define-primitive
  "vector-length"
  :params ((v :as vector))
  :returns "number"
  :doc "Number of elements in vector v.")

(define-primitive
  "vector-ref"
  :params ((v :as vector) (i :as number))
  :returns "any"
  :doc "Element at 0-based index i. Error if out of bounds.")

(define-primitive
  "vector-set!"
  :params ((v :as vector) (i :as number) (val :as any))
  :returns "nil"
  :doc "Mutate element at index i to val. Error if out of bounds.")

(define-primitive
  "vector->list"
  :params ((v :as vector))
  :returns "list"
  :doc "Convert vector to a fresh list.")

(define-primitive
  "list->vector"
  :params ((l :as list))
  :returns "vector"
  :doc "Convert list to a fresh vector.")

;; --------------------------------------------------------------------------
;; Core — Predicates
;; --------------------------------------------------------------------------
(define-primitive
  "vector-fill!"
  :params ((v :as vector) (val :as any))
  :returns "nil"
  :doc "Set every element of v to val in place.")

(define-primitive
  "vector-copy"
  :params ((v :as vector)
    (start :as number :optional true)
    (end :as number :optional true))
  :returns "vector"
  :doc "Shallow copy of vector, optionally sliced from start (inclusive) to end (exclusive).")

(define-primitive
  "min"
  :params (&rest (args :as number))
  :returns "number"
  :doc "Minimum. Single list arg or variadic.")

(define-primitive
  "max"
  :params (&rest (args :as number))
  :returns "number"
  :doc "Maximum. Single list arg or variadic.")

(define-primitive
  "clamp"
  :params ((x :as number) (lo :as number) (hi :as number))
  :returns "number"
  :doc "Clamp x to range [lo, hi]."
  :body (max lo (min hi x)))

(define-primitive
  "inc"
  :params ((n :as number))
  :returns "number"
  :doc "Increment by 1."
  :body (+ n 1))

(define-primitive
  "dec"
  :params ((n :as number))
  :returns "number"
  :doc "Decrement by 1."
  :body (- n 1))

(define-module :core.comparison)

(define-primitive
  "="
  :params (a b)
  :returns "boolean"
  :doc "Deep structural equality. Alias for equal?.")

(define-primitive
  "!="
  :params (a b)
  :returns "boolean"
  :doc "Inequality."
  :body (not (= a b)))

(define-primitive
  "eq?"
  :params (a b)
  :returns "boolean"
  :doc "Identity equality. True only if a and b are the same object."
  :body (identical? a b))

(define-primitive
  "eqv?"
  :params (a b)
  :returns "boolean"
  :doc "Equivalent value for atoms, identity for compound objects.\n        Returns true for identical objects (eq?), and also for numbers,\n        strings, booleans, and nil with the same value. For lists, dicts,\n        lambdas, and components, only true if same identity.")

(define-primitive
  "equal?"
  :params (a b)
  :returns "boolean"
  :doc "Deep structural equality. Recursively compares collections."
  :body (= a b))

;; --------------------------------------------------------------------------
;; Core — Logic
;; --------------------------------------------------------------------------
(define-primitive
  "<"
  :params ((a :as number) (b :as number))
  :returns "boolean"
  :doc "Less than.")

(define-primitive
  ">"
  :params ((a :as number) (b :as number))
  :returns "boolean"
  :doc "Greater than.")

;; --------------------------------------------------------------------------
;; Core — Strings
;; --------------------------------------------------------------------------
(define-primitive
  "<="
  :params ((a :as number) (b :as number))
  :returns "boolean"
  :doc "Less than or equal."
  :body (or (< a b) (= a b)))

(define-primitive
  ">="
  :params ((a :as number) (b :as number))
  :returns "boolean"
  :doc "Greater than or equal."
  :body (or (> a b) (= a b)))

(define-module :core.predicates)

(define-primitive
  "odd?"
  :params ((n :as number))
  :returns "boolean"
  :doc "True if n is odd."
  :body (= (mod n 2) 1))

(define-primitive
  "even?"
  :params ((n :as number))
  :returns "boolean"
  :doc "True if n is even."
  :body (= (mod n 2) 0))

(define-primitive
  "zero?"
  :params ((n :as number))
  :returns "boolean"
  :doc "True if n is zero."
  :body (= n 0))

(define-primitive
  "nil?"
  :params (x)
  :returns "boolean"
  :doc "True if x is nil/null/None."
  :body (= (type-of x) "nil"))

(define-primitive
  "boolean?"
  :params (x)
  :returns "boolean"
  :doc "True if x is a boolean (true or false)."
  :body (= (type-of x) "boolean"))

(define-primitive
  "number?"
  :params (x)
  :returns "boolean"
  :doc "True if x is any number — exact integer or inexact float."
  :body (or (= (type-of x) "number") (integer? x)))

(define-primitive
  "integer?"
  :params (x)
  :returns "boolean"
  :doc "True if x is an exact integer, or a float with no fractional part (e.g. 1.0).")

(define-primitive
  "float?"
  :params (x)
  :returns "boolean"
  :doc "True if x is an inexact float (Number type). Does not match exact integers.")

(define-primitive
  "string?"
  :params (x)
  :returns "boolean"
  :doc "True if x is a string."
  :body (= (type-of x) "string"))

(define-primitive
  "list?"
  :params (x)
  :returns "boolean"
  :doc "True if x is a list/array."
  :body (= (type-of x) "list"))

(define-primitive
  "dict?"
  :params (x)
  :returns "boolean"
  :doc "True if x is a dict/map."
  :body (= (type-of x) "dict"))

(define-primitive
  "continuation?"
  :params (x)
  :returns "boolean"
  :doc "True if x is a captured continuation."
  :body (= (type-of x) "continuation"))

(define-primitive
  "empty?"
  :params (coll)
  :returns "boolean"
  :doc "True if coll is nil or has length 0."
  :body (or (nil? coll) (= (len coll) 0)))

(define-primitive
  "contains?"
  :params (coll key)
  :returns "boolean"
  :doc "True if coll contains key. Strings: substring check. Dicts: key check. Lists: membership.")

(define-module :core.logic)

(define-primitive
  "not"
  :params (x)
  :returns "boolean"
  :doc "Logical negation. Note: and/or are special forms, not primitives."
  :body (if x false true))

(define-module :core.strings)

(define-primitive
  "str"
  :params (&rest args)
  :returns "string"
  :doc "Concatenate all args as strings. nil → empty string, bool → true/false.")

;; --------------------------------------------------------------------------
;; Core — Collections
;; --------------------------------------------------------------------------
(define-primitive
  "concat"
  :params (&rest (colls :as list))
  :returns "list"
  :doc "Concatenate multiple lists into one. Skips nil values.")

(define-primitive
  "upper"
  :params ((s :as string))
  :returns "string"
  :doc "Uppercase string.")

(define-primitive
  "upcase"
  :params ((s :as string))
  :returns "string"
  :doc "Alias for upper. Uppercase string.")

(define-primitive
  "lower"
  :params ((s :as string))
  :returns "string"
  :doc "Lowercase string.")

(define-primitive
  "downcase"
  :params ((s :as string))
  :returns "string"
  :doc "Alias for lower. Lowercase string.")

(define-primitive
  "string-length"
  :params ((s :as string))
  :returns "number"
  :doc "Length of string in characters.")

(define-primitive
  "char-from-code"
  :params ((n :as number))
  :returns "string"
  :doc "Convert Unicode code point to single-character string.")

(define-primitive
  "char-code"
  :params ((s :as string))
  :returns "number"
  :doc "Unicode codepoint of the first character of string s.")

(define-primitive
  "substring"
  :params ((s :as string) (start :as number) (end :as number))
  :returns "string"
  :doc "Extract substring from start (inclusive) to end (exclusive).")

(define-primitive
  "string-contains?"
  :params ((s :as string) (needle :as string))
  :returns "boolean"
  :doc "True if string s contains substring needle.")

(define-primitive
  "trim"
  :params ((s :as string))
  :returns "string"
  :doc "Strip leading/trailing whitespace.")

(define-primitive
  "split"
  :params ((s :as string) &rest (sep :as string))
  :returns "list"
  :doc "Split string by separator (default space).")

(define-primitive
  "join"
  :params ((sep :as string) (coll :as list))
  :returns "string"
  :doc "Join collection items with separator string.")

(define-primitive
  "replace"
  :params ((s :as string) (old :as string) (new :as string))
  :returns "string"
  :doc "Replace all occurrences of old with new in s.")

(define-primitive
  "slice"
  :params (coll (start :as number) &rest (end :as number))
  :returns "any"
  :doc "Slice a string or list from start to end (exclusive). End is optional.")

(define-primitive
  "index-of"
  :params ((s :as string) (needle :as string) &rest (from :as number))
  :returns "number"
  :doc "Index of first occurrence of needle in s, or -1 if not found. Optional start index.")

(define-primitive
  "starts-with?"
  :params ((s :as string) (prefix :as string))
  :returns "boolean"
  :doc "True if string s starts with prefix.")

;; --------------------------------------------------------------------------
;; Core — Dict operations
;; --------------------------------------------------------------------------
(define-primitive
  "ends-with?"
  :params ((s :as string) (suffix :as string))
  :returns "boolean"
  :doc "True if string s ends with suffix.")

(define-module :core.collections)

(define-primitive
  "list"
  :params (&rest args)
  :returns "list"
  :doc "Create a list from arguments.")

(define-primitive
  "dict"
  :params (&rest pairs)
  :returns "dict"
  :doc "Create a dict from key/value pairs: (dict :a 1 :b 2).")

(define-primitive
  "range"
  :params ((start :as number) (end :as number) &rest (step :as number))
  :returns "list"
  :doc "Integer range [start, end) with optional step.")

(define-primitive
  "get"
  :params (coll key &rest default)
  :returns "any"
  :doc "Get value from dict by key, or list by index. Optional default.")

(define-primitive
  "len"
  :params (coll)
  :returns "number"
  :doc "Length of string, list, or dict.")

(define-primitive
  "first"
  :params ((coll :as list))
  :returns "any"
  :doc "First element, or nil if empty.")

(define-primitive
  "last"
  :params ((coll :as list))
  :returns "any"
  :doc "Last element, or nil if empty.")

;; --------------------------------------------------------------------------
;; Stdlib — Format
;; --------------------------------------------------------------------------
(define-primitive
  "rest"
  :params ((coll :as list))
  :returns "list"
  :doc "All elements except the first.")

(define-primitive
  "nth"
  :params ((coll :as list) (n :as number))
  :returns "any"
  :doc "Element at index n, or nil if out of bounds.")

(define-primitive
  "cons"
  :params (x (coll :as list))
  :returns "list"
  :doc "Prepend x to coll.")

(define-primitive
  "append"
  :params ((coll :as list) x)
  :returns "list"
  :doc "If x is a list, concatenate. Otherwise append x as single element.")

(define-primitive
  "append!"
  :params ((coll :as list) x)
  :returns "list"
  :doc "Mutate coll by appending x in-place. Returns coll.")

;; --------------------------------------------------------------------------
;; Stdlib — Text
;; --------------------------------------------------------------------------
(define-primitive
  "reverse"
  :params ((coll :as list))
  :returns "list"
  :doc "Return coll in reverse order.")

(define-primitive
  "flatten"
  :params ((coll :as list))
  :returns "list"
  :doc "Flatten one level of nesting. Nested lists become top-level elements.")

(define-primitive
  "chunk-every"
  :params ((coll :as list) (n :as number))
  :returns "list"
  :doc "Split coll into sub-lists of size n.")

(define-primitive
  "zip-pairs"
  :params ((coll :as list))
  :returns "list"
  :doc "Consecutive pairs: (1 2 3 4) → ((1 2) (2 3) (3 4)).")

;; --------------------------------------------------------------------------
;; Stdlib — Style
;; --------------------------------------------------------------------------
;; --------------------------------------------------------------------------
;; Stdlib — Debug
;; --------------------------------------------------------------------------
(define-module :core.dict)

(define-primitive
  "keys"
  :params ((d :as dict))
  :returns "list"
  :doc "List of dict keys.")

;; --------------------------------------------------------------------------
;; Type introspection — platform primitives
;; --------------------------------------------------------------------------
(define-primitive
  "vals"
  :params ((d :as dict))
  :returns "list"
  :doc "List of dict values.")

(define-primitive
  "merge"
  :params (&rest (dicts :as dict))
  :returns "dict"
  :doc "Merge dicts left to right. Later keys win. Skips nil.")

(define-primitive
  "has-key?"
  :params ((d :as dict) key)
  :returns "boolean"
  :doc "True if dict d contains key.")

(define-primitive
  "assoc"
  :params ((d :as dict) &rest pairs)
  :returns "dict"
  :doc "Return new dict with key/value pairs added/overwritten.")

(define-primitive
  "dissoc"
  :params ((d :as dict) &rest keys)
  :returns "dict"
  :doc "Return new dict with keys removed.")

(define-primitive
  "dict-set!"
  :params ((d :as dict) key val)
  :returns "any"
  :doc "Mutate dict d by setting key to val in-place. Returns val.")

(define-primitive
  "into"
  :params (target coll)
  :returns "any"
  :doc "Pour coll into target. List target: convert to list. Dict target: convert pairs to dict.")

(define-module :stdlib.format)

(define-primitive
  "format-date"
  :params ((date-str :as string) (fmt :as string))
  :returns "string"
  :doc "Parse ISO date string and format with strftime-style format.")

(define-primitive
  "format-decimal"
  :params ((val :as number) &rest (places :as number))
  :returns "string"
  :doc "Format number with fixed decimal places (default 2).")

(define-primitive
  "parse-int"
  :params (val &rest default)
  :returns "number"
  :doc "Parse string to integer with optional default on failure.")

(define-primitive
  "parse-datetime"
  :params ((s :as string))
  :returns "string"
  :doc "Parse datetime string — identity passthrough (returns string or nil).")

(define-module :stdlib.text)

(define-primitive
  "pluralize"
  :params ((count :as number) &rest (forms :as string))
  :returns "string"
  :doc "Pluralize: (pluralize 1) → \"\", (pluralize 2) → \"s\". Or (pluralize n \"item\" \"items\").")

(define-primitive
  "escape"
  :params ((s :as string))
  :returns "string"
  :doc "HTML-escape a string (&, <, >, \", ').")

(define-primitive
  "strip-tags"
  :params ((s :as string))
  :returns "string"
  :doc "Remove HTML tags from string.")

(define-module :stdlib.debug)

(define-primitive
  "assert"
  :params (condition &rest message)
  :returns "boolean"
  :doc "Assert condition is truthy; raise error with message if not.")

(define-module :stdlib.types)

(define-primitive
  "type-of"
  :params (x)
  :returns "string"
  :doc "Return type name: number, string, boolean, nil, symbol, keyword, list, dict, lambda, component, island, macro.")

(define-primitive
  "symbol-name"
  :params ((sym :as symbol))
  :returns "string"
  :doc "Return the name string of a symbol.")

(define-primitive
  "keyword-name"
  :params ((kw :as keyword))
  :returns "string"
  :doc "Return the name string of a keyword.")

(define-primitive
  "sx-parse"
  :params ((source :as string))
  :returns "list"
  :doc "Parse SX source string into a list of AST expressions.")

(define-primitive
  "make-string-buffer"
  :params ()
  :returns "string-buffer"
  :doc "Create a new empty mutable string buffer for O(1) amortised append.")

(define-primitive
  "make-buffer"
  :params ()
  :returns "string-buffer"
  :doc "Create a new mutable buffer (alias for make-string-buffer with terser name).")

(define-primitive
  "buffer-append!"
  :params (buf v)
  :returns "nil"
  :doc "Append a value to a buffer; coerces non-strings to their printed form.")

(define-primitive
  "buffer->string"
  :params (buf)
  :returns "string"
  :doc "Finalize a buffer to a single string.")

(define-module :stdlib.coroutines)

(define-module :stdlib.bitwise)

(define-primitive
  "bitwise-and"
  :params (((a :as number) (b :as number)))
  :returns "number"
  :doc "Bitwise AND of two integers.")

(define-primitive
  "bitwise-or"
  :params (((a :as number) (b :as number)))
  :returns "number"
  :doc "Bitwise OR of two integers.")

(define-primitive
  "bitwise-xor"
  :params (((a :as number) (b :as number)))
  :returns "number"
  :doc "Bitwise XOR of two integers.")

(define-primitive
  "bitwise-not"
  :params ((a :as number))
  :returns "number"
  :doc "Bitwise NOT (one's complement) of an integer.")

(define-primitive
  "arithmetic-shift"
  :params (((a :as number) (count :as number)))
  :returns "number"
  :doc "Arithmetic shift: left if count > 0, right if count < 0.")

(define-primitive
  "bit-count"
  :params ((a :as number))
  :returns "number"
  :doc "Count set bits (popcount) in a non-negative integer.")

(define-primitive
  "integer-length"
  :params ((a :as number))
  :returns "number"
  :doc "Number of bits needed to represent integer a (excluding sign).")

(define-module :stdlib.ports)

(define-primitive
  "eof-object"
  :params ()
  :returns "eof-object"
  :doc "The EOF sentinel value.")

(define-primitive
  "eof-object?"
  :params (v)
  :returns "boolean"
  :doc "True if v is the EOF sentinel.")

(define-primitive
  "open-input-string"
  :params ((s :as string))
  :returns "input-port"
  :doc "Open a string as an input port.")

(define-primitive
  "open-output-string"
  :params ()
  :returns "output-port"
  :doc "Open a fresh output string port.")

(define-primitive
  "get-output-string"
  :params ((p :as output-port))
  :returns "string"
  :doc "Flush output port contents to a string.")

(define-primitive
  "port?"
  :params (v)
  :returns "boolean"
  :doc "True if v is any port.")

(define-primitive
  "input-port?"
  :params (v)
  :returns "boolean"
  :doc "True if v is an input port.")

(define-primitive
  "output-port?"
  :params (v)
  :returns "boolean"
  :doc "True if v is an output port.")

(define-primitive
  "close-port"
  :params ((p :as port))
  :returns "nil"
  :doc "Close a port.")

(define-primitive
  "read-char"
  :params (&rest (p :as input-port))
  :returns "any"
  :doc "Read next char from port; returns eof-object at end.")

(define-primitive
  "peek-char"
  :params (&rest (p :as input-port))
  :returns "any"
  :doc "Peek next char without consuming; returns eof-object at end.")

(define-primitive
  "read-line"
  :params (&rest (p :as input-port))
  :returns "any"
  :doc "Read a line from port; returns eof-object at end.")

(define-primitive
  "write-char"
  :params ((c :as char) &rest (p :as output-port))
  :returns "nil"
  :doc "Write a char to output port.")

(define-primitive
  "write-string"
  :params ((s :as string) &rest (p :as output-port))
  :returns "nil"
  :doc "Write a string to output port.")

(define-primitive
  "char-ready?"
  :params (&rest (p :as input-port))
  :returns "boolean"
  :doc "True if a char is immediately available on the port.")


(define-primitive
  "read"
  :params (&rest (p :as input-port))
  :returns "any"
  :doc "Read one datum from port; returns eof-object at end.")

(define-primitive
  "write"
  :params (v &rest (p :as output-port))
  :returns "nil"
  :doc "Serialize v to port with quoting — strings quoted, chars as #\\a notation.")

(define-primitive
  "display"
  :params (v &rest (p :as output-port))
  :returns "nil"
  :doc "Serialize v to port without quoting — strings unquoted, chars as characters.")

(define-primitive
  "newline"
  :params (&rest (p :as output-port))
  :returns "nil"
  :doc "Write a newline to port.")

(define-primitive
  "write-to-string"
  :params (v)
  :returns "string"
  :doc "Serialize v with write quoting, return as string.")

(define-primitive
  "display-to-string"
  :params (v)
  :returns "string"
  :doc "Serialize v with display format, return as string.")

(define-primitive
  "current-input-port"
  :params ()
  :returns "any"
  :doc "Return current default input port.")

(define-primitive
  "current-output-port"
  :params ()
  :returns "any"
  :doc "Return current default output port.")

(define-primitive
  "current-error-port"
  :params ()
  :returns "any"
  :doc "Return current error port.")

(define-module :stdlib.math)

(define-primitive
  "sin"
  :params ((x :as number))
  :returns "float"
  :doc "Sine of x (radians).")

(define-primitive
  "cos"
  :params ((x :as number))
  :returns "float"
  :doc "Cosine of x (radians).")

(define-primitive
  "tan"
  :params ((x :as number))
  :returns "float"
  :doc "Tangent of x (radians).")

(define-primitive
  "asin"
  :params ((x :as number))
  :returns "float"
  :doc "Arc sine of x; result in radians.")

(define-primitive
  "acos"
  :params ((x :as number))
  :returns "float"
  :doc "Arc cosine of x; result in radians.")

(define-primitive
  "atan"
  :params ((x :as number) &rest (y :as number))
  :returns "float"
  :doc "Arc tangent. (atan x) → radians in (-π/2, π/2). (atan y x) → atan2(y, x).")

(define-primitive
  "exp"
  :params ((x :as number))
  :returns "float"
  :doc "e raised to the power x.")

(define-primitive
  "log"
  :params ((x :as number))
  :returns "float"
  :doc "Natural logarithm of x.")

(define-primitive
  "expt"
  :params ((base :as number) (exp :as number))
  :returns "number"
  :doc "base raised to the power exp. Alias: pow.")

(define-primitive
  "quotient"
  :params ((a :as number) (b :as number))
  :returns "integer"
  :doc "Integer quotient: truncate(a / b) toward zero. Sign follows dividend.")

(define-primitive
  "gcd"
  :params ((a :as number) (b :as number))
  :returns "integer"
  :doc "Greatest common divisor of a and b.")

(define-primitive
  "lcm"
  :params ((a :as number) (b :as number))
  :returns "integer"
  :doc "Least common multiple of a and b.")

(define-primitive
  "number->string"
  :params ((n :as number) &rest (radix :as number))
  :returns "string"
  :doc "Convert number n to string. Optional radix (default 10). E.g. (number->string 255 16) → \"ff\".")

(define-primitive
  "string->number"
  :params ((s :as string) &rest (radix :as number))
  :returns "any"
  :doc "Parse string s as a number. Optional radix (default 10). Returns nil on failure.")

(define-module :stdlib.rational)

(define-primitive
  "make-rational"
  :params (n d)
  :returns "rational"
  :doc "Rational n/d, auto-reduced by GCD. Error if d=0.")

(define-primitive
  "rational?"
  :params (v)
  :returns "boolean"
  :doc "True if v is a rational number.")

(define-primitive
  "numerator"
  :params ((r :as rational))
  :returns "integer"
  :doc "Numerator of rational r (after reduction).")

(define-primitive
  "denominator"
  :params ((r :as rational))
  :returns "integer"
  :doc "Denominator of rational r (after reduction, always positive).")

(define-module :stdlib.hash-table)

(define-module :stdlib.sets)

(define-primitive
  "make-set"
  :params (&rest (lst :as list))
  :returns "set"
  :doc "Create a fresh empty set. Optional list argument seeds the set: (make-set '(1 2 3)).")

(define-primitive
  "set?"
  :params (v)
  :returns "boolean"
  :doc "True if v is a set.")

(define-primitive
  "set-add!"
  :params (s val)
  :returns "nil"
  :doc "Add val to set s in place. No-op if already present.")

(define-primitive
  "set-member?"
  :params (s val)
  :returns "boolean"
  :doc "True if val is in set s.")

(define-primitive
  "set-remove!"
  :params (s val)
  :returns "nil"
  :doc "Remove val from set s in place. No-op if absent.")

(define-primitive
  "set-size"
  :params (s)
  :returns "integer"
  :doc "Number of elements in set s.")

(define-primitive
  "set->list"
  :params (s)
  :returns "list"
  :doc "All elements of set s as a list (unspecified order).")

(define-primitive
  "list->set"
  :params (lst)
  :returns "set"
  :doc "Create a new set containing all elements of lst.")

(define-primitive
  "set-union"
  :params (s1 s2)
  :returns "set"
  :doc "New set with all elements from s1 and s2.")

(define-primitive
  "set-intersection"
  :params (s1 s2)
  :returns "set"
  :doc "New set with elements present in both s1 and s2.")

(define-primitive
  "set-difference"
  :params (s1 s2)
  :returns "set"
  :doc "New set with elements in s1 that are not in s2.")

(define-primitive
  "set-for-each"
  :params (s fn)
  :returns "nil"
  :doc "Call (fn val) for each element in set s. Order unspecified.")

(define-primitive
  "set-map"
  :params (s fn)
  :returns "set"
  :doc "New set of results of (fn val) for each element in s.")

(define-module :stdlib.regexp)

(define-primitive
  "make-regexp"
  :params ((pattern :as string) &rest (flags :as string))
  :returns "regexp"
  :doc "Compile regexp from pattern string and optional flags string (\"i\" case-insensitive, \"m\" multiline, \"s\" dotall).")

(define-primitive
  "regexp?"
  :params (v)
  :returns "boolean"
  :doc "True if v is a compiled regexp.")

(define-primitive
  "regexp-source"
  :params ((re :as regexp))
  :returns "string"
  :doc "Pattern string of a regexp.")

(define-primitive
  "regexp-flags"
  :params ((re :as regexp))
  :returns "string"
  :doc "Flags string of a regexp.")

(define-primitive
  "regexp-match"
  :params ((re :as regexp) (str :as string))
  :returns "any"
  :doc "First match of re in str. Returns {:match \"...\" :start N :end N :groups (...)} or nil.")

(define-primitive
  "regexp-match-all"
  :params ((re :as regexp) (str :as string))
  :returns "list"
  :doc "All non-overlapping matches of re in str as a list of match dicts.")

(define-primitive
  "regexp-replace"
  :params ((re :as regexp) (str :as string) (replacement :as string))
  :returns "string"
  :doc "Replace first match of re in str with replacement. $& = whole match, $1..$9 = groups.")

(define-primitive
  "regexp-replace-all"
  :params ((re :as regexp) (str :as string) (replacement :as string))
  :returns "string"
  :doc "Replace all matches of re in str with replacement.")

(define-primitive
  "regexp-split"
  :params ((re :as regexp) (str :as string))
  :returns "list"
  :doc "Split str on every match of re; returns list of strings.")

(define-module :stdlib.bytevectors)

(define-primitive
  "make-bytevector"
  :params (n &rest fill)
  :returns "bytevector"
  :doc "Create a bytevector of n bytes, all initialised to fill (default 0).")

(define-primitive
  "bytevector?"
  :params (v)
  :returns "boolean"
  :doc "True if v is a bytevector.")

(define-primitive
  "bytevector-length"
  :params ((bv :as bytevector))
  :returns "number"
  :doc "Number of bytes in bv.")

(define-primitive
  "bytevector-u8-ref"
  :params ((bv :as bytevector) (i :as number))
  :returns "number"
  :doc "Byte value 0-255 at index i.")

(define-primitive
  "bytevector-u8-set!"
  :params ((bv :as bytevector) (i :as number) (byte :as number))
  :returns "nil"
  :doc "Set byte at index i to byte 0-255. Mutates bv.")

(define-primitive
  "bytevector-copy"
  :params ((bv :as bytevector) &rest bounds)
  :returns "bytevector"
  :doc "Fresh copy of bv, optionally sliced to [start, end).")

(define-primitive
  "bytevector-copy!"
  :params ((dst :as bytevector) (at :as number) (src :as bytevector) &rest bounds)
  :returns "nil"
  :doc "Copy bytes from src[start..end) into dst starting at at. Mutates dst.")

(define-primitive
  "bytevector-append"
  :params (&rest bvs)
  :returns "bytevector"
  :doc "Concatenate bytevectors into a new bytevector.")

(define-primitive
  "utf8->string"
  :params ((bv :as bytevector) &rest bounds)
  :returns "string"
  :doc "Decode bv[start..end) as UTF-8 and return the string.")

(define-primitive
  "string->utf8"
  :params ((s :as string) &rest bounds)
  :returns "bytevector"
  :doc "Encode s[start..end) as UTF-8 and return a bytevector.")

(define-primitive
  "bytevector->list"
  :params ((bv :as bytevector))
  :returns "list"
  :doc "Convert bytevector to a list of byte integers.")

(define-primitive
  "list->bytevector"
  :params ((lst :as list))
  :returns "bytevector"
  :doc "Build a bytevector from a list of byte integers 0-255.")

(define-primitive
  "format"
  :params ((template :as string) &rest args)
  :returns "string"
  :doc "CL-style format string. Directives: ~a display, ~s write, ~d decimal, ~x hex, ~o octal, ~b binary, ~f fixed-point, ~e scientific, ~% newline, ~& fresh-line, ~~ tilde, ~t tab. Optional first arg: output-port.")
