///|
/// WAST arbitrary generation keeps local text-shape opcode pickers instead of calling typed `gen_valid`; keep duplicated coverage aligned with `docs/wiki/fuzzing/wast-arbitrary-parity-plan.md`.
fn rand_bounded(max_exclusive : UInt32, st : RandomState) -> UInt32 {
  if max_exclusive == 0 {
    0
  } else {
    st.next_uint() % max_exclusive
  }
}

///|
fn bytes_of(s : String) -> Bytes {
  let bytes = @buffer.new(size_hint=s.length())
  for i = 0; i < s.length(); i = i + 1 {
    bytes.write_byte(s[i].to_int().to_byte())
  }
  bytes.to_bytes()
}

// Exact text-surface counters for WAST arbitrary's FZG mirror prelude.
// These are parser/printer counters, not typed GenValid validity proof.

///|
pub struct WastArbitraryFeatureStats {
  gc_type_fields : Int32
  exported_tags : Int32
  descriptor_ref_ops : Int32
  bulk_memory_ops : Int32
  bulk_table_ops : Int32
  tail_call_ops : Int32
  exception_ops : Int32
  simd_const_ops : Int32
  control_ops : Int32
  parametric_ops : Int32
  variable_ops : Int32
  table_ops : Int32
  memory_ops : Int32
  numeric_ops : Int32
  reference_ops : Int32
  simd_ops : Int32
} derive(Eq, Debug)

///|
fn wast_arbitrary_empty_feature_stats() -> WastArbitraryFeatureStats {
  {
    gc_type_fields: 0,
    exported_tags: 0,
    descriptor_ref_ops: 0,
    bulk_memory_ops: 0,
    bulk_table_ops: 0,
    tail_call_ops: 0,
    exception_ops: 0,
    simd_const_ops: 0,
    control_ops: 0,
    parametric_ops: 0,
    variable_ops: 0,
    table_ops: 0,
    memory_ops: 0,
    numeric_ops: 0,
    reference_ops: 0,
    simd_ops: 0,
  }
}

///|
fn wast_arbitrary_add_feature_stats(
  a : WastArbitraryFeatureStats,
  b : WastArbitraryFeatureStats,
) -> WastArbitraryFeatureStats {
  {
    gc_type_fields: a.gc_type_fields + b.gc_type_fields,
    exported_tags: a.exported_tags + b.exported_tags,
    descriptor_ref_ops: a.descriptor_ref_ops + b.descriptor_ref_ops,
    bulk_memory_ops: a.bulk_memory_ops + b.bulk_memory_ops,
    bulk_table_ops: a.bulk_table_ops + b.bulk_table_ops,
    tail_call_ops: a.tail_call_ops + b.tail_call_ops,
    exception_ops: a.exception_ops + b.exception_ops,
    simd_const_ops: a.simd_const_ops + b.simd_const_ops,
    control_ops: a.control_ops + b.control_ops,
    parametric_ops: a.parametric_ops + b.parametric_ops,
    variable_ops: a.variable_ops + b.variable_ops,
    table_ops: a.table_ops + b.table_ops,
    memory_ops: a.memory_ops + b.memory_ops,
    numeric_ops: a.numeric_ops + b.numeric_ops,
    reference_ops: a.reference_ops + b.reference_ops,
    simd_ops: a.simd_ops + b.simd_ops,
  }
}

