// Histogram: frequency distribution of raw samples.

///|
/// Count `values` into `bins` equal-width bins covering `[lo, hi]`. Samples
/// outside the range are ignored; the top edge closes into the last bin.
fn hist_counts(
  values : Array[Double],
  lo : Double,
  hi : Double,
  bins : Int,
) -> Array[Int] {
  let counts : Array[Int] = []
  for _i in 0..= lo && v <= hi {
      let mut idx = ((v - lo) / span * bins.to_double()).to_int()
      if idx >= bins {
        idx = bins - 1
      }
      counts[idx] = counts[idx] + 1
    }
  }
  counts
}

///|
/// Render a histogram of raw `values` as a standalone SVG document string:
/// samples are counted into `bins` equal-width bins over a nicely rounded
/// range and drawn as near-flush bars. `bins`, `title`, `width`, `height` and
/// `theme` are optional.
pub fn histogram(
  values : Array[Double],
  bins? : Int = 10,
  title? : String = "",
  width? : Double = 480.0,
  height? : Double = 320.0,
  theme? : Theme = Theme::light(),
) -> String {
  let left = 48.0
  let right = 16.0
  let top = if title == "" { 16.0 } else { 40.0 }
  let bottom = 40.0
  let plot_w = width - left - right
  let plot_h = height - top - bottom
  let bins = if bins < 1 { 1 } else { bins }
  // Round the sample range so bin edges land on friendly numbers.
  let (raw_lo, raw_hi) = extent(values)
  let (lo, hi, x_ticks) = nice_ticks(raw_lo, raw_hi, 6)
  let counts = hist_counts(values, lo, hi, bins)
  let mut max_c = 0
  for c in counts {
    if c > max_c {
      max_c = c
    }
  }
  let max_c = if max_c == 0 { 1 } else { max_c }
  let (axis_lo, axis_hi, ticks) = nice_ticks(0.0, max_c.to_double(), 5)
  let body = StringBuilder::new()
  body.write_string(background_rect(theme, width, height))
  body.write_string(
    y_axis(theme, left, top, plot_w, plot_h, axis_lo, axis_hi, ticks),
  )
  body.write_string(x_axis(theme, left, top, plot_w, plot_h, lo, hi, x_ticks))
  let bw = plot_w / bins.to_double()
  for i in 0.. 0 {
      let h = counts[i].to_double() / axis_hi * plot_h
      body.write_string(
        elem("rect", [
          ("x", num(left + bw * i.to_double() + bw * 0.04)),
          ("y", num(top + plot_h - h)),
          ("width", num(bw * 0.92)),
          ("height", num(h)),
          ("fill", theme.color_at(0)),
        ]),
      )
    }
  }
  if title != "" {
    body.write_string(
      label(width / 2.0, 24.0, title, size=16, weight="bold", fill=theme.title),
    )
  }
  document(width, height, body.to_string())
}