///|
pub(all) struct Remnant {
source_bar : Int32
source_stock : String
material : String
length : Int32
start : Int32
end : Int32
} derive(Debug, Eq)
///|
pub extend Remnant with @debug.Debug::{to_repr}
///|
pub extend Remnant with Eq::{equal, not_equal}
///|
pub fn remnants(p : Problem, plan : Plan) -> Result[Array[Remnant], String] {
let maps = match cutting_map(p, plan) {
Err(e) => return Err(e)
Ok(m) => m
}
let result : Array[Remnant] = []
for i, m in maps {
if m.reusable {
result.push({
source_bar: i,
source_stock: m.stock_id,
material: p.stocks[plan.bars[i].stock].material,
length: m.remnant_end - m.remnant_start,
start: m.remnant_start,
end: m.remnant_end,
})
}
}
Ok(result)
}
///|
/// Aggregate equivalent remnants as zero-purchase-cost inventory with no inherited trims.
/// Caller chooses whether and how handling/requalification costs should be added.
pub fn remnant_stocks(p : Problem, plan : Plan) -> Result[Array[Stock], String] {
let entries = match remnants(p, plan) {
Err(e) => return Err(e)
Ok(v) => v
}
let result : Array[Stock] = []
for r in entries {
let mut existing = -1
for i, s in result {
if s.material == r.material && s.length == r.length {
existing = i
break
}
}
if existing >= 0 {
let s = result[existing]
result[existing] = { ..s, quantity: s.quantity + 1, }
} else {
result.push(
stock(
"remnant-" + result.length().to_string(),
r.material,
r.length,
0,
1,
),
)
}
}
Ok(result)
}
///|
/// Inventory transition is local data only; it never modifies the input problem.
/// Fails rather than silently dropping stock types if the next problem exceeds bounds.
pub fn rollover(
p : Problem,
plan : Plan,
next_demands : Array[Demand],
keep_inventory~ : Bool = true,
) -> Result[Problem, String] {
match audit(p, plan) {
Err(e) => return Err(e)
Ok(_) => ()
}
let result : Array[Stock] = []
let usage = stock_usage(p, plan).unwrap()
if keep_inventory {
for i, s in p.stocks {
let quantity = if s.quantity < 0 { -1 } else { s.quantity - usage[i] }
if quantity != 0 {
result.push({ ..s, quantity, })
}
}
}
let recovered = match remnant_stocks(p, plan) {
Err(e) => return Err(e)
Ok(v) => v
}
for r in recovered {
let mut id = r.id
let mut suffix = 0
while result.any(s => s.id == id) {
suffix += 1
id = r.id + "-" + suffix.to_string()
}
result.push({ ..r, id, })
}
problem(next_demands, result, kerf=p.kerf, min_remnant=p.min_remnant)
}