# ICPP — The Internet C++ Interpreter — User Manual

**ICPP** is the scripting mode of the **kcpp** toolchain, invoked as `kcpp -icpp <file>`.
It is a C-like interpreted language that reuses kcpp's own parser and type system, adds
exactly **one** new piece of syntax (`$var`), and exposes a large library of **225 registered
builtin functions** — database access, strings/regex/sed, files and filesystem, binary
reading, containers (lists/maps/sets/tuples/dicts), hex/bit manipulation, process execution
and a test harness, threads and CPU affinity, image/OCR/biometric utilities, a software
graphics canvas, an MCP + OpenAI client, and a full tensor / neural-network / autodiff engine.

> Copyright (c) 2026 **Muhammad Anisur Rahman**. Free to use and distribute under the **MIT License**.
> SPDX-License-Identifier: MIT.

---

## Table of contents

1. [Introduction — the single-syntax philosophy](#1-introduction)
2. [Running ICPP](#2-running-icpp)
3. [Language reference](#3-language-reference)
4. [Builtin function reference](#4-builtin-function-reference) — the core of the manual
   - [4.1 Output & string building](#41-output--string-building)
   - [4.2 Strings](#42-strings)
   - [4.3 Regex (`icpp_re`) and sed (`ised`)](#43-regex-and-sed)
   - [4.4 SQL / ISQL database bridge](#44-sql--isql-database-bridge)
   - [4.5 Files and file inspection](#45-files-and-file-inspection)
   - [4.6 Filesystem operations](#46-filesystem-operations)
   - [4.7 Sequential file reading](#47-sequential-file-reading)
   - [4.8 Environment & time](#48-environment--time)
   - [4.9 Binary file reading](#49-binary-file-reading)
   - [4.10 Hex and bit manipulation](#410-hex-and-bit-manipulation)
   - [4.11 Lists and split/join](#411-lists-and-splitjoin)
   - [4.12 Maps and dicts](#412-maps-and-dicts)
   - [4.13 Sets](#413-sets)
   - [4.14 Tuples](#414-tuples)
   - [4.15 Process execution & test harness](#415-process-execution--test-harness)
   - [4.16 The `i*` shell utilities](#416-the-i-shell-utilities)
   - [4.17 Threads and CPU affinity](#417-threads-and-cpu-affinity)
   - [4.18 Images, OCR, fingerprints/AFIS](#418-images-ocr-fingerprintsafis)
   - [4.19 Software graphics canvas](#419-software-graphics-canvas)
   - [4.20 MCP client + JSON](#420-mcp-client--json)
   - [4.21 OpenAI-compatible chat](#421-openai-compatible-chat)
   - [4.22 CGI parameter access](#422-cgi-parameter-access)
   - [4.23 Tensor engine — core operations](#423-tensor-engine--core-operations)
   - [4.24 Tensor engine — neural-network / LLM layers](#424-tensor-engine--neural-network--llm-layers)
   - [4.25 Autodiff (backward / gradient rules)](#425-autodiff-backward--gradient-rules)
   - [4.26 Optimizer & persistence](#426-optimizer--persistence)
   - [4.27 Text embedding & chatbot/RAG support](#427-text-embedding--chatbotrag-support)
   - [4.28 Core C builtins](#428-core-c-builtins)
5. [The library / `#include` manifest system](#5-the-library--include-manifest-system)
6. [The `i*` aliases for Files/FS builtins](#6-the-i-aliases-for-filesfs-builtins)
7. [Shell redirection in command lines](#7-shell-redirection-in-command-lines)
8. [Known issues](#8-known-issues)
9. [Worked examples](#9-worked-examples)
10. [Appendix A — complete alphabetical builtin index (all 225)](#appendix-a--complete-alphabetical-builtin-index-all-225)

---

## 1. Introduction

### What ICPP is

ICPP ("Internet C++") is an interpreter embedded in `kcpp.exe`. It runs a C-like script
directly, without a separate compile/link/run cycle. It was created to give the kcpp
toolchain a scripting layer for three broad jobs that recur across this project:

- **RTL / regression driving** — run a tool (compiler, simulator, fitter), inspect what it
  produced, assert on it, and exit with a status a CI job can read.
- **Web / template work** — `<?icpp ... ?>` blocks embedded in HTML, served by `iwebserver`.
- **AI / ML work** — a native tensor engine with forward layers, an autodiff (backward) set,
  an SGD optimizer, tokenizer, plus clients for MCP tool servers and OpenAI-compatible chat.

### The single-syntax philosophy

ICPP is deliberately **almost entirely just C**. Functions, `int` / `double` / `char *`
locals and parameters, `if` / `else` / `while` / `do`-`while` / `for`, arithmetic and logical
operators, `++` / `--`, `#include` — all behave as in C. **The one and only new piece of
syntax is `$var`**, a dynamically-typed local. Everything else that looks "new" — strings,
lists, tensors, regex, database queries — is expressed as ordinary **function calls** into the
builtin library. There is no new object syntax, no new control flow, no operator overloading.
If you know C and you know the names of the builtins, you know ICPP.

Two small optional keyword additions (`function`/`subroutine` for typeless function
definitions, and `rtlbegin`/`rtlend` for embedded verbatim Verilog) are described in
[§3](#3-language-reference); both exist only in `-icpp` mode and are backward-compatible with
plain C that happens to use those identifiers.

---

## 2. Running ICPP

### From the command line

```
kcpp.exe -icpp myscript.icpp
```

The conventional extension is `.icpp`, though `.c` also works (kcpp's normal parser handles
the file either way). A script needs a `main()`:

```c
// hello.icpp
int main(void)
{
    iprint("hello, world\n");
    return 0;
}
```

```
> kcpp.exe -icpp hello.icpp
hello, world
```

`main()` may be declared `int main(void)` or `void main()`; both appear throughout the test
suite. When it returns `int`, that value is **not** automatically the process exit code — use
`icpp_exit(code)` when you need to set the exit status explicitly (the idiomatic CI ending is
`icpp_exit(icpp_test_summary());`).

### Diagnostics and teaching mode

- **`KCPP_DIAG_ICPP=1`** (environment variable) — turns on a detailed trace of parsing,
  identifier resolution, and expression evaluation. This is the single most useful tool for
  debugging a script that produces a wrong number or crashes.
- **`kcpp -icpp -llmtrace script.icpp`** (or `KCPP_LLMTRACE=1`) — prints an educational,
  step-by-step walkthrough of every LLM-pipeline builtin as it runs (tokenizing, embedding
  lookup, positional encoding, linear layers, attention with per-head scores / causal masking
  / softmax weights, loss, the backward pass, and the SGD update), with *why* each step
  exists, in student-facing language. Matrices print up to 8×8. Output lines are prefixed
  `[llmtrace]` so they are easy to filter.

### Embedded in HTML (`<?icpp ... ?>`)

An HTML file may embed ICPP code inside `<?icpp ... ?>` processing blocks. When such a page is
requested through `iwebserver`, the server sets the CGI-style `QUERY_STRING` environment
variable from the request's query parameters and runs the interpreter over the page; the code
inside each `<?icpp ... ?>` block executes and its output is spliced into the surrounding HTML
sent to the browser. Inside the block, [`icpp_getparam(name)`](#422-cgi-parameter-access)
reads a query parameter, and [`iprint`](#41-output--string-building) writes into the page.

```html
<html>
<body>
  <h1>Search results</h1>
  <?icpp
    char *q;
    q = icpp_getparam("q");
    iprint("<p>You searched for: ", q, "</p>");
  ?>
</body>
</html>
```

### Including other files

`#include "otherfile.icpp"` splices another ICPP source file in at that point — for both
function definitions and top-level statements. This is how reusable ICPP libraries
(for example `icpp_autodiff.c`, or the conformance suite's `libfixture.icpp`) are pulled into a
script. See [§5](#5-the-library--include-manifest-system) for the manifest conventions that go
with it.

---

## 3. Language reference

### Types

ICPP supports the C scalar types the interpreter needs:

| Type | Notes |
|---|---|
| `int` | 32-bit integer. Also the type used to hold **handles** (see below) — a tensor/list/map/connection handle is just an `int` you pass around. |
| `double` | IEEE double. Real double arithmetic, parameters, and return values all work correctly (this was historically buggy and is now fixed and regression-covered). |
| `char *` | String pointer. A `char *` local may be assigned a string literal (`p = "hello";`) and passed to any string builtin. |
| `void` | For functions that return nothing. |
| `$var` | The one dynamically-typed slot — see below. |

Global variables are supported (`int g_global;` at file scope), and behave as globals across
functions.

### `$var` — the dynamically-typed local

```c
$x = 5;
$y = tensor_get(handle, 0, 0);
```

A `$var` needs no declaration and infers its type (int / double / handle) from whatever it is
assigned. Internally it has a single generic 8-byte storage slot, wide enough for an int, a
double, or a handle.

**Important semantic**: a `$var`'s initializer is evaluated **at parse time**, not at the point
the statement appears to run. Ordinary (non-`$var`) statements run at normal interpreted
runtime. Mixing the two where ordering matters can surprise you. The safe, well-tested pattern:
**use plain typed locals (`int`, `double`) for anything inside a function body or a loop**, and
reserve `$var` for simple top-level scratch values in `main()` where parse-time evaluation does
not matter. A `$var` initializer must be resolvable at parse time — so a `$var` inside a
function body **cannot** be initialized from that function's parameters:

```c
int f(int x)
{
    $y = x * 2;   // parse error: "Initializing symbol 'y' must be constant number"
    return $y;
}
```

Use a plain typed local instead (`int y; y = x * 2; return y;`).

### Variable declaration & statements

Declarations are C-style (`int x; double d; char *s;`). Multiple declarations are typically
written one per statement in this codebase's scripts. Assignment, calls, and control flow are
all standard C.

### Functions — typed and typeless

**Typed functions** are ordinary C:

```c
int add(int a, int b) { return a + b; }
double scaled(double x) { return x * 1.5; }
```

**Typeless functions** use the `function` keyword (synonym: `subroutine`) — no return type, no
parameter types. Both are determined **at runtime, per call**; the same target can serve
doubles on one call and ints on the next:

```c
function scale(x, f)
{
    return x * f;
}

d = scale(2.5, 4.0);   // double path -> 10.0
i = scale(6, 7);       // SAME function, int path -> 42
m = scale(1.25, 2);    // mixed args promote like C -> 2.5
```

Rules:

- Top-level definitions only, and only in `-icpp` mode (in a plain kcpp compile, the keyword
  is a clean error).
- Each parameter is the same generic 8-byte runtime-typed slot a `$var` uses; its actual type
  is stamped from whatever each call's argument evaluates to.
- `return expr;` returns whatever type `expr` evaluated to at runtime; a function with no
  `return` may be called as a plain statement.
- Typeless functions compose and recurse; define before use, same as typed functions.
- Supported value kinds: ints, handles, and doubles. (String-typed *typeless* parameters/
  returns are not covered in this version — a typed `char *f(char *s)` is the way to return a
  string, as `libfixture.icpp`'s `fx_shout` does.)

### Control flow & operators

`if`/`else`, `while`, `do { } while (...)`, and `for (init; cond; step)` all work, with both
braced and braceless bodies, and standalone `{ }` scoping blocks. Arithmetic (`+ - * / %`),
comparison (`< <= > >= == !=`), logical (`&& || !`), and `++`/`--` (post-increment/decrement)
are supported. From the core conformance suite, every one of these asserts the correct value:

```c
int fact(int n) { if (n <= 1) return 1; int r; r = n * fact(n - 1); return r; }
int sum_while(int n) { int i; int s; s = 0; i = 1; while (i <= n) { s = s + i; i = i + 1; } return s; }
int for_sum(int n)   { int i; int s; s = 0; for (i = 1; i <= n; i = i + 1) s = s + i; return s; }
// fact(5)==120, sum_while(100)==5050, for_sum(10)==55
```

(See [§8](#8-known-issues) for the one recursion caveat: compute a recursive step through a
local rather than directly inside a `return` expression.)

### Handles

Nearly everything that is not a plain number or string in this library — a tensor, a database
connection, a query result, a list, a map, a set, an open file, an image canvas, an MCP
connection — is an **opaque handle**: an `int` returned by a constructor function, passed to
every function that operates on that object, and released with a matching
`_free` / `_disconnect` / `_fclose` call. Handles are never dereferenced in script code.

```c
int t;
t = tensor_new(2, 2);      // t is a handle
tensor_set(t, 0, 0, 5.0);  // operate via the handle
tensor_free(t);            // release when done
```

### `rtlbegin` / `rtlend` — embedded verbatim Verilog

```c
rtlbegin
module hand_counter (input clk, input rst_n, output [7:0] count);
    always @(posedge clk or negedge rst_n)
        if (!rst_n) count <= 0; else count <= count + 1;
endmodule
rtlend
```

Everything between the two keywords is passed straight through: the ICPP parser never
tokenizes it, and the interpreter ignores the block. `kcpp -rtl` copies it byte-for-byte into
the `.v` output, ahead of the generated modules so those can instantiate it. Multiple blocks
concatenate in source order. Both keywords are `-icpp`-mode only; an unterminated block is an
error naming the opening line.

---

## 4. Builtin function reference

This is the core of the manual. Every one of the **225** registered builtins appears below,
grouped by category. Each entry gives the **signature** — the name, its argument count and the
return type exactly as registered in `sysfunc.c` — a one-line description, and, for
representative builtins, a short example.

Conventions used in the signatures:

- A trailing `-> T` is the registered return type. `-> int (handle)` means the return is an
  `int` used as an opaque handle.
- Argument *names* are descriptive; the interpreter checks **arity** (argument count), so the
  count shown is authoritative. `(...)` marks a **variadic** builtin (registered arg count −1).
- "1/0" means an `int` boolean; "−1" is the common bad-handle / not-found sentinel.

### 4.1 Output & string building

| Builtin | Signature | Description |
|---|---|---|
| `iprint` | `iprint(...)` -> void | Prints each argument in order, concatenated, **no format string**. Strings pass through as-is; doubles auto-format with `%g`, ints with `%d`. The general-purpose print statement for ICPP. |
| `ifprint` | `ifprint(path, ...)` -> void | `iprint`'s print-to-**file** sibling: same argument formatting, **appended** to `path` as a line (file created if absent). Good for building transcripts/logs and appending to a knowledge-base file. |
| `isprint` | `isprint(...)` -> `char *` | `iprint`'s build-a-**string** sibling — returns the concatenation instead of printing. Shares its implementation with `icpp_concat` (two names, one handler). |
| `icpp_concat` | `icpp_concat(...)` -> `char *` | Builds a string from mixed strings/numbers. Identical to `isprint`. |
| `printf` | `printf(...)` -> void | Standard-C-style printf **with important limits**: only `%d` and `%s` actually substitute; `%x`/`%f`/`%u`/etc. are parsed but silently no-op, and `\n`/`\r`/`\t` are handled. **Prefer `iprint`** for reliable output. |

```c
iprint("count=", 3, " ratio=", 1.5, "\n");        // count=3 ratio=1.5
char *msg; msg = isprint("user", 42, "@site");     // "user42@site"
ifprint("bot_kb.txt", "We open at 9am and close at 6pm");  // append one line
```

### 4.2 Strings

| Builtin | Signature | Description |
|---|---|---|
| `icpp_strlen` | `icpp_strlen(char *s)` -> `int` | String length. |
| `icpp_strcmp` | `icpp_strcmp(char *a, char *b)` -> `int` | −1 / 0 / 1 (clamped), 0 when equal. |
| `icpp_strstr` | `icpp_strstr(char *hay, char *needle)` -> `int` | 0-based index of first occurrence, −1 if absent. |
| `icpp_starts_with` | `icpp_starts_with(char *s, char *prefix)` -> `int` | 1/0. |
| `icpp_ends_with` | `icpp_ends_with(char *s, char *suffix)` -> `int` | 1/0. |
| `icpp_substr` | `icpp_substr(char *s, int start, int len)` -> `char *` | Substring of `len` chars from 0-based `start`. |
| `icpp_upper` | `icpp_upper(char *s)` -> `char *` | Uppercased copy. |
| `icpp_lower` | `icpp_lower(char *s)` -> `char *` | Lowercased copy. |
| `icpp_trim` | `icpp_trim(char *s)` -> `char *` | Leading/trailing whitespace removed. |
| `icpp_replace_all` | `icpp_replace_all(char *s, char *from, char *to)` -> `char *` | Every occurrence of `from` replaced by `to`. |
| `icpp_atoi` | `icpp_atoi(char *s)` -> `int` | Decimal string to int; skips leading whitespace, honors a leading sign, stops at the first non-digit (so `"350 ps"` -> 350). Use this for decimal text; `icpp_parse_hex` is base-16. |

```c
icpp_assert_eq_str(icpp_substr("hello world", 6, 5), "world", "substr");
icpp_assert_eq_str(icpp_upper("Hello"), "HELLO", "upper");
icpp_assert_eq_str(icpp_trim("  hi  "), "hi", "trim");
icpp_assert_eq_str(icpp_replace_all("a-b-c", "-", "+"), "a+b+c", "replace_all");
icpp_assert_eq_int(icpp_atoi("123"), 123, "atoi");
```

### 4.3 Regex and sed

| Builtin | Signature | Description |
|---|---|---|
| `icpp_re` | `icpp_re(char *input, char *directive)` -> `char *` | Regex match/substitute over a clean-room engine. |
| `ised` | `ised(char *text, char *script)` -> `char *` | `sed`-style stream editor on a string; returns the edited text. |
| `ised_file` | `ised_file(char *path, char *script)` -> `int` | Applies the sed script to a file **in place**; returns 1 if it changed the file, 0 if unchanged, −1 if unreadable. Only rewrites when something actually changed. |

**`icpp_re` directive language** (a small sed-like mini-language):

- `"m/PATTERN/"` — return the first matched substring, or `""` if no match.
- `"s/PATTERN/REPLACE/"` — return `input` with the **first** match of `PATTERN` replaced by
  `REPLACE`; unchanged if no match. `REPLACE` may reference capture groups with `\1`..`\9`.
  Single substitution only (no `/g` flag on `icpp_re`).
- Anything else — treated as a bare `PATTERN`, same as `"m/PATTERN/"`.

Pattern syntax: literal chars, `.`, `^`/`$` anchors, greedy `*` `+` `?`,
`[abc]`/`[^abc]`/`[a-z]` classes, `\d \D \s \S \w \W` shorthands, `(...)` capture groups,
`|` alternation.

```c
iprint(icpp_re("abc123", "m/[0-9]+/"));           // "123"
iprint(icpp_re("2026-07-31", "s/-/./"));          // "2026.07-31" (first match only)
```

**`ised`** implements the `s` command from the POSIX description (clean-room, no GNU sed read):
`s<D>pattern<D>replacement<D>[g][i]` where the delimiter `<D>` is whatever follows `s` (so
`s|/usr|/opt|` works without escaping slashes). `&` is the whole match, `\1`..`\9` are capture
groups, `\&` is a literal ampersand, `\n`/`\t` are recognized in the replacement, and the `g`
flag substitutes all occurrences on each line. `ised` processes line by line and never returns
NULL (so it chains with `icat`/`igrep`). An unparsable script returns the text **unchanged**
(and warns on stderr) rather than blanking it.

```c
iprint(ised("a-b-c", "s/-/+/g"));                 // "a+b+c"
ised_file("config.txt", "s|/usr/local|/opt|g");   // rewrite paths in place
```

> Note the two documented open bugs affecting these: interpret-mode ternary and regex `\N`
> backreference escaping — see [§8](#8-known-issues).

### 4.4 SQL / ISQL database bridge

Bridges ICPP to the ISQL database engine (statically linked; `isqldll.dll` is a load-time
dependency of `kcpp.exe`). Connection and result handles are `int`s.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_sql_connect` | `icpp_sql_connect(char *dsn)` -> `int` | Open a connection; returns a connection handle. |
| `icpp_sql_query` | `icpp_sql_query(int conn, char *sql)` -> `int` | Run a query; returns a result handle. |
| `icpp_sql_next` | `icpp_sql_next(int result)` -> `int` | Advance to the next row: 1 = a row is available, 0 = done. |
| `icpp_sql_get` | `icpp_sql_get(int result, char *colname)` -> `char *` | The current row's value for `colname`. |
| `icpp_sql_count` | `icpp_sql_count(int result)` -> `int` | Row count. |
| `icpp_sql_free` | `icpp_sql_free(int result)` -> void | Release a result handle. |
| `icpp_sql_disconnect` | `icpp_sql_disconnect(int conn)` -> void | Close a connection. |
| `icpp_sql_status` | `icpp_sql_status(int conn)` -> `char *` | Last status / error message. |

```c
int c; int r;
c = icpp_sql_connect("default");
r = icpp_sql_query(c, "SELECT name, city FROM people");
while (icpp_sql_next(r)) {
    iprint(icpp_sql_get(r, "name"), " - ", icpp_sql_get(r, "city"), "\n");
}
icpp_sql_free(r);
icpp_sql_disconnect(c);
```

Compose SQL text with `isprint(...)`:
`icpp_sql_query(c, isprint("SELECT * FROM t WHERE id=", id));`

### 4.5 Files and file inspection

Text-file helpers built for verification workflows (comparing a simulation transcript against
an expected file) but general-purpose. Every function here also has a short `i*` alias — see
[§6](#6-the-i-aliases-for-filesfs-builtins).

| Builtin | Signature | Description |
|---|---|---|
| `icpp_file_read` | `icpp_file_read(char *path)` -> `char *` | Whole file as text (bounded ~4 KB — for larger files use `icat`, or the streaming helpers below). |
| `icpp_file_write` | `icpp_file_write(char *path, char *text)` -> `int` | **Truncating** write (binary mode, byte-for-byte); 1 on success, 0 on failure. Counterpart to `ifprint`'s append. |
| `icpp_file_size` | `icpp_file_size(char *path)` -> `int` | File size in bytes. |
| `icpp_file_exists` | `icpp_file_exists(char *path)` -> `int` | 1/0. |
| `icpp_file_mtime` | `icpp_file_mtime(char *path)` -> `int` | Modification time (epoch seconds). |
| `icpp_file_lines` | `icpp_file_lines(char *path)` -> `int` | Line count. |
| `icpp_file_line` | `icpp_file_line(char *path, int n)` -> `char *` | The 1-based `n`-th line. |
| `icpp_file_contains` | `icpp_file_contains(char *path, char *needle)` -> `int` | 1/0, −1 unreadable; **streams**, so huge logs are fine. |
| `icpp_file_replace` | `icpp_file_replace(char *path, char *from, char *to)` -> `int` | In-file replace `from`→`to`. |
| `icpp_file_diff` | `icpp_file_diff(char *a, char *b)` -> `int` | 0 = identical; else the 1-based first differing line; −1 unopenable. Line endings normalized (CRLF vs LF compare equal). |
| `icpp_file_hash` | `icpp_file_hash(char *path)` -> `char *` | FNV-1a 32-bit content hash, hex (distinct files essentially never collide). |
| `icpp_file_fingerprint` | `icpp_file_fingerprint(char *path)` -> `char *` | Block-level fuzzy fingerprint: file split into 16 chunks, each FNV-1a-hashed (128 hex chars). A **byte-level** near-duplicate detector, NOT a perceptual/visual hash. |

```c
ifile_write("probe.txt", "line1\nline2\nline3\n");
icpp_assert_eq_int(icpp_file_lines("probe.txt"), 3, "file_lines");
icpp_assert_eq_int(icpp_file_contains("probe.txt", "line2"), 1, "file_contains");
```

### 4.6 Filesystem operations

Each also has an `i*` alias (see [§6](#6-the-i-aliases-for-filesfs-builtins)).

| Builtin | Signature | Description |
|---|---|---|
| `icpp_mkdir` | `icpp_mkdir(char *path)` -> `int` | Create a directory. |
| `icpp_remove` | `icpp_remove(char *path)` -> `int` | Delete a file. |
| `icpp_rename` | `icpp_rename(char *from, char *to)` -> `int` | Rename/move. |
| `icpp_cwd` | `icpp_cwd()` -> `char *` | Current working directory. |
| `icpp_chdir` | `icpp_chdir(char *path)` -> `int` | Change directory. |
| `icpp_glob` | `icpp_glob(char *pattern)` -> `int` (list handle) | Non-recursive glob (e.g. `"*.v"`); returns a **list handle** of matching file names (directories excluded). Free the list with `icpp_list_free`. |
| `icpp_glob_r` | `icpp_glob_r(char *dir, char *pattern)` -> `int` (list handle) | Recursive glob under `dir`. |

```c
int files; int i;
files = icpp_glob("*.icpp");
for (i = 0; i < icpp_list_count(files); i = i + 1)
    iprint(icpp_list_get(files, i), "\n");
icpp_list_free(files);
```

### 4.7 Sequential file reading

Line-at-a-time reading over an opaque file handle. Each has an `if*` alias.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_fopen` | `icpp_fopen(char *path)` -> `int` (file handle) | Open for sequential reading; 0/bad handle on failure. |
| `icpp_fgets` | `icpp_fgets(int handle)` -> `char *` | Next line, with the trailing `\n`/`\r` stripped; `""` at EOF. |
| `icpp_feof` | `icpp_feof(int handle)` -> `int` | 1 once end-of-file has been reached. |
| `icpp_fclose` | `icpp_fclose(int handle)` -> void | Close the handle. |

```c
int f; char *line;
f = ifopen("big.log");
while (!ifeof(f)) {
    line = ifgets(f);
    if (icpp_strstr(line, "ERROR") >= 0) iprint(line, "\n");
}
ifclose(f);
```

### 4.8 Environment & time

| Builtin | Signature | Description |
|---|---|---|
| `icpp_getenv` | `icpp_getenv(char *name)` -> `char *` | Environment variable value, `""` if unset. Alias: `igetenv`. |
| `icpp_setenv` | `icpp_setenv(char *name, char *value)` -> `int` | Set an environment variable. Alias: `isetenv`. |
| `icpp_time` | `icpp_time()` -> `int` | Current time as Unix epoch seconds. |
| `icpp_date_string` | `icpp_date_string()` -> `char *` | Local date-time as `"YYYY-MM-DD HH:MM:SS"`. |
| `icpp_elapsed_ms` | `icpp_elapsed_ms()` -> `int` | Milliseconds since its first call — a run timer for "suite took N ms" reporting. |

```c
iprint("started ", icpp_date_string(), "\n");
// ... work ...
iprint("took ", icpp_elapsed_ms(), " ms\n");
```

### 4.9 Binary file reading

Reads raw bytes (the one thing the text file builtins cannot do). A buffer is an opaque handle;
free it when done or you leak the whole file.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_bin_read` | `icpp_bin_read(char *path)` -> `int` (buffer handle) | Reads the whole file into one allocation; 0 on failure. |
| `icpp_bin_size` | `icpp_bin_size(int handle)` -> `int` | Byte count; −1 on a bad handle. |
| `icpp_bin_get` | `icpp_bin_get(int handle, int offset)` -> `int` | The byte (0..255) at `offset`; **−1** out of range (so a real 0 byte is distinguishable from an error). |
| `icpp_bin_int32` | `icpp_bin_int32(int handle, int offset)` -> `int` | 32-bit **little-endian** integer at that byte offset; 0 if out of range. |
| `icpp_bin_float` | `icpp_bin_float(int handle, int offset)` -> `double` | 32-bit float at that **byte** offset, widened to double; 0.0 if out of range. |
| `icpp_bin_find` | `icpp_bin_find(int handle, char *text)` -> `int` | Byte offset of the first occurrence of `text`, −1 if absent. Length-based search, so embedded NULs do not end it early (e.g. "which DLLs does this .exe import"). |
| `icpp_bin_free` | `icpp_bin_free(int handle)` -> `int` | Free the buffer. |

```c
int b;
b = icpp_bin_read("vec.bin");
iprint("size=", icpp_bin_size(b), " first float=", icpp_bin_float(b, 0), "\n");
if (icpp_bin_find(b, "msvcr") >= 0) iprint("links a VC runtime\n");
icpp_bin_free(b);
```

### 4.10 Hex and bit manipulation

RTL work is hex-first; these give a script hex I/O and Verilog-style bit slicing.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_hex` | `icpp_hex(int value, int digits)` -> `char *` | Zero-padded lowercase hex, **no** `0x` prefix; `digits<=0` means "as many as needed", clamped to 8. Negatives print as their 32-bit two's-complement pattern (`icpp_hex(-1, 8)` -> `"ffffffff"`). |
| `icpp_parse_hex` | `icpp_parse_hex(char *s)` -> `int` | Parse hex; tolerates `ff`, `FF`, `0xff`, `0XFF`, and leading spaces. |
| `icpp_bits` | `icpp_bits(int value, int hi, int lo)` -> `int` | Verilog's `value[hi:lo]` bit slice. Indices in either order are accepted (swapped), clamped to 0..31. |
| `icpp_bin` | `icpp_bin(int value, int width)` -> `char *` | MSB-first `0`/`1` text, exactly `width` bits (clamped to 32). |

```c
icpp_assert_eq_int(icpp_parse_hex("ff"), 255, "parse_hex");
icpp_assert_eq_str(icpp_hex(255, 2), "ff", "hex");
iprint("ctrl[7:4]=", icpp_bits(0xAB, 7, 4), "\n");   // 10
iprint("bin=", icpp_bin(5, 4), "\n");                // 0101
```

### 4.11 Lists and split/join

Handle-based, ordered list of strings. Stored strings are copied (reuse your buffers freely).

| Builtin | Signature | Description |
|---|---|---|
| `icpp_list_new` | `icpp_list_new()` -> `int` (handle) | New empty list. |
| `icpp_list_add` | `icpp_list_add(int h, char *s)` -> `int` | Append; returns the new count. |
| `icpp_list_get` | `icpp_list_get(int h, int i)` -> `char *` | 0-based element; out-of-range gives `""`. |
| `icpp_list_count` | `icpp_list_count(int h)` -> `int` | Element count. |
| `icpp_list_sort` | `icpp_list_sort(int h)` -> `int` | Lexicographic in-place sort. |
| `icpp_list_free` | `icpp_list_free(int h)` -> void | Free the list. |
| `icpp_split` | `icpp_split(char *text, char *sep)` -> `int` (list handle) | Split `text` on the **literal string** `sep`; the trailing field is kept. Returns a list handle. |
| `icpp_join` | `icpp_join(int h, char *sep)` -> `char *` | Join a list's elements with `sep`. |

```c
int L;
L = icpp_split("a::b::c", "::");           // 3 items
icpp_assert_eq_int(icpp_list_count(L), 3, "split");
iprint(icpp_join(L, "-"));                 // "a-b-c"
icpp_list_free(L);
```

### 4.12 Maps and dicts

A linear-scan association array of string→string, sized for script use. The `icpp_dict_*`
names are **Python-familiar aliases** for the identical `icpp_map_*` implementation (same
handle interchangeably usable through either name).

| Builtin | Signature | Description |
|---|---|---|
| `icpp_map_new` | `icpp_map_new()` -> `int` (handle) | New empty map. |
| `icpp_map_set` | `icpp_map_set(int h, char *key, char *val)` -> `int` | Insert/**replace** (existing key is overwritten, not duplicated). |
| `icpp_map_get` | `icpp_map_get(int h, char *key)` -> `char *` | Value, `""` if absent — use `has` to distinguish. |
| `icpp_map_has` | `icpp_map_has(int h, char *key)` -> `int` | 1/0. |
| `icpp_map_count` | `icpp_map_count(int h)` -> `int` | Entry count. |
| `icpp_map_keys` | `icpp_map_keys(int h)` -> `int` (list handle) | Keys in insertion order (free the returned list). |
| `icpp_map_delete` | `icpp_map_delete(int h, char *key)` -> `int` | Remove a key. |
| `icpp_map_free` | `icpp_map_free(int h)` -> void | Free the map. |
| `icpp_dict_new` | `icpp_dict_new()` -> `int` (handle) | Alias of `icpp_map_new`. |
| `icpp_dict_set` | `icpp_dict_set(int h, char *k, char *v)` -> `int` | Alias of `icpp_map_set`. |
| `icpp_dict_get` | `icpp_dict_get(int h, char *k)` -> `char *` | Alias of `icpp_map_get`. |
| `icpp_dict_has` | `icpp_dict_has(int h, char *k)` -> `int` | Alias of `icpp_map_has`. |
| `icpp_dict_count` | `icpp_dict_count(int h)` -> `int` | Alias of `icpp_map_count`. |
| `icpp_dict_keys` | `icpp_dict_keys(int h)` -> `int` (list handle) | Alias of `icpp_map_keys`. |
| `icpp_dict_delete` | `icpp_dict_delete(int h, char *k)` -> `int` | Alias of `icpp_map_delete`. |
| `icpp_dict_free` | `icpp_dict_free(int h)` -> void | Alias of `icpp_map_free`. |

```c
int M;
M = icpp_map_new();
icpp_map_set(M, "one", "1");
icpp_map_set(M, "two", "2");
icpp_assert_eq_str(icpp_map_get(M, "one"), "1", "map get");
icpp_map_delete(M, "one");
icpp_assert_eq_int(icpp_map_has(M, "one"), 0, "map delete");
icpp_map_free(M);
```

### 4.13 Sets

A set of unique strings.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_set_new` | `icpp_set_new()` -> `int` (handle) | New empty set. |
| `icpp_set_add` | `icpp_set_add(int h, char *value)` -> `int` | Add (duplicates ignored). |
| `icpp_set_has` | `icpp_set_has(int h, char *value)` -> `int` | 1/0 membership. |
| `icpp_set_count` | `icpp_set_count(int h)` -> `int` | Element count. |
| `icpp_set_remove` | `icpp_set_remove(int h, char *value)` -> `int` | Remove an element. |
| `icpp_set_items` | `icpp_set_items(int h)` -> `int` (list handle) | Members as a list (free it). |
| `icpp_set_free` | `icpp_set_free(int h)` -> void | Free the set. |

```c
int S; int L;
S = icpp_set_new();
icpp_set_add(S, "apple");
icpp_set_add(S, "apple");                 // dup -> ignored
icpp_assert_eq_int(icpp_set_count(S), 1, "dedup");
L = icpp_set_items(S);
icpp_list_free(L);
icpp_set_free(S);
```

### 4.14 Tuples

An immutable ordered sequence of strings: once you **read** it (any `count`/`get`), it is
sealed and further `add`s are rejected with −1.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_tuple_new` | `icpp_tuple_new()` -> `int` (handle) | New empty tuple (still mutable until first read). |
| `icpp_tuple_add` | `icpp_tuple_add(int h, char *value)` -> `int` | Append while unsealed; **−1** once sealed. |
| `icpp_tuple_get` | `icpp_tuple_get(int h, int index)` -> `char *` | 0-based element (this seals the tuple). |
| `icpp_tuple_count` | `icpp_tuple_count(int h)` -> `int` | Element count (this seals the tuple). |
| `icpp_tuple_free` | `icpp_tuple_free(int h)` -> void | Free the tuple. |

```c
int T; int n;
T = icpp_tuple_new();
icpp_tuple_add(T, "x"); icpp_tuple_add(T, "y");
icpp_assert_eq_int(icpp_tuple_count(T), 2, "count seals it");
n = icpp_tuple_add(T, "z");
icpp_assert_eq_int(n, -1, "immutable after read");
icpp_tuple_free(T);
```

### 4.15 Process execution & test harness

These make an ICPP script a drop-in replacement for a Perl/Python/shell regression driver.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_exec` | `icpp_exec(char *cmdline)` -> `int` | Run it; child output goes to your console; returns the exit code (−1 = could not start). |
| `icpp_exec_capture` | `icpp_exec_capture(char *cmdline)` -> `char *` | Returns the child's stdout **and stderr** merged as a string. |
| `icpp_exec_to_file` | `icpp_exec_to_file(char *cmdline, char *path)` -> `int` | Both streams into `path`; then search it with the streaming `icpp_file_contains`/`icpp_file_diff`. |
| `icpp_exec_status` | `icpp_exec_status()` -> `int` | Exit code of the last `icpp_exec_capture` (which returns text, not a code). |
| `icpp_assert` | `icpp_assert(int cond, char *msg)` -> `int` | Prints `PASS:`/`FAIL:`, counts it. |
| `icpp_assert_eq_int` | `icpp_assert_eq_int(int actual, int expected, char *msg)` -> `int` | Same, printing **both** values on mismatch. |
| `icpp_assert_eq_str` | `icpp_assert_eq_str(char *actual, char *expected, char *msg)` -> `int` | Same, for strings. |
| `icpp_test_summary` | `icpp_test_summary()` -> `int` | Prints `=== N checks: P passed, F failed ===`; returns **F** (the failure count). |
| `icpp_exit` | `icpp_exit(int code)` -> void | Terminate immediately with that exit status. |

The idiomatic CI ending: `icpp_exit(icpp_test_summary());` — the process exit code becomes the
number of failed checks.

```c
int rc;
rc = icpp_exec_to_file("kcpp.exe -rtl design.icpp", "gen.log");
icpp_assert_eq_int(rc, 0, "RTL generated");
icpp_assert_eq_int(icpp_file_contains("gen.log", "** Error"), 0, "no errors");
icpp_exit(icpp_test_summary());
```

### 4.16 The `i*` shell utilities

grep/cat/cut/etc. without leaving ICPP. Every string-returning one allocates a result sized to
what it produced (no 4 KB ceiling), so `icat` of a 200 KB log returns 200 KB.

| Builtin | Signature | Description |
|---|---|---|
| `icat` | `icat(char *path)` -> `char *` | The whole file. |
| `ihead` | `ihead(char *path, int n)` -> `char *` | First `n` lines. |
| `itail` | `itail(char *path, int n)` -> `char *` | Last `n` lines (ring buffer — works on a multi-GB transcript). |
| `igrep` | `igrep(char *path, char *text)` -> `char *` | The lines containing `text`. |
| `igrep_count` | `igrep_count(char *path, char *text)` -> `int` | How many lines contain it. |
| `icut` | `icut(char *text, char *delim, int field)` -> `char *` | The 1-based `field` of every line. Takes **text** (not a path), so it composes: `icut(icat(f), ":", 3)`. |
| `ils` | `ils(char *dir)` -> `char *` | One entry per line; directories end in `/`. |
| `ifind` | `ifind(char *dir, char *pattern)` -> `char *` | Matching paths, searched recursively (`*` and `?`). |
| `ireplace` | `ireplace(char *path, char *from, char *to)` -> `int` | Byte-exact in-file replace; returns the count. |
| `ibash` | `ibash(char *cmdline)` -> `char *` | Runs the line under `bash -c`; returns its output. Needs a bash on PATH (Git for Windows/WSL/MSYS2); with none, the result is empty and `icpp_exec_status()` is −1. |
| `imake` | `imake(char *args)` -> `int` | Runs **kmake** (not nmake, not mingw32-make); build output streams to the console. Finds kmake via `$KMAKE_EXE`, then beside the running exe, then PATH. |
| `ikill` | `ikill(char *who)` -> `int` | Kill matching processes (`who` = pid or image name); returns how many. Terminates without cleanup, and by name kills every copy. |
| `irunning` | `irunning(char *who)` -> `int` | How many matching processes are alive. |
| `isleep` | `isleep(int ms)` -> `int` | Block for `ms` milliseconds. |
| `ised` | `ised(char *text, char *script)` -> `char *` | sed on a string — see [§4.3](#43-regex-and-sed). |
| `ised_file` | `ised_file(char *path, char *script)` -> `int` | sed on a file in place — see [§4.3](#43-regex-and-sed). |

(`iprint`, `ifprint`, `isprint` are in [§4.1](#41-output--string-building); the file-oriented
`i*` aliases such as `icat`'s cousins `ifile_read`/`iglob`/etc. are in
[§6](#6-the-i-aliases-for-filesfs-builtins).)

```c
if (icpp_strstr(itail("vsim.log", 20), "ALL PASSED") >= 0)
    iprint("passed with ", igrep_count("vsim.log", "Warning"), " warnings\n");
iprint(icut(igrep("parts.txt", "MRR"), ":", 3));   // 3rd colon field of matching lines
if (irunning("vsim.exe")) ikill("vsim.exe");
imake("makefile");
```

### 4.17 Threads and CPU affinity

Run several **external commands** at once and pin them to cores.

| Builtin | Signature | Description |
|---|---|---|
| `ithread_create` | `ithread_create(char *cmdline)` -> `int` | Start the command on a thread; returns a handle, or −1. |
| `ithread_join` | `ithread_join(int handle)` -> `int` | Wait; returns **that command's** exit code. |
| `ithread_barrier` | `ithread_barrier()` -> `int` | Wait for every outstanding thread; returns how many. |
| `ithread_affinity_set` | `ithread_affinity_set(int handle, int mask)` -> `int` | `handle>=0` pins that worker thread; `handle<0` sets the **process** affinity (which children inherit). |
| `CPU_ZERO` | `CPU_ZERO()` -> `int` | Return an empty CPU mask. |
| `CPU_SET` | `CPU_SET(int cpu, int mask)` -> `int` | **Return** `mask` with `cpu` added (ICPP has no output parameters, so the mask is returned). |
| `icpu_count` | `icpu_count()` -> `int` | Logical processor count, so a script can size its own fan-out. |

```c
int m; int a; int b;
m = CPU_ZERO(); m = CPU_SET(0, m); m = CPU_SET(1, m);
ithread_affinity_set(-1, m);               // children inherit PROCESS affinity
a = ithread_create("vsim.exe -c -do run_a.do");
b = ithread_create("vsim.exe -c -do run_b.do");
icpp_assert_eq_int(ithread_join(a), 0, "run A passed");
ithread_barrier();
```

> **A thread runs an external command, never an interpreted ICPP function.** The interpreter is
> not reentrant (a call writes shared state in the function prototype), so two threads inside
> the same ICPP function would corrupt each other's parameter addresses. Prefer `ithread_join`'s
> return value over `icpp_exec_status()` with threads (that status is a single global the
> threads race for). To pin a *tool* to cores, set the **process** affinity (`handle<0`) before
> launching it; a child inherits the process mask, not the creating thread's.

### 4.18 Images, OCR, fingerprints/AFIS

Two **independent** fingerprint matchers with non-comparable scales, plus perceptual image
hashing, cropping, and OCR.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_image_phash` | `icpp_image_phash(char *path)` -> `char *` | Perceptual hash of an image (decodes real pixels via stb_image). |
| `icpp_phash_similarity` | `icpp_phash_similarity(char *a, char *b)` -> `int` | Similarity (0–100-ish) between two perceptual hashes. |
| `icpp_image_crop` | `icpp_image_crop(char *in, int x, int y, int w, int h, char *out)` -> `int` | Crop a rectangle from `in` and write it to `out`; success 1/0. |
| `icpp_ocr_extract` | `icpp_ocr_extract(char *image_path)` -> `char *` | Extract text from an image (shells to the vendored Tesseract). |
| `icpp_file_fingerprint` | `icpp_file_fingerprint(char *path)` -> `char *` | Byte-level block fingerprint — see [§4.5](#45-files-and-file-inspection). |
| `icpp_fingerprint_similarity` | `icpp_fingerprint_similarity(char *a, char *b)` -> `int` | Percentage (0–100) of matching blocks between two `icpp_file_fingerprint` strings. |
| `icpp_fingerprint_match` | `icpp_fingerprint_match(char *scan_path, char *template_path)` -> `int` | Biometric fingerprint match via **NIST NBIS** (mindtct + bozorth3). |
| `icpp_afis_extract` | `icpp_afis_extract(char *image_path)` -> `int` (handle) | Extract minutiae with the in-tree **SourceAFIS-to-C** port; 0 = failure. Handle-based so 1:N identification extracts once and matches many. |
| `icpp_afis_minutiae` | `icpp_afis_minutiae(int handle)` -> `int` | Minutiae count; −1 = bad handle. |
| `icpp_afis_match` | `icpp_afis_match(int probe, int candidate)` -> `double` | Similarity score (the probe's edge hash is built on first use and cached on its handle). |
| `icpp_afis_match_files` | `icpp_afis_match_files(char *p1, char *p2)` -> `double` | One-shot convenience: extract both and match. |
| `icpp_afis_free` | `icpp_afis_free(int handle)` -> void | Free an AFIS handle. |

> The NBIS (`icpp_fingerprint_match`) and SourceAFIS (`icpp_afis_*`) scores are **not comparable
> to each other** — different algorithms, unrelated scales — but each separates genuine from
> impostor pairs on its own scale. Pick a threshold per matcher from your own data.

```c
int probe; int candidate;
probe = icpp_afis_extract("probe.png");
candidate = icpp_afis_extract("candidate.png");
if (icpp_afis_match(probe, candidate) >= 40.0) iprint("MATCH\n");
icpp_afis_free(probe);
icpp_afis_free(candidate);
```

### 4.19 Software graphics canvas

A handle-based RGB canvas rendered in software and saved as a real PNG (vendored
`stb_image_write`). Colors are `0xRRGGBB` ints (red `16711680`, green `65280`, blue `255`).

| Builtin | Signature | Description |
|---|---|---|
| `icpp_img_new` | `icpp_img_new(int w, int h)` -> `int` (handle) | New white canvas. |
| `icpp_img_set` | `icpp_img_set(int h, int x, int y, int rgb)` -> `int` | Set a pixel. |
| `icpp_img_get` | `icpp_img_get(int h, int x, int y)` -> `int` | Pixel color, −1 out of bounds. |
| `icpp_img_line` | `icpp_img_line(int h, int x0, int y0, int x1, int y1, int rgb)` -> `int` | Bresenham line. |
| `icpp_img_rect` | `icpp_img_rect(int h, int x, int y, int w, int ht, int rgb)` -> `int` | Rectangle outline. |
| `icpp_img_fill_rect` | `icpp_img_fill_rect(int h, int x, int y, int w, int ht, int rgb)` -> `int` | Filled rectangle. |
| `icpp_img_save_png` | `icpp_img_save_png(int h, char *path)` -> `int` | Write PNG; 1/0. |
| `icpp_img_free` | `icpp_img_free(int h)` -> void | Free the canvas. |

```c
int c;
c = icpp_img_new(64, 64);
icpp_img_fill_rect(c, 0, 0, 64, 64, 255);          // blue background
icpp_img_line(c, 0, 0, 63, 63, 16711680);          // red diagonal
icpp_img_save_png(c, "out.png");
icpp_img_free(c);
```

### 4.20 MCP client + JSON

ICPP is an MCP (Model Context Protocol) **client** over stdio: it spawns a tool server as a
child process and speaks newline-delimited JSON-RPC 2.0 over its stdin/stdout, with the
`initialize` handshake included.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_mcp_connect` | `icpp_mcp_connect(char *cmdline)` -> `int` (handle) | Spawn the server and perform the handshake; 0 = failure. A bare relative exe name resolves against the current directory. |
| `icpp_mcp_list_tools` | `icpp_mcp_list_tools(int h)` -> `char *` | The server's raw `tools/list` JSON — drill in with `icpp_json_get`. |
| `icpp_mcp_call` | `icpp_mcp_call(int h, char *tool, char *argsjson)` -> `char *` | Call a tool; returns `result.content[0].text` (or the raw response). If `argsjson` has no `"`, you may use single quotes — `"{'a':3,'b':4}"` — auto-converted. |
| `icpp_mcp_disconnect` | `icpp_mcp_disconnect(int h)` -> void | Close the server's stdin (EOF), wait, terminate a straggler. |
| `icpp_json_get` | `icpp_json_get(char *json, char *path)` -> `char *` | Extract by dotted path, e.g. `"result.tools.0.name"` (numeric segment = array index). Strings come back unescaped; objects/arrays come back as raw JSON for further drilling. Works on any JSON. |

```c
int h;
h = icpp_mcp_connect("test_mcp_server.exe");
iprint(icpp_json_get(icpp_mcp_list_tools(h), "result.tools.0.name"), "\n");  // add
iprint(icpp_mcp_call(h, "add", "{'a':3,'b':4}"), "\n");                       // 7
icpp_mcp_disconnect(h);
```

### 4.21 OpenAI-compatible chat

| Builtin | Signature | Description |
|---|---|---|
| `icpp_openai_chat` | `icpp_openai_chat(char *base_url, char *api_key, char *model, char *prompt)` -> `char *` | One-call chat completion: POSTs `{base_url}/v1/chat/completions` (WinHTTP handles TLS), returns the assistant reply (`choices[0].message.content`), or the raw error JSON, or `""` on transport failure. Works against `https://api.openai.com` (key required) and local OpenAI-compatible servers (Ollama, LM Studio, etc.). Empty key = no Authorization header. |

```c
iprint(icpp_openai_chat("http://localhost:11434", "", "llama3", "Say hi in one word"));
```

### 4.22 CGI parameter access

| Builtin | Signature | Description |
|---|---|---|
| `icpp_getparam` | `icpp_getparam(char *name)` -> `char *` | Read one parameter from a CGI-style `QUERY_STRING` (as set by `iwebserver` before running the interpreter), URL-decoded; `""` if not found or no query string. Used inside `<?icpp ... ?>` pages. |

```c
char *user;
user = icpp_getparam("user");                 // ?user=anis  ->  "anis"
iprint("<p>Hello, ", user, "</p>");
```

### 4.23 Tensor engine — core operations

A tensor is a 2D, row-major array of `double`, referenced by an opaque handle. All shapes are
`[rows, cols]`. Handles are freed with `tensor_free`.

| Builtin | Signature | Description |
|---|---|---|
| `tensor_new` | `tensor_new(int rows, int cols)` -> `int` (handle) | Zero-filled tensor. |
| `tensor_free` | `tensor_free(int handle)` -> void | Release. |
| `tensor_get` | `tensor_get(int handle, int row, int col)` -> `double` | Element read. |
| `tensor_set` | `tensor_set(int handle, int row, int col, double value)` -> `int` | Element write (1 = ok). |
| `tensor_rows` | `tensor_rows(int handle)` -> `int` | Row count. |
| `tensor_cols` | `tensor_cols(int handle)` -> `int` | Column count. |
| `tensor_init_spread` | `tensor_init_spread(int handle, int seed)` -> `int` | Deterministically fill with spread-out values from `seed` (a training-free initializer). |
| `tensor_matmul` | `tensor_matmul(int A, int B)` -> `int` (handle) | Matrix multiply; `A.cols` must equal `B.rows`. |
| `tensor_add` | `tensor_add(int A, int B)` -> `int` (handle) | Elementwise add (identical shapes, no broadcasting). |
| `tensor_sub` | `tensor_sub(int A, int B)` -> `int` (handle) | Elementwise subtract. |
| `tensor_scale` | `tensor_scale(int handle, double factor)` -> `int` (handle) | Elementwise scalar multiply. |
| `tensor_transpose` | `tensor_transpose(int handle)` -> `int` (handle) | `[r,c]` -> `[c,r]`. |
| `tensor_relu` | `tensor_relu(int handle)` -> `int` (handle) | Elementwise `max(0,x)`. |
| `tensor_softmax` | `tensor_softmax(int handle)` -> `int` (handle) | Row-wise, numerically stable. |
| `tensor_argmax` | `tensor_argmax(int handle, int row)` -> `int` | Column index of the largest value in that row ("which class"). |
| `tensor_linear` | `tensor_linear(int x, int weight, int bias)` -> `int` (handle) | `x @ weight + bias`, bias `[1,out]` broadcast down each row. The standard trainable projection layer. |

```c
int a; int b; int c;
a = tensor_new(2, 2); tensor_set(a, 0, 0, 1.0); tensor_set(a, 1, 1, 1.0);
b = tensor_new(2, 2); tensor_set(b, 0, 1, 2.0); tensor_set(b, 1, 0, 3.0);
c = tensor_matmul(a, b);
iprint("c[0][1]=", tensor_get(c, 0, 1), "\n");
tensor_free(a); tensor_free(b); tensor_free(c);
```

### 4.24 Tensor engine — neural-network / LLM layers

| Builtin | Signature | Description |
|---|---|---|
| `tensor_layernorm` | `tensor_layernorm(int x, int gamma, int beta, double eps)` -> `int` | Row-wise normalize to mean 0 / var 1, then scale/shift; `gamma`/`beta` are `[1,cols]`. |
| `tensor_embedding_lookup` | `tensor_embedding_lookup(int table, int ids)` -> `int` | `table` is `[vocab,d_model]`, `ids` is `[1,n]` of token IDs (doubles, rounded); gathers `n` rows. |
| `tensor_causal_mask` | `tensor_causal_mask(int scores)` -> `int` | Force upper-triangular (future) positions to a large negative number, for autoregressive attention. |
| `tensor_positional_encoding` | `tensor_positional_encoding(int seq_len, int d_model)` -> `int` | Fixed sinusoidal encoding, no learned parameters. |
| `tensor_multihead_attention` | `tensor_multihead_attention(int Q, int K, int V, int num_heads, int causal)` -> `int` | Real per-head column slicing; Q's row count may differ from K/V's (cross-attention). |
| `tensor_self_attention` | `tensor_self_attention(int x, int wq, int bq, int wk, int bk, int wv, int bv, int wo, int bo, int num_heads, int causal)` -> `int` | Full trainable layer: Q/K/V/output projections + attention, all from one input `x`. (11 args.) |
| `tensor_cross_attention` | `tensor_cross_attention(int query_x, int kv_x, int wq, int bq, int wk, int bk, int wv, int bv, int wo, int bo, int num_heads, int causal)` -> `int` | As above, but Q from `query_x` and K/V from a different sequence `kv_x`. (12 args.) |
| `tensor_tokenize` | `tensor_tokenize(char *text)` -> `int` | Byte-level: a `[1,strlen(text)]` tensor of raw byte values (vocab 256, no OOV). |
| `tensor_detokenize` | `tensor_detokenize(int handle)` -> `char *` | Exact inverse of `tensor_tokenize`. |
| `tensor_cross_entropy_loss` | `tensor_cross_entropy_loss(int logits, int targets)` -> `double` | `logits` `[seq_len,vocab]`, `targets` `[1,seq_len]`; returns the **mean** per-token loss. |

> **Scope note**: attention's *backward* pass (§4.25) is only mathematically correct for
> `num_heads=1`. Forward attention supports any `num_heads`; training a true multi-head model
> would need a native multi-head backward op.

### 4.25 Autodiff (backward / gradient rules)

This is **explicit backprop**, not an autodiff graph: save whatever forward tensors a backward
call needs, and call the backward functions yourself in reverse order. Each rule below was
verified by numerical gradient checking.

| Builtin | Signature | Gradient of |
|---|---|---|
| `tensor_relu_backward` | `tensor_relu_backward(int x, int dy)` -> `int` (dx) | `relu(x)`; needs the forward **input** `x`. |
| `tensor_softmax_backward` | `tensor_softmax_backward(int y, int dy)` -> `int` (dx) | `softmax(x)`; needs the forward **output** `y`. |
| `tensor_layernorm_backward_x` | `tensor_layernorm_backward_x(int x, int gamma, int dy, double eps)` -> `int` (dx) | layernorm input gradient. |
| `tensor_layernorm_backward_gamma` | `tensor_layernorm_backward_gamma(int x, int dy, double eps)` -> `int` (dgamma) | layernorm scale gradient. |
| `tensor_layernorm_backward_beta` | `tensor_layernorm_backward_beta(int dy)` -> `int` (dbeta) | Pure sum-over-rows; also reusable for any broadcast-bias gradient. |
| `tensor_embedding_lookup_backward` | `tensor_embedding_lookup_backward(int table, int ids, int dy)` -> `int` (dtable) | Scatter-**add** (a repeated token ID accumulates gradient from every occurrence). |
| `tensor_cross_entropy_loss_backward` | `tensor_cross_entropy_loss_backward(int logits, int targets)` -> `int` (dlogits) | `(softmax(logits) - one_hot(target)) / N`; no upstream gradient argument (always the last op). |

### 4.26 Optimizer & persistence

| Builtin | Signature | Description |
|---|---|---|
| `tensor_sgd_step` | `tensor_sgd_step(int param, int grad, double lr)` -> void | **Mutates `param` in place** — the one deliberate exception to the "always return a fresh handle" rule, because a parameter persists across thousands of steps (like a real `optimizer.step()`). There is no `zero_grad`; accumulate a mini-batch with ordinary `tensor_add` into a fresh (zero-filled) accumulator. |
| `tensor_save` | `tensor_save(int handle, char *path)` -> `int` | Persist a tensor as inspectable text (`ICPPTENSOR rows cols` + one `%.17g` value per line). This is how an LLM trained by one ICPP script is used by another. |
| `tensor_load` | `tensor_load(char *path)` -> `int` (handle) | Load a tensor saved with `tensor_save`. |

### 4.27 Text embedding & chatbot/RAG support

Native support for the customer-support chatbot library (`icpp_chatbot.c`, itself written with
ICPP's typeless `function`s). See also `icpp_file_line`/`icpp_file_lines`
([§4.5](#45-files-and-file-inspection)) and `icpp_openai_chat`
([§4.21](#421-openai-compatible-chat)), which the bot also uses.

| Builtin | Signature | Description |
|---|---|---|
| `icpp_text_embed` | `icpp_text_embed(char *text, int dim)` -> `int` (tensor `[1,dim]`) | Hashed character-bigram embedding (deterministic, training-free; lowercased, whitespace-collapsed). Use dim 256. |
| `tensor_cosine` | `tensor_cosine(int a, int b)` -> `double` | Cosine similarity of two same-shaped tensors — the ranking half of retrieval. |

```c
// simple RAG retrieval: pick the closest KB line to a question
int q; int best; int i; double s; double bestscore;
q = icpp_text_embed("what time do you open", 256);
best = -1; bestscore = -2.0;
for (i = 1; i <= icpp_file_lines("bot_kb.txt"); i = i + 1) {
    int e; e = icpp_text_embed(icpp_file_line("bot_kb.txt", i), 256);
    s = tensor_cosine(q, e);
    if (s > bestscore) { bestscore = s; best = i; }
    tensor_free(e);
}
iprint("bot: ", icpp_file_line("bot_kb.txt", best), "\n");
tensor_free(q);
```

### 4.28 Core C builtins

| Builtin | Signature | Description |
|---|---|---|
| `malloc` | `malloc(int size)` -> `void *` | Allocate `size` zero-initialized bytes (the returned block is zeroed). |
| `printf` | `printf(...)` -> void | See [§4.1](#41-output--string-building) — only `%d`/`%s` substitute; prefer `iprint`. |

---

## 5. The library / `#include` manifest system

An ICPP library is an ordinary `.icpp` file pulled in with `#include "libname.icpp"`. It may
carry a **manifest** in special `//@icpp-*` comment tags at the top, which the `icpp_lib_*`
builtins can query at runtime. This is how a script can gate on a library's version for
backward compatibility.

### Manifest tags

```c
//@icpp-library    libfixture
//@icpp-version     1.2.0
//@icpp-author      Muhammad Anisur Rahman
//@icpp-copyright   (c) 2026 Muhammad Anisur Rahman - MIT
//@icpp-contact     contact@chapai.ai
//@icpp-requires    0.40                ; minimum interpreter version
//@icpp-since       1.2.0: added fx_shout(); 1.1.0: initial   ; what's new
//---------------------------------------------------------------
char *fx_shout(char *s) { return icpp_upper(isprint(s, "!")); }
int   fx_double(int x)  { return x + x; }
```

Recognized tags: `//@icpp-library`, `//@icpp-version`, `//@icpp-author`, `//@icpp-copyright`,
`//@icpp-contact`, `//@icpp-requires`, `//@icpp-since`. Text after a `;` on a tag line is a
comment.

### Query builtins

| Builtin | Signature | Description |
|---|---|---|
| `icpp_lib_field` | `icpp_lib_field(char *file, char *field)` -> `char *` | Read one manifest field (e.g. `"library"`, `"author"`) from a file's header. |
| `icpp_lib_version` | `icpp_lib_version(char *file)` -> `char *` | Shortcut for the `version` field. |
| `icpp_version_cmp` | `icpp_version_cmp(char *a, char *b)` -> `int` | **Semantic** version compare: −1 / 0 / 1. `1.10.0 > 1.9.0` (unlike a string compare). |

```c
#include "libfixture.icpp"
// the included functions are callable:
icpp_assert_eq_str(fx_shout("hi"), "HI!", "included lib function");
icpp_assert_eq_int(fx_double(21), 42, "included lib function 2");
// query the manifest:
icpp_assert_eq_str(icpp_lib_version("libfixture.icpp"), "1.2.0", "lib_version");
icpp_assert_eq_str(icpp_lib_field("libfixture.icpp", "author"), "Muhammad Anisur Rahman", "author");
// backward-compat gate: require >= 1.2.0
icpp_assert_eq_int(icpp_version_cmp(icpp_lib_version("libfixture.icpp"), "1.2.0") >= 0, 1, "gate");
```

---

## 6. The `i*` aliases for Files/FS builtins

Every Files/Filesystem/sequential-read/env builtin is registered **twice**: under its
`icpp_*` name and under a short `i*` name. Both names dispatch to the identical implementation
(they share a function id), and the `icpp_*` names keep working — the aliases are purely
additive. Use whichever reads better.

| `icpp_*` name | `i*` alias |
|---|---|
| `icpp_fopen` | `ifopen` |
| `icpp_fclose` | `ifclose` |
| `icpp_fgets` | `ifgets` |
| `icpp_feof` | `ifeof` |
| `icpp_file_read` | `ifile_read` |
| `icpp_file_write` | `ifile_write` |
| `icpp_file_exists` | `ifile_exists` |
| `icpp_file_size` | `ifile_size` |
| `icpp_file_mtime` | `ifile_mtime` |
| `icpp_file_line` | `ifile_line` |
| `icpp_file_lines` | `ifile_lines` |
| `icpp_file_contains` | `ifile_contains` |
| `icpp_file_replace` | `ifile_replace` |
| `icpp_file_diff` | `ifile_diff` |
| `icpp_file_hash` | `ifile_hash` |
| `icpp_file_fingerprint` | `ifile_fingerprint` |
| `icpp_glob` | `iglob` |
| `icpp_glob_r` | `iglob_r` |
| `icpp_mkdir` | `imkdir` |
| `icpp_chdir` | `ichdir` |
| `icpp_cwd` | `icwd` |
| `icpp_remove` | `iremove` |
| `icpp_rename` | `irename` |
| `icpp_getenv` | `igetenv` |
| `icpp_setenv` | `isetenv` |

```c
ifile_write("conf_probe.txt", "line1\nline2\nline3\n");   // == icpp_file_write
iremove("conf_probe.txt");                                // == icpp_remove
```

(The `icpp_dict_*` names in [§4.12](#412-maps-and-dicts) are the same idea applied to the map
builtins — Python-familiar aliases sharing the map implementation.)

---

## 7. Shell redirection in command lines

Any command line passed to `icpp_exec` (and friends), `ithread_create`, etc. may use the
shell's own operators — `2>&1`, `>`, `>>`, `<`, `|`, `&`, `&&`. When one of these is present,
the command is handed to the command processor, so it means exactly what it means at a prompt:

```c
icpp_exec("vsim.exe -c -do run.do > sim.log 2>&1");   // merge stderr into the log
icpp_exec("vlog.exe design.v 2> errors.txt");         // stderr only
icpp_exec("vlog.exe design.v 2>/dev/null");           // discard stderr
icpp_exec("kcpp.exe -rtl d.icpp | findstr Error");    // pipes work too
```

Specifically supported redirection forms include `2>&1` (merge stderr into stdout) and
`2>/dev/null` / `2>NUL` (discard stderr). Without any operator, the program is started
**directly**, which is faster and keeps a bare relative program name resolving against the
current directory.

Note that `icpp_exec_capture` and `icpp_exec_to_file` **already** merge stderr into what they
collect, so `2>&1` is only needed there if you want the shell to do something *further* with
it. A shell builtin (`dir`, `echo`, `copy`) is not a program, so it needs the shell — give it
an operator, or spell it `cmd /c dir ...`.

---

## 8. Known issues

These are real, confirmed interpreter behaviors, each found via an actual failing repro, and
are tracked in `test/icpp_conformance/90_known_issues.icpp` (a file the runner treats as
expected-to-fail, so the suite stays green while the bugs stay visible).

### 8.1 Interpret-mode ternary `?:` — false branch

```c
t = (0 > 5) ? 10 : 20;   // WRONG: yields 10; correct is 20
```

In interpret mode the ternary operator emits codegen instead of interpreting, so it yields the
**true** value even when the condition is false. The function-return false-branch form can even
halt the interpreter. **Workaround**: use a plain `if`/`else`:

```c
int t;
if (0 > 5) t = 10; else t = 20;   // t == 20
```

### 8.2 Regex `\N` backreference escaping in `s///`

```c
r = icpp_re("cat", "s/(c)(a)(t)/\3\2\1/");   // WRONG: emits literal digits; correct is "tac"
```

The `\N` backreference in a `s///` **directive string** loses its backslash at the
string-literal/escaping layer, so `\3\2\1` reaches the engine as literal digits. The regex
engine's group expansion itself is correct; the loss happens before it. **Workarounds**: the
match form (`m/.../`) and capture-group **matching** work; for reliable capture-group
substitution prefer `ised`, whose own delimiter/escape handling expands `\1`..`\9` correctly:

```c
iprint(ised("cat", "s/\\(c\\)\\(a\\)\\(t\\)/\\3\\2\\1/"));   // (use ised for backref substitution)
```

### 8.3 A recursive call inside a `return` expression

```c
int fact(int n) { if (n <= 1) return 1; return n * fact(n - 1); }   // fact(5) is wrong
```

Evaluating a call to the same function **inside a `return` expression** clobbers the
already-resolved sibling operand. This is a long-standing interpreter bug affecting typed and
typeless functions alike. **Workaround** — compute through a local first (this is what the
conformance suite does):

```c
int fact(int n) { if (n <= 1) return 1; int r; r = n * fact(n - 1); return r; }
```

### 8.4 `$var` initialized from a function parameter

A `$var`'s initializer must be resolvable at parse time, so it cannot depend on a function
parameter (whose value is unknown until the call). Use a plain typed local — see
[§3](#3-language-reference).

### 8.5 Notes on things that are **fixed** (no workaround needed)

The following were historically buggy and are now fixed and regression-covered — listed only so
a future contributor knows the ground was covered: braceless `if`/`for`/`while` bodies and
standalone `{ }` blocks; `char *` locals assigned a string literal; and all `double` arithmetic,
parameters, and return values (including passing a `double` variable as an argument).

---

## 9. Worked examples

All examples draw on real conformance-suite / test syntax so they actually run.

### 9.1 A self-checking core-language test (`kcpp -icpp`)

```c
// core.icpp
int g_global;
int add(int a, int b) { return a + b; }
int for_sum(int n) { int i; int s; s = 0; for (i = 1; i <= n; i = i + 1) s = s + i; return s; }

void main()
{
    int x; int y; int rc;
    icpp_assert_eq_int(2 + 3, 5, "add");
    icpp_assert_eq_int(17 % 5, 2, "mod");
    x = 10; y = 3;
    if (x > y) icpp_assert_eq_int(1, 1, "gt"); else icpp_assert_eq_int(0, 1, "gt");
    icpp_assert_eq_int(add(add(1, 2), add(3, 4)), 10, "nested calls");
    icpp_assert_eq_int(for_sum(10), 55, "for");
    g_global = 77;
    icpp_assert_eq_int(g_global, 77, "global");
    x = 5; x++;
    icpp_assert_eq_int(x, 6, "post-increment");
    rc = icpp_test_summary();
    icpp_exit(rc);                 // exit code = number of failed checks
}
```

### 9.2 A dynamic web page (`<?icpp ?>` + `iwebserver`)

```html
<!-- greet.icpp.html -->
<html><body>
<h1>Greeter</h1>
<?icpp
    char *name;
    name = icpp_getparam("name");
    if (icpp_strlen(name) == 0) name = "stranger";
    iprint("<p>Hello, ", name, "!</p>");
    iprint("<p>Server time: ", icpp_date_string(), "</p>");
?>
</body></html>
```

Request `greet.icpp.html?name=Anis` and the page renders "Hello, Anis!" with the current time.

### 9.3 A data-processing script (parse a simulation transcript)

```c
// scan.icpp - the kind of job normally written in Perl
void main()
{
    int fields; int kv; int seen; int i; int val; int rc;
    seen = icpp_map_new();
    for (i = 1; i <= icpp_file_lines("sim.log"); i = i + 1) {
        // "# 100 ns SIG=top.dut.addr VAL=deadbeef"
        fields = icpp_split(icpp_file_line("sim.log", i), " ");
        if (icpp_list_count(fields) >= 5) {
            kv = icpp_split(icpp_list_get(fields, 4), "=");   // VAL=deadbeef
            if (icpp_list_count(kv) == 2)
                icpp_map_set(seen, "addr", icpp_list_get(kv, 1));
            icpp_list_free(kv);
        }
        icpp_list_free(fields);
    }
    val = icpp_parse_hex(icpp_map_get(seen, "addr"));         // 0xdeadbeef
    iprint("upper nibble = ", icpp_hex(icpp_bits(val, 31, 28), 1), "\n");   // d
    icpp_map_free(seen);
    rc = icpp_test_summary();
    icpp_exit(rc);
}
```

### 9.4 A tiny tensor / ML snippet (forward + backward + SGD step)

```c
// one training step of a single linear layer + cross-entropy
void main()
{
    int x; int w; int b; int logits; int targets; int dlogits; int dw; int db;
    double loss;

    x = tensor_new(1, 3); tensor_set(x, 0, 0, 1.0); tensor_set(x, 0, 1, 0.5); tensor_set(x, 0, 2, -1.0);
    w = tensor_new(3, 2); tensor_init_spread(w, 1);
    b = tensor_new(1, 2);
    targets = tensor_new(1, 1); tensor_set(targets, 0, 0, 1.0);   // class 1

    logits = tensor_linear(x, w, b);
    loss   = tensor_cross_entropy_loss(logits, targets);
    iprint("loss=", loss, "\n");

    dlogits = tensor_cross_entropy_loss_backward(logits, targets);
    dw = tensor_linear_backward_w(x, w, dlogits);   // from icpp_autodiff.c
    db = tensor_linear_backward_b(dlogits);          // from icpp_autodiff.c
    tensor_sgd_step(w, dw, 0.05);
    tensor_sgd_step(b, db, 0.05);

    tensor_free(x); tensor_free(w); tensor_free(b); tensor_free(targets);
    tensor_free(logits); tensor_free(dlogits); tensor_free(dw); tensor_free(db);
}
```

(`tensor_linear_backward_w` / `tensor_linear_backward_b` are script-composed helpers in the
reusable `icpp_autodiff.c` library — `#include "icpp_autodiff.c"` — built from the native
backward primitives in [§4.25](#425-autodiff-backward--gradient-rules). The full tested MLP and
transformer training loops live in `test/PhaseA/dnn_mlp_check.c` and
`test/PhaseA/training_loop_check.c`.)

### 9.5 A regex / string example

```c
void main()
{
    int rc;
    // match: pull the first run of digits
    icpp_assert_eq_str(icpp_re("order#4821 shipped", "m/[0-9]+/"), "4821", "match digits");
    // string transforms
    icpp_assert_eq_str(icpp_upper(icpp_trim("  hello  ")), "HELLO", "trim+upper");
    icpp_assert_eq_str(icpp_replace_all("a.b.c", ".", "/"), "a/b/c", "replace_all");
    // sed on a string, global
    icpp_assert_eq_str(ised("x-y-z", "s/-/_/g"), "x_y_z", "sed global");
    rc = icpp_test_summary();
    icpp_exit(rc);
}
```

---

## Appendix A — complete alphabetical builtin index (all 225)

Every registered builtin, with its argument count (`va` = variadic) and return type, and the
section that documents it. `H` = handle (registered as `int`).

| # | Builtin | Args | Returns | Section |
|---|---|---|---|---|
| 1 | `CPU_SET` | 2 | int | 4.17 |
| 2 | `CPU_ZERO` | 0 | int | 4.17 |
| 3 | `ibash` | 1 | char * | 4.16 |
| 4 | `icat` | 1 | char * | 4.16 |
| 5 | `ichdir` | 1 | int | 4.6 / 6 |
| 6 | `icpp_afis_extract` | 1 | int (H) | 4.18 |
| 7 | `icpp_afis_free` | 1 | void | 4.18 |
| 8 | `icpp_afis_match` | 2 | double | 4.18 |
| 9 | `icpp_afis_match_files` | 2 | double | 4.18 |
| 10 | `icpp_afis_minutiae` | 1 | int | 4.18 |
| 11 | `icpp_assert` | 2 | int | 4.15 |
| 12 | `icpp_assert_eq_int` | 3 | int | 4.15 |
| 13 | `icpp_assert_eq_str` | 3 | int | 4.15 |
| 14 | `icpp_atoi` | 1 | int | 4.2 / 4.10 |
| 15 | `icpp_bin` | 2 | char * | 4.10 |
| 16 | `icpp_bin_find` | 2 | int | 4.9 |
| 17 | `icpp_bin_float` | 2 | double | 4.9 |
| 18 | `icpp_bin_free` | 1 | int | 4.9 |
| 19 | `icpp_bin_get` | 2 | int | 4.9 |
| 20 | `icpp_bin_int32` | 2 | int | 4.9 |
| 21 | `icpp_bin_read` | 1 | int (H) | 4.9 |
| 22 | `icpp_bin_size` | 1 | int | 4.9 |
| 23 | `icpp_bits` | 3 | int | 4.10 |
| 24 | `icpp_chdir` | 1 | int | 4.6 |
| 25 | `icpp_concat` | va | char * | 4.1 |
| 26 | `icpp_cwd` | 0 | char * | 4.6 |
| 27 | `icpp_date_string` | 0 | char * | 4.8 |
| 28 | `icpp_dict_count` | 1 | int | 4.12 |
| 29 | `icpp_dict_delete` | 2 | int | 4.12 |
| 30 | `icpp_dict_free` | 1 | void | 4.12 |
| 31 | `icpp_dict_get` | 2 | char * | 4.12 |
| 32 | `icpp_dict_has` | 2 | int | 4.12 |
| 33 | `icpp_dict_keys` | 1 | int (H) | 4.12 |
| 34 | `icpp_dict_new` | 0 | int (H) | 4.12 |
| 35 | `icpp_dict_set` | 3 | int | 4.12 |
| 36 | `icpp_elapsed_ms` | 0 | int | 4.8 |
| 37 | `icpp_ends_with` | 2 | int | 4.2 |
| 38 | `icpp_exec` | 1 | int | 4.15 |
| 39 | `icpp_exec_capture` | 1 | char * | 4.15 |
| 40 | `icpp_exec_status` | 0 | int | 4.15 |
| 41 | `icpp_exec_to_file` | 2 | int | 4.15 |
| 42 | `icpp_exit` | 1 | void | 4.15 |
| 43 | `icpp_fclose` | 1 | void | 4.7 |
| 44 | `icpp_feof` | 1 | int | 4.7 |
| 45 | `icpp_fgets` | 1 | char * | 4.7 |
| 46 | `icpp_file_contains` | 2 | int | 4.5 |
| 47 | `icpp_file_diff` | 2 | int | 4.5 |
| 48 | `icpp_file_exists` | 1 | int | 4.5 |
| 49 | `icpp_file_fingerprint` | 1 | char * | 4.5 / 4.18 |
| 50 | `icpp_file_hash` | 1 | char * | 4.5 |
| 51 | `icpp_file_line` | 2 | char * | 4.5 |
| 52 | `icpp_file_lines` | 1 | int | 4.5 |
| 53 | `icpp_file_mtime` | 1 | int | 4.5 |
| 54 | `icpp_file_read` | 1 | char * | 4.5 |
| 55 | `icpp_file_replace` | 3 | int | 4.5 |
| 56 | `icpp_file_size` | 1 | int | 4.5 |
| 57 | `icpp_file_write` | 2 | int | 4.5 |
| 58 | `icpp_fingerprint_match` | 2 | int | 4.18 |
| 59 | `icpp_fingerprint_similarity` | 2 | int | 4.18 |
| 60 | `icpp_fopen` | 1 | int (H) | 4.7 |
| 61 | `icpp_getenv` | 1 | char * | 4.8 |
| 62 | `icpp_getparam` | 1 | char * | 4.22 |
| 63 | `icpp_glob` | 1 | int (H) | 4.6 |
| 64 | `icpp_glob_r` | 2 | int (H) | 4.6 |
| 65 | `icpp_hex` | 2 | char * | 4.10 |
| 66 | `icpp_image_crop` | 6 | int | 4.18 |
| 67 | `icpp_image_phash` | 1 | char * | 4.18 |
| 68 | `icpp_img_fill_rect` | 6 | int | 4.19 |
| 69 | `icpp_img_free` | 1 | void | 4.19 |
| 70 | `icpp_img_get` | 3 | int | 4.19 |
| 71 | `icpp_img_line` | 6 | int | 4.19 |
| 72 | `icpp_img_new` | 2 | int (H) | 4.19 |
| 73 | `icpp_img_rect` | 6 | int | 4.19 |
| 74 | `icpp_img_save_png` | 2 | int | 4.19 |
| 75 | `icpp_img_set` | 4 | int | 4.19 |
| 76 | `icpp_join` | 2 | char * | 4.11 |
| 77 | `icpp_json_get` | 2 | char * | 4.20 |
| 78 | `icpp_lib_field` | 2 | char * | 5 |
| 79 | `icpp_lib_version` | 1 | char * | 5 |
| 80 | `icpp_list_add` | 2 | int | 4.11 |
| 81 | `icpp_list_count` | 1 | int | 4.11 |
| 82 | `icpp_list_free` | 1 | void | 4.11 |
| 83 | `icpp_list_get` | 2 | char * | 4.11 |
| 84 | `icpp_list_new` | 0 | int (H) | 4.11 |
| 85 | `icpp_list_sort` | 1 | int | 4.11 |
| 86 | `icpp_lower` | 1 | char * | 4.2 |
| 87 | `icpp_map_count` | 1 | int | 4.12 |
| 88 | `icpp_map_delete` | 2 | int | 4.12 |
| 89 | `icpp_map_free` | 1 | void | 4.12 |
| 90 | `icpp_map_get` | 2 | char * | 4.12 |
| 91 | `icpp_map_has` | 2 | int | 4.12 |
| 92 | `icpp_map_keys` | 1 | int (H) | 4.12 |
| 93 | `icpp_map_new` | 0 | int (H) | 4.12 |
| 94 | `icpp_map_set` | 3 | int | 4.12 |
| 95 | `icpp_mcp_call` | 3 | char * | 4.20 |
| 96 | `icpp_mcp_connect` | 1 | int (H) | 4.20 |
| 97 | `icpp_mcp_disconnect` | 1 | void | 4.20 |
| 98 | `icpp_mcp_list_tools` | 1 | char * | 4.20 |
| 99 | `icpp_mkdir` | 1 | int | 4.6 |
| 100 | `icpp_ocr_extract` | 1 | char * | 4.18 |
| 101 | `icpp_openai_chat` | 4 | char * | 4.21 |
| 102 | `icpp_parse_hex` | 1 | int | 4.10 |
| 103 | `icpp_phash_similarity` | 2 | int | 4.18 |
| 104 | `icpp_re` | 2 | char * | 4.3 |
| 105 | `icpp_remove` | 1 | int | 4.6 |
| 106 | `icpp_rename` | 2 | int | 4.6 |
| 107 | `icpp_replace_all` | 3 | char * | 4.2 |
| 108 | `icpp_set_add` | 2 | int | 4.13 |
| 109 | `icpp_set_count` | 1 | int | 4.13 |
| 110 | `icpp_set_free` | 1 | void | 4.13 |
| 111 | `icpp_set_has` | 2 | int | 4.13 |
| 112 | `icpp_set_items` | 1 | int (H) | 4.13 |
| 113 | `icpp_set_new` | 0 | int (H) | 4.13 |
| 114 | `icpp_set_remove` | 2 | int | 4.13 |
| 115 | `icpp_setenv` | 2 | int | 4.8 |
| 116 | `icpp_split` | 2 | int (H) | 4.11 |
| 117 | `icpp_sql_connect` | 1 | int (H) | 4.4 |
| 118 | `icpp_sql_count` | 1 | int | 4.4 |
| 119 | `icpp_sql_disconnect` | 1 | void | 4.4 |
| 120 | `icpp_sql_free` | 1 | void | 4.4 |
| 121 | `icpp_sql_get` | 2 | char * | 4.4 |
| 122 | `icpp_sql_next` | 1 | int | 4.4 |
| 123 | `icpp_sql_query` | 2 | int (H) | 4.4 |
| 124 | `icpp_sql_status` | 1 | char * | 4.4 |
| 125 | `icpp_starts_with` | 2 | int | 4.2 |
| 126 | `icpp_strcmp` | 2 | int | 4.2 |
| 127 | `icpp_strlen` | 1 | int | 4.2 |
| 128 | `icpp_strstr` | 2 | int | 4.2 |
| 129 | `icpp_substr` | 3 | char * | 4.2 |
| 130 | `icpp_test_summary` | 0 | int | 4.15 |
| 131 | `icpp_text_embed` | 2 | int (H) | 4.27 |
| 132 | `icpp_time` | 0 | int | 4.8 |
| 133 | `icpp_trim` | 1 | char * | 4.2 |
| 134 | `icpp_tuple_add` | 2 | int | 4.14 |
| 135 | `icpp_tuple_count` | 1 | int | 4.14 |
| 136 | `icpp_tuple_free` | 1 | void | 4.14 |
| 137 | `icpp_tuple_get` | 2 | char * | 4.14 |
| 138 | `icpp_tuple_new` | 0 | int (H) | 4.14 |
| 139 | `icpp_upper` | 1 | char * | 4.2 |
| 140 | `icpp_version_cmp` | 2 | int | 5 |
| 141 | `icpu_count` | 0 | int | 4.17 |
| 142 | `icut` | 3 | char * | 4.16 |
| 143 | `icwd` | 0 | char * | 4.6 / 6 |
| 144 | `ifclose` | 1 | void | 4.7 / 6 |
| 145 | `ifeof` | 1 | int | 4.7 / 6 |
| 146 | `ifgets` | 1 | char * | 4.7 / 6 |
| 147 | `ifile_contains` | 2 | int | 6 |
| 148 | `ifile_diff` | 2 | int | 6 |
| 149 | `ifile_exists` | 1 | int | 6 |
| 150 | `ifile_fingerprint` | 1 | char * | 6 |
| 151 | `ifile_hash` | 1 | char * | 6 |
| 152 | `ifile_line` | 2 | char * | 6 |
| 153 | `ifile_lines` | 1 | int | 6 |
| 154 | `ifile_mtime` | 1 | int | 6 |
| 155 | `ifile_read` | 1 | char * | 6 |
| 156 | `ifile_replace` | 3 | int | 6 |
| 157 | `ifile_size` | 1 | int | 6 |
| 158 | `ifile_write` | 2 | int | 6 |
| 159 | `ifind` | 2 | char * | 4.16 |
| 160 | `ifopen` | 1 | int (H) | 4.7 / 6 |
| 161 | `ifprint` | va | void | 4.1 |
| 162 | `igetenv` | 1 | char * | 4.8 / 6 |
| 163 | `iglob` | 1 | int (H) | 6 |
| 164 | `iglob_r` | 2 | int (H) | 6 |
| 165 | `igrep` | 2 | char * | 4.16 |
| 166 | `igrep_count` | 2 | int | 4.16 |
| 167 | `ihead` | 2 | char * | 4.16 |
| 168 | `ikill` | 1 | int | 4.16 |
| 169 | `ils` | 1 | char * | 4.16 |
| 170 | `imake` | 1 | int | 4.16 |
| 171 | `imkdir` | 1 | int | 6 |
| 172 | `iprint` | va | void | 4.1 |
| 173 | `iremove` | 1 | int | 6 |
| 174 | `irename` | 2 | int | 6 |
| 175 | `ireplace` | 3 | int | 4.16 |
| 176 | `irunning` | 1 | int | 4.16 |
| 177 | `ised` | 2 | char * | 4.3 / 4.16 |
| 178 | `ised_file` | 2 | int | 4.3 / 4.16 |
| 179 | `isetenv` | 2 | int | 4.8 / 6 |
| 180 | `isleep` | 1 | int | 4.16 / 4.17 |
| 181 | `isprint` | va | char * | 4.1 |
| 182 | `itail` | 2 | char * | 4.16 |
| 183 | `ithread_affinity_set` | 2 | int | 4.17 |
| 184 | `ithread_barrier` | 0 | int | 4.17 |
| 185 | `ithread_create` | 1 | int | 4.17 |
| 186 | `ithread_join` | 1 | int | 4.17 |
| 187 | `malloc` | 1 | void * | 4.28 |
| 188 | `printf` | va | void | 4.1 / 4.28 |
| 189 | `tensor_add` | 2 | int (H) | 4.23 |
| 190 | `tensor_argmax` | 2 | int | 4.23 |
| 191 | `tensor_causal_mask` | 1 | int (H) | 4.24 |
| 192 | `tensor_cols` | 1 | int | 4.23 |
| 193 | `tensor_cosine` | 2 | double | 4.27 |
| 194 | `tensor_cross_attention` | 12 | int (H) | 4.24 |
| 195 | `tensor_cross_entropy_loss` | 2 | double | 4.24 |
| 196 | `tensor_cross_entropy_loss_backward` | 2 | int (H) | 4.25 |
| 197 | `tensor_detokenize` | 1 | char * | 4.24 |
| 198 | `tensor_embedding_lookup` | 2 | int (H) | 4.24 |
| 199 | `tensor_embedding_lookup_backward` | 3 | int (H) | 4.25 |
| 200 | `tensor_free` | 1 | void | 4.23 |
| 201 | `tensor_get` | 3 | double | 4.23 |
| 202 | `tensor_init_spread` | 2 | int | 4.23 |
| 203 | `tensor_layernorm` | 4 | int (H) | 4.24 |
| 204 | `tensor_layernorm_backward_beta` | 1 | int (H) | 4.25 |
| 205 | `tensor_layernorm_backward_gamma` | 3 | int (H) | 4.25 |
| 206 | `tensor_layernorm_backward_x` | 4 | int (H) | 4.25 |
| 207 | `tensor_linear` | 3 | int (H) | 4.23 |
| 208 | `tensor_load` | 1 | int (H) | 4.26 |
| 209 | `tensor_matmul` | 2 | int (H) | 4.23 |
| 210 | `tensor_multihead_attention` | 5 | int (H) | 4.24 |
| 211 | `tensor_new` | 2 | int (H) | 4.23 |
| 212 | `tensor_positional_encoding` | 2 | int (H) | 4.24 |
| 213 | `tensor_relu` | 1 | int (H) | 4.23 |
| 214 | `tensor_relu_backward` | 2 | int (H) | 4.25 |
| 215 | `tensor_rows` | 1 | int | 4.23 |
| 216 | `tensor_save` | 2 | int | 4.26 |
| 217 | `tensor_scale` | 2 | int (H) | 4.23 |
| 218 | `tensor_self_attention` | 11 | int (H) | 4.24 |
| 219 | `tensor_set` | 4 | int | 4.23 |
| 220 | `tensor_sgd_step` | 3 | void | 4.26 |
| 221 | `tensor_softmax` | 1 | int (H) | 4.23 |
| 222 | `tensor_softmax_backward` | 2 | int (H) | 4.25 |
| 223 | `tensor_sub` | 2 | int (H) | 4.23 |
| 224 | `tensor_tokenize` | 1 | int (H) | 4.24 |
| 225 | `tensor_transpose` | 1 | int (H) | 4.23 |

---

*End of the ICPP User Manual. Copyright (c) 2026 Muhammad Anisur Rahman — MIT License.*
