///|
pub struct DifferentialAdapters {
  wasm_tools_validate : (Bytes) -> Result[Bool, String]
  binaryen_validate : (Bytes) -> Result[Bool, String]
  per_case_timeout_ms : Int32?
}

///|
pub fn DifferentialAdapters::new(
  wasm_tools_validate~ : (Bytes) -> Result[Bool, String] = fn(_) {
    Err("wasm-tools differential validator unavailable")
  },
  binaryen_validate~ : (Bytes) -> Result[Bool, String] = fn(_) {
    Err("binaryen differential validator unavailable")
  },
  per_case_timeout_ms~ : Int32? = None,
) -> DifferentialAdapters {
  { wasm_tools_validate, binaryen_validate, per_case_timeout_ms, }
}

///|
pub struct TextParsePrintLowerReport {
  adapter_name : String
  original_parse_ok : Bool
  print_ok : Bool
  reparse_lower_ok : Bool
  printed_text : String?
  original_lowered_bytes : Bytes?
  reparsed_lowered_bytes : Bytes?
  message : String?
} derive(Eq, Debug)

///|
fn encode_text_matrix_module(mod : Module) -> Result[Bytes, String] {
  match encode_module(mod) {
    Ok(bytes) => Ok(bytes)
    Err(e) => Err("encode failed: \{e}")
  }
}

///|
pub fn run_local_text_parse_print_lower_matrix(
  source : String,
  filename~ : String = "fuzz-text.wat",
) -> TextParsePrintLowerReport {
  let adapter_name = "starshine-local"
  let parsed = match @wast.wast_to_module(source, filename~) {
    Ok(wast_mod) => wast_mod
    Err(e) =>
      return TextParsePrintLowerReport::{
        adapter_name,
        original_parse_ok: false,
        print_ok: false,
        reparse_lower_ok: false,
        printed_text: None,
        original_lowered_bytes: None,
        reparsed_lowered_bytes: None,
        message: Some("parse failed: \{e}"),
      }
  }
  let original_lowered = match @wast.wast_ast_to_binary_module(parsed) {
    Ok(mod) => mod
    Err(e) =>
      return TextParsePrintLowerReport::{
        adapter_name,
        original_parse_ok: true,
        print_ok: false,
        reparse_lower_ok: false,
        printed_text: None,
        original_lowered_bytes: None,
        reparsed_lowered_bytes: None,
        message: Some("lower failed: \{e}"),
      }
  }
  let original_lowered_bytes = match
    encode_text_matrix_module(original_lowered) {
    Ok(bytes) => Some(bytes)
    Err(e) =>
      return TextParsePrintLowerReport::{
        adapter_name,
        original_parse_ok: true,
        print_ok: false,
        reparse_lower_ok: false,
        printed_text: None,
        original_lowered_bytes: None,
        reparsed_lowered_bytes: None,
        message: Some(e),
      }
  }
  let printed_text = match @wast.module_to_wast(parsed) {
    Ok(text) => text
    Err(e) =>
      return TextParsePrintLowerReport::{
        adapter_name,
        original_parse_ok: true,
        print_ok: false,
        reparse_lower_ok: false,
        printed_text: None,
        original_lowered_bytes,
        reparsed_lowered_bytes: None,
        message: Some("print failed: \{e}"),
      }
  }
  let reparsed = match
    @wast.wast_to_module(printed_text, filename="\{filename}.printed") {
    Ok(wast_mod) => wast_mod
    Err(e) =>
      return TextParsePrintLowerReport::{
        adapter_name,
        original_parse_ok: true,
        print_ok: true,
        reparse_lower_ok: false,
        printed_text: Some(printed_text),
        original_lowered_bytes,
        reparsed_lowered_bytes: None,
        message: Some("reparse failed: \{e}"),
      }
  }
  let reparsed_lowered = match @wast.wast_ast_to_binary_module(reparsed) {
    Ok(mod) => mod
    Err(e) =>
      return TextParsePrintLowerReport::{
        adapter_name,
        original_parse_ok: true,
        print_ok: true,
        reparse_lower_ok: false,
        printed_text: Some(printed_text),
        original_lowered_bytes,
        reparsed_lowered_bytes: None,
        message: Some("reparse lower failed: \{e}"),
      }
  }
  let reparsed_lowered_bytes = match
    encode_text_matrix_module(reparsed_lowered) {
    Ok(bytes) => Some(bytes)
    Err(e) =>
      return TextParsePrintLowerReport::{
        adapter_name,
        original_parse_ok: true,
        print_ok: true,
        reparse_lower_ok: false,
        printed_text: Some(printed_text),
        original_lowered_bytes,
        reparsed_lowered_bytes: None,
        message: Some(e),
      }
  }
  TextParsePrintLowerReport::{
    adapter_name,
    original_parse_ok: true,
    print_ok: true,
    reparse_lower_ok: true,
    printed_text: Some(printed_text),
    original_lowered_bytes,
    reparsed_lowered_bytes,
    message: None,
  }
}

///|
pub struct TextAggregateAdapterResult {
  adapter_name : String
  parse_ok : Bool
  print_ok : Bool?
  lower_ok : Bool?
  validates_ok : Bool?
  unsupported_syntax : Bool
  adapter_available : Bool
  message : String?
} derive(Eq, Debug)

///|
pub enum TextAggregateClassification {
  Accepted
  ParseDisagreement
  PrintDisagreement
  LowerDisagreement
  UnsupportedSyntax
  SemanticValidationDisagreement
  AdapterUnavailable
} derive(Eq, Debug)

///|
pub fn TextAggregateClassification::accepted() -> TextAggregateClassification {
  Accepted
}

///|
pub fn TextAggregateClassification::parse_disagreement() -> TextAggregateClassification {
  ParseDisagreement
}

///|
pub fn TextAggregateClassification::print_disagreement() -> TextAggregateClassification {
  PrintDisagreement
}

///|
pub fn TextAggregateClassification::lower_disagreement() -> TextAggregateClassification {
  LowerDisagreement
}

///|
pub fn TextAggregateClassification::unsupported_syntax() -> TextAggregateClassification {
  UnsupportedSyntax
}

///|
pub fn TextAggregateClassification::semantic_validation_disagreement() -> TextAggregateClassification {
  SemanticValidationDisagreement
}

///|
pub fn TextAggregateClassification::adapter_unavailable() -> TextAggregateClassification {
  AdapterUnavailable
}

///|
pub fn TextAggregateAdapterResult::external_accepted(
  adapter_name : String,
  validates_ok~ : Bool? = None,
) -> TextAggregateAdapterResult {
  TextAggregateAdapterResult::{
    adapter_name,
    parse_ok: true,
    print_ok: None,
    lower_ok: Some(true),
    validates_ok,
    unsupported_syntax: false,
    adapter_available: true,
    message: None,
  }
}

///|
pub fn TextAggregateAdapterResult::external_parse_error(
  adapter_name : String,
  message : String,
) -> TextAggregateAdapterResult {
  TextAggregateAdapterResult::{
    adapter_name,
    parse_ok: false,
    print_ok: None,
    lower_ok: None,
    validates_ok: None,
    unsupported_syntax: false,
    adapter_available: true,
    message: Some(message),
  }
}

///|
pub fn TextAggregateAdapterResult::external_unsupported_syntax(
  adapter_name : String,
  message : String,
) -> TextAggregateAdapterResult {
  TextAggregateAdapterResult::{
    adapter_name,
    parse_ok: false,
    print_ok: None,
    lower_ok: None,
    validates_ok: None,
    unsupported_syntax: true,
    adapter_available: true,
    message: Some(message),
  }
}

///|
pub fn TextAggregateAdapterResult::external_unavailable(
  adapter_name : String,
  message : String,
) -> TextAggregateAdapterResult {
  TextAggregateAdapterResult::{
    adapter_name,
    parse_ok: false,
    print_ok: None,
    lower_ok: None,
    validates_ok: None,
    unsupported_syntax: false,
    adapter_available: false,
    message: Some(message),
  }
}

///|
pub fn text_aggregate_local_result(
  report : TextParsePrintLowerReport,
  validates_ok~ : Bool? = None,
) -> TextAggregateAdapterResult {
  let lowered_bytes_equal = match
    (report.original_lowered_bytes, report.reparsed_lowered_bytes) {
    (Some(a), Some(b)) => a == b
    _ => false
  }
  let lower_ok = report.reparse_lower_ok && lowered_bytes_equal
  TextAggregateAdapterResult::{
    adapter_name: report.adapter_name,
    parse_ok: report.original_parse_ok,
    print_ok: Some(report.print_ok),
    lower_ok: Some(lower_ok),
    validates_ok,
    unsupported_syntax: false,
    adapter_available: true,
    message: report.message,
  }
}

///|
fn text_aggregate_has_bool_disagreement(values : Array[Bool?]) -> Bool {
  let mut seen = None
  for value in values {
    match value {
      Some(v) =>
        match seen {
          Some(prev) => if prev != v { return true }
          None => seen = Some(v)
        }
      None => ()
    }
  }
  false
}

///|
fn text_aggregate_has_false(values : Array[Bool?]) -> Bool {
  for value in values {
    match value {
      Some(false) => return true
      _ => ()
    }
  }
  false
}

///|
pub fn classify_n_way_text_aggregate(
  results : Array[TextAggregateAdapterResult],
) -> TextAggregateClassification {
  let print_values = Array::new()
  let lower_values = Array::new()
  let validation_values = Array::new()
  let mut any_parse_failed = false
  for result in results {
    if !result.adapter_available {
      return AdapterUnavailable
    }
    if result.unsupported_syntax {
      return UnsupportedSyntax
    }
    if !result.parse_ok {
      any_parse_failed = true
    }
    print_values.push(result.print_ok)
    lower_values.push(result.lower_ok)
    validation_values.push(result.validates_ok)
  }
  if any_parse_failed {
    return ParseDisagreement
  }
  if text_aggregate_has_false(print_values) ||
    text_aggregate_has_bool_disagreement(print_values) {
    return PrintDisagreement
  }
  if text_aggregate_has_false(lower_values) ||
    text_aggregate_has_bool_disagreement(lower_values) {
    return LowerDisagreement
  }
  if text_aggregate_has_bool_disagreement(validation_values) {
    return SemanticValidationDisagreement
  }
  Accepted
}

///|
pub fn text_aggregate_classification_label(
  classification : TextAggregateClassification,
) -> String {
  match classification {
    Accepted => "accepted"
    ParseDisagreement => "parse-disagreement"
    PrintDisagreement => "print-disagreement"
    LowerDisagreement => "lower-disagreement"
    UnsupportedSyntax => "unsupported-syntax"
    SemanticValidationDisagreement => "semantic-validation-disagreement"
    AdapterUnavailable => "adapter-unavailable"
  }
}

///|
pub struct TextDifferentialArtifactPaths {
  case_dir : String
  original_text_path : String
  starshine_printed_text_path : String?
  starshine_original_wasm_path : String?
  starshine_reparsed_wasm_path : String?
  adapters_path : String
  classification_path : String
} derive(Eq, Debug)

///|
fn text_adapter_result_meta(result : TextAggregateAdapterResult) -> String {
  let out = StringBuilder::new()
  out.write_string("adapter=\{result.adapter_name}\n")
  out.write_string("adapter_available=\{result.adapter_available}\n")
  out.write_string("unsupported_syntax=\{result.unsupported_syntax}\n")
  out.write_string("parse_ok=\{result.parse_ok}\n")
  out.write_string("print_ok=\{@debug.to_string(result.print_ok)}\n")
  out.write_string("lower_ok=\{@debug.to_string(result.lower_ok)}\n")
  out.write_string("validates_ok=\{@debug.to_string(result.validates_ok)}\n")
  match result.message {
    Some(message) => out.write_string("message=\{fuzz_single_line(message)}\n")
    None => out.write_string("message=\n")
  }
  out.to_string()
}

///|
fn text_differential_adapters_meta(
  local_report : TextParsePrintLowerReport,
  external_results : Array[TextAggregateAdapterResult],
) -> String {
  let out = StringBuilder::new()
  out.write_string("[local]\n")
  out.write_string(
    text_adapter_result_meta(text_aggregate_local_result(local_report)),
  )
  let mut i = 0
  while i < external_results.length() {
    out.write_string("[external \{i}]\n")
    out.write_string(text_adapter_result_meta(external_results[i]))
    i += 1
  }
  out.to_string()
}

