///|
/// Each output piece consumes its length plus one kerf, including the final piece.
pub fn pattern(
  p : Problem,
  stock_index : Int32,
  counts : Array[Int32],
) -> Result[Pattern, String] {
  if stock_index < 0 ||
    stock_index >= p.stocks.length() ||
    counts.length() != p.demands.length() {
    return Err("pattern stock index or count-vector dimension invalid")
  }
  let s = p.stocks[stock_index]
  let mut used = s.trim_start + s.trim_end
  let mut pieces = 0
  for i, count in counts {
    let d = p.demands[i]
    if count < 0 || count > d.quantity {
      return Err("pattern count exceeds demand")
    }
    if count > 0 && d.material != s.material {
      return Err("pattern material mismatch")
    }
    pieces += count
    used += count * (d.length + p.kerf)
  }
  if pieces == 0 {
    return Err("empty patterns are not cutting operations")
  }
  if used > s.length {
    return Err("pattern exceeds usable stock length")
  }
  Ok({
    stock: stock_index,
    counts: counts.copy(),
    used,
    remaining: s.length - used,
  })
}

///|
fn copy_pattern(p : Pattern) -> Pattern {
  {
    stock: p.stock,
    counts: p.counts.copy(),
    used: p.used,
    remaining: p.remaining,
  }
}

///|
pub fn Pattern::stock_index(self : Pattern) -> Int32 {
  self.stock
}

///|
pub fn Pattern::counts(self : Pattern) -> Array[Int32] {
  self.counts.copy()
}

///|
pub fn Pattern::used(self : Pattern) -> Int32 {
  self.used
}

///|
pub fn Pattern::remaining(self : Pattern) -> Int32 {
  self.remaining
}

///|
pub fn Pattern::pieces(self : Pattern) -> Int32 {
  let mut n = 0
  for count in self.counts {
    n += count
  }
  n
}

///|
pub fn empty_plan() -> Plan {
  { bars: [], }
}

///|
pub fn Plan::bars(self : Plan) -> Array[Pattern] {
  self.bars.map(copy_pattern)
}

///|
pub fn Plan::bar_count(self : Plan) -> Int32 {
  self.bars.length()
}