///|
pub(all) struct Totals {
cost : Int32
purchased : Int32
useful : Int32
kerf_loss : Int32
trim_loss : Int32
reusable : Int32
scrap : Int32
bars : Int32
pieces : Int32
} derive(Debug, Eq)
///|
pub extend Totals with @debug.Debug::{to_repr}
///|
pub extend Totals with Eq::{equal, not_equal}
///|
/// Rebuild every pattern from count vectors, independently of cached geometry.
pub fn audit(p : Problem, plan : Plan) -> Result[Totals, String] {
if plan.bars.length() > 128 {
return Err("plan exceeds 128 bars")
}
let delivered = Array::make(p.demands.length(), 0)
let inventory = Array::make(p.stocks.length(), 0)
let mut cost = 0
let mut purchased = 0
let mut useful = 0
let mut kerf_loss = 0
let mut trim_loss = 0
let mut reusable = 0
let mut scrap = 0
let mut pieces = 0
for bar in plan.bars {
let rebuilt = match pattern(p, bar.stock, bar.counts) {
Err(e) => return Err(e)
Ok(a) => a
}
if rebuilt.used != bar.used || rebuilt.remaining != bar.remaining {
return Err("cached pattern geometry is inconsistent")
}
let s = p.stocks[bar.stock]
inventory[bar.stock] += 1
if s.quantity >= 0 && inventory[bar.stock] > s.quantity {
return Err("stock inventory exceeded: " + s.id)
}
cost += s.cost
purchased += s.length
trim_loss += s.trim_start + s.trim_end
for i, count in bar.counts {
delivered[i] += count
useful += count * p.demands[i].length
kerf_loss += count * p.kerf
pieces += count
}
if bar.remaining >= p.min_remnant {
reusable += bar.remaining
} else {
scrap += bar.remaining
}
}
for i, d in p.demands {
if delivered[i] != d.quantity {
return Err("demand not fulfilled exactly: " + d.id)
}
}
if purchased != useful + kerf_loss + trim_loss + reusable + scrap {
return Err("length conservation failed")
}
Ok({
cost,
purchased,
useful,
kerf_loss,
trim_loss,
reusable,
scrap,
bars: plan.bars.length(),
pieces,
})
}
///|
/// Import a complete plan; rejects overproduction, shortages and inventory violations.
pub fn plan_from_patterns(
p : Problem,
bars : Array[Pattern],
) -> Result[Plan, String] {
let plan = Plan::{ bars: bars.map(copy_pattern), }
match audit(p, plan) {
Err(e) => Err(e)
Ok(_) => Ok(plan)
}
}
///|
pub fn stock_usage(p : Problem, plan : Plan) -> Result[Array[Int32], String] {
match audit(p, plan) {
Err(e) => return Err(e)
Ok(_) => ()
}
let result = Array::make(p.stocks.length(), 0)
for bar in plan.bars {
result[bar.stock] += 1
}
Ok(result)
}
///|
pub fn demand_output(p : Problem, plan : Plan) -> Result[Array[Int32], String] {
match audit(p, plan) {
Err(e) => return Err(e)
Ok(_) => ()
}
let result = Array::make(p.demands.length(), 0)
for bar in plan.bars {
for i, count in bar.counts {
result[i] += count
}
}
Ok(result)
}