fn expect_int(label: String, got: Int, want: Int) -> Void {
    if got == want { println("ok   " + label) }
    else { println("FAIL " + label + " got=" + int_to_str(got) + " want=" + int_to_str(want)) }
}
fn expect_str(label: String, got: String, want: String) -> Void {
    if str_eq(got, want) { println("ok   " + label) }
    else { println("FAIL " + label + " got='" + got + "' want='" + want + "'") }
}

// 1. basic char access across a string
let s: String = "hello"
expect_int("char[0]=h", str_char_code(s, 0), 104)
expect_int("char[4]=o", str_char_code(s, 4), 111)
expect_int("char[5] OOB -> 0", str_char_code(s, 5), 0)
expect_int("char[-1] OOB -> 0", str_char_code(s, -1), 0)
expect_int("empty string OOB", str_char_code("", 0), 0)

// 2. slices
expect_str("slice(0,5)", str_slice(s, 0, 5), "hello")
expect_str("slice(1,3)", str_slice(s, 1, 3), "el")
expect_str("slice past end clamps", str_slice(s, 3, 99), "lo")
expect_str("slice inverted -> empty", str_slice(s, 4, 2), "")

// 3. DIFFERENT strings must not share a cached length (the real hazard)
let a: String = "abc"
let b: String = "abcdefghij"
expect_int("a[2]=c", str_char_code(a, 2), 99)
expect_int("a[3] OOB", str_char_code(a, 3), 0)
expect_int("b[9]=j", str_char_code(b, 9), 106)
expect_int("b[3]=d after a", str_char_code(b, 3), 100)
expect_int("a[3] still OOB after b", str_char_code(a, 3), 0)

// 4. many distinct strings interleaved — forces cache slot collisions
fn interleave(n: Int) -> Int {
    let i: Int = 0
    let bad: Int = 0
    while i < n {
        let t: String = int_to_str(i)
        let l: Int = str_len(t)
        let last: Int = str_char_code(t, l - 1)
        let oob: Int = str_char_code(t, l)
        if oob != 0 { let bad2: Int = bad + 1
                      let bad: Int = bad2 }
        if last == 0 { let bad3: Int = bad + 1
                       let bad: Int = bad3 }
        let i2: Int = i + 1
        let i: Int = i2
    }
    return bad
}
expect_int("1000 interleaved strings, no bad reads", interleave(1000), 0)

// 5. concatenation changes length — cache must not report the old one
let g: String = "12345"
let g2: String = g + "6789"
expect_int("grown string len via char", str_char_code(g2, 8), 57)
expect_int("original still bounded", str_char_code(g, 5), 0)
println("done")