///|
fn text_differential_classification_meta(
  local_report : TextParsePrintLowerReport,
  classification : TextAggregateClassification,
) -> String {
  let out = StringBuilder::new()
  out.write_string(
    "classification=\{text_aggregate_classification_label(classification)}\n",
  )
  out.write_string("local_adapter=\{local_report.adapter_name}\n")
  out.write_string("original_parse_ok=\{local_report.original_parse_ok}\n")
  out.write_string("print_ok=\{local_report.print_ok}\n")
  out.write_string("reparse_lower_ok=\{local_report.reparse_lower_ok}\n")
  match local_report.message {
    Some(message) =>
      out.write_string("local_message=\{fuzz_single_line(message)}\n")
    None => out.write_string("local_message=\n")
  }
  out.to_string()
}

///|
pub fn persist_text_differential_artifacts(
  original_text : String,
  local_report : TextParsePrintLowerReport,
  external_results : Array[TextAggregateAdapterResult],
  classification : TextAggregateClassification,
  io : FuzzFailurePersistIO,
  repro_dir~ : String = "text-repros",
  seed~ : UInt64 = 0UL,
  attempt~ : Int32 = 0,
) -> Result[TextDifferentialArtifactPaths, String] {
  let trimmed = repro_dir.trim().to_owned()
  let base_dir = if trimmed.length() == 0 { "text-repros" } else { trimmed }
  match (io.ensure_dir)(base_dir) {
    Ok(_) => ()
    Err(e) => return Err("failed to ensure text repro dir \{base_dir}: \{e}")
  }
  let class_slug = text_aggregate_classification_label(classification)
  let case_dir = "\{base_dir}/seed-\{seed}-attempt-\{attempt}-\{class_slug}"
  match (io.ensure_dir)(case_dir) {
    Ok(_) => ()
    Err(e) =>
      return Err("failed to ensure text repro case dir \{case_dir}: \{e}")
  }
  let original_path = "\{case_dir}/original.wat"
  match (io.write_file)(original_path, fuzz_bytes_from_string(original_text)) {
    Ok(_) => ()
    Err(e) =>
      return Err(
        "failed to write original text artifact \{original_path}: \{e}",
      )
  }
  let printed_path = match local_report.printed_text {
    Some(text) => {
      let path = "\{case_dir}/starshine.printed.wat"
      match (io.write_file)(path, fuzz_bytes_from_string(text)) {
        Ok(_) => Some(path)
        Err(e) =>
          return Err(
            "failed to write Starshine printed text artifact \{path}: \{e}",
          )
      }
    }
    None => None
  }
  let original_wasm_path = match local_report.original_lowered_bytes {
    Some(bytes) => {
      let path = "\{case_dir}/starshine.original.wasm"
      match (io.write_file)(path, bytes) {
        Ok(_) => Some(path)
        Err(e) =>
          return Err(
            "failed to write Starshine original wasm artifact \{path}: \{e}",
          )
      }
    }
    None => None
  }
  let reparsed_wasm_path = match local_report.reparsed_lowered_bytes {
    Some(bytes) => {
      let path = "\{case_dir}/starshine.reparsed.wasm"
      match (io.write_file)(path, bytes) {
        Ok(_) => Some(path)
        Err(e) =>
          return Err(
            "failed to write Starshine reparsed wasm artifact \{path}: \{e}",
          )
      }
    }
    None => None
  }
  let adapters_path = "\{case_dir}/adapters.txt"
  match
    (io.write_file)(
      adapters_path,
      fuzz_bytes_from_string(
        text_differential_adapters_meta(local_report, external_results),
      ),
    ) {
    Ok(_) => ()
    Err(e) =>
      return Err("failed to write text adapter artifact \{adapters_path}: \{e}")
  }
  let classification_path = "\{case_dir}/classification.txt"
  match
    (io.write_file)(
      classification_path,
      fuzz_bytes_from_string(
        text_differential_classification_meta(local_report, classification),
      ),
    ) {
    Ok(_) => ()
    Err(e) =>
      return Err(
        "failed to write text classification artifact \{classification_path}: \{e}",
      )
  }
  Ok(TextDifferentialArtifactPaths::{
    case_dir,
    original_text_path: original_path,
    starshine_printed_text_path: printed_path,
    starshine_original_wasm_path: original_wasm_path,
    starshine_reparsed_wasm_path: reparsed_wasm_path,
    adapters_path,
    classification_path,
  })
}

///|
pub enum BinaryValidationOutcome {
  BinaryValid
  BinaryInvalidDecode
  BinaryInvalidValidate
  BinaryToolFailure
  BinaryUnsupportedFeature
  BinaryAdapterUnavailable
} derive(Eq, Debug)

///|
pub struct BinaryDifferentialAdapterResult {
  adapter_name : String
  outcome : BinaryValidationOutcome
  message : String?
} derive(Eq, Debug)

///|
pub enum BinaryDifferentialClassification {
  BinaryAgreeValid
  BinaryAgreeInvalid
  BinaryProposalGap
  BinaryDecoderStageDisagreement
  BinaryValidatorStageDisagreement
  BinaryToolFailureClassification
  BinaryUnsupportedFeatureClassification
  BinaryAdapterUnavailableClassification
} derive(Eq, Debug)

///|
pub fn BinaryDifferentialAdapterResult::valid(
  adapter_name : String,
) -> BinaryDifferentialAdapterResult {
  { adapter_name, outcome: BinaryValid, message: None, }
}

///|
pub fn BinaryDifferentialAdapterResult::invalid_decode(
  adapter_name : String,
  message : String,
) -> BinaryDifferentialAdapterResult {
  { adapter_name, outcome: BinaryInvalidDecode, message: Some(message), }
}

///|
pub fn BinaryDifferentialAdapterResult::invalid_validate(
  adapter_name : String,
  message : String,
) -> BinaryDifferentialAdapterResult {
  { adapter_name, outcome: BinaryInvalidValidate, message: Some(message), }
}

///|
pub fn BinaryDifferentialAdapterResult::tool_failure(
  adapter_name : String,
  message : String,
) -> BinaryDifferentialAdapterResult {
  { adapter_name, outcome: BinaryToolFailure, message: Some(message), }
}

///|
pub fn BinaryDifferentialAdapterResult::unsupported_feature(
  adapter_name : String,
  message : String,
) -> BinaryDifferentialAdapterResult {
  { adapter_name, outcome: BinaryUnsupportedFeature, message: Some(message), }
}

///|
pub fn BinaryDifferentialAdapterResult::adapter_unavailable(
  adapter_name : String,
  message : String,
) -> BinaryDifferentialAdapterResult {
  { adapter_name, outcome: BinaryAdapterUnavailable, message: Some(message), }
}

///|
pub fn BinaryDifferentialClassification::agree_valid() -> BinaryDifferentialClassification {
  BinaryAgreeValid
}

///|
pub fn BinaryDifferentialClassification::agree_invalid() -> BinaryDifferentialClassification {
  BinaryAgreeInvalid
}

///|
pub fn BinaryDifferentialClassification::proposal_gap() -> BinaryDifferentialClassification {
  BinaryProposalGap
}

///|
pub fn BinaryDifferentialClassification::decoder_stage_disagreement() -> BinaryDifferentialClassification {
  BinaryDecoderStageDisagreement
}

///|
pub fn BinaryDifferentialClassification::validator_stage_disagreement() -> BinaryDifferentialClassification {
  BinaryValidatorStageDisagreement
}

///|
pub fn BinaryDifferentialClassification::tool_failure() -> BinaryDifferentialClassification {
  BinaryToolFailureClassification
}

///|
pub fn BinaryDifferentialClassification::unsupported_feature() -> BinaryDifferentialClassification {
  BinaryUnsupportedFeatureClassification
}

///|
pub fn BinaryDifferentialClassification::adapter_unavailable() -> BinaryDifferentialClassification {
  BinaryAdapterUnavailableClassification
}

///|
pub fn binary_differential_classification_label(
  classification : BinaryDifferentialClassification,
) -> String {
  match classification {
    BinaryAgreeValid => "agree-valid"
    BinaryAgreeInvalid => "agree-invalid"
    BinaryProposalGap => "proposal-gap"
    BinaryDecoderStageDisagreement => "decoder-stage-disagreement"
    BinaryValidatorStageDisagreement => "validator-stage-disagreement"
    BinaryToolFailureClassification => "tool-failure"
    BinaryUnsupportedFeatureClassification => "unsupported-feature"
    BinaryAdapterUnavailableClassification => "adapter-unavailable"
  }
}

///|
pub fn classify_binary_differential_results(
  results : Array[BinaryDifferentialAdapterResult],
) -> BinaryDifferentialClassification {
  let mut saw_valid = false
  let mut saw_decode_invalid = false
  let mut saw_validate_invalid = false
  let mut saw_unsupported = false
  for result in results {
    match result.outcome {
      BinaryAdapterUnavailable => return BinaryAdapterUnavailableClassification
      BinaryToolFailure => return BinaryToolFailureClassification
      BinaryUnsupportedFeature => saw_unsupported = true
      BinaryValid => saw_valid = true
      BinaryInvalidDecode => saw_decode_invalid = true
      BinaryInvalidValidate => saw_validate_invalid = true
    }
  }
  if saw_unsupported {
    if saw_valid || saw_decode_invalid || saw_validate_invalid {
      return BinaryProposalGap
    }
    return BinaryUnsupportedFeatureClassification
  }
  if saw_valid && !saw_decode_invalid && !saw_validate_invalid {
    return BinaryAgreeValid
  }
  if !saw_valid && saw_decode_invalid && !saw_validate_invalid {
    return BinaryAgreeInvalid
  }
  if !saw_valid && !saw_decode_invalid && saw_validate_invalid {
    return BinaryAgreeInvalid
  }
  if saw_decode_invalid {
    return BinaryDecoderStageDisagreement
  }
  BinaryValidatorStageDisagreement
}

///|
fn binary_differential_message_is_unsupported(message : String) -> Bool {
  let lower = differential_adapter_ascii_lower(message)
  lower.contains("unsupported") || lower.contains("proposal")
}

///|
pub fn binary_differential_failed_validation_result(
  adapter_name : String,
  bytes : Bytes,
  message : String,
) -> BinaryDifferentialAdapterResult {
  if binary_differential_message_is_unsupported(message) {
    return BinaryDifferentialAdapterResult::unsupported_feature(
      adapter_name, message,
    )
  }
  match decode_module(bytes) {
    Err(_) =>
      BinaryDifferentialAdapterResult::invalid_decode(adapter_name, message)
    Ok(_) =>
      BinaryDifferentialAdapterResult::invalid_validate(adapter_name, message)
  }
}

///|
pub struct BinaryDifferentialSmokeAdapter {
  adapter_name : String
  run : (Bytes) -> BinaryDifferentialAdapterResult
}

///|
pub fn BinaryDifferentialSmokeAdapter::new(
  adapter_name : String,
  run : (Bytes) -> BinaryDifferentialAdapterResult,
) -> BinaryDifferentialSmokeAdapter {
  { adapter_name, run, }
}

///|
pub struct BinaryDifferentialSmokeReport {
  suite : String
  cases : Int32
  agree_valid : Int32
  agree_invalid : Int32
  proposal_gap : Int32
  decoder_stage_disagreement : Int32
  validator_stage_disagreement : Int32
  tool_failure : Int32
  unsupported_feature : Int32
  adapter_unavailable : Int32
  external_adapters : Array[String]
} derive(Eq, Debug)

///|
fn binary_differential_append_unique_name(
  names : Array[String],
  name : String,
) -> Unit {
  for existing in names {
    if existing == name {
      return
    }
  }
  names.push(name)
}

///|
fn binary_differential_starshine_result(
  bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  match decode_module(bytes) {
    Err(e) =>
      BinaryDifferentialAdapterResult::invalid_decode(
        "starshine",
        "decode failed: \{e}",
      )
    Ok(mod) =>
      match validate_module(mod) {
        Ok(_) => BinaryDifferentialAdapterResult::valid("starshine")
        Err(e) =>
          BinaryDifferentialAdapterResult::invalid_validate(
            "starshine",
            validation_error_message(e),
          )
      }
  }
}