///|
fn wast_arbitrary_count_instructions(
  instrs : Array[Instruction],
) -> WastArbitraryFeatureStats {
  let mut stats = wast_arbitrary_empty_feature_stats()
  for instr in instrs {
    let current = match instr {
      Unreachable
      | Nop
      | Block(_, _)
      | Loop(_, _)
      | If(_, _, _)
      | Br(_)
      | BrIf(_)
      | BrOnNull(_)
      | BrOnNonNull(_)
      | BrOnCastDescEq(_, _, _)
      | BrOnCastDescEqFail(_, _, _)
      | BrTable(_, _)
      | Return
      | Call(_)
      | CallIndirect(_, _)
      | CallRef(_) =>
        { ..wast_arbitrary_empty_feature_stats(), control_ops: 1, }
      ReturnCall(_) | ReturnCallIndirect(_, _) | ReturnCallRef(_) =>
        {
          ..wast_arbitrary_empty_feature_stats(),
          control_ops: 1,
          tail_call_ops: 1,
        }
      Throw(_)
      | ThrowRef
      | Rethrow(_)
      | TryTable(_, _, _)
      | TryLegacy(_, _, _, _) =>
        {
          ..wast_arbitrary_empty_feature_stats(),
          control_ops: 1,
          exception_ops: 1,
        }
      Drop | Select(_) =>
        { ..wast_arbitrary_empty_feature_stats(), parametric_ops: 1, }
      LocalGet(_) | LocalSet(_) | LocalTee(_) | GlobalGet(_) | GlobalSet(_) =>
        { ..wast_arbitrary_empty_feature_stats(), variable_ops: 1, }
      TableGet(_) | TableSet(_) | TableSize(_) | TableGrow(_) =>
        { ..wast_arbitrary_empty_feature_stats(), table_ops: 1, }
      TableFill(_) | TableCopy(_, _) | TableInit(_, _) | ElemDrop(_) =>
        {
          ..wast_arbitrary_empty_feature_stats(),
          table_ops: 1,
          bulk_table_ops: 1,
        }
      ArrayLoad(op, _, _) | ArrayStore(op, _, _) =>
        {
          ..wast_arbitrary_empty_feature_stats(),
          reference_ops: 1,
          memory_ops: 1,
          simd_ops: if op is V128Load || op is V128Store {
            1
          } else {
            0
          },
        }
      MemoryAtomic(_, _, _, _)
      | AtomicFence(_)
      | Load(_, _)
      | Store(_, _)
      | MemorySize
      | MemoryGrow => { ..wast_arbitrary_empty_feature_stats(), memory_ops: 1, }
      MemoryFill | MemoryCopy | MemoryInit(_) | DataDrop(_) =>
        {
          ..wast_arbitrary_empty_feature_stats(),
          memory_ops: 1,
          bulk_memory_ops: 1,
        }
      Const(_, _) | Unary(_) | Binary(_) | Test(_) | Compare(_) | Convert(_) =>
        { ..wast_arbitrary_empty_feature_stats(), numeric_ops: 1, }
      RefNull(_)
      | RefIsNull
      | RefFunc(_)
      | StringConst(_)
      | StringMeasureWtf16
      | StringConcat
      | StringEq
      | StringAsWtf16
      | StringViewWtf16GetCodeunit
      | StringViewWtf16Slice
      | StringNewUtf8Array
      | StringNewWtf16Array
      | StringEncodeUtf8Array
      | StringEncodeWtf16Array
      | StringNewLossyUtf8Array
      | StringNewWtf8Array
      | StringEncodeLossyUtf8Array
      | StringEncodeWtf8Array
      | StructNew(_)
      | StructNewDefault(_)
      | ArrayNew(_)
      | ArrayNewDefault(_)
      | ArrayNewFixed(_, _)
      | ArrayNewData(_, _)
      | ArrayNewElem(_, _)
      | StructGet(_, _)
      | StructGetS(_, _)
      | StructGetU(_, _)
      | WaitqueueNew
      | WaitqueueNotify
      | StructWait(_, _)
      | Publish
      | StructAtomicSet(_, _, _)
      | ArrayAtomicSet(_, _)
      | StructAtomicGet(_, _, _)
      | StructAtomicGetS(_, _, _)
      | StructAtomicGetU(_, _, _)
      | StructAtomicRmw(_, _, _, _)
      | StructAtomicCmpxchg(_, _, _)
      | ArrayAtomicGet(_, _)
      | ArrayAtomicGetS(_, _)
      | ArrayAtomicGetU(_, _)
      | ArrayAtomicRmw(_, _, _)
      | ArrayAtomicCmpxchg(_, _)
      | StructNewDesc(_)
      | StructNewDefaultDesc(_)
      | RefGetDesc(_) =>
        { ..wast_arbitrary_empty_feature_stats(), reference_ops: 1, }
      RefTestDesc(_, _) | RefCastDescEq(_, _) =>
        {
          ..wast_arbitrary_empty_feature_stats(),
          reference_ops: 1,
          descriptor_ref_ops: 1,
        }
      V128Const(_) =>
        {
          ..wast_arbitrary_empty_feature_stats(),
          simd_ops: 1,
          simd_const_ops: 1,
        }
      V128Load(_)
      | V128Load8x8S(_)
      | V128Load8x8U(_)
      | V128Load16x4S(_)
      | V128Load16x4U(_)
      | V128Load32x2S(_)
      | V128Load32x2U(_)
      | V128Load8Splat(_)
      | V128Load16Splat(_)
      | V128Load32Splat(_)
      | V128Load64Splat(_)
      | V128Load32Zero(_)
      | V128Load64Zero(_)
      | V128Store(_)
      | V128Load8Lane(_, _)
      | V128Load16Lane(_, _)
      | V128Load32Lane(_, _)
      | V128Load64Lane(_, _)
      | V128Store8Lane(_, _)
      | V128Store16Lane(_, _)
      | V128Store32Lane(_, _)
      | V128Store64Lane(_, _)
      | I8x16Shuffle(_)
      | I8x16Swizzle
      | I8x16Splat
      | I16x8Splat
      | I32x4Splat
      | I64x2Splat
      | F32x4Splat
      | F64x2Splat
      | I8x16ExtractLaneS(_)
      | I8x16ExtractLaneU(_)
      | I8x16ReplaceLane(_)
      | I16x8ExtractLaneS(_)
      | I16x8ExtractLaneU(_)
      | I16x8ReplaceLane(_)
      | I32x4ExtractLane(_)
      | I32x4ReplaceLane(_)
      | I64x2ExtractLane(_)
      | I64x2ReplaceLane(_)
      | F32x4ExtractLane(_)
      | F32x4ReplaceLane(_)
      | F64x2ExtractLane(_)
      | F64x2ReplaceLane(_)
      | SimdUnary(_)
      | SimdBinary(_)
      | SimdTernary(_)
      | SimdShift(_)
      | SimdTest(_)
      | SimdCompare(_)
      | SimdConvert(_)
      | SimdRelaxed(_) =>
        { ..wast_arbitrary_empty_feature_stats(), simd_ops: 1, }
    }
    stats = wast_arbitrary_add_feature_stats(stats, current)
    let nested = match instr {
      Block(_, body) | Loop(_, body) => wast_arbitrary_count_instructions(body)
      If(_, then_body, else_body) =>
        wast_arbitrary_add_feature_stats(
          wast_arbitrary_count_instructions(then_body),
          wast_arbitrary_count_instructions(else_body),
        )
      TryTable(_, _, body) => wast_arbitrary_count_instructions(body)
      _ => wast_arbitrary_empty_feature_stats()
    }
    stats = wast_arbitrary_add_feature_stats(stats, nested)
  }
  stats
}

