Imports

CLRS Section 17.4 - Dynamic tables

This first-pass section keeps dynamic tables at the abstract size/count level. It records the state invariant and a conservative potential wrapper that later resize-transition proofs can instantiate.

Main results:

  • Predicate DynamicTableState.Valid: the number of stored elements does not exceed allocated table size.

  • Theorem dynamicPotential_nonneg: the first-pass table potential is nonnegative.

  • Theorems dynamicTableInsert_potential_nonneg and dynamicTableDelete_potential_nonneg: concrete post-transition states have nonnegative first-pass potential.

  • Theorems dynamicTableInsertSize_fits and dynamicTableDeleteSize_fits: the first-pass capacity choices can hold the post-operation number of stored elements.

  • Theorems dynamicTableInsertSize_ge_size and dynamicTableDeleteSize_le_size: insertion never shrinks capacity, and deletion never grows capacity for a valid table.

  • Theorems dynamicTableInsertSize_ge_double_of_expand, dynamicTableInsert_capacity_ge_double_of_expand, dynamicTableDeleteSize_le_half_of_contract, and dynamicTableDelete_capacity_le_half_of_contract: direct capacity direction wrappers for resizing branches.

  • Theorems dynamicTableInsertSize_of_fits, dynamicTableInsertSize_of_expand, dynamicTableDeleteSize_of_contract, and dynamicTableDeleteSize_of_no_contract: direct case specifications for the first-pass capacity-choice definitions.

  • Theorems dynamicTableInsertCost_le_num_succ and dynamicTableDeleteCost_le_num: the first-pass transition costs are bounded by the natural element-count copying budgets.

  • Theorems dynamicTableInsertCost_pos and dynamicTableDeleteCost_pos_of_nonempty: first-pass nonempty transitions have positive actual cost.

  • Theorems dynamicTableDeleteCost_pos_iff_nonempty and dynamicTableDeleteCost_zero_iff_empty: deletion cost positivity and zero-cost behavior exactly match whether the table is nonempty.

  • Theorems dynamicTableInsertCost_of_fits, dynamicTableInsertCost_of_expand, dynamicTableDeleteCost_empty, dynamicTableDeleteCost_of_contract, and dynamicTableDeleteCost_of_no_contract: direct case specifications for the first-pass actual-cost definitions.

  • Theorems dynamicTableDeleteCost_eq_num_of_contract and dynamicTableDeleteCost_eq_one_of_no_contract: deletion actual-cost branch wrappers without an explicit nonempty-table premise.

  • Theorem dynamicTableInsert_valid: the first-pass insertion transition preserves the table-size invariant.

  • Theorem dynamicTableDelete_valid: the first-pass deletion/contraction transition preserves the table-size invariant.

  • Theorems dynamicTableInsert_num, dynamicTableInsert_size, dynamicTableDelete_num, and dynamicTableDelete_size: direct post-state field equations for the transition wrappers.

  • Theorems dynamicTableInsert_size_of_fits, dynamicTableInsert_size_of_expand, dynamicTableDelete_size_of_contract, and dynamicTableDelete_size_of_no_contract: direct post-state allocation-size case specifications for the transition wrappers.

  • Theorems dynamicTableInsert_num_gt, dynamicTableInsert_num_pos, dynamicTableInsert_num_ge, dynamicTableDelete_num_le, dynamicTableDelete_num_empty, and dynamicTableDelete_num_pos_of_one_lt, and dynamicTableDelete_num_lt_of_nonempty: direct post-state stored-count direction corollaries for insertion and deletion.

  • Theorems dynamicTableInsert_capacity_fits, dynamicTableInsert_capacity_pos, dynamicTableInsert_capacity_ge_size, dynamicTableDelete_capacity_fits, dynamicTableDelete_capacity_pos_of_one_lt, and dynamicTableDelete_capacity_le_size: direct post-state capacity corollaries for insertion and deletion.

  • Theorems dynamicTableInsert_amortizedBound and dynamicTableDelete_amortizedBound: the concrete first-pass transitions instantiate the generic amortized-cost wrapper.

  • Theorems dynamicTableInsert_amortizedCost_eq and dynamicTableDelete_amortizedCost_eq: concrete transition amortized costs unfold to actual cost plus the post-potential minus the pre-potential.

  • Theorem dynamicTable_amortizedBound: the abstract dynamic-table amortized cost is bounded by actual cost plus the post-operation potential.