///|
pub fn run_binary_differential_smoke(
  corpus : Array[Bytes],
  external_adapters~ : Array[BinaryDifferentialSmokeAdapter] = [],
) -> BinaryDifferentialSmokeReport {
  let external_names : Array[String] = []
  for adapter in external_adapters {
    binary_differential_append_unique_name(external_names, adapter.adapter_name)
  }
  let mut agree_valid = 0
  let mut agree_invalid = 0
  let mut proposal_gap = 0
  let mut decoder_stage_disagreement = 0
  let mut validator_stage_disagreement = 0
  let mut tool_failure = 0
  let mut unsupported_feature = 0
  let mut adapter_unavailable = 0
  for bytes in corpus {
    let results = [binary_differential_starshine_result(bytes)]
    for adapter in external_adapters {
      results.push((adapter.run)(bytes))
    }
    match classify_binary_differential_results(results) {
      BinaryAgreeValid => agree_valid += 1
      BinaryAgreeInvalid => agree_invalid += 1
      BinaryProposalGap => proposal_gap += 1
      BinaryDecoderStageDisagreement => decoder_stage_disagreement += 1
      BinaryValidatorStageDisagreement => validator_stage_disagreement += 1
      BinaryToolFailureClassification => tool_failure += 1
      BinaryUnsupportedFeatureClassification => unsupported_feature += 1
      BinaryAdapterUnavailableClassification => adapter_unavailable += 1
    }
  }
  BinaryDifferentialSmokeReport::{
    suite: "binary-differential",
    cases: corpus.length(),
    agree_valid,
    agree_invalid,
    proposal_gap,
    decoder_stage_disagreement,
    validator_stage_disagreement,
    tool_failure,
    unsupported_feature,
    adapter_unavailable,
    external_adapters: external_names,
  }
}

///|
fn binary_differential_json_escape(input : String) -> String {
  let out = StringBuilder::new()
  for c in input {
    if c == '\\' {
      out.write_string("\\\\")
    } else if c == '"' {
      out.write_string("\\\"")
    } else if c == '\n' {
      out.write_string("\\n")
    } else if c == '\r' {
      out.write_string("\\r")
    } else if c == '\t' {
      out.write_string("\\t")
    } else {
      out.write_char(c)
    }
  }
  out.to_string()
}

///|
fn binary_differential_adapter_names_json(names : Array[String]) -> String {
  let out = StringBuilder::new()
  out.write_char('[')
  let mut i = 0
  while i < names.length() {
    if i > 0 {
      out.write_char(',')
    }
    out.write_char('"')
    out.write_string(binary_differential_json_escape(names[i]))
    out.write_char('"')
    i += 1
  }
  out.write_char(']')
  out.to_string()
}

///|
pub fn format_binary_differential_smoke_report_json(
  report : BinaryDifferentialSmokeReport,
) -> String {
  "{\"suite\":\"\{binary_differential_json_escape(report.suite)}\",\"cases\":\{report.cases},\"agree_valid\":\{report.agree_valid},\"agree_invalid\":\{report.agree_invalid},\"proposal_gap\":\{report.proposal_gap},\"decoder_stage_disagreement\":\{report.decoder_stage_disagreement},\"validator_stage_disagreement\":\{report.validator_stage_disagreement},\"tool_failure\":\{report.tool_failure},\"unsupported_feature\":\{report.unsupported_feature},\"adapter_unavailable\":\{report.adapter_unavailable},\"external_adapters\":\{binary_differential_adapter_names_json(report.external_adapters)}}"
}

///|
pub struct DifferentialValidationReport {
  internal_valid : Bool
  wasm_tools_valid : Bool?
  binaryen_valid : Bool?
  per_case_timeout_ms : Int32?
  wasm_tools_timeout : Bool
  binaryen_timeout : Bool
} derive(Eq, Debug)

///|
pub impl Show for DifferentialValidationReport with fn output(self, logger) {
  logger.write_string(@debug.to_string(self))
}

///|
pub enum BinaryCanonicalityInputClass {
  CanonicalInput
  AcceptedNoncanonicalInput
  MalformedInput
} derive(Eq, Debug)

///|
pub enum BinaryCanonicalityClassification {
  CanonicalAccepted
  AcceptedNoncanonical
  MalformedRejected
  ExternalPolicyDisagreement
  DecoderBugDisagreement
} derive(Eq, Debug)

///|
pub fn BinaryCanonicalityInputClass::canonical() -> BinaryCanonicalityInputClass {
  CanonicalInput
}

///|
pub fn BinaryCanonicalityInputClass::accepted_noncanonical() -> BinaryCanonicalityInputClass {
  AcceptedNoncanonicalInput
}

///|
pub fn BinaryCanonicalityInputClass::malformed() -> BinaryCanonicalityInputClass {
  MalformedInput
}

///|
pub fn BinaryCanonicalityClassification::canonical_accepted() -> BinaryCanonicalityClassification {
  CanonicalAccepted
}

///|
pub fn BinaryCanonicalityClassification::accepted_noncanonical() -> BinaryCanonicalityClassification {
  AcceptedNoncanonical
}

///|
pub fn BinaryCanonicalityClassification::malformed_rejected() -> BinaryCanonicalityClassification {
  MalformedRejected
}

///|
pub fn BinaryCanonicalityClassification::external_policy_disagreement() -> BinaryCanonicalityClassification {
  ExternalPolicyDisagreement
}

///|
pub fn BinaryCanonicalityClassification::decoder_bug_disagreement() -> BinaryCanonicalityClassification {
  DecoderBugDisagreement
}

///|
fn binary_canonicality_external_disagrees(
  internal_valid : Bool,
  wasm_tools_valid : Bool?,
  binaryen_valid : Bool?,
) -> Bool {
  match wasm_tools_valid {
    Some(v) => if v != internal_valid { return true }
    None => ()
  }
  match binaryen_valid {
    Some(v) => if v != internal_valid { return true }
    None => ()
  }
  false
}

///|
pub fn classify_binary_canonicality_differential(
  input_class : BinaryCanonicalityInputClass,
  internal_valid : Bool,
  wasm_tools_valid : Bool?,
  binaryen_valid : Bool?,
) -> BinaryCanonicalityClassification {
  if binary_canonicality_external_disagrees(
      internal_valid, wasm_tools_valid, binaryen_valid,
    ) {
    match input_class {
      AcceptedNoncanonicalInput => ExternalPolicyDisagreement
      _ => DecoderBugDisagreement
    }
  } else {
    match input_class {
      CanonicalInput => CanonicalAccepted
      AcceptedNoncanonicalInput => AcceptedNoncanonical
      MalformedInput => MalformedRejected
    }
  }
}

///|
pub struct BinaryCanonicalitySummary {
  total : Int32
  canonical : Int32
  accepted_noncanonical : Int32
  malformed_rejected : Int32
  external_policy_disagreements : Int32
  decoder_bug_disagreements : Int32
} derive(Eq, Debug)

///|
pub fn summarize_binary_canonicality_classifications(
  classifications : Array[BinaryCanonicalityClassification],
) -> BinaryCanonicalitySummary {
  let mut canonical = 0
  let mut accepted_noncanonical = 0
  let mut malformed_rejected = 0
  let mut external_policy_disagreements = 0
  let mut decoder_bug_disagreements = 0
  for classification in classifications {
    match classification {
      CanonicalAccepted => canonical = canonical + 1
      AcceptedNoncanonical => accepted_noncanonical = accepted_noncanonical + 1
      MalformedRejected => malformed_rejected = malformed_rejected + 1
      ExternalPolicyDisagreement =>
        external_policy_disagreements = external_policy_disagreements + 1
      DecoderBugDisagreement =>
        decoder_bug_disagreements = decoder_bug_disagreements + 1
    }
  }
  {
    total: classifications.length(),
    canonical,
    accepted_noncanonical,
    malformed_rejected,
    external_policy_disagreements,
    decoder_bug_disagreements,
  }
}

///|
pub enum ExportInvocationResult {
  Value(String)
  Trap(String)
  UnsupportedRuntime(String)
  NondeterministicImport(String)
} derive(Eq, Debug)

///|
pub fn ExportInvocationResult::value(value : String) -> ExportInvocationResult {
  Value(value)
}

///|
pub fn ExportInvocationResult::trap(message : String) -> ExportInvocationResult {
  Trap(message)
}

///|
pub fn ExportInvocationResult::unsupported_runtime(
  message : String,
) -> ExportInvocationResult {
  UnsupportedRuntime(message)
}

///|
pub fn ExportInvocationResult::nondeterministic_import(
  import_name : String,
) -> ExportInvocationResult {
  NondeterministicImport(import_name)
}

///|
pub enum ExportInvocationClassification {
  EqualResult
  EqualTrap
  UnsupportedRuntime
  NondeterministicImport
  SemanticMismatch(String)
} derive(Eq, Debug)

///|
pub struct ExportInvocationComparisonReport {
  export_name : String
  args : Array[String]
  left_result : ExportInvocationResult
  right_result : ExportInvocationResult
  classification : ExportInvocationClassification
} derive(Eq, Debug)

///|
pub struct ExportInvocationMatrixSummary {
  total : Int32
  equal_results : Int32
  equal_traps : Int32
  unsupported_runtimes : Int32
  nondeterministic_imports : Int32
  semantic_mismatches : Int32
} derive(Eq, Debug)

///|
pub enum ExportInvocationMatrixOutcome {
  Empty
  AllEqual
  Blocked
  SemanticMismatch
} derive(Eq, Debug)

///|
pub enum ExportInvocationFailurePolicy {
  Informational
  FailOnSemanticMismatch
} derive(Eq, Debug)

///|
pub fn ExportInvocationClassification::equal_result() -> ExportInvocationClassification {
  EqualResult
}

///|
pub fn ExportInvocationClassification::equal_trap() -> ExportInvocationClassification {
  EqualTrap
}

///|
pub fn ExportInvocationClassification::unsupported_runtime() -> ExportInvocationClassification {
  UnsupportedRuntime
}

///|
pub fn ExportInvocationClassification::nondeterministic_import() -> ExportInvocationClassification {
  NondeterministicImport
}

///|
pub fn ExportInvocationClassification::semantic_mismatch(
  message : String,
) -> ExportInvocationClassification {
  SemanticMismatch(message)
}

///|
pub fn ExportInvocationMatrixOutcome::empty() -> ExportInvocationMatrixOutcome {
  Empty
}

///|
pub fn ExportInvocationMatrixOutcome::all_equal() -> ExportInvocationMatrixOutcome {
  AllEqual
}

///|
pub fn ExportInvocationMatrixOutcome::blocked() -> ExportInvocationMatrixOutcome {
  Blocked
}

///|
pub fn ExportInvocationMatrixOutcome::semantic_mismatch() -> ExportInvocationMatrixOutcome {
  SemanticMismatch
}

///|
pub fn ExportInvocationFailurePolicy::informational() -> ExportInvocationFailurePolicy {
  Informational
}

///|
pub fn ExportInvocationFailurePolicy::fail_on_semantic_mismatch() -> ExportInvocationFailurePolicy {
  FailOnSemanticMismatch
}

///|
pub fn choose_simple_export_invocation_args(
  params : Array[@lib.ValType],
) -> Result[Array[String], String] {
  let args = Array::new(capacity=params.length())
  for param in params {
    match param {
      @lib.NumTypeValType(@lib.I32NumType) => args.push("i32:0")
      @lib.NumTypeValType(@lib.I64NumType) => args.push("i64:0")
      @lib.NumTypeValType(@lib.F32NumType) => args.push("f32:0")
      @lib.NumTypeValType(@lib.F64NumType) => args.push("f64:0")
      other =>
        return Err(
          "unsupported export param type for simple invocation: \{@debug.to_string(other)}",
        )
    }
  }
  Ok(args)
}