///|
pub fn wast_arbitrary_feature_stats(mod : Module) -> WastArbitraryFeatureStats {
  let mut stats = wast_arbitrary_empty_feature_stats()
  for field in mod.fields {
    let current = match field {
      TypeField(t) =>
        match t.body {
          Struct(_) | Array(_) | Shared(_) =>
            { ..wast_arbitrary_empty_feature_stats(), gc_type_fields: 1, }
          _ => wast_arbitrary_empty_feature_stats()
        }
      TagField(t) =>
        {
          ..wast_arbitrary_empty_feature_stats(),
          exported_tags: if t.exports.length() > 0 {
            1
          } else {
            0
          },
        }
      FuncField(f) => wast_arbitrary_count_instructions(f.body)
      _ => wast_arbitrary_empty_feature_stats()
    }
    stats = wast_arbitrary_add_feature_stats(stats, current)
  }
  stats
}

///|
fn rand_len(size : Int32, st : RandomState, max_len : UInt32) -> Int32 {
  let base : UInt32 = if size <= 0 { 1 } else { size.reinterpret_as_uint() + 1 }
  let cap = if max_len < base { max_len } else { base }
  rand_bounded(cap + 1, st).reinterpret_as_int()
}

///|
fn rand_ident(st : RandomState, prefix : String) -> String {
  "\{prefix}\{st.next_uint() % 64}"
}

///|
fn rand_index(st : RandomState, bound : UInt32) -> Index {
  Num(rand_bounded(bound, st))
}

///|
fn pick_value_type(st : RandomState) -> ValueType {
  match st.next_uint() % 6 {
    0 => I32
    1 => I64
    2 => F32
    3 => F64
    4 => FuncRef
    _ => ExternRef
  }
}

