plan.mojo (3421B)
1 from std.collections import List 2 3 4 @fieldwise_init 5 struct Allocation(Copyable, Movable): 6 var lot_id: String 7 var revision: String 8 var line_id: String 9 var value: Int 10 var scale: Int 11 12 13 @fieldwise_init 14 struct LotCapacity(Copyable, Movable): 15 var lot_id: String 16 var revision: String 17 var value: Int 18 var scale: Int 19 20 21 @fieldwise_init 22 struct MatchPlan(Copyable, Movable): 23 var plan_id: String 24 var supplier_id: String 25 var allocations: List[Allocation] 26 27 28 def allocation( 29 lot_id: String, revision: String, line_id: String, value: Int, scale: Int 30 ) raises -> Allocation: 31 if lot_id.strip() == "" or revision.strip() == "" or line_id.strip() == "": 32 raise Error("allocation requires lot id, revision and line id") 33 if value < 0: 34 raise Error("allocation value must be non-negative") 35 return Allocation( 36 lot_id=String(lot_id), 37 revision=String(revision), 38 line_id=String(line_id), 39 value=value, 40 scale=scale, 41 ) 42 43 44 def match_plan( 45 plan_id: String, supplier_id: String, allocations: List[Allocation] 46 ) raises -> MatchPlan: 47 if plan_id.strip() == "" or supplier_id.strip() == "": 48 raise Error("plan requires plan id and supplier id") 49 var copied = List[Allocation]() 50 for entry in allocations: 51 copied.append(entry.copy()) 52 return MatchPlan( 53 plan_id=String(plan_id), 54 supplier_id=String(supplier_id), 55 allocations=copied^, 56 ) 57 58 59 def _rescale(value: Int, scale: Int, target: Int) -> Int: 60 var result = value 61 for _ in range(target - scale): 62 result *= 10 63 return result 64 65 66 def plan_conservation_violations( 67 plan: MatchPlan, lots: List[LotCapacity] 68 ) raises -> List[String]: 69 var violations = List[String]() 70 var lot_keys = List[String]() 71 var allocated = List[Int]() 72 var allocated_scale = List[Int]() 73 for entry in plan.allocations: 74 var key = entry.lot_id + "@" + entry.revision 75 var found = -1 76 for index in range(len(lot_keys)): 77 if lot_keys[index] == key: 78 found = index 79 var scale = entry.scale 80 var value = entry.value 81 if found >= 0: 82 if allocated_scale[found] > scale: 83 scale = allocated_scale[found] 84 value = _rescale(value, entry.scale, scale) + _rescale( 85 allocated[found], allocated_scale[found], scale 86 ) 87 allocated[found] = value 88 allocated_scale[found] = scale 89 else: 90 lot_keys.append(String(key)) 91 allocated.append(value) 92 allocated_scale.append(scale) 93 94 for index in range(len(lot_keys)): 95 var capacity_value = -1 96 var capacity_scale = 0 97 for lot in lots: 98 if lot.lot_id + "@" + lot.revision == lot_keys[index]: 99 capacity_value = lot.value 100 capacity_scale = lot.scale 101 var scale = ( 102 capacity_scale if capacity_scale 103 > allocated_scale[index] else allocated_scale[index] 104 ) 105 if capacity_value < 0: 106 violations.append(lot_keys[index] + ": unknown capacity") 107 continue 108 if _rescale(allocated[index], allocated_scale[index], scale) > _rescale( 109 capacity_value, capacity_scale, scale 110 ): 111 violations.append(lot_keys[index] + ": over-allocated") 112 return violations^