///|
pub fn classify_export_invocation_results(
  left : ExportInvocationResult,
  right : ExportInvocationResult,
) -> ExportInvocationClassification {
  match (left, right) {
    (UnsupportedRuntime(_), _) | (_, UnsupportedRuntime(_)) =>
      UnsupportedRuntime
    (NondeterministicImport(_), _) | (_, NondeterministicImport(_)) =>
      NondeterministicImport
    (Trap(_), Trap(_)) => EqualTrap
    (Value(a), Value(b)) =>
      if a == b {
        EqualResult
      } else {
        SemanticMismatch("value mismatch: left=\{a} right=\{b}")
      }
    (Trap(a), Value(b)) =>
      SemanticMismatch("trap/value mismatch: left=\{a} right=\{b}")
    (Value(a), Trap(b)) =>
      SemanticMismatch("value/trap mismatch: left=\{a} right=\{b}")
  }
}

///|
pub fn compare_export_invocation_results(
  export_name : String,
  args : Array[String],
  left : ExportInvocationResult,
  right : ExportInvocationResult,
) -> ExportInvocationComparisonReport {
  ExportInvocationComparisonReport::{
    export_name,
    args,
    left_result: left,
    right_result: right,
    classification: classify_export_invocation_results(left, right),
  }
}

///|
pub fn summarize_export_invocation_matrix(
  reports : Array[ExportInvocationComparisonReport],
) -> ExportInvocationMatrixSummary {
  let mut equal_results = 0
  let mut equal_traps = 0
  let mut unsupported_runtimes = 0
  let mut nondeterministic_imports = 0
  let mut semantic_mismatches = 0
  for report in reports {
    match report.classification {
      EqualResult => equal_results = equal_results + 1
      EqualTrap => equal_traps = equal_traps + 1
      UnsupportedRuntime => unsupported_runtimes = unsupported_runtimes + 1
      NondeterministicImport =>
        nondeterministic_imports = nondeterministic_imports + 1
      SemanticMismatch(_) => semantic_mismatches = semantic_mismatches + 1
    }
  }
  ExportInvocationMatrixSummary::{
    total: reports.length(),
    equal_results,
    equal_traps,
    unsupported_runtimes,
    nondeterministic_imports,
    semantic_mismatches,
  }
}

///|
pub fn export_invocation_summary_has_semantic_mismatch(
  summary : ExportInvocationMatrixSummary,
) -> Bool {
  summary.semantic_mismatches > 0
}

///|
pub fn classify_export_invocation_matrix(
  summary : ExportInvocationMatrixSummary,
) -> ExportInvocationMatrixOutcome {
  if summary.total == 0 {
    Empty
  } else if summary.semantic_mismatches > 0 {
    SemanticMismatch
  } else if summary.unsupported_runtimes > 0 ||
    summary.nondeterministic_imports > 0 {
    Blocked
  } else {
    AllEqual
  }
}

///|
pub fn export_invocation_semantic_mismatch_reports(
  reports : Array[ExportInvocationComparisonReport],
) -> Array[ExportInvocationComparisonReport] {
  let mismatches = Array::new()
  for report in reports {
    match report.classification {
      SemanticMismatch(_) => mismatches.push(report)
      _ => ()
    }
  }
  mismatches
}

///|
pub fn export_invocation_matrix_should_fail(
  summary : ExportInvocationMatrixSummary,
  policy : ExportInvocationFailurePolicy,
) -> Bool {
  match policy {
    Informational => false
    FailOnSemanticMismatch => summary.semantic_mismatches > 0
  }
}

///|
pub struct CmdFuzzStats {
  attempts : Int32
  generated_valid : Int32
  generated_invalid : Int32
  pipeline_validated : Int32
  optimized : Int32
  roundtripped : Int32
  differential_checked : Int32
  optimize_idempotence_checked : Int32
  encode_decode_idempotence_checked : Int32
  optimizer_determinism_checked : Int32
  generator_profile : String
  pass_profile : String
} derive(Eq, Debug)

///|
pub impl Show for CmdFuzzStats with fn output(self, logger) {
  logger.write_string(@debug.to_string(self))
}

///|
pub fn CmdFuzzStats::new(
  attempts~ : Int32 = 0,
  generated_valid~ : Int32 = 0,
  generated_invalid~ : Int32 = 0,
  pipeline_validated~ : Int32 = 0,
  optimized~ : Int32 = 0,
  roundtripped~ : Int32 = 0,
  differential_checked~ : Int32 = 0,
  optimize_idempotence_checked~ : Int32 = 0,
  encode_decode_idempotence_checked~ : Int32 = 0,
  optimizer_determinism_checked~ : Int32 = 0,
  generator_profile~ : String = "natural-wide",
  pass_profile~ : String = "custom",
) -> CmdFuzzStats {
  {
    attempts,
    generated_valid,
    generated_invalid,
    pipeline_validated,
    optimized,
    roundtripped,
    differential_checked,
    optimize_idempotence_checked,
    encode_decode_idempotence_checked,
    optimizer_determinism_checked,
    generator_profile,
    pass_profile,
  }
}

///|
pub struct FuzzFailureReport {
  seed : UInt64
  attempt : Int32
  generated_valid : Int32
  stage : String
  message : String
  optimize_passes : Array[String]
  minimized_passes : Array[String]
  generator_profile : String
  pass_profile : String
  generator_config_label : String
  generated_attempts : Int32
  feature_facts : String
  wasm : Bytes?
  reduced_wasm : Bytes?
  reduction_original_size : Int32?
  reduction_final_size : Int32?
  reduction_predicate_evaluations : Int32?
  reduction_steps : Array[FuzzReductionStep]
} derive(Eq, Debug)

///|
pub struct FuzzCorpusSource {
  seed : UInt64
  attempt : Int32
  generator_profile : String
  pass_profile : String
  stage : String
} derive(Eq, Debug)

///|
pub fn FuzzCorpusSource::new(
  seed : UInt64,
  attempt : Int32,
  generator_profile : String,
  pass_profile : String,
  stage : String,
) -> FuzzCorpusSource {
  { seed, attempt, generator_profile, pass_profile, stage, }
}

///|
pub struct FuzzCorpusDedupDecision {
  hash : String
  duplicate : Bool
  canonical_seed : UInt64?
} derive(Eq, Debug)

///|
struct FuzzCorpusDedupSourceEntry {
  source : FuzzCorpusSource
  duplicate : Bool
  canonical_seed : UInt64
} derive(Eq, Debug)

///|
struct FuzzCorpusDedupHashEntry {
  hash : String
  canonical_seed : UInt64
  sources : Array[FuzzCorpusDedupSourceEntry]
} derive(Eq, Debug)

///|
pub struct FuzzCorpusDedupIndex {
  entries : Array[FuzzCorpusDedupHashEntry]
  positions : @hashmap.HashMap[String, Int32]
} derive(Debug)

///|
pub fn FuzzCorpusDedupIndex::new() -> FuzzCorpusDedupIndex {
  { entries: [], positions: @hashmap.new(capacity=64), }
}

///|
pub impl Show for FuzzFailureReport with fn output(self, logger) {
  logger.write_string(@debug.to_string(self))
}

///|
pub fn FuzzFailureReport::new(
  seed : UInt64,
  attempt : Int32,
  generated_valid : Int32,
  stage : String,
  message : String,
  optimize_passes~ : Array[String] = [],
  minimized_passes~ : Array[String] = [],
  generator_profile~ : String = "natural-wide",
  pass_profile~ : String = "custom",
  generator_config_label~ : String = "unknown",
  generated_attempts~ : Int32 = 0,
  feature_facts~ : String = "unknown",
  wasm~ : Bytes? = None,
  reduced_wasm~ : Bytes? = None,
  reduction_original_size~ : Int32? = None,
  reduction_final_size~ : Int32? = None,
  reduction_predicate_evaluations~ : Int32? = None,
  reduction_steps~ : Array[FuzzReductionStep] = [],
) -> FuzzFailureReport {
  {
    seed,
    attempt,
    generated_valid,
    stage,
    message,
    optimize_passes,
    minimized_passes,
    generator_profile,
    pass_profile,
    generator_config_label,
    generated_attempts,
    feature_facts,
    wasm,
    reduced_wasm,
    reduction_original_size,
    reduction_final_size,
    reduction_predicate_evaluations,
    reduction_steps,
  }
}

///|
pub struct FuzzFailurePersistIO {
  ensure_dir : (String) -> Result[Unit, String]
  write_file : (String, Bytes) -> Result[Unit, String]
}

///|
pub fn FuzzFailurePersistIO::new(
  ensure_dir~ : (String) -> Result[Unit, String] = fn(path) {
    Err("ensure_dir not configured: \{path}")
  },
  write_file~ : (String, Bytes) -> Result[Unit, String] = fn(path, _) {
    Err("write_file not configured: \{path}")
  },
) -> FuzzFailurePersistIO {
  { ensure_dir, write_file, }
}

///|
fn fuzz_clone_passes(values : Array[String]) -> Array[String] {
  let out = Array::new(capacity=values.length())
  for value in values {
    out.push(value)
  }
  out
}

///|
pub struct FuzzReductionStep {
  kind : String
  start : Int32
  len : Int32
  before_size : Int32
  after_size : Int32
} derive(Eq, Debug)

///|
pub struct FuzzReductionReport[T] {
  result : T
  original_size : Int32
  final_size : Int32
  predicate_evaluations : Int32
  steps : Array[FuzzReductionStep]
} derive(Debug)

///|
fn[T] fuzz_clone_sequence(values : Array[T]) -> Array[T] {
  let out = Array::new(capacity=values.length())
  for value in values {
    out.push(value)
  }
  out
}

///|
fn[T] fuzz_without_sequence_range(
  values : Array[T],
  start : Int32,
  len : Int32,
) -> Array[T] {
  let out = Array::new(capacity=values.length())
  let end = start + len
  let mut i = 0
  while i < values.length() {
    if i < start || i >= end {
      out.push(values[i])
    }
    i += 1
  }
  out
}

///|
pub fn[T] reduce_fuzz_sequence_by_deletion(
  values : Array[T],
  reproduces : (Array[T]) -> Bool,
) -> FuzzReductionReport[Array[T]] {
  let mut predicate_evaluations = 1
  let steps : Array[FuzzReductionStep] = []
  if !reproduces(values) {
    let result = fuzz_clone_sequence(values)
    return {
      result,
      original_size: values.length(),
      final_size: result.length(),
      predicate_evaluations,
      steps,
    }
  }
  let mut current = fuzz_clone_sequence(values)
  let mut chunk = current.length() / 2
  while chunk > 0 {
    let mut changed = false
    let mut i = 0
    while i + chunk <= current.length() {
      let candidate = fuzz_without_sequence_range(current, i, chunk)
      predicate_evaluations = predicate_evaluations + 1
      if reproduces(candidate) {
        steps.push({
          kind: "delete-range",
          start: i,
          len: chunk,
          before_size: current.length(),
          after_size: candidate.length(),
        })
        current = candidate
        changed = true
      } else {
        i += 1
      }
    }
    if !changed {
      chunk = chunk / 2
    } else if chunk > current.length() {
      chunk = current.length()
    }
  }
  let mut i = 0
  while i < current.length() {
    let candidate = fuzz_without_sequence_range(current, i, 1)
    predicate_evaluations = predicate_evaluations + 1
    if reproduces(candidate) {
      steps.push({
        kind: "delete-range",
        start: i,
        len: 1,
        before_size: current.length(),
        after_size: candidate.length(),
      })
      current = candidate
    } else {
      i += 1
    }
  }
  {
    result: current,
    original_size: values.length(),
    final_size: current.length(),
    predicate_evaluations,
    steps,
  }
}

///|
fn fuzz_without_byte_range(bytes : Bytes, start : Int32, len : Int32) -> Bytes {
  let out = @buffer.new(size_hint=bytes.length())
  let end = start + len
  let mut i = 0
  while i < bytes.length() {
    if i < start || i >= end {
      out.write_byte(bytes[i])
    }
    i += 1
  }
  out.to_bytes()
}

///|
fn fuzz_clone_bytes(bytes : Bytes) -> Bytes {
  fuzz_without_byte_range(bytes, bytes.length(), 0)
}