///|
fn pick_numeric_value_type(st : RandomState) -> ValueType {
  match st.next_uint() % 4 {
    0 => I32
    1 => I64
    2 => F32
    _ => F64
  }
}

///|
fn pick_numeric_opcode(st : RandomState) -> Opcode {
  match st.next_uint() % 14 {
    0 => I32Const
    1 => I64Const
    2 => F32Const
    3 => F64Const
    4 => I32Add
    5 => I32Sub
    6 => I64Add
    7 => I64Sub
    8 => F32Add
    9 => F32Sub
    10 => F64Add
    11 => F64Sub
    12 => I32Eqz
    _ => I64Eqz
  }
}

///|
fn pick_unary_opcode(st : RandomState) -> Opcode {
  match st.next_uint() % 12 {
    0 => I32Clz
    1 => I32Ctz
    2 => I32Popcnt
    3 => I64Clz
    4 => I64Ctz
    5 => I64Popcnt
    6 => F32Abs
    7 => F32Neg
    8 => F64Abs
    9 => F64Neg
    10 => I32Extend8S
    _ => I64Extend32S
  }
}

///|
fn pick_binary_opcode(st : RandomState) -> Opcode {
  match st.next_uint() % 20 {
    0 => I32Add
    1 => I32Sub
    2 => I32Mul
    3 => I32And
    4 => I32Or
    5 => I32Xor
    6 => I64Add
    7 => I64Sub
    8 => I64Mul
    9 => I64And
    10 => I64Or
    11 => I64Xor
    12 => F32Add
    13 => F32Sub
    14 => F32Mul
    15 => F32Div
    16 => F64Add
    17 => F64Sub
    18 => F64Mul
    _ => F64Div
  }
}

///|
fn pick_compare_opcode(st : RandomState) -> Opcode {
  match st.next_uint() % 12 {
    0 => I32Eq
    1 => I32Ne
    2 => I32LtS
    3 => I32GtS
    4 => I64Eq
    5 => I64Ne
    6 => I64LtS
    7 => I64GtS
    8 => F32Eq
    9 => F32Lt
    10 => F64Eq
    _ => F64Lt
  }
}

///|
fn pick_convert_opcode(st : RandomState) -> Opcode {
  match st.next_uint() % 16 {
    0 => I32WrapI64
    1 => I32TruncF32S
    2 => I32TruncF64S
    3 => I64ExtendI32S
    4 => I64TruncF32S
    5 => I64TruncF64S
    6 => F32ConvertI32S
    7 => F32ConvertI64S
    8 => F64ConvertI32S
    9 => F64ConvertI64S
    10 => F32DemoteF64
    11 => F64PromoteF32
    12 => I32ReinterpretF32
    13 => I64ReinterpretF64
    14 => F32ReinterpretI32
    _ => F64ReinterpretI64
  }
}

///|
fn pick_const_text(op : Opcode, st : RandomState) -> String {
  match op {
    I32Const => "\{(st.next_uint() % 200).reinterpret_as_int() - 100}"
    I64Const =>
      "\{(st.next_uint() % 200).reinterpret_as_int().to_int64() - 100L}"
    F32Const | F64Const =>
      match st.next_uint() % 8 {
        0 => "0.0"
        1 => "1.0"
        2 => "-1.0"
        3 => "3.5"
        4 => "-2.25"
        5 => "inf"
        6 => "-inf"
        _ => "nan"
      }
    _ => "0"
  }
}

///|
fn gen_memarg(st : RandomState, natural_align : UInt64) -> MemArg {
  let align = if (st.next_uint() & 1) == 1 {
    natural_align
  } else {
    match st.next_uint() % 4 {
      0 => 1UL
      1 => 2UL
      2 => 4UL
      _ => natural_align
    }
  }
  { align, offset: (st.next_uint() % 64).to_uint64(), }
}

