agentlang-index · task medium
Read a count then that many u32 integers and write their sum, or error on any failure
016-parse-list-sum. Read line 1 as `N`, a decimal integer count in the range 0 to 1000
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.
# 016-parse-list-sum
Read line 1 as `N`, a decimal integer count in the range 0 to 1000
inclusive. Then read `N` more lines, each a decimal unsigned integer
that fits in u32 (range 0 through 4294967295 inclusive). Sum all
`N` values. Write the sum as decimal followed by a single newline.
If any of the following hold, write `error\n` instead and exit:
1. Line 1 fails to parse as a u32 or exceeds 1000.
2. Fewer than `N` additional value lines are available.
3. Any value line fails to parse as a u32 (empty body, non-digit
characters, leading `+`/`-` sign, or value exceeds u32 max).
4. The running sum overflows u32 (would exceed 4294967295).
Trailing whitespace on each line is tolerated and trimmed before
parsing. Leading zeros are accepted (`007` parses as 7). The empty
sum (`N=0`) writes `0\n`.
Output exactly one line followed by `\n`. Do not write to standard
error. Exit with status 0 in every case.
## Examples
Input (stdin):
```
3
1
2
3
```
Output: `6\n`
Input (stdin):
```
0
```
Output: `0\n`
Input (stdin):
```
2
1
abc
```
Output: `error\n` (non-digit mid-list)
Input (stdin):
```
2
4294967295
1
```
Output: `error\n` (running sum overflows u32)
## Zero input convention
Zero 0.1.2 has no exposed stdin capability. The Zero reference reads
`N` from `argv[1]` and the `N` values from `argv[2]`, `argv[3]`, etc.;
values are interpreted exactly as the corresponding 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, loop, 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 | ✓ | wrong output | ✓ | ✓ |
| gpt-4o-mini | compile | ✓ | wrong output | ✓ | ✓ |
| gpt-5 | compile | ✓ | ✓ | ✓ | ✓ |
Failure excerpts
5 of 15 attempts failed. Each card is one attempt, with the captured first line of the diagnostic.
-
ref.zero:6:8 PAR100: expected '{' before block -
(no diagnostic captured) -
ref.zero:3:13 PAR100: expected '{' before block -
(no diagnostic captured) -
ref.zero:1:1 IMP001: unknown package-local import 'std'
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 161 lines
// Parse-list-sum reference for AgentLang Index.
//
// Zero 0.1.2 has no exposed stdin, so N comes from argv[1] and the N
// values come from argv[2..N+1]. std.args.len() includes the program
// name at index 0. Three failure modes collapse to "error\n":
// - N argv missing, malformed, or > 1000
// - fewer than N value argv present
// - any value argv non-digit / empty / > u32::MAX, OR running sum > u32::MAX
//
// std.parse.parseU* rejects runtime String inputs on the direct ELF64
// backend (CGEN004), so each parse is implemented inline with a u64
// digit-accumulator and a u32::MAX bounds check.
//
// 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_n = std.args.get(1)
if maybe_n.has == false {
check world.out.write("error\n")
return
}
let n_in = std.mem.span(maybe_n.value)
// ---- Parse N as u32, reject > 1000 ----
let n_len = std.mem.len(n_in)
let mut n_acc: u64 = 0_u64
let mut n_have: Bool = false
let mut n_ok: Bool = true
let mut n_i: usize = 0
while n_i < n_len {
let c = n_in[n_i]
if c >= 48_u8 {
if c <= 57_u8 {
let d: u64 = (c - 48_u8) as u64
n_acc = n_acc * 10_u64 + d
n_have = true
n_i = n_i + 1
} else {
n_ok = false
n_i = n_len
}
} else {
n_ok = false
n_i = n_len
}
}
if n_ok == false {
check world.out.write("error\n")
return
}
if n_have == false {
check world.out.write("error\n")
return
}
if n_acc > 1000_u64 {
check world.out.write("error\n")
return
}
let n_val: u32 = n_acc as u32
// ---- Verify std.args.len() >= n_val + 2 (program + N-arg + N values) ----
let argc = std.args.len()
let needed: usize = (n_val as usize) + 2_usize
if argc < needed {
check world.out.write("error\n")
return
}
// ---- Loop N times: parse argv[i+1] (i in 1..=N), accumulate in u64 ----
let mut total: u64 = 0_u64
let mut idx: u32 = 0_u32
let mut loop_ok: Bool = true
while idx < n_val {
let arg_pos: usize = (idx as usize) + 2_usize
let maybe_v = std.args.get(arg_pos)
if maybe_v.has == false {
loop_ok = false
idx = n_val
} else {
let v_in = std.mem.span(maybe_v.value)
let v_len = std.mem.len(v_in)
let mut v_acc: u64 = 0_u64
let mut v_have: Bool = false
let mut v_ok: Bool = true
let mut v_i: usize = 0
while v_i < v_len {
let c = v_in[v_i]
if c >= 48_u8 {
if c <= 57_u8 {
let d: u64 = (c - 48_u8) as u64
v_acc = v_acc * 10_u64 + d
v_have = true
v_i = v_i + 1
} else {
v_ok = false
v_i = v_len
}
} else {
v_ok = false
v_i = v_len
}
}
if v_ok == false {
loop_ok = false
idx = n_val
} else {
if v_have == false {
loop_ok = false
idx = n_val
} else {
if v_acc > 4294967295_u64 {
loop_ok = false
idx = n_val
} else {
total = total + v_acc
if total > 4294967295_u64 {
loop_ok = false
idx = n_val
} else {
idx = idx + 1_u32
}
}
}
}
}
}
if loop_ok == false {
check world.out.write("error\n")
return
}
// ---- Render total (u64, but constrained to u32 range) as decimal + newline ----
let mut out: [16]u8 = [0_u8; 16]
let mut out_n: usize = 0
if total == 0_u64 {
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: u64 = total
while v > 0_u64 {
let digit_u64: u64 = 48_u64 + (v % 10_u64)
tmp[tn] = digit_u64 as u8
tn = tn + 1
v = v / 10_u64
}
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 62 lines
const U32_MAX = 4294967295n;
const N_MAX = 1000n;
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 < 1) {
process.stdout.write("error\n");
return;
}
const n = parseU32(lines[0]);
if (n === null || n > N_MAX) {
process.stdout.write("error\n");
return;
}
const count = Number(n);
if (lines.length < count + 1) {
process.stdout.write("error\n");
return;
}
let total = 0n;
for (let i = 1; i <= count; i++) {
const v = parseU32(lines[i]);
if (v === null) {
process.stdout.write("error\n");
return;
}
total += v;
if (total > U32_MAX) {
process.stdout.write("error\n");
return;
}
}
process.stdout.write(`${total}\n`);
}
main();
Rust 52 lines
use std::io::{self, Read, Write};
const U32_MAX_U64: u64 = 4294967295;
const N_MAX: u32 = 1000;
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 run() -> Option<u64> {
let mut input = String::new();
io::stdin().read_to_string(&mut input).ok()?;
let mut lines = input.split('\n');
let n_line = lines.next()?;
let n = parse_u32(n_line)?;
if n > N_MAX {
return None;
}
let mut total: u64 = 0;
for _ in 0..n {
let line = lines.next()?;
let v = parse_u32(line)? as u64;
total += v;
if total > U32_MAX_U64 {
return None;
}
}
Some(total)
}
fn main() {
let stdout = io::stdout();
let mut out = stdout.lock();
match run() {
Some(total) => {
let _ = writeln!(out, "{}", total);
}
None => {
let _ = out.write_all(b"error\n");
}
}
}
Go 83 lines
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
const u32MaxU64 uint64 = 4294967295
const nMax uint32 = 1000
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 readAll(r *bufio.Reader) string {
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()
}
func run(data string) (uint64, bool) {
lines := strings.Split(data, "\n")
if len(lines) < 1 {
return 0, false
}
n, ok := parseU32(lines[0])
if !ok || n > nMax {
return 0, false
}
if uint32(len(lines)) < n+1 {
return 0, false
}
var total uint64 = 0
for i := uint32(1); i <= n; i++ {
v, ok := parseU32(lines[i])
if !ok {
return 0, false
}
total += uint64(v)
if total > u32MaxU64 {
return 0, false
}
}
return total, true
}
func main() {
w := bufio.NewWriter(os.Stdout)
defer w.Flush()
data := readAll(bufio.NewReader(os.Stdin))
total, ok := run(data)
if !ok {
fmt.Fprint(w, "error\n")
return
}
fmt.Fprintf(w, "%d\n", total)
}
Python 52 lines
#!/usr/bin/env python3
import sys
U32_MAX = 4294967295
N_MAX = 1000
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) < 1:
sys.stdout.write("error\n")
return
n = parse_u32(lines[0])
if n is None or n > N_MAX:
sys.stdout.write("error\n")
return
if len(lines) < n + 1:
sys.stdout.write("error\n")
return
total = 0
for i in range(1, n + 1):
v = parse_u32(lines[i])
if v is None:
sys.stdout.write("error\n")
return
total += v
if total > U32_MAX:
sys.stdout.write("error\n")
return
sys.stdout.write(f"{total}\n")
if __name__ == "__main__":
main()
Design notes
Algorithm, failure modes, cross-language parity, and where Zero needed a workaround. From corpus/016-parse-list-sum/notes.md.
Algorithm
Read a count N (u32, capped at 1000) from line 1. Then read N
more lines, each a u32. Sum them and write the sum as decimal + \n.
Three failure-mode classes collapse to error\n:
- Line 1 fails to parse, exceeds 1000, or is missing.
- Fewer than
Nvalue lines are available. - Any value line fails to parse, OR the running sum overflows u32.
The running sum is accumulated in u64 so the overflow check is a
single > 4294967295_u64 comparison after each addition.
Loop-with-mid-iteration-failure shape
This is the structural distinction from task 015. Where 015 had three independent flat failure modes that short-circuit at gate checks before the work, 016 has a counted loop where ANY iteration can fail and must terminate the loop cleanly with an error signal. Each language threads this differently:
- Python / TypeScript: early
returnfrommaininside the loop. - Rust:
?onOption<u32>inside a helperrunthat returnsOption<u64>;mainmatches once at the end. - Go: explicit
(uint64, bool)return from a helper; loop checks!okand short-circuits. - Zero:
loop_ok: Boolflag plusidx = n_valto break the outer loop (nobreakin 0.1.2). The post-loop branch reads the flag.
The Zero pattern is the same flag-then-set-counter-to-end trick used in tasks 008, 011 for inner scans — the loop-with-mid-iteration shape isn't new but this is the first task where it gates the whole output decision rather than just an inner-loop short-circuit.
Cross-implementation parity
All five share the same dispatch:
- parse N
- validate N <= 1000
- validate enough lines/argv remain
- loop N times: parse value, check value <= u32::MAX, add to u64 running sum, check sum <= u32::MAX
- emit sum +
\n
Byte-exact agreement on every case.
Zero-specific notes
- argv[1] is N; argv[2..N+1] are the values.
std.args.len()includes the program name at index 0, soargc >= N + 2checks that enough argv slots exist. - The parse helper is inlined twice (once for N, once per value
iteration) per the seventh quirk forbidding Span
-taking user functions on direct ELF64. The two parse blocks are byte-identical apart from the local-variable prefix ( n_*vsv_*). std.parse.parseU32is still unusable for runtime data per the eighth quirk (CGEN004 on direct ELF64 for non-literal input).- main ends with an explicit
returnto dodge the trailing-write byte-count-as-exit-code codegen quirk. - Sum is rendered from u64 (after the u32::MAX bounds check), not u32, because the rendering loop reuses the same u64 path that was used in the accumulator. The final value is always <= u32::MAX.
No new codegen quirks surfaced during 016 — the eighth quirk from 015 was already in scope, and the rest of the program follows patterns established in 013-015.
Cost
| Model | Prompt tokens | Completion tokens | API ms |
|---|---|---|---|
| gpt-4o | 3,345 | 1,595 | 12,519 |
| gpt-4o-mini | 3,345 | 1,492 | 24,693 |
| gpt-5 | 3,340 | 26,635 | 233,567 |
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.