///|
pub fn reduce_fuzz_bytes_by_slice_deletion(
  bytes : Bytes,
  reproduces : (Bytes) -> Bool,
) -> FuzzReductionReport[Bytes] {
  let mut predicate_evaluations = 1
  let steps : Array[FuzzReductionStep] = []
  if !reproduces(bytes) {
    let result = fuzz_clone_bytes(bytes)
    return {
      result,
      original_size: bytes.length(),
      final_size: result.length(),
      predicate_evaluations,
      steps,
    }
  }
  let mut current = fuzz_clone_bytes(bytes)
  let mut chunk = current.length() / 2
  while chunk > 0 {
    let mut changed = false
    let mut i = 0
    while i + chunk <= current.length() {
      let candidate = fuzz_without_byte_range(current, i, chunk)
      predicate_evaluations = predicate_evaluations + 1
      if reproduces(candidate) {
        steps.push({
          kind: "delete-byte-range",
          start: i,
          len: chunk,
          before_size: current.length(),
          after_size: candidate.length(),
        })
        current = candidate
        changed = true
      } else {
        i += 1
      }
    }
    if !changed {
      chunk = chunk / 2
    } else if chunk > current.length() {
      chunk = current.length()
    }
  }
  let mut i = 0
  while i < current.length() {
    let candidate = fuzz_without_byte_range(current, i, 1)
    predicate_evaluations = predicate_evaluations + 1
    if reproduces(candidate) {
      steps.push({
        kind: "delete-byte-range",
        start: i,
        len: 1,
        before_size: current.length(),
        after_size: candidate.length(),
      })
      current = candidate
    } else {
      i += 1
    }
  }
  {
    result: current,
    original_size: bytes.length(),
    final_size: current.length(),
    predicate_evaluations,
    steps,
  }
}

///|
fn fuzz_text_is_space(c : Int32) -> Bool {
  c == 0x20 || c == 0x0a || c == 0x0d || c == 0x09
}

///|
fn fuzz_text_slice(text : String, start : Int32, end : Int32) -> String {
  let len = end - start
  let chars : Array[Char] = Array::make(len, '\u{00}')
  for i = 0; i < len; i = i + 1 {
    chars[i] = text[start + i].to_int().unsafe_to_char()
  }
  String::from_array(chars)
}

///|
fn fuzz_text_tokens(text : String) -> Array[String] {
  let tokens : Array[String] = []
  let mut start = 0
  let mut in_token = false
  for i = 0; i < text.length(); i = i + 1 {
    if fuzz_text_is_space(text[i].to_int()) {
      if in_token {
        tokens.push(fuzz_text_slice(text, start, i))
        in_token = false
      }
    } else if !in_token {
      start = i
      in_token = true
    }
  }
  if in_token {
    tokens.push(fuzz_text_slice(text, start, text.length()))
  }
  tokens
}

///|
fn fuzz_text_join_tokens(tokens : Array[String]) -> String {
  let out = StringBuilder::new()
  for i = 0; i < tokens.length(); i = i + 1 {
    if i > 0 {
      out.write_string(" ")
    }
    out.write_string(tokens[i])
  }
  out.to_string()
}

///|
fn fuzz_text_relabel_reduction_steps(
  steps : Array[FuzzReductionStep],
) -> Array[FuzzReductionStep] {
  let out : Array[FuzzReductionStep] = []
  for step in steps {
    out.push({
      kind: "delete-text-token-range",
      start: step.start,
      len: step.len,
      before_size: step.before_size,
      after_size: step.after_size,
    })
  }
  out
}

///|
pub fn reduce_fuzz_text_tokens_by_deletion(
  text : String,
  reproduces : (String) -> Bool,
) -> FuzzReductionReport[String] {
  let tokens = fuzz_text_tokens(text)
  let report = reduce_fuzz_sequence_by_deletion(tokens, fn(candidate_tokens) {
    reproduces(fuzz_text_join_tokens(candidate_tokens))
  })
  {
    result: fuzz_text_join_tokens(report.result),
    original_size: report.original_size,
    final_size: report.final_size,
    predicate_evaluations: report.predicate_evaluations,
    steps: fuzz_text_relabel_reduction_steps(report.steps),
  }
}

///|
fn fuzz_module_field_relabel_reduction_steps(
  steps : Array[FuzzReductionStep],
) -> Array[FuzzReductionStep] {
  let out : Array[FuzzReductionStep] = []
  for step in steps {
    out.push({
      kind: "delete-module-field-range",
      start: step.start,
      len: step.len,
      before_size: step.before_size,
      after_size: step.after_size,
    })
  }
  out
}

///|
pub fn reduce_fuzz_module_fields_by_deletion(
  fields : Array[ModuleField],
  reproduces : (Array[ModuleField]) -> Bool,
) -> FuzzReductionReport[Array[ModuleField]] {
  let report = reduce_fuzz_sequence_by_deletion(fields, reproduces)
  {
    result: report.result,
    original_size: report.original_size,
    final_size: report.final_size,
    predicate_evaluations: report.predicate_evaluations,
    steps: fuzz_module_field_relabel_reduction_steps(report.steps),
  }
}

///|
pub fn minimize_fuzz_passes(
  passes : Array[String],
  reproduces : (Array[String]) -> Bool,
) -> Array[String] {
  reduce_fuzz_sequence_by_deletion(passes, reproduces).result
}

///|
pub struct CmdFuzzHarnessProfileConfig {
  valid_target : Int32
  generator_profile : String
  generator_config : GenValidConfig
  pass_profile : String
  optimize_passes : Array[String]
  check_optimize_idempotence : Bool
  check_encode_decode_idempotence : Bool
  check_optimizer_determinism : Bool
} derive(Eq, Debug)

///|
fn cmd_fuzz_is_policy_pass(name : String) -> Bool {
  name == "no-inline" || name == "no-full-inline" || name == "no-partial-inline"
}

///|
fn cmd_fuzz_each_pass_profile() -> Array[String] {
  let passes : Array[String] = []
  for entry in @passes.pass_registry_all() {
    match entry.category {
      @passes.HotPassRegistryCategory::HotPass
      | @passes.HotPassRegistryCategory::ModulePass =>
        if !cmd_fuzz_is_policy_pass(entry.name) {
          passes.push(entry.name)
        }
      _ => ()
    }
  }
  passes
}

///|
pub fn cmd_fuzz_random_pass_sequence(
  profile : String,
  seed : UInt64,
) -> Array[String] {
  @passes.pass_registry_random_executable_sequence(profile, seed)
}

///|
fn cmd_fuzz_default_pipeline_profile() -> Result[Array[String], String] {
  match @passes.pass_registry_lookup("optimize") {
    Some(entry) => Ok(entry.expanded_passes)
    None =>
      Err("cmd-harness default-pipeline profile could not find optimize preset")
  }
}

///|
fn cmd_fuzz_common_cluster_profile() -> Array[String] {
  [
    "dead-code-elimination", "remove-unused-names", "remove-unused-brs", "vacuum",
    "merge-blocks", "remove-unused-brs", "vacuum",
  ]
}

///|
fn cmd_fuzz_pass_profile(
  profile : String,
  seed : UInt64,
) -> Result[Array[String], String] {
  match profile {
    "none" => Ok([])
    "cleanup" => Ok(["vacuum", "remove-unused-brs"])
    "optimize-core" => Ok(["optimize-instructions", "precompute", "vacuum"])
    "each-pass" => Ok(cmd_fuzz_each_pass_profile())
    "common-cluster" | "common-clusters" =>
      Ok(cmd_fuzz_common_cluster_profile())
    "fixed-point-cleanup" =>
      Ok(@passes.pass_registry_fixed_point_cleanup_sequence())
    "default-pipeline" => cmd_fuzz_default_pipeline_profile()
    "random-short" | "random-medium" | "random-long" | "random-mixed" =>
      Ok(cmd_fuzz_random_pass_sequence(profile, seed))
    _ =>
      Err(
        "unknown cmd-harness pass profile '\{profile}'; expected none|cleanup|optimize-core|each-pass|common-clusters|fixed-point-cleanup|default-pipeline|random-short|random-medium|random-long|random-mixed",
      )
  }
}

///|
pub fn cmd_fuzz_harness_profile_config(
  profile : String,
) -> Result[CmdFuzzHarnessProfileConfig, String] {
  cmd_fuzz_harness_profile_config_with_seed(profile, 0UL)
}

///|
pub fn cmd_fuzz_harness_profile_config_with_seed(
  profile : String,
  seed : UInt64,
) -> Result[CmdFuzzHarnessProfileConfig, String] {
  let raw = profile.trim().to_owned()
  let parts : Array[String] = []
  for part in raw.split("+") {
    parts.push(part.to_owned())
  }
  let base = if parts.length() == 0 || parts[0].trim().to_owned().length() == 0 {
    "smoke"
  } else {
    fuzz_profile_ascii_lower(parts[0].trim().to_owned())
  }
  let valid_target = match base {
    "smoke" => 128
    "ci" => 4096
    "stress" => 100000
    _ =>
      return Err(
        "unknown cmd-harness fuzz profile '\{profile}'; expected smoke|ci|stress",
      )
  }
  let mut generator_profile = "natural-wide"
  let mut pass_profile = "none"
  let mut check_optimize_idempotence = false
  let mut check_encode_decode_idempotence = false
  let mut check_optimizer_determinism = false
  let mut i = 1
  while i < parts.length() {
    let part = fuzz_profile_ascii_lower(parts[i].trim().to_owned())
    if part.length() == 0 {
      ()
    } else if fuzz_has_prefix(part, "gen=") {
      generator_profile = part.sub(start=4).to_owned()
    } else if fuzz_has_prefix(part, "generator=") {
      generator_profile = part.sub(start=10).to_owned()
    } else if fuzz_has_prefix(part, "passes=") {
      pass_profile = part.sub(start=7).to_owned()
    } else if part == "idempotence" ||
      part == "opt-idempotence" ||
      part == "check-idempotence" {
      check_optimize_idempotence = true
    } else if part == "codec-idempotence" || part == "encode-decode-idempotence" {
      check_encode_decode_idempotence = true
    } else if part == "determinism" || part == "optimizer-determinism" {
      check_optimizer_determinism = true
    } else {
      return Err(
        "unknown cmd-harness profile modifier '\{parts[i]}' in '\{profile}'",
      )
    }
    i += 1
  }
  let generator_config = match GenValidConfig::profile(generator_profile) {
    Ok(v) => v
    Err(msg) => return Err(msg)
  }
  let pass_profile = match pass_profile {
    "common-cluster" => "common-clusters"
    _ => pass_profile
  }
  let optimize_passes = match cmd_fuzz_pass_profile(pass_profile, seed) {
    Ok(v) => v
    Err(msg) => return Err(msg)
  }
  Ok({
    valid_target,
    generator_profile,
    generator_config,
    pass_profile,
    optimize_passes,
    check_optimize_idempotence,
    check_encode_decode_idempotence,
    check_optimizer_determinism,
  })
}

///|
fn fuzz_has_prefix(text : String, prefix : String) -> Bool {
  if text.length() < prefix.length() {
    return false
  }
  let head = text.sub(end=prefix.length()).to_owned()
  head == prefix
}

///|
fn fuzz_has_suffix(text : String, suffix : String) -> Bool {
  if text.length() < suffix.length() {
    return false
  }
  let start = text.length() - suffix.length()
  let tail = text.sub(start~).to_owned()
  tail == suffix
}

///|
fn fuzz_bytes_from_string(text : String) -> Bytes {
  let buf = @buffer.new(size_hint=text.length())
  for c in text {
    buf.write_byte(c.to_int().to_byte())
  }
  buf.to_bytes()
}

///|
fn fuzz_corpus_hash_bytes(bytes : Bytes) -> String {
  let mut hash = 1469598103934665603UL
  let prime = 1099511628211UL
  let mut i = 0
  while i < bytes.length() {
    hash = (hash ^ bytes[i].to_uint64()) * prime
    i += 1
  }
  "fnv1a64-\{hash}"
}

///|
pub fn FuzzCorpusDedupIndex::record(
  self : FuzzCorpusDedupIndex,
  wasm : Bytes,
  source : FuzzCorpusSource,
) -> FuzzCorpusDedupDecision {
  let hash = fuzz_corpus_hash_bytes(wasm)
  match self.positions.get(hash) {
    Some(pos) => {
      let canonical_seed = self.entries[pos].canonical_seed
      self.entries[pos].sources.push({
        source,
        duplicate: true,
        canonical_seed,
      })
      { hash, duplicate: true, canonical_seed: Some(canonical_seed), }
    }
    None => {
      let canonical_seed = source.seed
      let sources = [{ source, duplicate: false, canonical_seed, }]
      self.positions.set(hash, self.entries.length())
      self.entries.push({ hash, canonical_seed, sources, })
      { hash, duplicate: false, canonical_seed: Some(canonical_seed), }
    }
  }
}

