///|
fn fmt4(value : Double) -> String {
  let scaled = (value * 10000.0).round() / 10000.0
  scaled.to_string()
}

///|
fn main raise {
  println("=== Bio.motifs.thresholds (ScoreDistribution) Demo ===")
  // Eight-position motif: nucleotide counts (rows A, C, G, T) from 10 sites.
  let counts = [
    [7, 1, 7, 2, 6, 1, 5, 3],
    [1, 6, 1, 1, 2, 7, 2, 4],
    [1, 2, 1, 6, 1, 1, 2, 2],
    [1, 1, 1, 1, 1, 1, 1, 1],
  ]

  println("\n1. Build the discrete score distribution (precision=1000/column)")
  let dist = @src.MotifScoreDistribution::from_counts(counts)
  println(
    "   grid: n=\{dist.n_points()} bins, min score=\{fmt4(dist.min_score())}, step=\{fmt4(dist.step())}",
  )
  println("   information content IC=\{fmt4(dist.information_content())} bits")
  let mo = dist.motif_density()
  let bg = dist.background_density()
  let mut mo_sum = 0.0
  let mut bg_sum = 0.0
  for i in 0..  log-odds threshold \{fmt4(dist.threshold_fpr(fpr))}",
    )
  }

  println("\n3. Thresholds for target false negative rates (missed motifs)")
  for fnr in [0.001, 0.01, 0.05, 0.1] {
    println(
      "   FNR <= \{fnr}  ->  log-odds threshold \{fmt4(dist.threshold_fnr(fnr))}",
    )
  }

  println("\n4. Balanced error-rate thresholds and the patser criterion")
  for proportion in [0.5, 1.0, 2.0] {
    let (threshold, actual_fpr) = dist.threshold_balanced_with_rate(
      rate_proportion=proportion,
    )
    println(
      "   FNR = FPR * \{proportion}: threshold=\{fmt4(threshold)} (actual FPR=\{fmt4(actual_fpr)})",
    )
  }
  println("   patser (FPR = 2^-IC): \{fmt4(dist.threshold_patser())}")

  println("\n5. Effect of an AT-rich background (A/T 30%, C/G 20%)")
  let biased = @src.MotifScoreDistribution::from_counts(counts, background=[
    0.30, 0.20, 0.20, 0.30,
  ])
  println("   IC=\{fmt4(biased.information_content())} bits")
  println(
    "   balanced threshold=\{fmt4(biased.threshold_balanced())} (uniform: \{fmt4(dist.threshold_balanced())})",
  )
  println(
    "   patser threshold=\{fmt4(biased.threshold_patser())} (uniform: \{fmt4(dist.threshold_patser())})",
  )

  println("\n6. Pseudocounts rescue motifs with unobserved letters")
  let sparse = [[3, 1, 1, 2], [1, 0, 2, 1], [0, 2, 0, 1], [0, 1, 1, 0]]
  let rejected = try {
    let _ = @src.MotifScoreDistribution::from_counts(sparse)
    false
  } catch {
    _ => true
  }
  println("   zero-count motif without pseudocount rejected: \{rejected}")
  let smoothed = @src.MotifScoreDistribution::from_counts(
    sparse,
    pseudocount=0.5,
    precision=500,
  )
  println(
    "   with pseudocount 0.5: FPR 1% threshold=\{fmt4(smoothed.threshold_fpr(0.01))}",
  )

  println("\n7. Invalid matrices are rejected")
  let ragged = [[1.0, 2.0], [1.0], [1.0, 2.0], [1.0, 2.0]]
  let bad_shape = try {
    let _ = @src.MotifScoreDistribution::from_pssm(ragged)
    false
  } catch {
    _ => true
  }
  println("   ragged PSSM rejected: \{bad_shape}")

  println("\n=== Demo Complete ===")
}