///|
fn row(
  id : String,
  cells : Array[String],
  base? : Double = 1.0,
  lower? : Double = 0.001,
  upper? : Double = 1000.0,
) -> @rake.Row {
  { id, cells, base_weight: base, lower, upper, }
}

///|
fn margin(name : String, targets : Array[(String, Double)]) -> @rake.Margin {
  { name, targets: targets.map(fn(t) { { category: t.0, total: t.1, } }), }
}

///|
fn main {
  let survey : @rake.Problem = {
    rows: [
      row("a", ["F", "N"]),
      row("b", ["F", "S"]),
      row("c", ["M", "N"]),
      row("d", ["M", "S"]),
    ],
    margins: [
      margin("group", [("F", 60.0), ("M", 40.0)]),
      margin("region", [("N", 50.0), ("S", 50.0)]),
    ],
  }
  let fitted = @rake.fit(survey).unwrap()
  println("1 survey calibration: expected weights 30,30,20,20")
  for weighted in fitted.weights {
    println("  \{weighted.id}: \{weighted.weight}")
  }
  let weights = fitted.weights.map(fn(w) { w.weight })
  println(
    "weighted mean of 10,20,30,40 = \{@rake.weighted_mean([10.0,20.0,30.0,40.0],weights).unwrap()}",
  )

  let table : @rake.Problem = {
    rows: [
      row("11", ["R1", "C1"]),
      row("12", ["R1", "C2"]),
      row("13", ["R1", "C3"]),
      row("21", ["R2", "C1"]),
      row("22", ["R2", "C2"]),
      row("23", ["R2", "C3"]),
    ],
    margins: [
      margin("row", [("R1", 40.0), ("R2", 60.0)]),
      margin("column", [("C1", 20.0), ("C2", 30.0), ("C3", 50.0)]),
    ],
  }
  println("2 aggregate cell balancing: expected 8,12,20,12,18,30")
  println(@rake.fit(table).unwrap().to_json().stringify())

  println(
    "3 independent audit of old weights against changed population margins",
  )
  let changed = {
    ..survey,
    margins: [margin("group", [("F", 50.0), ("M", 50.0)]), survey.margins[1]],
  }
  println(@rake.evaluate(changed, weights).unwrap().to_json().stringify())
  println("recalibrated: \{@rake.fit(changed).unwrap().diagnostics.passes}")

  let one : @rake.Problem = {
    rows: [
      row("a", ["all"], base=3.0, lower=1.0, upper=4.0),
      row("b", ["all"], lower=1.0, upper=20.0),
    ],
    margins: [margin("total", [("all", 10.0)])],
  }
  println(
    "moon-causal unbounded baseline passes our bound audit=\{@rake.oneway_baseline(one).unwrap().diagnostics.passes}",
  )
  println(
    "bounded projection passes=\{@rake.fit(one).unwrap().diagnostics.passes}",
  )
  let incompatible : @rake.Problem = {
    rows: [row("a", ["A", "X"]), row("b", ["B", "Y"])],
    margins: [
      margin("m1", [("A", 60.0), ("B", 40.0)]),
      margin("m2", [("X", 50.0), ("Y", 50.0)]),
    ],
  }
  let limited = @rake.fit(incompatible, options={
    max_iterations: 5,
    tolerance: 1.0e-8,
  }).unwrap()
  println(
    "joint mismatch is reported as iteration limit=\{limited.status == @rake.IterationLimit}",
  )
  let unsupported = { ..one, margins: [margin("total", [("all", 100.0)])], }
  println("unsupported bounds rejected=\{@rake.fit(unsupported) is Err(_)}")
}