Current gaps:

  • Mutable-array copying and allocator semantics are deferred.

namespace CLRSnamespace Chapter17

Abstract dynamic-table state: stored element count and allocated size.

structure DynamicTableState where num : Nat size : Nat
namespace DynamicTableState

The table never stores more elements than its allocated size.

def Valid (s : DynamicTableState) : Prop := s.num <= s.size
end DynamicTableState

A simple nonnegative potential for first-pass dynamic-table amortized wrappers. Later resize-specific proofs can replace this with the sharper CLRS potential.

def dynamicPotential (s : DynamicTableState) : Int := Int.ofNat (2 * s.num + s.size)

The first-pass dynamic-table potential is nonnegative.

theorem dynamicPotential_nonneg (s : DynamicTableState) : 0 <= dynamicPotential s :=

Abstract dynamic-table amortized cost for one state transition.

def dynamicTableAmortizedCost (before after : DynamicTableState) (actual : Nat) : Int := Int.ofNat actual + dynamicPotential after - dynamicPotential before

Allocated size after one insertion. If the existing table has room, keep its size; otherwise choose a capacity that both fits the new element and doubles the old allocation budget.

def dynamicTableInsertSize (s : DynamicTableState) : Nat := if s.num + 1 <= s.size then s.size else max (s.num + 1) (2 * s.size)

First-pass dynamic-table insertion transition.

def dynamicTableInsert (s : DynamicTableState) : DynamicTableState := { num := s.num + 1, size := dynamicTableInsertSize s }

First-pass insertion cost: one write plus copied elements on expansion.

def dynamicTableInsertCost (s : DynamicTableState) : Nat := if s.num + 1 <= s.size then 1 else s.num + 1

Dynamic-table insertion has positive first-pass actual cost.

theorem dynamicTableInsertCost_pos (s : DynamicTableState) : 0 < dynamicTableInsertCost s :=

Insertion into a table with spare capacity costs one write.

theorem dynamicTableInsertCost_of_fits (s : DynamicTableState) (hfit : s.num + 1 <= s.size) : dynamicTableInsertCost s = 1 :=

Insertion into a full table costs the post-insertion element count.

theorem dynamicTableInsertCost_of_expand (s : DynamicTableState) (hfull : ¬ s.num + 1 <= s.size) : dynamicTableInsertCost s = s.num + 1 :=

The first-pass insertion cost is bounded by the post-insertion element count.

theorem dynamicTableInsertCost_le_num_succ (s : DynamicTableState) : dynamicTableInsertCost s <= s.num + 1 :=

Insertion with spare capacity keeps the old allocation size.

theorem dynamicTableInsertSize_of_fits (s : DynamicTableState) (hfit : s.num + 1 <= s.size) : dynamicTableInsertSize s = s.size :=

Insertion without spare capacity uses the first-pass expansion choice.

theorem dynamicTableInsertSize_of_expand (s : DynamicTableState) (hfull : ¬ s.num + 1 <= s.size) : dynamicTableInsertSize s = max (s.num + 1) (2 * s.size) :=

The insertion capacity choice can hold the inserted element.

theorem dynamicTableInsertSize_fits (s : DynamicTableState) : s.num + 1 <= dynamicTableInsertSize s :=

The insertion capacity choice never shrinks the table.

theorem dynamicTableInsertSize_ge_size (s : DynamicTableState) : s.size <= dynamicTableInsertSize s := exact Or.inr ( )

The insertion expansion branch allocates at least double the old capacity.

Dynamic-table insertion increments the stored-element count by one.

theorem dynamicTableInsert_num (s : DynamicTableState) : (dynamicTableInsert s).num = s.num + 1 :=

Dynamic-table insertion sets the post-state capacity to the insertion capacity choice.

theorem dynamicTableInsert_size (s : DynamicTableState) : (dynamicTableInsert s).size = dynamicTableInsertSize s :=