///|
fn gen_instruction(
  size : Int32,
  st : RandomState,
  func_bound : UInt32,
) -> Instruction {
  if size <= 0 {
    match st.next_uint() % 12 {
      0 => Nop
      1 => Drop
      2 => LocalGet(Num(0))
      3 => LocalSet(Num(0))
      4 => GlobalGet(Num(0))
      5 => MemorySize
      6 => TableSize(Num(0))
      7 => Const(I32Const, pick_const_text(I32Const, st))
      8 => Const(I64Const, pick_const_text(I64Const, st))
      9 => Const(F32Const, pick_const_text(F32Const, st))
      10 => Const(F64Const, pick_const_text(F64Const, st))
      _ => RefNull(FuncRef)
    }
  } else {
    let next = size / 2
    match st.next_uint() % 20 {
      0 => {
        let body : Array[Instruction] = []
        let n = rand_len(next, st, 3)
        for _ in 0.. {
        let body : Array[Instruction] = []
        let n = rand_len(next, st, 3)
        for _ in 0.. {
        let then_body : Array[Instruction] = []
        let else_body : Array[Instruction] = []
        let nt = rand_len(next, st, 2)
        let ne = rand_len(next, st, 2)
        for _ in 0.. Br(Num(st.next_uint() % 2))
      4 => BrIf(Num(st.next_uint() % 2))
      5 => BrTable([Num(0), Num(1)], Num(0))
      6 => Return
      7 => Call(Num(rand_bounded(func_bound, st)))
      8 =>
        CallIndirect(Num(0), {
          type_index: Some(Num(0)),
          func_type: { params: [], results: [], },
          param_ids: [],
        })
      9 => Load(I32Load, gen_memarg(st, 4))
      10 => Store(I32Store, gen_memarg(st, 4))
      11 => Unary(pick_unary_opcode(st))
      12 => Binary(pick_binary_opcode(st))
      13 => Test(I32Eqz)
      14 => Compare(pick_compare_opcode(st))
      15 => Convert(pick_convert_opcode(st))
      16 => RefFunc(Num(rand_bounded(func_bound, st)))
      17 => Select(Some([pick_numeric_value_type(st)]))
      18 => TableGet(Num(0))
      _ => {
        let op = pick_numeric_opcode(st)
        Const(op, pick_const_text(op, st))
      }
    }
  }
}

///|
fn gen_func_type(st : RandomState) -> FuncType {
  let params : Array[ValueType] = []
  let results : Array[ValueType] = []
  let pc = st.next_uint() % 3
  let rc = st.next_uint() % 2
  for _ in 0.. TypeUse {
  if type_count > 0 && (st.next_uint() & 1) == 1 {
    {
      type_index: Some(Num(st.next_uint() % type_count)),
      func_type: { params: [], results: [], },
      param_ids: [],
    }
  } else {
    let func_type = gen_func_type(st)
    let param_ids : Array[String?] = []
    for _ in func_type.params {
      param_ids.push(None)
    }
    { type_index: None, func_type, param_ids, }
  }
}

///|
fn append_wide_wast_surface(fields : Array[ModuleField]) -> Unit {
  fields.push(
    TypeField({
      id: Some("$gc_struct"),
      is_sub: false,
      final_: false,
      supers: [],
      metadata: { describes: None, descriptor: None, },
      body: TypeDefBody::Struct([
        { storage: FieldStorage::Value(I32), mutable_: true, },
        { storage: FieldStorage::Value(TypedFuncRef(Struct)), mutable_: false, },
      ]),
    }),
  )
  fields.push(
    TagField({
      id: Some("$tag0"),
      exports: [Export("tag0")],
      type_use: {
        type_index: None,
        func_type: { params: [I32], results: [], },
        param_ids: [None],
      },
    }),
  )
  fields.push(
    FuncField({
      id: Some("$wide_wast_surface"),
      annotations: [],
      exports: [Export("wide_wast_surface")],
      type_use: {
        type_index: None,
        func_type: { params: [], results: [], },
        param_ids: [],
      },
      locals: [],
      body: [
        RefNull(TypedFuncRef(Struct)),
        RefTestDesc(false, Num(0)),
        Drop,
        RefNull(TypedFuncRef(Struct)),
        RefCastDescEq(true, Num(0)),
        Drop,
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        MemoryFill,
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        MemoryCopy,
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        MemoryInit(Num(0)),
        DataDrop(Num(0)),
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        TableFill(Num(0)),
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        TableCopy(Num(0), Num(0)),
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        Const(I32Const, "0"),
        TableInit(Num(0), Num(0)),
        ElemDrop(Num(0)),
        V128Const({ shape: I32x4, values: ["0", "1", "2", "3"], }),
        Drop,
        ReturnCall(Num(0)),
        ReturnCallIndirect(Num(0), {
          type_index: Some(Num(0)),
          func_type: { params: [], results: [], },
          param_ids: [],
        }),
        ReturnCallRef({
          type_index: Some(Num(0)),
          func_type: { params: [], results: [], },
          param_ids: [],
        }),
        Const(I32Const, "0"),
        Throw(Num(0)),
        TryTable(
          { label: None, type_use: None, },
          [CatchClause(Num(0), Num(0))],
          [Nop],
        ),
      ],
    }),
  )
}

///|
fn gen_module(size : Int32, st : RandomState) -> Module {
  let fields : Array[ModuleField] = []
  append_wide_wast_surface(fields)

  // Always provide at least one func type for call_indirect / signature references.
  let type_count : UInt32 = st.next_uint() % 2 + 1
  for _ in 0.. 0 && (st.next_uint() & 1) == 1 {
    let funcs : Array[Index] = [Num(0)]
    if func_count > 1 {
      funcs.push(Num(1))
    }
    fields.push(
      ElemField({
        id: None,
        declarative: false,
        table_index: Num(0),
        offset: [Const(I32Const, "0")],
        func_indices: funcs,
        elem_exprs: [],
        elem_type: None,
      }),
    )
  }
  if (st.next_uint() & 1) == 1 {
    fields.push(
      DataField({
        id: None,
        memory_index: Num(0),
        offset: [Const(I32Const, "0")],
        data: bytes_of("fuzz"),
      }),
    )
  }

  // Optional export/start.
  if func_count > 0 {
    fields.push(ExportField({ name: "main", desc: ExportDesc::Func(Num(0)), }))
    if (st.next_uint() & 1) == 1 {
      fields.push(StartField({ func_index: Num(0), }))
    }
  }
  { id: None, fields, }
}

///|
test "wast arbitrary widened prelude reports exact FZG mirror counters" {
  let fields : Array[ModuleField] = []
  append_wide_wast_surface(fields)
  let stats = wast_arbitrary_feature_stats({ id: None, fields, })
  @debug.assert_eq(stats.gc_type_fields, 1)
  @debug.assert_eq(stats.exported_tags, 1)
  @debug.assert_eq(stats.descriptor_ref_ops, 2)
  @debug.assert_eq(stats.bulk_memory_ops, 4)
  @debug.assert_eq(stats.bulk_table_ops, 4)
  @debug.assert_eq(stats.tail_call_ops, 3)
  @debug.assert_eq(stats.exception_ops, 2)
  @debug.assert_eq(stats.simd_const_ops, 1)
  @debug.assert_eq(stats.control_ops, 6)
  @debug.assert_eq(stats.parametric_ops, 3)
  @debug.assert_eq(stats.variable_ops, 0)
  @debug.assert_eq(stats.table_ops, 4)
  @debug.assert_eq(stats.memory_ops, 4)
  @debug.assert_eq(stats.numeric_ops, 19)
  @debug.assert_eq(stats.reference_ops, 4)
  @debug.assert_eq(stats.simd_ops, 1)
}

///|
test "wast arbitrary module includes widened surface prelude" {
  let st = @splitmix.new(seed=27)
  let mod : Module = Arbitrary::arbitrary(4, st)
  let text = match module_to_wast(mod) {
    Ok(t) => t
    Err(e) => fail("unexpected module_to_wast error: \{e}")
  }
  assert_true(text.contains("(type $gc_struct (struct"))
  assert_true(text.contains("(tag $tag0"))
  assert_true(text.contains("return_call"))
  assert_true(text.contains("return_call_indirect"))
  assert_true(text.contains("return_call_ref"))
  assert_true(text.contains("try_table"))
  assert_true(text.contains("throw 0"))
  assert_true(text.contains("memory.fill"))
  assert_true(text.contains("memory.copy"))
  assert_true(text.contains("memory.init"))
  assert_true(text.contains("table.fill"))
  assert_true(text.contains("table.copy"))
  assert_true(text.contains("table.init"))
  assert_true(text.contains("ref.test_desc"))
  assert_true(text.contains("ref.cast_desc_eq_null"))
  assert_true(text.contains("v128.const i32x4"))
  match wast_to_module(text, filename="wide-wast-arbitrary.wat") {
    Ok(_) => ()
    Err(e) => fail("widened arbitrary WAST should parse back: \{e}")
  }
}

///|
pub impl Arbitrary for ValueType with fn arbitrary(_, st) {
  pick_value_type(st)
}

///|
pub impl Arbitrary for Index with fn arbitrary(size, st) {
  if (st.next_uint() & 1) == 1 {
    Num(rand_bounded((size + 1).reinterpret_as_uint(), st))
  } else {
    let suffix = rand_ident(st, "id")
    Id("$\{suffix}")
  }
}

///|
pub impl Arbitrary for Limits with fn arbitrary(_, st) {
  let min = st.next_uint() % 5
  let max : UInt32? = if (st.next_uint() & 1) == 1 {
    Some(min + st.next_uint() % 3)
  } else {
    None
  }
  { min, max, }
}

///|
pub impl Arbitrary for FuncType with fn arbitrary(_, st) {
  gen_func_type(st)
}

///|
pub impl Arbitrary for TypeUse with fn arbitrary(size, st) {
  gen_type_use(st, (size + 1).reinterpret_as_uint())
}

///|
pub impl Arbitrary for InlineExport with fn arbitrary(_, st) {
  Export("ex_\{st.next_uint() % 32}")
}

///|
pub impl Arbitrary for TableType with fn arbitrary(_, st) {
  {
    limits: Arbitrary::arbitrary(2, st),
    elem_type: if (st.next_uint() & 1) == 1 {
      FuncRef
    } else {
      ExternRef
    },
  }
}

///|
pub impl Arbitrary for MemoryType with fn arbitrary(_, st) {
  let limits : Limits = Arbitrary::arbitrary(2, st)
  MemoryType::new(
    limits.min.to_uint64(),
    max=limits.max.map(fn(value) { value.to_uint64() }),
  )
}

///|
pub impl Arbitrary for GlobalType with fn arbitrary(_, st) {
  {
    val_type: pick_numeric_value_type(st),
    mutable_: (st.next_uint() & 1) == 1,
  }
}

///|
pub impl Arbitrary for Local with fn arbitrary(_, st) {
  {
    id: if (st.next_uint() & 1) == 1 {
      Some("$l\{st.next_uint() % 32}")
    } else {
      None
    },
    val_type: pick_numeric_value_type(st),
  }
}

///|
pub impl Arbitrary for ImportDesc with fn arbitrary(size, st) {
  match st.next_uint() % 4 {
    0 => Func(Arbitrary::arbitrary(size, st))
    1 => Table(Arbitrary::arbitrary(size, st))
    2 => Memory(Arbitrary::arbitrary(size, st))
    _ => Global(Arbitrary::arbitrary(size, st))
  }
}

///|
pub impl Arbitrary for Import with fn arbitrary(size, st) {
  {
    module_name: "env",
    field_name: "f\{st.next_uint() % 64}",
    desc: Arbitrary::arbitrary(size, st),
    id: if (st.next_uint() & 1) == 1 {
      Some("$imp\{st.next_uint() % 64}")
    } else {
      None
    },
    annotations: [],
  }
}

///|
pub impl Arbitrary for ExportDesc with fn arbitrary(size, st) {
  match st.next_uint() % 4 {
    0 => Func(rand_index(st, (size + 1).reinterpret_as_uint()))
    1 => Table(rand_index(st, 1))
    2 => Memory(rand_index(st, 1))
    _ => Global(rand_index(st, 1))
  }
}

///|
pub impl Arbitrary for Export with fn arbitrary(size, st) {
  { name: "ex\{st.next_uint() % 64}", desc: Arbitrary::arbitrary(size, st), }
}

///|
pub impl Arbitrary for Start with fn arbitrary(size, st) {
  { func_index: rand_index(st, (size + 1).reinterpret_as_uint()), }
}

///|
pub impl Arbitrary for MemArg with fn arbitrary(_, st) {
  gen_memarg(st, 4)
}

///|
pub impl Arbitrary for BlockType with fn arbitrary(size, st) {
  {
    label: if (st.next_uint() & 1) == 1 {
      Some("$b\{st.next_uint() % 32}")
    } else {
      None
    },
    type_use: if (st.next_uint() & 1) == 1 {
      Some(Arbitrary::arbitrary(size, st))
    } else {
      None
    },
  }
}

///|
pub impl Arbitrary for SimdShape with fn arbitrary(_, st) {
  match st.next_uint() % 6 {
    0 => I8x16
    1 => I16x8
    2 => I32x4
    3 => I64x2
    4 => F32x4
    _ => F64x2
  }
}

///|
pub impl Arbitrary for V128Const with fn arbitrary(_, st) {
  let shape : SimdShape = Arbitrary::arbitrary(0, st)
  let values : Array[String] = []
  let count = match shape {
    I8x16 => 16
    I16x8 => 8
    I32x4 => 4
    I64x2 => 2
    F32x4 => 4
    F64x2 => 2
  }
  for _ in 0.. pick_binary_opcode(st)
    1 => pick_unary_opcode(st)
    2 => pick_compare_opcode(st)
    3 => pick_convert_opcode(st)
    4 => I32Const
    5 => I64Const
    6 => F32Const
    _ => F64Const
  }
}

///|
pub impl Arbitrary for Instruction with fn arbitrary(size, st) {
  gen_instruction(size, st, (size + 2).reinterpret_as_uint())
}

///|
pub impl Arbitrary for Func with fn arbitrary(size, st) {
  let locals : Array[Local] = []
  let local_n = rand_len(size, st, 3)
  for _ in 0.. TypeField(Arbitrary::arbitrary(size, st))
    1 => {
      let types : Array[TypeDef] = []
      let n = rand_len(size, st, 3)
      for _ in 0.. FuncField(Arbitrary::arbitrary(size, st))
    3 => TableField(Arbitrary::arbitrary(size, st))
    4 => MemoryField(Arbitrary::arbitrary(size, st))
    5 => GlobalField(Arbitrary::arbitrary(size, st))
    6 => ImportField(Arbitrary::arbitrary(size, st))
    7 => ExportField(Arbitrary::arbitrary(size, st))
    8 => StartField(Arbitrary::arbitrary(size, st))
    9 => ElemField(Arbitrary::arbitrary(size, st))
    _ => DataField(Arbitrary::arbitrary(size, st))
  }
}

///|
pub impl Arbitrary for Module with fn arbitrary(size, st) {
  gen_module(size, st)
}

///|
pub impl Arbitrary for WastValue with fn arbitrary(_, st) {
  match st.next_uint() % 6 {
    0 => I32("\{(st.next_uint() % 200).reinterpret_as_int() - 100}")
    1 => I64("\{(st.next_uint() % 200).reinterpret_as_int().to_int64() - 100L}")
    2 => F32("0.0")
    3 => F64("0.0")
    4 => RefNull(FuncRef)
    _ => RefFunc
  }
}

///|
pub impl Arbitrary for WastResult with fn arbitrary(size, st) {
  match st.next_uint() % 7 {
    0 => Value(Arbitrary::arbitrary(size, st))
    1 => F32NanCanonical
    2 => F32NanArithmetic
    3 => F64NanCanonical
    4 => F64NanArithmetic
    5 => RefNull(Some(FuncRef))
    _ => RefFunc
  }
}

///|
pub impl Arbitrary for WastAction with fn arbitrary(size, st) {
  let args : Array[WastValue] = []
  let n = rand_len(size, st, 4)
  for _ in 0.. Inline(Arbitrary::arbitrary(size, st))
    1 => Binary(b"\x00asm")
    _ => Quote(["(module)"])
  }
}

///|
pub impl Arbitrary for WastCommand with fn arbitrary(size, st) {
  match st.next_uint() % 9 {
    0 => Module(Arbitrary::arbitrary(size, st))
    1 => Register("m\{st.next_uint() % 8}", None)
    2 => Action(Arbitrary::arbitrary(size, st))
    3 =>
      AssertReturn(Arbitrary::arbitrary(size, st), [
        Arbitrary::arbitrary(size, st),
      ])
    4 => AssertTrap(Arbitrary::arbitrary(size, st), "trap")
    5 => AssertException(Arbitrary::arbitrary(size, st))
    6 => AssertExhaustion(Arbitrary::arbitrary(size, st), "exhaust")
    7 => AssertMalformed(Arbitrary::arbitrary(size, st), "malformed")
    _ => AssertInvalid(Arbitrary::arbitrary(size, st), "invalid")
  }
}

///|
pub impl Arbitrary for WastScript with fn arbitrary(size, st) {
  let commands : Array[WastCommand] = []
  let n = rand_len(size, st, 6)
  for _ in 0..