///|
pub fn FuzzCorpusDedupIndex::to_text(self : FuzzCorpusDedupIndex) -> String {
  let out = StringBuilder::new()
  out.write_string("version=1\n")
  for entry in self.entries {
    out.write_string(
      "hash=\{entry.hash} canonical_seed=\{entry.canonical_seed}\n",
    )
    for source_entry in entry.sources {
      let source = source_entry.source
      out.write_string(
        "source seed=\{source.seed} attempt=\{source.attempt} generator_profile=\{source.generator_profile} pass_profile=\{source.pass_profile} stage=\{source.stage} duplicate=\{source_entry.duplicate} canonical_seed=\{source_entry.canonical_seed}\n",
      )
    }
  }
  out.to_string()
}

///|
fn fuzz_single_line(text : String) -> String {
  let out = StringBuilder::new()
  for c in text {
    if c == '\n' || c == '\r' {
      out.write_char(' ')
    } else {
      out.write_char(c)
    }
  }
  out.to_string()
}

///|
fn fuzz_join_csv(values : Array[String]) -> String {
  let out = StringBuilder::new()
  let mut i = 0
  while i < values.length() {
    if i > 0 {
      out.write_char(',')
    }
    out.write_string(values[i])
    i += 1
  }
  out.to_string()
}

///|
fn fuzz_slug(value : String) -> String {
  let out = StringBuilder::new()
  let mut wrote_dash = false
  for c in value {
    let lower = if c >= 'A' && c <= 'Z' {
      (c.to_int() + 32).unsafe_to_char()
    } else {
      c
    }
    let is_alnum = (lower >= 'a' && lower <= 'z') ||
      (lower >= '0' && lower <= '9')
    if is_alnum {
      out.write_char(lower)
      wrote_dash = false
    } else if !wrote_dash {
      out.write_char('-')
      wrote_dash = true
    }
  }
  let raw = out.to_string()
  let trimmed = if fuzz_has_prefix(raw, "-") {
    raw.sub(start=1).to_owned()
  } else {
    raw
  }
  let cleaned = if fuzz_has_suffix(trimmed, "-") {
    trimmed.sub(end=trimmed.length() - 1).to_owned()
  } else {
    trimmed
  }
  if cleaned.length() == 0 {
    "unknown"
  } else {
    cleaned
  }
}

///|
fn fuzz_reduction_option_int_text(value : Int32?) -> String {
  match value {
    Some(v) => "\{v}"
    None => ""
  }
}

///|
fn fuzz_reduction_log_text(report : FuzzFailureReport) -> String {
  let out = StringBuilder::new()
  out.write_string(
    "original_size=\{fuzz_reduction_option_int_text(report.reduction_original_size)}\n",
  )
  out.write_string(
    "final_size=\{fuzz_reduction_option_int_text(report.reduction_final_size)}\n",
  )
  out.write_string(
    "predicate_evaluations=\{fuzz_reduction_option_int_text(report.reduction_predicate_evaluations)}\n",
  )
  let mut i = 0
  while i < report.reduction_steps.length() {
    let step = report.reduction_steps[i]
    out.write_string(
      "step kind=\{step.kind} start=\{step.start} len=\{step.len} before_size=\{step.before_size} after_size=\{step.after_size}\n",
    )
    i += 1
  }
  out.to_string()
}

///|
fn fuzz_failure_meta_text(
  report : FuzzFailureReport,
  reduced_wasm_path~ : String? = None,
  reduction_log_path~ : String? = None,
) -> String {
  let out = StringBuilder::new()
  out.write_string("seed=\{report.seed}\n")
  out.write_string("attempt=\{report.attempt}\n")
  out.write_string("generated_valid=\{report.generated_valid}\n")
  out.write_string("stage=\{fuzz_single_line(report.stage)}\n")
  out.write_string("message=\{fuzz_single_line(report.message)}\n")
  out.write_string("optimize_passes=\{fuzz_join_csv(report.optimize_passes)}\n")
  out.write_string(
    "minimized_passes=\{fuzz_join_csv(report.minimized_passes)}\n",
  )
  out.write_string("generator_profile=\{report.generator_profile}\n")
  out.write_string("pass_profile=\{report.pass_profile}\n")
  out.write_string("generator_config_label=\{report.generator_config_label}\n")
  out.write_string("generated_attempts=\{report.generated_attempts}\n")
  out.write_string("feature_facts=\{fuzz_single_line(report.feature_facts)}\n")
  out.write_string(
    "reduction_original_size=\{fuzz_reduction_option_int_text(report.reduction_original_size)}\n",
  )
  out.write_string(
    "reduction_final_size=\{fuzz_reduction_option_int_text(report.reduction_final_size)}\n",
  )
  out.write_string(
    "reduction_predicate_evaluations=\{fuzz_reduction_option_int_text(report.reduction_predicate_evaluations)}\n",
  )
  match reduced_wasm_path {
    Some(path) => out.write_string("reduced_wasm_path=\{path}\n")
    None => out.write_string("reduced_wasm_path=\n")
  }
  match reduction_log_path {
    Some(path) => out.write_string("reduction_log_path=\{path}\n")
    None => out.write_string("reduction_log_path=\n")
  }
  out.to_string()
}

///|
pub fn persist_fuzz_failure_report(
  report : FuzzFailureReport,
  io : FuzzFailurePersistIO,
  corpus_dir~ : String = "fuzz-corpus",
) -> Result[(String, String?), String] {
  let trimmed = corpus_dir.trim().to_owned()
  let dir = if trimmed.length() == 0 { "fuzz-corpus" } else { trimmed }
  match (io.ensure_dir)(dir) {
    Ok(_) => ()
    Err(e) => return Err("failed to ensure corpus dir \{dir}: \{e}")
  }
  let slug = fuzz_slug(report.stage)
  let prefix = "\{dir}/seed-\{report.seed}-attempt-\{report.attempt}-valid-\{report.generated_valid}-\{slug}"
  let meta_path = "\{prefix}.meta.txt"
  let reduced_wasm_path = match report.reduced_wasm {
    Some(_) => Some("\{prefix}.reduced.wasm")
    None => None
  }
  let has_reduction_log = report.reduction_steps.length() > 0 ||
    report.reduction_original_size != None ||
    report.reduction_final_size != None ||
    report.reduction_predicate_evaluations != None
  let reduction_log_path = if has_reduction_log {
    Some("\{prefix}.reduction.txt")
  } else {
    None
  }
  let meta_bytes = fuzz_bytes_from_string(
    fuzz_failure_meta_text(report, reduced_wasm_path~, reduction_log_path~),
  )
  match (io.write_file)(meta_path, meta_bytes) {
    Ok(_) => ()
    Err(e) => return Err("failed to write corpus metadata \{meta_path}: \{e}")
  }
  let wasm_path = match report.wasm {
    Some(bytes) => {
      let path = "\{prefix}.wasm"
      match (io.write_file)(path, bytes) {
        Ok(_) => Some(path)
        Err(e) => return Err("failed to write corpus wasm \{path}: \{e}")
      }
    }
    None => None
  }
  match (report.reduced_wasm, reduced_wasm_path) {
    (Some(bytes), Some(path)) =>
      match (io.write_file)(path, bytes) {
        Ok(_) => ()
        Err(e) =>
          return Err("failed to write reduced corpus wasm \{path}: \{e}")
      }
    _ => ()
  }
  match reduction_log_path {
    Some(path) =>
      match
        (io.write_file)(
          path,
          fuzz_bytes_from_string(fuzz_reduction_log_text(report)),
        ) {
        Ok(_) => ()
        Err(e) => return Err("failed to write reduction log \{path}: \{e}")
      }
    None => ()
  }
  Ok((meta_path, wasm_path))
}

///|
fn fuzz_finalize_failure(
  on_failure : ((FuzzFailureReport) -> Result[Unit, String])?,
  report : FuzzFailureReport,
  message : String,
) -> String {
  match on_failure {
    Some(cb) =>
      match cb(report) {
        Ok(_) => message
        Err(e) => "\{message}; failure callback error: \{e}"
      }
    None => message
  }
}

///|
fn fuzz_bytes_equal(lhs : Bytes, rhs : Bytes) -> Bool {
  if lhs.length() != rhs.length() {
    return false
  }
  let mut i = 0
  while i < lhs.length() {
    if lhs[i] != rhs[i] {
      return false
    }
    i += 1
  }
  true
}

///|
fn decode_then_validate(bytes : Bytes) -> Result[Bool, String] {
  match decode_module(bytes) {
    Ok(mod) =>
      match validate_module(mod) {
        Ok(_) => Ok(true)
        Err(_) => Ok(false)
      }
    Err(_) => Ok(false)
  }
}

///|
fn fuzz_minimize_optimize_failure(
  bytes : Bytes,
  passes : Array[String],
) -> Array[String] {
  minimize_fuzz_passes(passes, fn(candidate) {
    match decode_module(bytes) {
      Ok(mod) =>
        match
          optimize_module_with_pass_flags(
            mod,
            candidate,
            OptimizeOptions::new(),
          ) {
          Ok(_) => false
          Err(_) => true
        }
      Err(_) => false
    }
  })
}

///|
fn fuzz_minimize_optimizer_idempotence(
  bytes : Bytes,
  passes : Array[String],
) -> Array[String] {
  minimize_fuzz_passes(passes, fn(candidate) {
    let first_input = match decode_module(bytes) {
      Ok(mod) => mod
      Err(_) => return false
    }
    let first_optimized = match
      optimize_module_with_pass_flags(
        first_input,
        candidate,
        OptimizeOptions::new(),
      ) {
      Ok(mod) => mod
      Err(_) => return false
    }
    let first_bytes = match encode_module(first_optimized) {
      Ok(encoded) => encoded
      Err(_) => return false
    }
    let second_input = match decode_module(first_bytes) {
      Ok(mod) => mod
      Err(_) => return false
    }
    let second_optimized = match
      optimize_module_with_pass_flags(
        second_input,
        candidate,
        OptimizeOptions::new(),
      ) {
      Ok(mod) => mod
      Err(_) => return false
    }
    let second_bytes = match encode_module(second_optimized) {
      Ok(encoded) => encoded
      Err(_) => return false
    }
    !fuzz_bytes_equal(first_bytes, second_bytes)
  })
}

///|
fn fuzz_minimize_optimizer_nondeterminism(
  bytes : Bytes,
  passes : Array[String],
) -> Array[String] {
  minimize_fuzz_passes(passes, fn(candidate) {
    let left = match decode_module(bytes) {
      Ok(mod) => mod
      Err(_) => return false
    }
    let right = match decode_module(bytes) {
      Ok(mod) => mod
      Err(_) => return false
    }
    let left_optimized = match
      optimize_module_with_pass_flags(left, candidate, OptimizeOptions::new()) {
      Ok(mod) => mod
      Err(_) => return false
    }
    let right_optimized = match
      optimize_module_with_pass_flags(right, candidate, OptimizeOptions::new()) {
      Ok(mod) => mod
      Err(_) => return false
    }
    let left_bytes = match encode_module(left_optimized) {
      Ok(bytes) => bytes
      Err(_) => return false
    }
    let right_bytes = match encode_module(right_optimized) {
      Ok(bytes) => bytes
      Err(_) => return false
    }
    !fuzz_bytes_equal(left_bytes, right_bytes)
  })
}

///|
fn differential_adapter_ascii_lower(input : String) -> String {
  let out = StringBuilder::new()
  for c in input {
    if c >= 'A' && c <= 'Z' {
      out.write_char((c.to_int() + 32).unsafe_to_char())
    } else {
      out.write_char(c)
    }
  }
  out.to_string()
}

///|
fn differential_adapter_error_is_timeout(msg : String) -> Bool {
  let lower = differential_adapter_ascii_lower(msg)
  lower.contains("timeout") ||
  lower.contains("timed out") ||
  lower.contains("resource limit")
}

