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 ZeroTypeScriptRustGoPython
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.

  1. gpt-4o Zero compile
    ref.zero:1:1 IMP001: unknown package-local import 'std'
  2. gpt-4o-mini Zero compile
    ref.zero:3:13 PAR100: expected '{' before block
  3. gpt-4o-mini TypeScript other
    (no diagnostic captured)
  4. gpt-5 Zero compile
    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:

  1. argv/stdin shape missing (under two lines or two argv values).
  2. Either integer fails to parse (empty body, non-digit byte, leading +/-, or value exceeds u32::MAX = 4294967295).
  3. 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_u32 that trims, validates ASCII digits, delegates to int(), bounds-checks against U32_MAX.
  • TypeScript: same shape using BigInt and a charCode digit check.
  • Rust: parse_u32 that trims, byte-validates b'0'..=b'9', calls str::parse::<u32> (the bounds check is built into the parse).
  • Go: parseU32 that trims, byte-validates, calls strconv.ParseUint(t, 10, 32). The bitSize=32 arg 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) returns Maybe<String>. The .value is a String, which std.mem.span(.) converts to a Span<u8> for the manual digit scan.
  • The natural API for this task would be std.parse.parseU32(maybe_a.value)parseU32 takes a String and returns Maybe<u32> with built-in overflow rejection. But the direct ELF64 backend rejects runtime-String inputs to std.parse.* with CGEN004:
    CGEN004: direct backend std.parse helpers currently require literal text
    
    So the parse is implemented inline: u64 accumulator (i.e. 0_u64..=18446744073709551615_u64), digit-by-digit ASCII check, followed by if 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 in main.
  • main ends with an explicit return to 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:

  1. (task 012) trailing-write byte-count returned as exit code unless main ends with explicit return.
  2. (task 013) responseBodyOffset returns the offset within the shared response buffer, not into the body slice itself.
  3. (task 013) std.http.fetch accepts [N]u8 arrays directly as the request envelope rather than requiring std.mem.span(.).
  4. (task 014) headerValue/headerFound/headerOffset/headerLen is the only documented way to scan a response header; no higher-level helper.
  5. (task 013) user functions taking Span<u8> / MutSpan<u8> / shape locals are forbidden on direct ELF64 — entire body of main must be one inline pass.
  6. (task 013) JSON literals must be assembled byte-by-byte; no std.json.write(.) equivalent for emitting JSON.
  7. (task 013) member access on shape values only supported for Maybe<MutSpan<u8>>.has and .value on direct ELF64.
  8. (task 015 — THIS TASK) std.parse.parseU* rejects runtime String inputs with CGEN004. 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.