Insertion with spare capacity keeps the post-state allocation size.

Insertion without spare capacity uses the expansion choice as the post-state size.

Dynamic-table insertion strictly increases the stored-element count.

Dynamic-table insertion leaves a positive stored-element count.

Dynamic-table insertion never decreases the stored-element count.

theorem dynamicTableInsert_num_ge (s : DynamicTableState) : s.num <= (dynamicTableInsert s).num :=

Dynamic-table insertion leaves enough capacity for the post-insertion count.

theorem dynamicTableInsert_capacity_fits (s : DynamicTableState) : (dynamicTableInsert s).num <= (dynamicTableInsert s).size :=

Dynamic-table insertion leaves a positive post-state capacity.

theorem dynamicTableInsert_capacity_pos (s : DynamicTableState) : 0 < (dynamicTableInsert s).size :=

Dynamic-table insertion never shrinks the post-state capacity below the old size.

theorem dynamicTableInsert_capacity_ge_size (s : DynamicTableState) : s.size <= (dynamicTableInsert s).size :=

The insertion expansion branch leaves post-state capacity at least double the old capacity.

Dynamic-table insertion preserves the table-size invariant.

Allocated size after one deletion. If the post-deletion load is low, shrink toward half the old allocation while keeping enough room for all stored elements.

def dynamicTableDeleteSize (s : DynamicTableState) : Nat := let newNum := s.num - 1 if 4 * newNum <= s.size then max newNum (s.size / 2) else s.size

First-pass dynamic-table deletion/contraction transition.

def dynamicTableDelete (s : DynamicTableState) : DynamicTableState := { num := s.num - 1, size := dynamicTableDeleteSize s }

First-pass deletion cost: one deletion plus copied elements on contraction.

def dynamicTableDeleteCost (s : DynamicTableState) : Nat := if s.num = 0 then 0 else if 4 * (s.num - 1) <= s.size then s.num else 1

Insertion leaves a state with nonnegative first-pass potential.

theorem dynamicTableInsert_potential_nonneg (s : DynamicTableState) : 0 <= dynamicPotential (dynamicTableInsert s) :=

Deletion leaves a state with nonnegative first-pass potential.

theorem dynamicTableDelete_potential_nonneg (s : DynamicTableState) : 0 <= dynamicPotential (dynamicTableDelete s) :=

Deleting from a nonempty dynamic table has positive first-pass actual cost.

theorem dynamicTableDeleteCost_pos_of_nonempty (s : DynamicTableState) (hnum : s.num 0) : 0 < dynamicTableDeleteCost s :=

Deleting from an empty table has zero first-pass cost.

theorem dynamicTableDeleteCost_empty (s : DynamicTableState) (hempty : s.num = 0) : dynamicTableDeleteCost s = 0 :=

Dynamic-table deletion has positive cost exactly when the table is nonempty.

theorem dynamicTableDeleteCost_pos_iff_nonempty (s : DynamicTableState) : 0 < dynamicTableDeleteCost s <-> s.num 0 :=

Dynamic-table deletion has zero cost exactly when the table is empty.

theorem dynamicTableDeleteCost_zero_iff_empty (s : DynamicTableState) : dynamicTableDeleteCost s = 0 <-> s.num = 0 :=

Contracting after deletion costs copying the remaining represented elements.

theorem dynamicTableDeleteCost_of_contract (s : DynamicTableState) (hnum : s.num 0) (hcontract : 4 * (s.num - 1) <= s.size) : dynamicTableDeleteCost s = s.num :=

Deletion without contraction costs one unit in the first-pass model.

theorem dynamicTableDeleteCost_of_no_contract (s : DynamicTableState) (hnum : s.num 0) (hcontract : ¬ 4 * (s.num - 1) <= s.size) : dynamicTableDeleteCost s = 1 :=

The contraction branch costs the pre-deletion element count, including empty tables.

The no-contraction branch costs one unit and necessarily comes from a nonempty table.

The first-pass deletion cost is bounded by the pre-deletion element count.