///|
pub fn differential_validate_wasm(
  bytes : Bytes,
  adapters~ : DifferentialAdapters = default_differential_adapters(),
) -> Result[DifferentialValidationReport, String] {
  let internal_valid = match decode_then_validate(bytes) {
    Ok(v) => v
    Err(e) => return Err("internal validation execution failed: \{e}")
  }
  let mut wasm_tools_timeout = false
  let wasm_tools_valid = match (adapters.wasm_tools_validate)(bytes) {
    Ok(v) => Some(v)
    Err(msg) => {
      wasm_tools_timeout = differential_adapter_error_is_timeout(msg)
      None
    }
  }
  let mut binaryen_timeout = false
  let binaryen_valid = match (adapters.binaryen_validate)(bytes) {
    Ok(v) => Some(v)
    Err(msg) => {
      binaryen_timeout = differential_adapter_error_is_timeout(msg)
      None
    }
  }

  match wasm_tools_valid {
    Some(v) =>
      if v != internal_valid {
        return Err(
          "differential mismatch against wasm-tools: internal=\{internal_valid} wasm-tools=\{v}",
        )
      }
    None => ()
  }
  match binaryen_valid {
    Some(v) =>
      if v != internal_valid {
        return Err(
          "differential mismatch against binaryen: internal=\{internal_valid} binaryen=\{v}",
        )
      }
    None => ()
  }
  Ok(DifferentialValidationReport::{
    internal_valid,
    wasm_tools_valid,
    binaryen_valid,
    per_case_timeout_ms: adapters.per_case_timeout_ms,
    wasm_tools_timeout,
    binaryen_timeout,
  })
}

///|
pub fn run_cmd_fuzz_harness(
  valid_target : Int32,
  seed~ : UInt64 = 0x5eedUL,
  optimize_passes~ : Array[String] = [],
  generator_profile~ : String = "natural-wide",
  generator_config~ : GenValidConfig? = None,
  pass_profile~ : String = "custom",
  check_optimize_idempotence~ : Bool = false,
  check_encode_decode_idempotence~ : Bool = false,
  check_optimizer_determinism~ : Bool = false,
  differential_adapters~ : DifferentialAdapters? = None,
  differential_every~ : Int32 = 0,
  on_failure~ : ((FuzzFailureReport) -> Result[Unit, String])? = None,
) -> Result[CmdFuzzStats, String] {
  let pass_names = fuzz_clone_passes(optimize_passes)
  let gen_config = match generator_config {
    Some(config) => config
    None =>
      match GenValidConfig::profile(generator_profile) {
        Ok(config) => config
        Err(msg) => return Err(msg)
      }
  }
  let gen_profile_label = generator_profile
  if valid_target < 0 {
    let report = FuzzFailureReport::new(
      seed,
      0,
      0,
      "config",
      "valid_target must be non-negative",
      optimize_passes=pass_names,
      minimized_passes=pass_names,
      generator_profile=gen_profile_label,
      pass_profile~,
    )
    return Err(
      fuzz_finalize_failure(
        on_failure, report, "valid_target must be non-negative",
      ),
    )
  }
  let rnd = @splitmix.new(seed~)
  let mut attempts = 0
  let mut generated_valid = 0
  let mut generated_invalid = 0
  let mut pipeline_validated = 0
  let mut optimized_count = 0
  let mut roundtripped = 0
  let mut differential_checked = 0
  let mut optimize_idempotence_checked = 0
  let mut encode_decode_idempotence_checked = 0
  let mut optimizer_determinism_checked = 0
  let max_attempts = if valid_target <= 0 { 0 } else { valid_target * 25 }

  while generated_valid < valid_target {
    if attempts >= max_attempts {
      let msg = "failed to reach valid target: target=\{valid_target} attempts=\{attempts} valid=\{generated_valid} invalid=\{generated_invalid}"
      let report = FuzzFailureReport::new(
        seed,
        attempts,
        generated_valid,
        "generation-target",
        msg,
        optimize_passes=pass_names,
        minimized_passes=pass_names,
        generator_profile=gen_profile_label,
        pass_profile~,
      )
      return Err(fuzz_finalize_failure(on_failure, report, msg))
    }
    attempts += 1
    let generated = match gen_valid_module_result(rnd, gen_config) {
      Ok(v) => v
      Err(_) => {
        generated_invalid += 1
        continue
      }
    }
    let generated_config_label = generated.config_label
    let generated_attempts = generated.attempts
    let feature_facts = @debug.to_string(generated.facts)
    let mod = generated.wasm_module
    match validate_module(mod) {
      Ok(_) => ()
      Err(_) => {
        generated_invalid += 1
        continue
      }
    }
    let encoded = match encode_module(mod) {
      Ok(bytes) => bytes
      Err(_) => {
        generated_invalid += 1
        continue
      }
    }
    let decoded = match decode_module(encoded) {
      Ok(decoded_mod) => decoded_mod
      Err(e) => {
        let msg = "decode failed at attempt #\{attempts}: \{e}"
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "decode",
          "\{e}",
          optimize_passes=pass_names,
          minimized_passes=pass_names,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(encoded),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
    }
    if check_encode_decode_idempotence {
      let recoded = match encode_module(decoded) {
        Ok(bytes) => bytes
        Err(e) => {
          let msg = "encode/decode idempotence encode failed at attempt #\{attempts}: \{e}"
          let report = FuzzFailureReport::new(
            seed,
            attempts,
            generated_valid,
            "encode-decode-idempotence",
            "\{e}",
            optimize_passes=pass_names,
            minimized_passes=pass_names,
            generator_profile=gen_profile_label,
            pass_profile~,
            generator_config_label=generated_config_label,
            generated_attempts~,
            feature_facts~,
            wasm=Some(encoded),
          )
          return Err(fuzz_finalize_failure(on_failure, report, msg))
        }
      }
      let recoded_mod = match decode_module(recoded) {
        Ok(v) => v
        Err(e) => {
          let msg = "encode/decode idempotence recode failed to decode at attempt #\{attempts}: \{e}"
          let report = FuzzFailureReport::new(
            seed,
            attempts,
            generated_valid,
            "encode-decode-idempotence",
            "\{e}",
            optimize_passes=pass_names,
            minimized_passes=pass_names,
            generator_profile=gen_profile_label,
            pass_profile~,
            generator_config_label=generated_config_label,
            generated_attempts~,
            feature_facts~,
            wasm=Some(recoded),
          )
          return Err(fuzz_finalize_failure(on_failure, report, msg))
        }
      }
      let recoded_again = match encode_module(recoded_mod) {
        Ok(v) => v
        Err(e) => {
          let msg = "encode/decode idempotence second encode failed at attempt #\{attempts}: \{e}"
          let report = FuzzFailureReport::new(
            seed,
            attempts,
            generated_valid,
            "encode-decode-idempotence",
            "\{e}",
            optimize_passes=pass_names,
            minimized_passes=pass_names,
            generator_profile=gen_profile_label,
            pass_profile~,
            generator_config_label=generated_config_label,
            generated_attempts~,
            feature_facts~,
            wasm=Some(recoded),
          )
          return Err(fuzz_finalize_failure(on_failure, report, msg))
        }
      }
      if !fuzz_bytes_equal(recoded, recoded_again) {
        let msg = "encode/decode idempotence changed canonical bytes at attempt #\{attempts}"
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "encode-decode-idempotence",
          msg,
          optimize_passes=pass_names,
          minimized_passes=pass_names,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(recoded_again),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
      encode_decode_idempotence_checked += 1
    }
    match validate_module(decoded) {
      Ok(_) => pipeline_validated += 1
      Err(e) => {
        let msg = "decoded module failed validation at attempt #\{attempts}: \{e}"
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "post-decode-validate",
          "\{e}",
          optimize_passes=pass_names,
          minimized_passes=pass_names,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(encoded),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
    }

    let optimized = match
      optimize_module_with_pass_flags(
        decoded,
        optimize_passes,
        OptimizeOptions::new(),
      ) {
      Ok(v) => v
      Err(e) => {
        let msg = "optimize failed at attempt #\{attempts}: \{e}"
        let minimized = fuzz_minimize_optimize_failure(encoded, pass_names)
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "optimize",
          "\{e}",
          optimize_passes=pass_names,
          minimized_passes=minimized,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(encoded),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
    }
    optimized_count += 1

    let roundtrip_bytes = match encode_module(optimized) {
      Ok(bytes) => bytes
      Err(e) => {
        let msg = "post-opt encode failed at attempt #\{attempts}: \{e}"
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "post-opt-encode",
          "\{e}",
          optimize_passes=pass_names,
          minimized_passes=pass_names,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(encoded),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
    }
    if check_optimizer_determinism {
      let second_input = match decode_module(encoded) {
        Ok(mod) => mod
        Err(e) => {
          let msg = "optimizer determinism second decode failed at attempt #\{attempts}: \{e}"
          let report = FuzzFailureReport::new(
            seed,
            attempts,
            generated_valid,
            "optimizer-nondeterminism",
            "\{e}",
            optimize_passes=pass_names,
            minimized_passes=pass_names,
            generator_profile=gen_profile_label,
            pass_profile~,
            generator_config_label=generated_config_label,
            generated_attempts~,
            feature_facts~,
            wasm=Some(encoded),
          )
          return Err(fuzz_finalize_failure(on_failure, report, msg))
        }
      }
      let second_optimized = match
        optimize_module_with_pass_flags(
          second_input,
          optimize_passes,
          OptimizeOptions::new(),
        ) {
        Ok(mod) => mod
        Err(e) => {
          let msg = "optimizer determinism second optimize failed at attempt #\{attempts}: \{e}"
          let report = FuzzFailureReport::new(
            seed,
            attempts,
            generated_valid,
            "optimizer-nondeterminism",
            "\{e}",
            optimize_passes=pass_names,
            minimized_passes=pass_names,
            generator_profile=gen_profile_label,
            pass_profile~,
            generator_config_label=generated_config_label,
            generated_attempts~,
            feature_facts~,
            wasm=Some(encoded),
          )
          return Err(fuzz_finalize_failure(on_failure, report, msg))
        }
      }
      let second_bytes = match encode_module(second_optimized) {
        Ok(bytes) => bytes
        Err(e) => {
          let msg = "optimizer determinism second encode failed at attempt #\{attempts}: \{e}"
          let report = FuzzFailureReport::new(
            seed,
            attempts,
            generated_valid,
            "optimizer-nondeterminism",
            "\{e}",
            optimize_passes=pass_names,
            minimized_passes=pass_names,
            generator_profile=gen_profile_label,
            pass_profile~,
            generator_config_label=generated_config_label,
            generated_attempts~,
            feature_facts~,
            wasm=Some(encoded),
          )
          return Err(fuzz_finalize_failure(on_failure, report, msg))
        }
      }
      if !fuzz_bytes_equal(roundtrip_bytes, second_bytes) {
        let msg = "optimizer-nondeterminism at attempt #\{attempts}: fresh executions changed bytes"
        let minimized = fuzz_minimize_optimizer_nondeterminism(
          encoded, pass_names,
        )
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "optimizer-nondeterminism",
          msg,
          optimize_passes=pass_names,
          minimized_passes=minimized,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(encoded),
          reduced_wasm=Some(second_bytes),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
      optimizer_determinism_checked += 1
    }
    let roundtrip_mod = match decode_module(roundtrip_bytes) {
      Ok(decoded_mod) => decoded_mod
      Err(e) => {
        let msg = "post-opt decode failed at attempt #\{attempts}: \{e}"
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "post-opt-decode",
          "\{e}",
          optimize_passes=pass_names,
          minimized_passes=pass_names,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(roundtrip_bytes),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
    }
    if check_optimize_idempotence {
      let optimized_again = match
        optimize_module_with_pass_flags(
          roundtrip_mod,
          optimize_passes,
          OptimizeOptions::new(),
        ) {
        Ok(v) => v
        Err(e) => {
          let msg = "optimize idempotence rerun failed at attempt #\{attempts}: \{e}"
          let report = FuzzFailureReport::new(
            seed,
            attempts,
            generated_valid,
            "optimize-idempotence",
            "\{e}",
            optimize_passes=pass_names,
            minimized_passes=pass_names,
            generator_profile=gen_profile_label,
            pass_profile~,
            generator_config_label=generated_config_label,
            generated_attempts~,
            feature_facts~,
            wasm=Some(roundtrip_bytes),
          )
          return Err(fuzz_finalize_failure(on_failure, report, msg))
        }
      }
      let optimized_again_bytes = match encode_module(optimized_again) {
        Ok(bytes) => bytes
        Err(e) => {
          let msg = "optimize idempotence encode failed at attempt #\{attempts}: \{e}"
          let report = FuzzFailureReport::new(
            seed,
            attempts,
            generated_valid,
            "optimize-idempotence",
            "\{e}",
            optimize_passes=pass_names,
            minimized_passes=pass_names,
            generator_profile=gen_profile_label,
            pass_profile~,
            generator_config_label=generated_config_label,
            generated_attempts~,
            feature_facts~,
            wasm=Some(roundtrip_bytes),
          )
          return Err(fuzz_finalize_failure(on_failure, report, msg))
        }
      }
      if !fuzz_bytes_equal(roundtrip_bytes, optimized_again_bytes) {
        let msg = "optimize idempotence changed bytes at attempt #\{attempts}"
        let minimized = fuzz_minimize_optimizer_idempotence(encoded, pass_names)
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "optimize-idempotence",
          msg,
          optimize_passes=pass_names,
          minimized_passes=minimized,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(encoded),
          reduced_wasm=Some(optimized_again_bytes),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
      optimize_idempotence_checked += 1
    }
    let roundtrip_validate_mod = match decode_module(roundtrip_bytes) {
      Ok(decoded_mod) => decoded_mod
      Err(e) => {
        let msg = "post-opt validation decode failed at attempt #\{attempts}: \{e}"
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "post-opt-decode",
          "\{e}",
          optimize_passes=pass_names,
          minimized_passes=pass_names,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(roundtrip_bytes),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
    }
    match validate_module(roundtrip_validate_mod) {
      Ok(_) => roundtripped += 1
      Err(e) => {
        let msg = "roundtripped module failed validation at attempt #\{attempts}: \{e}"
        let report = FuzzFailureReport::new(
          seed,
          attempts,
          generated_valid,
          "post-opt-validate",
          "\{e}",
          optimize_passes=pass_names,
          minimized_passes=pass_names,
          generator_profile=gen_profile_label,
          pass_profile~,
          generator_config_label=generated_config_label,
          generated_attempts~,
          feature_facts~,
          wasm=Some(roundtrip_bytes),
        )
        return Err(fuzz_finalize_failure(on_failure, report, msg))
      }
    }

    generated_valid += 1

    if differential_every > 0 && generated_valid % differential_every == 0 {
      let adapters = match differential_adapters {
        Some(v) => v
        None => default_differential_adapters()
      }
      match differential_validate_wasm(roundtrip_bytes, adapters~) {
        Ok(_) => differential_checked += 1
        Err(e) => {
          let msg = "differential validation failed at module #\{generated_valid}: \{e}"
          let report = FuzzFailureReport::new(
            seed,
            attempts,
            generated_valid,
            "differential",
            "\{e}",
            optimize_passes=pass_names,
            minimized_passes=pass_names,
            generator_profile=gen_profile_label,
            pass_profile~,
            generator_config_label=generated_config_label,
            generated_attempts~,
            feature_facts~,
            wasm=Some(roundtrip_bytes),
          )
          return Err(fuzz_finalize_failure(on_failure, report, msg))
        }
      }
    }
  }
  Ok(
    CmdFuzzStats::new(
      attempts~,
      generated_valid~,
      generated_invalid~,
      pipeline_validated~,
      optimized=optimized_count,
      roundtripped~,
      differential_checked~,
      optimize_idempotence_checked~,
      encode_decode_idempotence_checked~,
      optimizer_determinism_checked~,
      generator_profile=gen_profile_label,
      pass_profile~,
    ),
  )
}

///|
fn fuzz_profile_ascii_lower(input : String) -> String {
  let out = StringBuilder::new()
  for c in input {
    if c >= 'A' && c <= 'Z' {
      out.write_char((c.to_int() + 32).unsafe_to_char())
    } else {
      out.write_char(c)
    }
  }
  out.to_string()
}

///|
pub fn run_cmd_fuzz_harness_profile(
  profile : String,
  seed : UInt64,
) -> Result[CmdFuzzStats, String] {
  let config = match cmd_fuzz_harness_profile_config_with_seed(profile, seed) {
    Ok(v) => v
    Err(msg) => return Err(msg)
  }
  run_cmd_fuzz_harness(
    config.valid_target,
    seed~,
    optimize_passes=config.optimize_passes,
    generator_profile=config.generator_profile,
    generator_config=Some(config.generator_config),
    pass_profile=config.pass_profile,
    check_optimize_idempotence=config.check_optimize_idempotence,
    check_encode_decode_idempotence=config.check_encode_decode_idempotence,
    check_optimizer_determinism=config.check_optimizer_determinism,
  )
}

///|
#cfg(target="native")
fn cmd_native_has_command(name : String) -> Bool {
  let command = "command -v \{cmd_native_shell_quote(name)} >/dev/null 2>&1"
  cmd_native_system(cmd_native_c_string(command)) == 0
}

///|
#cfg(target="native")
fn cmd_native_validate_wasm_with(
  executable : String,
  command : String,
  bytes : Bytes,
) -> Result[Bool, String] {
  if !cmd_native_has_command(executable) {
    return Err("\{executable} not available")
  }
  let path = cmd_native_tmp_path("differential", ".wasm")
  if cmd_native_write_file(path, bytes) is Err(e) {
    cmd_native_remove_file(path)
    return Err("failed to stage wasm for \{command}: \{e}")
  }
  let run = "\{command} \{cmd_native_shell_quote(path)} >/dev/null 2>&1"
  let code = cmd_native_system(cmd_native_c_string(run))
  cmd_native_remove_file(path)
  Ok(code == 0)
}

///|
#cfg(target="native")
fn run_native_binary_validation_adapter(
  adapter_name : String,
  executable : String,
  command_label : String,
  bytes : Bytes,
  command_builder : (String, String) -> String,
) -> BinaryDifferentialAdapterResult {
  if !cmd_native_has_command(executable) {
    return BinaryDifferentialAdapterResult::adapter_unavailable(
      adapter_name,
      "\{executable} not available",
    )
  }
  let path = cmd_native_tmp_path("\{adapter_name}-binary-adapter", ".wasm")
  let log_path = cmd_native_tmp_path("\{adapter_name}-binary-adapter", ".log")
  if cmd_native_write_file(path, bytes) is Err(e) {
    cmd_native_remove_file(path)
    cmd_native_remove_file(log_path)
    return BinaryDifferentialAdapterResult::tool_failure(
      adapter_name,
      "failed to stage wasm for \{command_label}: \{e}",
    )
  }
  let run = command_builder(path, log_path)
  let code = cmd_native_system(cmd_native_c_string(run))
  let message = match cmd_native_read_file(log_path) {
    Ok(log_bytes) => {
      let text = @utf8.decode(log_bytes) catch {
        _ => "\{command_label} emitted non-utf8 diagnostics"
      }
      text.trim().to_owned()
    }
    Err(e) => "failed to read \{command_label} diagnostics: \{e}"
  }
  cmd_native_remove_file(path)
  cmd_native_remove_file(log_path)
  if code == 0 {
    BinaryDifferentialAdapterResult::valid(adapter_name)
  } else {
    binary_differential_failed_validation_result(adapter_name, bytes, message)
  }
}

///|
#cfg(target="native")
pub fn run_wasm_tools_binary_validation_adapter(
  bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  run_native_binary_validation_adapter(
    "wasm-tools",
    "wasm-tools",
    "wasm-tools validate",
    bytes,
    fn(path, log_path) {
      "wasm-tools validate \{cmd_native_shell_quote(path)} >\{cmd_native_shell_quote(log_path)} 2>&1"
    },
  )
}

///|
#cfg(target="native")
pub fn run_wabt_binary_validation_adapter(
  bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  run_native_binary_validation_adapter(
    "wabt",
    "wasm-validate",
    "wasm-validate",
    bytes,
    fn(path, log_path) {
      "wasm-validate \{cmd_native_shell_quote(path)} >\{cmd_native_shell_quote(log_path)} 2>&1"
    },
  )
}

///|
#cfg(target="native")
pub fn run_binaryen_binary_validation_adapter(
  bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  run_native_binary_validation_adapter(
    "binaryen",
    "wasm-opt",
    "wasm-opt --validate",
    bytes,
    fn(path, log_path) {
      let out_path = cmd_native_tmp_path("binaryen-binary-adapter", ".out.wasm")
      "wasm-opt --all-features --validate \{cmd_native_shell_quote(path)} -o \{cmd_native_shell_quote(out_path)} >\{cmd_native_shell_quote(log_path)} 2>&1; code=$?; rm -f \{cmd_native_shell_quote(out_path)}; exit $code"
    },
  )
}

///|
#cfg(target="wasm-gc")
pub fn run_wasm_tools_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "wasm-tools", "wasm-tools adapter requires native target",
  )
}

///|
#cfg(target="wasm")
pub fn run_wasm_tools_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "wasm-tools", "wasm-tools adapter requires native target",
  )
}

///|
#cfg(target="js")
pub fn run_wasm_tools_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "wasm-tools", "wasm-tools adapter requires native target",
  )
}

///|
#cfg(target="llvm")
pub fn run_wasm_tools_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "wasm-tools", "wasm-tools adapter requires native target",
  )
}

///|
#cfg(target="wasm-gc")
pub fn run_wabt_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "wabt", "wabt adapter requires native target",
  )
}

