///|
/// Conservative starting-size heuristic, not a probabilistic success guarantee.
/// Allocate one cell per estimated difference in each of the three bands.
pub struct CapacityPlan {
  priv initial : Config
  priv max_width : Int32
}

///|
pub fn CapacityPlan::new(
  expected_differences : Int32,
  max_wire_bytes~ : Int32 = 3932228,
  seed~ : UInt64 = 1UL,
) -> CapacityPlan raise ReconcileError {
  if expected_differences < 0 {
    raise InvalidConfig("negative difference estimate")
  }
  if max_wire_bytes < 128 {
    raise LimitExceeded("wire budget cannot hold a minimum sketch")
  }
  let available = (max_wire_bytes - 68) / 60
  let max_width = if available > 262144 { 262144 } else { available }
  let width = if expected_differences == 0 { 1 } else { expected_differences }
  if width > max_width {
    raise LimitExceeded("difference estimate exceeds wire budget")
  }
  { initial: Config::new(width, seed~), max_width, }
}

///|
pub fn CapacityPlan::initial(self : CapacityPlan) -> Config {
  self.initial
}

///|
/// Maximum retry width that still fits the supplied snapshot wire budget.
pub fn CapacityPlan::max_width(self : CapacityPlan) -> Int32 {
  self.max_width
}

///|
pub fn Config::wire_bytes(self : Config) -> Int32 {
  68 + self.cells() * 20
}

///|
/// Compare only summary vs raw UInt64 ID bytes, excluding protocol/payload costs.
pub fn CapacityPlan::smaller_than_full_ids(
  self : CapacityPlan,
  members : Int32,
) -> Bool raise ReconcileError {
  if members < 0 {
    raise InvalidConfig("negative full-set member count")
  }
  self.initial.wire_bytes().to_int64() < members.to_int64() * 8L
}