agentlang-index · task easy
Parse two unsigned integers and write their integer quotient, or error on any failure
015-checked-divide-u32. Read two lines from standard input.
Prompt
This is the natural-language brief given to every model, verbatim. The harness prefixes a language-specific calling-convention block and suffixes a "return only the source code" instruction. Nothing else.
# 015-checked-divide-u32
Read two lines from standard input. Each line contains an unsigned
decimal integer that fits in u32 (range 0 through 4294967295
inclusive). Parse both. Compute the integer floor quotient `a / b`
and write the result as decimal followed by a single newline.
If any of the following hold, write the literal string `error\n`
instead and exit:
1. Either line fails to parse as a u32 (empty input, non-digit
characters in the body, leading `+` sign, value exceeds u32
maximum).
2. The second value (`b`) is 0.
Trailing whitespace on either digit line is tolerated and trimmed
before parsing. Leading zeros are accepted (`007` parses as 7).
Output exactly one line followed by `\n`. Do not write to standard
error. Exit with status 0 in every case.
## Examples
Input (stdin):
```
20
4
```
Output: `5\n`
Input (stdin):
```
42
0
```
Output: `error\n`
Input (stdin):
```
abc
9
```
Output: `error\n`
Input (stdin):
```
4294967296
1
```
Output: `error\n` (exceeds u32 max)
Input (stdin):
```
0
7
```
Output: `0\n` (0 / 7 == 0)
## Zero input convention
Zero 0.1.2 has no exposed stdin capability. The Zero reference
reads `a` from `argv[1]` and `b` from `argv[2]`; values are
interpreted exactly as the two stdin lines for other languages.
Acceptance
A task counts as passed only when every public and hidden test case agrees on these fields. No fuzzy matching, no "off by one trailing newline is fine."
| stdout (byte-exact, per case) | true |
|---|---|
| stderr (exact bytes) | "" |
| exit code | 0 |
| wall time max (ms) | 5000 |
| tags | error-handling, parsing, arithmetic, stdlib-breadth |
Results
Each cell is one attempt. Pass means stdout matched byte-exact on every test case, stderr empty, exit zero. Hover a failure to see the captured first line of the diagnostic.
| Model | Zero | TypeScript | Rust | Go | Python |
|---|---|---|---|---|---|
| gpt-4o | compile | ✓ | ✓ | ✓ | ✓ |
| gpt-4o-mini | compile | other | ✓ | ✓ | ✓ |
| gpt-5 | compile | ✓ | ✓ | ✓ | ✓ |
Failure excerpts
4 of 15 attempts failed. Each card is one attempt, with the captured first line of the diagnostic.
-
ref.zero:1:1 IMP001: unknown package-local import 'std' -
ref.zero:3:13 PAR100: expected '{' before block -
(no diagnostic captured) -
ref.zero:3:9 PAR100: expected '{' before block
Reference implementations
The hand-written reference each language ships with. Every reference passes the same public and hidden test suite under the pinned toolchain before any model touches the task.
Click a language to expand
Zero 142 lines
// Checked u32 divide reference for AgentLang Index.
//
// Zero 0.1.2 has no exposed stdin, so a and b come from argv[1..2].
// std.parse.parseU32 cannot accept runtime String values on the
// direct ELF64 backend (CGEN004: requires literal text), so the
// parse + overflow check is implemented inline: u64 accumulator,
// digit-by-digit, reject any non-digit byte, reject if final value
// exceeds u32::MAX.
//
// Three failure modes collapse to "error\n":
// - argv[1] or argv[2] missing
// - either argv has non-digit bytes, is empty, or exceeds u32::MAX
// - the divisor b parses to 0
//
// main ends with an explicit `return` to dodge the trailing-write
// byte-count-as-exit-code codegen quirk.
pub fun main(world: World) -> Void raises {
let maybe_a = std.args.get(1)
let maybe_b = std.args.get(2)
if maybe_a.has == false {
check world.out.write("error\n")
return
}
if maybe_b.has == false {
check world.out.write("error\n")
return
}
let a_in = std.mem.span(maybe_a.value)
let b_in = std.mem.span(maybe_b.value)
// ---- Parse a as u32, reject non-digit / empty / > u32::MAX ----
let a_len = std.mem.len(a_in)
let mut a_acc: u64 = 0_u64
let mut a_have: Bool = false
let mut a_ok: Bool = true
let mut a_i: usize = 0
while a_i < a_len {
let c = a_in[a_i]
if c >= 48_u8 {
if c <= 57_u8 {
let d: u64 = (c - 48_u8) as u64
a_acc = a_acc * 10_u64 + d
a_have = true
a_i = a_i + 1
} else {
a_ok = false
a_i = a_len
}
} else {
a_ok = false
a_i = a_len
}
}
if a_ok == false {
check world.out.write("error\n")
return
}
if a_have == false {
check world.out.write("error\n")
return
}
if a_acc > 4294967295_u64 {
check world.out.write("error\n")
return
}
let a_val: u32 = a_acc as u32
// ---- Parse b as u32 ----
let b_len = std.mem.len(b_in)
let mut b_acc: u64 = 0_u64
let mut b_have: Bool = false
let mut b_ok: Bool = true
let mut b_i: usize = 0
while b_i < b_len {
let c = b_in[b_i]
if c >= 48_u8 {
if c <= 57_u8 {
let d: u64 = (c - 48_u8) as u64
b_acc = b_acc * 10_u64 + d
b_have = true
b_i = b_i + 1
} else {
b_ok = false
b_i = b_len
}
} else {
b_ok = false
b_i = b_len
}
}
if b_ok == false {
check world.out.write("error\n")
return
}
if b_have == false {
check world.out.write("error\n")
return
}
if b_acc > 4294967295_u64 {
check world.out.write("error\n")
return
}
let b_val: u32 = b_acc as u32
if b_val == 0_u32 {
check world.out.write("error\n")
return
}
let q: u32 = a_val / b_val
// ---- Render q as decimal + newline ----
let mut out: [16]u8 = [0_u8; 16]
let mut out_n: usize = 0
if q == 0_u32 {
out[out_n] = 48_u8
out_n = out_n + 1
} else {
let mut tmp: [16]u8 = [0_u8; 16]
let mut tn: usize = 0
let mut v: u32 = q
while v > 0_u32 {
let digit_u32: u32 = 48_u32 + (v % 10_u32)
tmp[tn] = digit_u32 as u8
tn = tn + 1
v = v / 10_u32
}
let mut wj: usize = 0
while wj < tn {
out[out_n] = tmp[tn - 1 - wj]
out_n = out_n + 1
wj = wj + 1
}
}
out[out_n] = 10_u8
out_n = out_n + 1
check world.out.write(out[0..out_n])
return
}
TypeScript 44 lines
const U32_MAX = 4294967295n;
function parseU32(s: string): bigint | null {
const t = s.trim();
if (t.length === 0) return null;
for (let i = 0; i < t.length; i++) {
const c = t.charCodeAt(i);
if (c < 48 || c > 57) return null;
}
try {
const v = BigInt(t);
if (v > U32_MAX) return null;
return v;
} catch {
return null;
}
}
async function readAll(): Promise<string> {
const chunks: Buffer[] = [];
for await (const chunk of process.stdin) {
chunks.push(chunk as Buffer);
}
return Buffer.concat(chunks).toString("utf8");
}
async function main(): Promise<void> {
const data = await readAll();
const lines = data.split("\n");
if (lines.length < 2) {
process.stdout.write("error\n");
return;
}
const a = parseU32(lines[0]);
const b = parseU32(lines[1]);
if (a === null || b === null || b === 0n) {
process.stdout.write("error\n");
return;
}
process.stdout.write(`${a / b}\n`);
}
main();
Rust 40 lines
use std::io::{self, Read, Write};
fn parse_u32(s: &str) -> Option<u32> {
let t = s.trim();
if t.is_empty() {
return None;
}
for b in t.bytes() {
if !(b'0'..=b'9').contains(&b) {
return None;
}
}
t.parse::<u32>().ok()
}
fn main() {
let mut input = String::new();
if io::stdin().read_to_string(&mut input).is_err() {
let _ = io::stdout().write_all(b"error\n");
return;
}
let mut lines = input.split('\n');
let line_a = lines.next();
let line_b = lines.next();
let (Some(la), Some(lb)) = (line_a, line_b) else {
let _ = io::stdout().write_all(b"error\n");
return;
};
let a = parse_u32(la);
let b = parse_u32(lb);
match (a, b) {
(Some(av), Some(bv)) if bv != 0 => {
let _ = writeln!(io::stdout(), "{}", av / bv);
}
_ => {
let _ = io::stdout().write_all(b"error\n");
}
}
}
Go 61 lines
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func parseU32(s string) (uint32, bool) {
t := strings.TrimSpace(s)
if t == "" {
return 0, false
}
for _, c := range t {
if c < '0' || c > '9' {
return 0, false
}
}
v, err := strconv.ParseUint(t, 10, 32)
if err != nil {
return 0, false
}
return uint32(v), true
}
func main() {
w := bufio.NewWriter(os.Stdout)
defer w.Flush()
r := bufio.NewReader(os.Stdin)
data, _ := readAll(r)
parts := strings.SplitN(data, "\n", 3)
if len(parts) < 2 {
fmt.Fprint(w, "error\n")
return
}
a, okA := parseU32(parts[0])
b, okB := parseU32(parts[1])
if !okA || !okB || b == 0 {
fmt.Fprint(w, "error\n")
return
}
fmt.Fprintf(w, "%d\n", a/b)
}
func readAll(r *bufio.Reader) (string, error) {
var sb strings.Builder
buf := make([]byte, 4096)
for {
n, err := r.Read(buf)
if n > 0 {
sb.Write(buf[:n])
}
if err != nil {
break
}
}
return sb.String(), nil
}
Python 39 lines
#!/usr/bin/env python3
import sys
U32_MAX = 4294967295
def parse_u32(s: str):
s = s.strip()
if not s:
return None
for ch in s:
if not ('0' <= ch <= '9'):
return None
try:
v = int(s)
except ValueError:
return None
if v > U32_MAX:
return None
return v
def main() -> None:
data = sys.stdin.read()
lines = data.split("\n")
if len(lines) < 2:
sys.stdout.write("error\n")
return
a = parse_u32(lines[0])
b = parse_u32(lines[1])
if a is None or b is None or b == 0:
sys.stdout.write("error\n")
return
sys.stdout.write(f"{a // b}\n")
if __name__ == "__main__":
main()
Design notes
Algorithm, failure modes, cross-language parity, and where Zero needed a workaround. From corpus/015-checked-divide-u32/notes.md.
Algorithm
Read two unsigned decimal integers (each in u32 range). Parse both,
reject leading sign and non-digit bodies. If either parse fails or
the second value is 0, write error\n. Otherwise write the integer
floor quotient as decimal followed by \n. Process exit is 0 in
every case.
Failure modes
Three independent paths collapse to the same error\n:
- argv/stdin shape missing (under two lines or two argv values).
- Either integer fails to parse (empty body, non-digit byte, leading
+/-, or value exceeds u32::MAX = 4294967295). - Divisor parses to 0.
Leading zeros are accepted (007 parses to 7). Trailing whitespace
on the stdin lines is tolerated and trimmed before parsing; for the
Zero argv path the values are taken exactly as the OS passes them
(no embedded whitespace).
Cross-implementation parity
- Python: hand-rolled
parse_u32that trims, validates ASCII digits, delegates toint(), bounds-checks againstU32_MAX. - TypeScript: same shape using
BigIntand a charCode digit check. - Rust:
parse_u32that trims, byte-validatesb'0'..=b'9', callsstr::parse::<u32>(the bounds check is built into the parse). - Go:
parseU32that trims, byte-validates, callsstrconv.ParseUint(t, 10, 32). ThebitSize=32arg enforces the u32 cap. - Zero: inline u64 accumulator with an upper-bound check against
4294967295_u64(see "Zero-specific notes" below).
All five share the same dispatch: parse a, parse b, divide-by-zero check, render decimal. Byte-exact agreement on every case.
Zero-specific notes
- argv[1..2] carry a and b (no exposed stdin in Zero 0.1.2).
std.args.get(i)returnsMaybe<String>. The.valueis a String, whichstd.mem.span(.)converts to aSpan<u8>for the manual digit scan.- The natural API for this task would be
std.parse.parseU32(maybe_a.value)—parseU32takes aStringand returnsMaybe<u32>with built-in overflow rejection. But the direct ELF64 backend rejects runtime-String inputs tostd.parse.*withCGEN004:
So the parse is implemented inline: u64 accumulator (i.e.CGEN004: direct backend std.parse helpers currently require literal text0_u64..=18446744073709551615_u64), digit-by-digit ASCII check, followed byif a_acc > 4294967295_u64 { error }. - The direct backend ELF64 MVP forbids user functions taking
Span<u8>/MutSpan<u8>/ shape values (seventh quirk surfaced in task 013), so the parse loop and the output rendering loop are both inline inmain. - main ends with an explicit
returnto dodge the trailing-write byte-count-as-exit-code codegen quirk surfaced in task 012.
Eighth direct-backend codegen quirk (CGEN004)
std.parse.parseU8, parseU16, parseU32 accept any String per
their type signatures, but on the direct ELF64 backend they only
codegen for compile-time string literals. Passing a runtime
String (e.g. std.args.get(1).value) raises CGEN004 with the
message "direct backend std.parse helpers currently require literal
text". This means the natural use case of "parse a numeric argv
value" is impossible via the standard library helper on native
targets; programs must either target WASM (untested by this task)
or implement parsing manually.
This is the eighth quirk in the running list against the direct ELF64 backend that the AgentLang Index has surfaced so far:
- (task 012) trailing-write byte-count returned as exit code unless
mainends with explicitreturn. - (task 013)
responseBodyOffsetreturns the offset within the shared response buffer, not into the body slice itself. - (task 013)
std.http.fetchaccepts[N]u8arrays directly as the request envelope rather than requiringstd.mem.span(.). - (task 014)
headerValue/headerFound/headerOffset/headerLenis the only documented way to scan a response header; no higher-level helper. - (task 013) user functions taking
Span<u8>/MutSpan<u8>/ shape locals are forbidden on direct ELF64 — entire body ofmainmust be one inline pass. - (task 013) JSON literals must be assembled byte-by-byte; no
std.json.write(.)equivalent for emitting JSON. - (task 013) member access on shape values only supported for
Maybe<MutSpan<u8>>.hasand.valueon direct ELF64. - (task 015 — THIS TASK)
std.parse.parseU*rejects runtimeStringinputs withCGEN004. Only compile-time string literals compile through the direct backend.
These will go into a single upstream issue once the AgentLang Index v0.1 ships.
Cost
| Model | Prompt tokens | Completion tokens | API ms |
|---|---|---|---|
| gpt-4o | 3,045 | 1,166 | 10,398 |
| gpt-4o-mini | 3,045 | 1,243 | 19,451 |
| gpt-5 | 3,040 | 21,094 | 188,503 |
Tokens and API ms are summed across the five languages this model attempted for this task.
Compare
Model deep-dives: gpt-4o · gpt-4o-mini · gpt-5 . Back to the leaderboard and methodology.