///|
#cfg(target="wasm")
pub fn run_wabt_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "wabt", "wabt adapter requires native target",
  )
}

///|
#cfg(target="js")
pub fn run_wabt_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "wabt", "wabt adapter requires native target",
  )
}

///|
#cfg(target="llvm")
pub fn run_wabt_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "wabt", "wabt adapter requires native target",
  )
}

///|
#cfg(target="wasm-gc")
pub fn run_binaryen_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "binaryen", "binaryen adapter requires native target",
  )
}

///|
#cfg(target="wasm")
pub fn run_binaryen_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "binaryen", "binaryen adapter requires native target",
  )
}

///|
#cfg(target="js")
pub fn run_binaryen_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "binaryen", "binaryen adapter requires native target",
  )
}

///|
#cfg(target="llvm")
pub fn run_binaryen_binary_validation_adapter(
  _bytes : Bytes,
) -> BinaryDifferentialAdapterResult {
  BinaryDifferentialAdapterResult::adapter_unavailable(
    "binaryen", "binaryen adapter requires native target",
  )
}

///|
#cfg(target="native")
pub fn native_differential_tools_available() -> (Bool, Bool) {
  (
    cmd_native_has_command("wasm-tools"),
    cmd_native_has_command("wasm-validate"),
  )
}

///|
#cfg(target="wasm-gc")
pub fn native_differential_tools_available() -> (Bool, Bool) {
  (false, false)
}

///|
#cfg(target="wasm")
pub fn native_differential_tools_available() -> (Bool, Bool) {
  (false, false)
}

///|
#cfg(target="js")
pub fn native_differential_tools_available() -> (Bool, Bool) {
  (false, false)
}

///|
#cfg(target="llvm")
pub fn native_differential_tools_available() -> (Bool, Bool) {
  (false, false)
}

///|
#cfg(target="native")
fn default_differential_adapters() -> DifferentialAdapters {
  DifferentialAdapters::new(
    wasm_tools_validate=fn(bytes) {
      cmd_native_validate_wasm_with("wasm-tools", "wasm-tools validate", bytes)
    },
    binaryen_validate=fn(bytes) {
      cmd_native_validate_wasm_with("wasm-validate", "wasm-validate", bytes)
    },
  )
}

///|
#cfg(target="wasm-gc")
fn default_differential_adapters() -> DifferentialAdapters {
  DifferentialAdapters::new()
}

///|
#cfg(target="wasm")
fn default_differential_adapters() -> DifferentialAdapters {
  DifferentialAdapters::new()
}

///|
#cfg(target="js")
fn default_differential_adapters() -> DifferentialAdapters {
  DifferentialAdapters::new()
}

///|
#cfg(target="llvm")
fn default_differential_adapters() -> DifferentialAdapters {
  DifferentialAdapters::new()
}