theorem dynamicTableDeleteCost_le_num (s : DynamicTableState) : dynamicTableDeleteCost s <= s.num :=

Deletion with low post-deletion load uses the first-pass contraction choice.

theorem dynamicTableDeleteSize_of_contract (s : DynamicTableState) (hcontract : 4 * (s.num - 1) <= s.size) : dynamicTableDeleteSize s = max (s.num - 1) (s.size / 2) :=

Deletion without contraction keeps the old allocation size.

theorem dynamicTableDeleteSize_of_no_contract (s : DynamicTableState) (hcontract : ¬ 4 * (s.num - 1) <= s.size) : dynamicTableDeleteSize s = s.size :=

The deletion capacity choice can hold the remaining elements of a valid table.

theorem dynamicTableDeleteSize_fits (s : DynamicTableState) (hvalid : DynamicTableState.Valid s) : s.num - 1 <= dynamicTableDeleteSize s :=

The deletion capacity choice never grows a valid table.

theorem dynamicTableDeleteSize_le_size (s : DynamicTableState) (hvalid : DynamicTableState.Valid s) : dynamicTableDeleteSize s <= s.size :=

The deletion contraction branch allocates no more than half the old capacity.

Dynamic-table deletion decrements the stored-element count, saturating at zero.

theorem dynamicTableDelete_num (s : DynamicTableState) : (dynamicTableDelete s).num = s.num - 1 :=

Dynamic-table deletion sets the post-state capacity to the deletion capacity choice.

theorem dynamicTableDelete_size (s : DynamicTableState) : (dynamicTableDelete s).size = dynamicTableDeleteSize s :=

Deletion with low post-deletion load uses the contraction choice as the post-state size.

Deletion without contraction keeps the old allocation size as the post-state size.

Dynamic-table deletion never increases the stored-element count.

Deleting from an empty table leaves the stored-element count at zero.

Deleting from a table with at least two elements leaves a positive count.

Deleting from a nonempty table strictly decreases the stored-element count.

Dynamic-table deletion leaves enough capacity for the post-deletion count.

theorem dynamicTableDelete_capacity_fits (s : DynamicTableState) (hvalid : DynamicTableState.Valid s) : (dynamicTableDelete s).num <= (dynamicTableDelete s).size :=

Deleting from a valid table with at least two elements leaves positive capacity.

theorem dynamicTableDelete_capacity_pos_of_one_lt (s : DynamicTableState) (hvalid : DynamicTableState.Valid s) (hnum : 1 < s.num) : 0 < (dynamicTableDelete s).size :=

Dynamic-table deletion never grows the post-state capacity for a valid table.

theorem dynamicTableDelete_capacity_le_size (s : DynamicTableState) (hvalid : DynamicTableState.Valid s) : (dynamicTableDelete s).size <= s.size :=

The deletion contraction branch leaves post-state capacity no more than half the old capacity.

Dynamic-table deletion/contraction preserves the table-size invariant.

Concrete insertion amortized cost unfolds to actual plus potential change.

theorem dynamicTableInsert_amortizedCost_eq (s : DynamicTableState) : dynamicTableAmortizedCost s (dynamicTableInsert s) (dynamicTableInsertCost s) = Int.ofNat (dynamicTableInsertCost s) + dynamicPotential (dynamicTableInsert s) - dynamicPotential s :=

Concrete deletion amortized cost unfolds to actual plus potential change.

theorem dynamicTableDelete_amortizedCost_eq (s : DynamicTableState) : dynamicTableAmortizedCost s (dynamicTableDelete s) (dynamicTableDeleteCost s) = Int.ofNat (dynamicTableDeleteCost s) + dynamicPotential (dynamicTableDelete s) - dynamicPotential s :=

The abstract amortized transition cost is bounded by actual cost plus the post-operation potential, because the pre-operation potential is nonnegative.

theorem dynamicTable_amortizedBound (before after : DynamicTableState) (actual : Nat) : dynamicTableAmortizedCost before after actual <= Int.ofNat actual + dynamicPotential after :=

The concrete first-pass insertion transition instantiates the generic bound.

The concrete first-pass deletion transition instantiates the generic bound.

end Chapter17end CLRS