Doubly linked list
StructureDoubly linked list node#✓fully proved
A doubly linked list node with payloads in \(\alpha\) and identities in \(\iota\) consists of a payload in \(\alpha\) (the key together with any satellite data), a next pointer and a previous pointer, each of which is either an identity in \(\iota\) or NIL.
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
Used by 61
DefinitionStore representing a doubly linked list#✓fully proved
Let a store assign to each identity below \(n\) either nothing or a doubly linked list node whose pointers are identities below \(n\) or NIL, and let the head be an identity below \(n\) or NIL. The store and head represent the list of identities \(i_0, \dots, i_{k-1}\) when these identities are pairwise distinct, the head is \(i_0\) (and NIL when \(k = 0\)), and for each \(j < k\) the store entry at \(i_j\) is a node whose previous pointer is NIL if \(j = 0\) and \(i_{j-1}\) otherwise, and whose next pointer is \(i_{j+1}\) if \(j + 1 < k\) and NIL otherwise.
public def Represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ head = ids.head? ∧
∀ (i : Nat) (hi : i < ids.length), ∃ node,
store.get ids[i] = some node ∧
node.prev = (if i = 0 then none else ids[i - 1]?) ∧
node.next = ids[i + 1]?
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
Uses 1 · Used by 6
DefinitionPrepending a node to a doubly linked list#✓fully proved
Let a store assign to each identity below \(n\) either nothing or a doubly linked list node whose pointers are identities below \(n\) or NIL, let the head be an identity below \(n\) or NIL, and let \(x\) be an identity whose store entry is a node, where the store entry of the head is also a node when the head is not NIL. The procedure sets the next pointer of \(x\) to the head, then sets the previous pointer of \(x\) to NIL, then tests whether the head is NIL; if it is not, it sets the previous pointer of the node at the head (read after the two writes at \(x\)) to \(x\). Finally it makes \(x\) the new head and returns the updated store together with the head \(x\). One time unit is charged for each pointer assignment and for the NIL test, so the cost is \(4\) when the head is NIL and \(5\) otherwise.
Cslib.Algorithms.Lean.DoublyLinkedList.listPrepend · 34 lines
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
let store2 := store1.set x.val (some prevX) x.isLt
TimeM.tick 1
match he : head with
| none =>
TimeM.tick 1
pure (store2, some x)
| some h =>
have hAllocated : (store2.get h).isSome := by
change (store1.set x.val (some prevX) x.isLt)[h.val].isSome = true
rw [Vector.getElem_set]
split
· rfl
· change store1[h.val].isSome = true
rw [Vector.getElem_set]
split
· rfl
· exact hh h he
let currentH := (store2.get h).get hAllocated
TimeM.tick 1
let store3 := store2.set h.val (some { currentH with prev := some x }) h.isLt
TimeM.tick 1
pure (store3, some x)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
Uses 1 · Used by 12
TheoremPrepending makes the supplied node the head#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Then the head returned by List Prepend on \(s\), \(\mathrm{head}\) and \(x\) is \(x\).
public theorem listPrepend_head {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
(listPrepend store head x hx hh).ret.2 = some x
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 2 · Used by 1
TheoremExact cost of doubly linked list prepend#✓fully proved
Let a store assign to each identity below \(n\) either nothing or a doubly linked list node, let the head be an identity below \(n\) or NIL, and let \(x\) be an identity whose store entry is a node, where the store entry of the head is also a node when the head is not NIL. Then prepending \(x\) costs exactly \(5\) time units if the head is not NIL and exactly \(4\) if it is NIL, counting one unit per pointer assignment and per NIL test of the old head.
public theorem listPrepend_time {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
(listPrepend store head x hx hh).time = if head.isSome then 5 else 4
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 2 · Used by 1
TheoremResult of doubly-linked list prepend#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Let \(u\) be \(s\) with the cell at \(x\) replaced by the node \(s(x)\) with next pointer \(\mathrm{head}\) and previous pointer NIL. Then List Prepend on \(s\), \(\mathrm{head}\) and \(x\) returns the head \(x\) together with the store \(u\) when \(\mathrm{head}\) is NIL, and, when \(\mathrm{head}\) is an identity \(h\), together with the store obtained from \(u\) by replacing its cell at \(h\) with the node \(u(h)\) with previous pointer \(x\) (leaving the cell empty if \(u(h)\) is nothing).
This holds also when \(h = x\).
public theorem listPrepend_ret {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
let updated := store.set x.val
(some { (store.get x).get hx with next := head, prev := none }) x.isLt
(listPrepend store head x hx hh).ret =
(match head with
| none => updated
| some h => updated.set h.val
((updated.get h).map fun node => { node with prev := some x }) h.isLt,
some x)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 2 · Used by 1
TheoremCells after doubly-linked list prepend#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Let \(s'\) be the store returned by List Prepend on \(s\), \(\mathrm{head}\) and \(x\). For every identity \(z\), let \(c\) be the node \(s(x)\) with next pointer \(\mathrm{head}\) and previous pointer NIL if \(z = x\), and \(c = s(z)\) otherwise. Then \(s'(z)\) is \(c\) with previous pointer \(x\) (nothing, if \(c\) is nothing) when \(\mathrm{head} = z\), and \(s'(z) = c\) otherwise.
public theorem listPrepend_get {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) (z : Fin capacity) :
(listPrepend store head x hx hh).ret.1.get z =
let cell := if x = z then
some { (store.get x).get hx with next := head, prev := none } else store.get z
if head = some z then cell.map fun node => { node with prev := some x } else cell
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 3 · Used by 5
TheoremDoubly-linked list prepend leaves other cells unchanged#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Let \(s'\) be the store returned by List Prepend on \(s\), \(\mathrm{head}\) and \(x\). Then \(s'(z) = s(z)\) for every identity \(z\) with \(z \ne x\) and \(\mathrm{head} \ne z\).
public theorem listPrepend_frame {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) (z : Fin capacity)
(xz : x ≠ z) (hz : head ≠ some z) :
(listPrepend store head x hx hh).ret.1.get z = store.get z
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 3 · Used by 2
TheoremDoubly-linked list prepend preserves allocation#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Let \(s'\) be the store returned by List Prepend on \(s\), \(\mathrm{head}\) and \(x\). Then for every identity \(z\), \(s'(z)\) is a node if and only if \(s(z)\) is a node.
public theorem listPrepend_allocated {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) (z : Fin capacity) :
((listPrepend store head x hx hh).ret.1.get z).isSome = (store.get z).isSome
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 3
TheoremDoubly-linked list prepend preserves payloads#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Let \(s'\) be the store returned by List Prepend on \(s\), \(\mathrm{head}\) and \(x\). Then for every identity \(z\), either \(s(z)\) and \(s'(z)\) are both nothing, or both are nodes with the same payload.
public theorem listPrepend_payload {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) (z : Fin capacity) :
((listPrepend store head x hx hh).ret.1.get z).map Node.payload =
(store.get z).map Node.payload
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
payload : α
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:45
Uses 3
TheoremPointers of the node prepended to a doubly-linked list#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Suppose moreover that \(\mathrm{head}\) is not \(x\) (it may be NIL). Let \(s'\) be the store returned by List Prepend on \(s\), \(\mathrm{head}\) and \(x\). Then \(s'(x)\) is the node \(s(x)\) with next pointer \(\mathrm{head}\) and previous pointer NIL.
public theorem listPrepend_node {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) (fresh : head ≠ some x) :
(listPrepend store head x hx hh).ret.1.get x =
some { (store.get x).get hx with next := head, prev := none }
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 3 · Used by 1
TheoremOld head after doubly-linked list prepend#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Suppose moreover that \(\mathrm{head}\) is an identity \(h\) with \(h \ne x\). Let \(s'\) be the store returned by List Prepend on \(s\), \(\mathrm{head}\) and \(x\). Then \(s'(h)\) is the node \(s(h)\) with previous pointer \(x\).
public theorem listPrepend_oldHead {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) (h : Fin capacity)
(he : head = some h) (xh : x ≠ h) :
(listPrepend store head x hx hh).ret.1.get h =
some { (store.get h).get (hh h he) with prev := some x }
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 3 · Used by 1
TheoremCorrectness of doubly-linked list prepend#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Suppose moreover that \(s\) with head \(\mathrm{head}\) represents a list \(L = (\ell_1, \dots, \ell_k)\) of identities, that is, the \(\ell_i\) are distinct, \(\mathrm{head}\) is \(\ell_1\) (NIL if \(k = 0\)), and each \(s(\ell_i)\) is a node with previous pointer \(\ell_{i-1}\) and next pointer \(\ell_{i+1}\) (NIL past the ends of \(L\)), and that \(x\) is not in \(L\). Let \(s'\) and \(h'\) be the store and head returned by List Prepend on \(s\), \(\mathrm{head}\) and \(x\). Then \(s'\) with head \(h'\) represents the list \((x, \ell_1, \dots, \ell_k)\).
public theorem listPrepend_represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome)
(ids : List (Fin capacity)) (rep : Represents store head ids) (fresh : x ∉ ids) :
Represents (listPrepend store head x hx hh).ret.1
(listPrepend store head x hx hh).ret.2 (x :: ids)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def Represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ head = ids.head? ∧
∀ (i : Nat) (hi : i < ids.length), ∃ node,
store.get ids[i] = some node ∧
node.prev = (if i = 0 then none else ids[i - 1]?) ∧
node.next = ids[i + 1]?
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:53
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 7
TheoremDoubly-linked list prepend preserves disjoint lists#✓fully proved
Fix a capacity \(n\) and let \(s\) be a store assigning to each identity \(i < n\) either a node or nothing, let \(\mathrm{head}\) be NIL or an identity, and let \(x\) be an identity such that \(s(x)\) is a node and, when \(\mathrm{head}\) is an identity, \(s(\mathrm{head})\) is a node. Suppose moreover that \(s\) with some head \(o\) represents a list \(L = (\ell_1, \dots, \ell_k)\) of identities, that is, the \(\ell_i\) are distinct, \(o\) is \(\ell_1\) (NIL if \(k = 0\)), and each \(s(\ell_i)\) is a node with previous pointer \(\ell_{i-1}\) and next pointer \(\ell_{i+1}\) (NIL past the ends of \(L\)), that \(x\) is not in \(L\), and that \(\mathrm{head}\) is NIL or an identity not in \(L\). Let \(s'\) be the store returned by List Prepend on \(s\), \(\mathrm{head}\) and \(x\). Then \(s'\) with head \(o\) also represents \(L\).
public theorem listPrepend_disjoint_represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome)
(otherHead : Option (Fin capacity)) (otherIds : List (Fin capacity))
(rep : Represents store otherHead otherIds) (fresh : x ∉ otherIds)
(disjoint : ∀ h, head = some h → h ∉ otherIds) :
Represents (listPrepend store head x hx hh).ret.1 otherHead otherIds
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def Represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ head = ids.head? ∧
∀ (i : Nat) (hi : i < ids.length), ∃ node,
store.get ids[i] = some node ∧
node.prev = (if i = 0 then none else ids[i - 1]?) ∧
node.next = ids[i + 1]?
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:53
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
TheoremUniform cost bound for doubly linked list prepend#✓fully proved
Let a store assign to each identity below \(n\) either nothing or a doubly linked list node, let the head be an identity below \(n\) or NIL, and let \(x\) be an identity whose store entry is a node, where the store entry of the head is also a node when the head is not NIL. Then prepending \(x\) costs at most \(5\) time units, counting one unit per pointer assignment and per NIL test of the old head.
public theorem listPrepend_time_le {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
(listPrepend store head x hx hh).time ≤ 5
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listPrepend {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hh : ∀ h, head = some h → (store.get h).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
let nextX := { originalX with next := head }
let store1 := store.set x.val (some nextX) x.isLt
TimeM.tick 1
let prevX := { nextX with prev := none }
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:70
Uses 3
DefinitionDeletion from a doubly linked list#✓fully proved
Let the store be an array of cells, each either empty
or holding a node with a payload and optional next and previous pointers to cells, let \(h\) be
the optional head pointer, and let \(x\) be a cell holding a node such that each cell named by its
previous or next pointer also holds a node. Following CLRS LIST-DELETE, deleting \(x\) returns a
new store and head as follows. If the node at \(x\) has a previous pointer \(p\), the next pointer of
the node at \(p\) is set to the next pointer of \(x\) and the head stays \(h\); otherwise the head
becomes the next pointer of \(x\). Then, if the node at \(x\) has a next pointer \(s\), the previous
pointer of the node at \(s\) in the store from the first step is set to the previous pointer of \(x\).
The computation is charged one unit for each of the two NIL tests and one unit for each executed
pointer or head assignment, so three or four units in total.
The record stored at \(x\) is kept and no cell is freed; list consistency is needed only for correctness, not to run the procedure.
Cslib.Algorithms.Lean.DoublyLinkedList.listDelete · 28 lines
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
finishDelete store originalX.next x hx hn
| some p =>
let originalP := (store.get p).get (hp p he)
TimeM.tick 1
let store1 := store.set p.val (some { originalP with next := originalX.next }) p.isLt
have hxeq : store1.get x = store.get x := first_write_preserves_x store x p hx (hp p he)
have hx1 : (store1.get x).isSome := by rw [hxeq]; exact hx
have hn1 : ∀ n, ((store1.get x).get hx1).next = some n → (store1.get n).isSome := by
intro n hnext
have hn0 : (store.get n).isSome := hn n (by simpa only [hxeq] using hnext)
change (store.set p.val (some { originalP with next := originalX.next }) p.isLt)[n.val].isSome
rw [Vector.getElem_set]
split
· rfl
· exact hn0
finishDelete store1 head x hx1 hn1
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
private def finishDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let currentX := (store.get x).get hx
TimeM.tick 1
match he : currentX.next with
| none => pure (store, head)
| some n =>
let currentN := (store.get n).get (hn n he)
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:47
private lemma first_write_preserves_x {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x p : Fin capacity)
(hx : (store.get x).isSome) (hp : (store.get p).isSome) :
(store.set p.val
(some { (store.get p).get hp with next := ((store.get x).get hx).next }) p.isLt).get x =
store.get x
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:28
Uses 1 · Used by 16
TheoremRunning time of doubly linked list deletion#✓fully proved
Let the store be an array of cells, each either empty or holding a node with optional next and previous pointers, let \(h\) be the optional head pointer, and let \(x\) be a cell holding a node such that each cell named by its previous or next pointer also holds a node. Counting one unit per NIL test and per executed pointer or head assignment, deleting \(x\) from the list with head \(h\) costs exactly \(4\) units if the node at \(x\) has a next pointer, and exactly \(3\) units otherwise.
public theorem listDelete_time {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
(listDelete store head x hx hp hn).time =
if ((store.get x).get hx).next.isSome then 4 else 3
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
Uses 2 · Used by 1
TheoremHeader after doubly linked list deletion#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Let \(h'\) be the header returned by List Delete on \(S\), \(h\) and \(x\). Then \(h' = h\) if \(X.\mathrm{prev}\) is not NIL, and \(h' = X.\mathrm{next}\) if \(X.\mathrm{prev}\) is NIL.
public theorem listDelete_head {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
(listDelete store head x hx hp hn).ret.2 =
if ((store.get x).get hx).prev.isSome then head else ((store.get x).get hx).next
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
Uses 2 · Used by 1
TheoremResult of doubly linked list deletion#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Let \(T\) be \(S\) if \(X.\mathrm{prev}\) is NIL, and otherwise \(S\) with the next pointer of the node in cell \(X.\mathrm{prev}\) set to \(X.\mathrm{next}\). Then List Delete on \(S\), \(h\) and \(x\) returns the store equal to \(T\) if \(X.\mathrm{next}\) is NIL, and otherwise to \(T\) with the previous pointer of the node in cell \(X.\mathrm{next}\) of \(T\) set to \(X.\mathrm{prev}\), together with the header \(h\) if \(X.\mathrm{prev}\) is not NIL and \(X.\mathrm{next}\) if it is NIL. The successor's node is read from \(T\), so when \(X.\mathrm{prev}\) and \(X.\mathrm{next}\) are the same index (not NIL), both pointer updates apply to its node.
public theorem listDelete_ret {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
let originalX := (store.get x).get hx
let carried := match originalX.prev with
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
Uses 2 · Used by 6
TheoremCellwise result of doubly linked list deletion#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Let \(S'\) be the store returned by List Delete on \(S\), \(h\) and \(x\). For an index \(z\), let \(C\) be the cell \(S(z)\) with the next pointer of its node set to \(X.\mathrm{next}\) if \(X.\mathrm{prev} = z\), and \(C = S(z)\) otherwise. Then \(S'(z)\) is \(C\) with the previous pointer of its node set to \(X.\mathrm{prev}\) if \(X.\mathrm{next} = z\), and \(S'(z) = C\) otherwise; an empty cell stays empty. So the write at the predecessor is applied before the write at the successor.
public theorem listDelete_get {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome)
(z : Fin capacity) :
(listDelete store head x hx hp hn).ret.1.get z =
let originalX := (store.get x).get hx
let carried := if originalX.prev = some z then
(store.get z).map fun node => { node with next := originalX.next } else store.get z
if originalX.next = some z then
carried.map fun node => { node with prev := originalX.prev } else carried
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
Uses 3 · Used by 6
TheoremDeletion changes only the cells of the neighbors#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Let \(S'\) be the store returned by List Delete on \(S\), \(h\) and \(x\). Then \(S'(z) = S(z)\) for every index \(z\) with \(X.\mathrm{prev} \ne z\) and \(X.\mathrm{next} \ne z\).
public theorem listDelete_frame {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome)
(z : Fin capacity)
(hzp : ((store.get x).get hx).prev ≠ some z)
(hzn : ((store.get x).get hx).next ≠ some z) :
(listDelete store head x hx hp hn).ret.1.get z = store.get z
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
Uses 3 · Used by 2
TheoremDeletion from a doubly linked list allocates and frees nothing#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Let \(S'\) be the store returned by List Delete on \(S\), \(h\) and \(x\). Then for every index \(z\), the cell \(S'(z)\) holds a node if and only if \(S(z)\) does.
public theorem listDelete_allocated {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome)
(z : Fin capacity) :
((listDelete store head x hx hp hn).ret.1.get z).isSome = (store.get z).isSome
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
Uses 3 · Used by 1
TheoremDeletion preserves payloads#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Let \(S'\) be the store returned by List Delete on \(S\), \(h\) and \(x\). Then for every index \(z\), either \(S'(z)\) and \(S(z)\) are both empty, or both hold nodes and these two nodes have the same payload.
public theorem listDelete_payload {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome)
(z : Fin capacity) :
((listDelete store head x hx hp hn).ret.1.get z).map Node.payload =
(store.get z).map Node.payload
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
payload : α
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:45
Uses 3 · Used by 1
TheoremThe deleted node keeps its record#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Let \(S'\) be the store returned by List Delete on \(S\), \(h\) and \(x\). Then \(S'(x) = S(x)\): the deleted node stays allocated with its payload and both of its pointers unchanged.
public theorem listDelete_deleted_record {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
(listDelete store head x hx hp hn).ret.1.get x = store.get x
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
Uses 3 · Used by 1
TheoremDeletion preserves lists avoiding the deleted node's neighbors#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Let \(S'\) be the store returned by List Delete on \(S\), \(h\) and \(x\). Let \(h_0\) be a header and \(L\) a list of indices such that \(S\) with header \(h_0\) represents \(L\): the entries of \(L\) are distinct, \(h_0\) is the first entry of \(L\) (NIL if \(L\) is empty), and the cell of each entry of \(L\) holds a node whose previous and next pointers are the adjacent entries of \(L\), NIL at the ends. If neither \(X.\mathrm{prev}\) nor \(X.\mathrm{next}\) is an entry of \(L\), then \(S'\) with header \(h_0\) also represents \(L\).
public theorem listDelete_disjoint_represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome)
(otherHead : Option (Fin capacity)) (otherIds : List (Fin capacity))
(rep : Represents store otherHead otherIds)
(disjointP : ∀ p, ((store.get x).get hx).prev = some p → p ∉ otherIds)
(disjointN : ∀ n, ((store.get x).get hx).next = some n → n ∉ otherIds) :
Represents (listDelete store head x hx hp hn).ret.1 otherHead otherIds
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def Represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ head = ids.head? ∧
∀ (i : Nat) (hi : i < ids.length), ∃ node,
store.get ids[i] = some node ∧
node.prev = (if i = 0 then none else ids[i - 1]?) ∧
node.next = ids[i + 1]?
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:53
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
Uses 4
TheoremDoubly linked list deletion costs at most four events#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Then List Delete on \(S\), \(h\) and \(x\) costs at most \(4\), counting NIL tests and executed pointer and header assignments.
public theorem listDelete_time_le {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
(listDelete store head x hx hp hn).time ≤ 4
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
Uses 3
TheoremCorrectness of doubly linked list deletion#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node (a payload with next and previous pointers, each an index or NIL), let \(h\) be a header (an index in \(\{0, \dots, n-1\}\) or NIL), and let \(x\) be an index whose cell holds a node \(X\) such that the cells at the predecessor \(X.\mathrm{prev}\) and the successor \(X.\mathrm{next}\) hold nodes whenever these pointers are not NIL. Let \(S'\) and \(h'\) be the store and header returned by List Delete on \(S\), \(h\) and \(x\). Let \(L\) be a list of indices such that \(S\) with header \(h\) represents \(L\): the entries of \(L\) are distinct, \(h\) is the first entry of \(L\) (NIL if \(L\) is empty), and the cell of each entry of \(L\) holds a node whose previous and next pointers are the adjacent entries of \(L\), NIL at the ends. If \(x\) is an entry of \(L\), then \(S'\) with header \(h'\) represents the list obtained from \(L\) by removing \(x\).
public theorem listDelete_represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome)
(ids : List (Fin capacity)) (rep : Represents store head ids) (member : x ∈ ids) :
Represents (listDelete store head x hx hp hn).ret.1
(listDelete store head x hx hp hn).ret.2 (ids.erase x)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def Represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ head = ids.head? ∧
∀ (i : Nat) (hi : i < ids.length), ∃ node,
store.get ids[i] = some node ∧
node.prev = (if i = 0 then none else ids[i - 1]?) ∧
node.next = ids[i + 1]?
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:53
public def listDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hn : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity × Option (Fin capacity)) := do
let originalX := (store.get x).get hx
TimeM.tick 1
match he : originalX.prev with
| none =>
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Delete.lean:65
DefinitionDoubly-linked-list insertion (LIST-INSERT)#✓fully proved
A store is an array of \(n\) cells, each either empty or holding a node with a payload and optional next and previous pointers to cells. Given allocated cells \(x\) and \(y\) such that the successor of \(y\), if any, is allocated, inserting \(x\) after \(y\) performs the five lines of LIST-INSERT in order, each reading the store as updated so far: set the next pointer of \(x\) to the next pointer of \(y\); set the previous pointer of \(x\) to \(y\); test whether the next pointer of \(y\) is NIL; if it is some cell \(z\), set the previous pointer of \(z\) to \(x\); set the next pointer of \(y\) to \(x\). The result is the updated store, with cost one unit per pointer assignment and per NIL test.
Cslib.Algorithms.Lean.DoublyLinkedList.listInsert · 36 lines
public def listInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
TimeM.tick 1
let prevX := { (store1.get x).get hx1 with prev := some y }
let store2 := store1.set x.val (some prevX) x.isLt
have hy2 := (allocated_set store1 x hx1 prevX y).trans hy1
let currentY2 := (store2.get y).get hy2
have nextPreserved : currentY2.next = oldY.next :=
(y_next_after_prev store1 x y hx1 hy1).trans
(y_next_after_set store x y hx hy)
TimeM.tick 1
match he : currentY2.next with
| none =>
TimeM.tick 1
pure (store2.set y.val (some { currentY2 with next := some x }) y.isLt)
| some z =>
have hz := hs z (nextPreserved.symm.trans he)
have hz1 := (allocated_set store x hx nextX z).trans hz
have hz2 := (allocated_set store1 x hx1 prevX z).trans hz1
let currentZ := (store2.get z).get hz2
TimeM.tick 1
let newZ := { currentZ with prev := some x }
let store3 := store2.set z.val (some newZ) z.isLt
have hy3 := (allocated_set store2 z hz2 newZ y).trans hy2
let currentY3 := (store3.get y).get hy3
TimeM.tick 1
pure (store3.set y.val (some { currentY3 with next := some x }) y.isLt)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
Uses 1 · Used by 12
TheoremCost of doubly-linked-list insertion#✓fully proved
For a store \(S\) and allocated cells \(x\) and \(y\) such that the successor of \(y\) in \(S\), if any, is allocated, the cost of inserting \(x\) after \(y\), counting one unit per pointer assignment and per NIL test, is \(5\) if \(y\) has a successor in \(S\) and \(4\) if the next pointer of \(y\) in \(S\) is NIL.
No distinctness of \(x\) and \(y\) is assumed.
public theorem listInsert_time {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
(listInsert store x y hx hy hs).time =
if ((store.get y).get hy).next.isSome then 5 else 4
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Insert.lean:85
Uses 2
TheoremResult of doubly-linked-list insertion#✓fully proved
For a store \(S\) and allocated cells \(x\) and \(y\) such that the successor of \(y\) in \(S\), if any, is allocated, let \(s\) be the next pointer of \(y\) in \(S\). Let \(S_1\) be \(S\) with cell \(x\) replaced by the node of \(x\) with next pointer \(s\) and previous pointer \(y\). Let \(S_2 = S_1\) if \(s\) is NIL; if \(s\) is a cell \(z\), let \(S_2\) be \(S_1\) with the previous pointer of the node in cell \(z\) of \(S_1\) replaced by \(x\). Then the store returned by inserting \(x\) after \(y\) is \(S_2\) with the next pointer of the node in cell \(y\) of \(S_2\) replaced by \(x\).
No distinctness or representation hypothesis is assumed.
public theorem listInsert_ret {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
let updated := store.set x.val (some { (store.get x).get hx with
next := ((store.get y).get hy).next, prev := some y }) x.isLt
let backlinked := match ((store.get y).get hy).next with
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
Uses 2 · Used by 5
TheoremCells after doubly-linked-list insertion#✓fully proved
For a store \(S\), allocated cells \(x\) and \(y\) such that the successor of \(y\) in \(S\), if any, is allocated, and any cell \(z\), let \(s\) be the next pointer of \(y\) in \(S\). Let \(c_1\) be the node of \(x\) in \(S\) with next pointer replaced by \(s\) and previous pointer replaced by \(y\) if \(z = x\), and the content of cell \(z\) in \(S\) otherwise. Let \(c_2\) be \(c_1\) with previous pointer replaced by \(x\) if \(s = z\), and \(c_1\) otherwise. Then cell \(z\) of the store returned by inserting \(x\) after \(y\) is \(c_2\) with next pointer replaced by \(x\) if \(z = y\), and \(c_2\) otherwise (an empty cell stays empty).
No distinctness of \(x\), \(y\) and \(s\) is assumed.
public theorem listInsert_get {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y z : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ w, ((store.get y).get hy).next = some w → (store.get w).isSome) :
let first := if x = z then some { (store.get x).get hx with
next := ((store.get y).get hy).next, prev := some y } else store.get z
let second := if ((store.get y).get hy).next = some z then
first.map (fun (node : Node α (Fin n)) => { node with prev := some x }) else first
(listInsert store x y hx hy hs).ret.get z =
if y = z then second.map (fun (node : Node α (Fin n)) =>
{ node with next := some x }) else second
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Insert.lean:85
Uses 3 · Used by 5
TheoremFrame property of doubly-linked-list insertion#✓fully proved
For a store \(S\) and allocated cells \(x\) and \(y\) such that the successor of \(y\) in \(S\), if any, is allocated, let \(z\) be a cell with \(z \ne x\), \(z \ne y\), and \(z\) not the successor of \(y\) in \(S\). Then cell \(z\) of the store returned by inserting \(x\) after \(y\) equals cell \(z\) of \(S\).
public theorem listInsert_frame {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y z : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ w, ((store.get y).get hy).next = some w → (store.get w).isSome)
(hxz : x ≠ z) (hyz : y ≠ z) (hsz : ((store.get y).get hy).next ≠ some z) :
(listInsert store x y hx hy hs).ret.get z = store.get z
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Insert.lean:85
Uses 3 · Used by 2
TheoremDoubly-linked-list insertion neither allocates nor frees cells#✓fully proved
For a store \(S\) and allocated cells \(x\) and \(y\) such that the successor of \(y\) in \(S\), if any, is allocated, every cell \(z\) of the store returned by inserting \(x\) after \(y\) is allocated if and only if cell \(z\) of \(S\) is allocated. No distinctness of \(x\), \(y\) and the successor of \(y\) is assumed.
public theorem listInsert_allocated {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y z : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ w, ((store.get y).get hy).next = some w → (store.get w).isSome) :
((listInsert store x y hx hy hs).ret.get z).isSome =
(store.get z).isSome
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Insert.lean:85
Uses 3 · Used by 1
TheoremDoubly-linked-list insertion preserves payloads#✓fully proved
For a store \(S\) and allocated cells \(x\) and \(y\) such that the successor of \(y\) in \(S\), if any, is allocated, every cell \(z\) of the store returned by inserting \(x\) after \(y\) is empty exactly when cell \(z\) of \(S\) is empty, and otherwise holds a node with the same payload as the node in cell \(z\) of \(S\).
No hypothesis on the payloads, such as distinct keys, is needed.
public theorem listInsert_payload {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y z : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ w, ((store.get y).get hy).next = some w → (store.get w).isSome) :
((listInsert store x y hx hy hs).ret.get z).map Node.payload =
(store.get z).map Node.payload
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def listInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Insert.lean:85
payload : α
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:45
Uses 3 · Used by 1
TheoremInsertion into a represented doubly-linked list#✓fully proved
Suppose the store \(S\) with head \(h\) represents the list of distinct cells \((i_0, \dots, i_{m-1})\). Let \(k < m\), let \(x\) be an allocated cell not among \(i_0, \dots, i_{m-1}\), and assume that cell \(i_k\) and its successor in \(S\), if any, are allocated. Then the store returned by inserting \(x\) after \(i_k\), with the same head \(h\), represents the list \((i_0, \dots, i_k, x, i_{k+1}, \dots, i_{m-1})\).
The hypothesis that \(x\) is not in the list is used only in the proof; the insertion itself runs without it.
public theorem listInsert_represents {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x : Fin n)
(head : Option (Fin n)) (ids : List (Fin n)) (rep : Represents store head ids)
(fresh : x ∉ ids) (k : Nat) (hk : k < ids.length)
(hx : (store.get x).isSome) (hy : (store.get ids[k]).isSome)
(hs : ∀ z, ((store.get ids[k]).get hy).next = some z → (store.get z).isSome) :
Represents (listInsert store x ids[k] hx hy hs).ret head
(ids.insertIdx (k + 1) x)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def Represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ head = ids.head? ∧
∀ (i : Nat) (hi : i < ids.length), ∃ node,
store.get ids[i] = some node ∧
node.prev = (if i = 0 then none else ids[i - 1]?) ∧
node.next = ids[i + 1]?
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:53
public def listInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Insert.lean:85
Uses 4
TheoremDoubly-linked-list insertion preserves disjoint lists#✓fully proved
Suppose the store \(S\) with head \(h\) represents the list of cells \((i_0, \dots, i_{m-1})\) and with head \(h'\) represents a list \(L'\) of cells, and that the two lists have no cell in common. Let \(k < m\), let \(x\) be an allocated cell not in \(L'\), and assume that cell \(i_k\) and its successor in \(S\), if any, are allocated. Then the store returned by inserting \(x\) after \(i_k\), with head \(h'\), still represents \(L'\).
public theorem listInsert_disjoint_represents {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x : Fin n)
(head otherHead : Option (Fin n)) (ids otherIds : List (Fin n))
(rep : Represents store head ids) (otherRep : Represents store otherHead otherIds)
(disjoint : List.Disjoint ids otherIds) (freshOther : x ∉ otherIds)
(k : Nat) (hk : k < ids.length) (hx : (store.get x).isSome)
(hy : (store.get ids[k]).isSome)
(hs : ∀ z, ((store.get ids[k]).get hy).next = some z → (store.get z).isSome) :
Represents (listInsert store x ids[k] hx hy hs).ret otherHead otherIds
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def Represents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(head : Option (Fin capacity)) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ head = ids.head? ∧
∀ (i : Nat) (hi : i < ids.length), ∃ node,
store.get ids[i] = some node ∧
node.prev = (if i = 0 then none else ids[i - 1]?) ∧
node.next = ids[i + 1]?
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:53
public def listInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
TimeM.tick 1
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Insert.lean:85
Uses 4
DefinitionCircular doubly linked list with a sentinel#✓fully proved
Let \(S\) be a store of \(n\) slots indexed by \(\{0, \dots, n-1\}\), each either empty or holding a node with a payload and optional next and previous indices. The store \(S\) represents a circular list with sentinel \(s\) and data nodes \(x_1, \dots, x_k\) (a list of indices) when the \(x_i\) are pairwise distinct and all differ from \(s\); slot \(s\) holds a node whose next index is \(x_1\) and whose previous index is \(x_k\) (both are \(s\) when \(k = 0\)); and for every \(i\) with \(1 \le i \le k\), slot \(x_i\) holds a node whose previous index is \(x_{i-1}\) and whose next index is \(x_{i+1}\), where \(x_0 = x_{k+1} = s\).
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
Uses 1 · Used by 10
TheoremEmpty circular list with a sentinel#✓fully proved
Let \(S\) be a store of \(n\) slots indexed by \(\{0, \dots, n-1\}\), each either empty or holding a node with a payload and optional next and previous indices, and let \(s\) be an index. Then \(S\) represents the empty circular list with sentinel \(s\) if and only if slot \(s\) holds a node whose next index and previous index are both \(s\).
public theorem CircularRepresents_nil {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (s : Fin capacity) :
CircularRepresents store s [] ↔
∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some s ∧ sentinel.prev = some s
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
Uses 2
DefinitionDeletion from a circular list with a sentinel#✓fully proved
Let \(S\) be a node store with \(c\) cells, each either empty or holding a node (a payload, an optional next address and an optional previous address in \(\{0, \dots, c-1\}\)). Let \(x\) be an address whose cell holds a node \(\nu\) with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. Sentinel deletion of \(x\) from \(S\) is the timed computation that first sets the next address of the node at \(p\) to \(n\), at cost \(1\), then sets the previous address of the node at \(n\) in the resulting store to \(p\), at cost \(1\), and returns the store obtained.
These dereferences are its only preconditions: it does not check that \(x\) is not the sentinel or that \(x\) lies on a represented list.
Cslib.Algorithms.Lean.DoublyLinkedList.sentinelDelete · 40 lines
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
TimeM.tick 1
let updatedP := { currentP with next := nx.next }
let store1 := store.set p.val (some updatedP) p.isLt
have hStable : store1.get x = some nx :=
first_write_keeps_x store x p nx currentP
(Option.some_get hx).symm (Option.some_get hpAllocated).symm
have hx1 : (store1.get x).isSome := by
rw [hStable]
rfl
let currentX := (store1.get x).get hx1
have currentX_eq : currentX = nx :=
Option.some.inj ((Option.some_get hx1).trans hStable)
have hn1 : currentX.next.isSome := by
rw [currentX_eq]
exact hn
let n := currentX.next.get hn1
have hNext : nx.next = some n := by
rw [← currentX_eq]
exact (Option.some_get hn1).symm
have hnAllocated : (store1.get n).isSome := by
dsimp only [store1]
rw [get_set]
split
· rfl
· exact hnext n hNext
let currentN := (store1.get n).get hnAllocated
TimeM.tick 1
pure (store1.set n.val (some { currentN with prev := currentX.prev }) n.isLt)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
private lemma first_write_keeps_x {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x p : Fin n)
(nx np : Node α (Fin n))
(hx : store.get x = some nx) (hp : store.get p = some np) :
(store.set p.val (some { np with next := nx.next }) p.isLt).get x = some nx
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:52
Uses 1 · Used by 9
TheoremSentinel deletion takes two pointer assignments#✓fully proved
Let \(S\) be a node store and \(x\) an address whose cell holds a node with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. Then sentinel deletion of \(x\) from \(S\) has cost exactly \(2\), one for each pointer assignment.
This includes the alias cases in which \(p\), \(n\) and \(x\) are not pairwise distinct.
public theorem sentinelDelete_time {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
(sentinelDelete store x hx hp hn hprev hnext).time = 2
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:66
Uses 2
TheoremThe store returned by sentinel deletion#✓fully proved
Let \(S\) be a node store and \(x\) an address whose cell holds a node with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. Let \(S_1\) be \(S\) with the node at \(p\) replaced by a copy whose next address is \(n\). Then sentinel deletion of \(x\) from \(S\) returns \(S_1\) with the node of \(S_1\) at \(n\) replaced by a copy whose previous address is \(p\).
public theorem sentinelDelete_ret {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
let nx := (store.get x).get hx
let p := nx.prev.get hp
let n := nx.next.get hn
let carried := store.set p.val
((store.get p).map fun node => { node with next := nx.next }) p.isLt
(sentinelDelete store x hx hp hn hprev hnext).ret =
carried.set n.val ((carried.get n).map fun node => { node with prev := nx.prev }) n.isLt
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:66
Uses 2 · Used by 5
TheoremCells after sentinel deletion#✓fully proved
Let \(S\) be a node store and \(x\) an address whose cell holds a node with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. For every address \(z\), let \(C\) be the cell of \(S\) at \(z\) if \(z \ne p\), and that cell with its next address set to \(n\) if \(z = p\). Then after sentinel deletion of \(x\) from \(S\) the cell at \(z\) is \(C\) if \(z \ne n\), and \(C\) with its previous address set to \(p\) if \(z = n\).
public theorem sentinelDelete_get (z : Fin capacity) :
(sentinelDelete store x hx hp hn hprev hnext).ret.get z =
let nx := (store.get x).get hx
let carried := if nx.prev = some z then
(store.get z).map fun node => { node with next := nx.next } else store.get z
if nx.next = some z then
carried.map fun node => { node with prev := nx.prev } else carried
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:66
Uses 6 · Used by 1
TheoremSentinel deletion changes only the two neighbours#✓fully proved
Let \(S\) be a node store and \(x\) an address whose cell holds a node with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. For every address \(z\) with \(z \ne p\) and \(z \ne n\), the cell at \(z\) after sentinel deletion of \(x\) from \(S\) equals the cell of \(S\) at \(z\).
public theorem sentinelDelete_frame (z : Fin capacity)
(hzp : ((store.get x).get hx).prev ≠ some z)
(hzn : ((store.get x).get hx).next ≠ some z) :
(sentinelDelete store x hx hp hn hprev hnext).ret.get z = store.get z
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:66
TheoremSentinel deletion preserves allocation#✓fully proved
Let \(S\) be a node store and \(x\) an address whose cell holds a node with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. For every address \(z\), the cell at \(z\) after sentinel deletion of \(x\) from \(S\) is nonempty if and only if the cell of \(S\) at \(z\) is nonempty.
public theorem sentinelDelete_allocated (z : Fin capacity) :
((sentinelDelete store x hx hp hn hprev hnext).ret.get z).isSome =
(store.get z).isSome
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:66
TheoremSentinel deletion preserves payloads#✓fully proved
Let \(S\) be a node store and \(x\) an address whose cell holds a node with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. For every address \(z\), the cell at \(z\) after sentinel deletion of \(x\) from \(S\) and the cell of \(S\) at \(z\) are either both empty or both hold nodes with the same payload.
The payload includes any key and satellite data.
public theorem sentinelDelete_payload (z : Fin capacity) :
((sentinelDelete store x hx hp hn hprev hnext).ret.get z).map Node.payload =
(store.get z).map Node.payload
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:66
payload : α
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:45
TheoremSentinel deletion keeps the deleted node#✓fully proved
Let \(S\) be a node store and \(x\) an address whose cell holds a node with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. The cell at \(x\) after sentinel deletion of \(x\) from \(S\) equals the cell of \(S\) at \(x\), so the deleted node stays allocated and unchanged.
public theorem sentinelDelete_deleted_record :
(sentinelDelete store x hx hp hn hprev hnext).ret.get x = store.get x
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:66
TheoremSentinel deletion removes a node from its circular list#✓fully proved
Let \(S\) be a node store and \(x\) an address whose cell holds a node with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. If \(S\) represents a circular doubly linked list with sentinel \(s\) and data addresses \(\ell\), and \(x\) occurs in \(\ell\), then the store returned by sentinel deletion of \(x\) from \(S\) represents a circular doubly linked list with sentinel \(s\) and the list of data addresses obtained from \(\ell\) by removing the first occurrence of \(x\).
public theorem sentinelDelete_circular {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome)
(s : Fin capacity) (ids : List (Fin capacity))
(rep : CircularRepresents store s ids) (member : x ∈ ids) :
CircularRepresents (sentinelDelete store x hx hp hn hprev hnext).ret
s (ids.erase x)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:66
Uses 4
TheoremSentinel deletion preserves disjoint circular lists#✓fully proved
Let \(S\) be a node store and \(x\) an address whose cell holds a node with previous address \(p\) and next address \(n\), where the cells at \(p\) and \(n\) are nonempty. Suppose \(S\) represents a circular doubly linked list with sentinel \(s\) and data addresses \(\ell\), with \(x\) in \(\ell\), and also one with sentinel \(t\) and data addresses \(m\), such that no address occurs both among \(s\) and \(\ell\) and among \(t\) and \(m\). Then the store returned by sentinel deletion of \(x\) from \(S\) still represents the circular doubly linked list with sentinel \(t\) and data addresses \(m\).
public theorem sentinelDelete_disjoint_circular {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome)
(s t : Fin capacity) (ids otherIds : List (Fin capacity))
(rep : CircularRepresents store s ids) (member : x ∈ ids)
(otherRep : CircularRepresents store t otherIds)
(disjoint : (s :: ids).Disjoint (t :: otherIds)) :
CircularRepresents (sentinelDelete store x hx hp hn hprev hnext).ret
t otherIds
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
public def sentinelDelete {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity) (x : Fin capacity)
(hx : (store.get x).isSome)
(hp : ((store.get x).get hx).prev.isSome)
(hn : ((store.get x).get hx).next.isSome)
(hprev : ∀ p, ((store.get x).get hx).prev = some p → (store.get p).isSome)
(hnext : ∀ n, ((store.get x).get hx).next = some n → (store.get n).isSome) :
TimeM Nat (Vector (Option (Node α (Fin capacity))) capacity) := do
let nx := (store.get x).get hx
let p := nx.prev.get hp
have hpAllocated := hprev p (Option.some_get hp).symm
let currentP := (store.get p).get hpAllocated
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Delete.lean:66
DefinitionSentinel list insertion (LIST-INSERT′)#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node, in which the cells \(x\) and \(y\) are allocated, the next pointer of \(y\) is not NIL, and the cell \(w\) it points to is allocated. Sentinel insertion of \(x\) after \(y\) performs four pointer assignments in order, each costing one time unit, and returns the resulting store: set the next pointer of \(x\) to the next pointer of \(y\); set the previous pointer of \(x\) to \(y\); set the previous pointer of the successor \(w\) of \(y\) to \(x\); set the next pointer of \(y\) to \(x\).
Each assignment reads the store left by the previous ones, so aliased cells see earlier writes. Unlike LIST-INSERT, there is no NIL test.
Cslib.Algorithms.Lean.DoublyLinkedList.sentinelInsert · 34 lines
public def sentinelInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
TimeM.tick 1
let prevX := { (store1.get x).get hx1 with prev := some y }
let store2 := store1.set x.val (some prevX) x.isLt
have hy2 := (allocated_set store1 x hx1 prevX y).trans hy1
let currentY2 := (store2.get y).get hy2
have nextPreserved : currentY2.next = oldY.next :=
(y_next_after_prev store1 x y hx1 hy1).trans
(y_next_after_set store x y hx hy)
have hn2 : currentY2.next.isSome := by rw [nextPreserved]; exact hn
let z := currentY2.next.get hn2
have he : oldY.next = some z :=
nextPreserved.symm.trans (Option.some_get hn2).symm
have hz := hs z he
have hz1 := (allocated_set store x hx nextX z).trans hz
have hz2 := (allocated_set store1 x hx1 prevX z).trans hz1
TimeM.tick 1
let newZ := { (store2.get z).get hz2 with prev := some x }
let store3 := store2.set z.val (some newZ) z.isLt
have hy3 := (allocated_set store2 z hz2 newZ y).trans hy2
TimeM.tick 1
pure (store3.set y.val
(some { (store3.get y).get hy3 with next := some x }) y.isLt)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
Uses 1 · Used by 8
TheoremSentinel insertion takes four steps#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node, in which the cells \(x\) and \(y\) are allocated, the next pointer of \(y\) is not NIL, and the cell it points to is allocated. Then sentinel insertion of \(x\) after \(y\) in \(S\) costs exactly \(4\) time units, one per pointer assignment.
This counts logical pointer assignments, not full RAM cost.
public theorem sentinelInsert_time {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
(sentinelInsert store x y hx hy hn hs).time = 4
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def sentinelInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Insert.lean:74
Uses 2
TheoremThe store after sentinel insertion#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node, in which the cells \(x\) and \(y\) are allocated, the next pointer of \(y\) is not NIL, and the cell \(w\) it points to is allocated; write \(S[c]\) for the node at an allocated cell \(c\). Then the store returned by sentinel insertion of \(x\) after \(y\) is obtained from \(S\) by three updates, each applied to the result of the previous one: replace the content of \(x\) by the node \(S[x]\) with next pointer equal to the next pointer of \(S[y]\) and previous pointer \(y\); set the previous pointer of the node at \(w\) to \(x\); set the next pointer of the node at \(y\) to \(x\).
No freshness or distinctness of \(x\), \(y\) and \(w\) is assumed.
public theorem sentinelInsert_ret {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
let updated := store.set x.val (some { (store.get x).get hx with
next := ((store.get y).get hy).next, prev := some y }) x.isLt
let z := ((store.get y).get hy).next.get hn
let backlinked := updated.set z.val
((updated.get z).map fun (node : Node α (Fin n)) =>
{ node with prev := some x }) z.isLt
(sentinelInsert store x y hx hy hn hs).ret = backlinked.set y.val
((backlinked.get y).map fun (node : Node α (Fin n)) =>
{ node with next := some x }) y.isLt
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def sentinelInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Insert.lean:74
TheoremSentinel insertion, cell by cell#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node, in which the cells \(x\) and \(y\) are allocated, the next pointer of \(y\) is not NIL, and the cell it points to is allocated; write \(S[c]\) for the node at an allocated cell \(c\). Let \(S'\) be the store returned by sentinel insertion of \(x\) after \(y\). Then for every cell \(z\), the content of \(z\) in \(S'\) is obtained from its content in \(S\) by three steps in order: if \(z = x\), replace the content by the node \(S[x]\) with next pointer equal to the next pointer of \(S[y]\) and previous pointer \(y\); if the next pointer of \(S[y]\) is \(z\), set the previous pointer to \(x\); if \(z = y\), set the next pointer to \(x\). The last two steps leave an empty cell empty.
No distinctness of \(x\), \(y\), \(z\) and the successor of \(y\) is assumed.
public theorem sentinelInsert_get {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y z : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ w, ((store.get y).get hy).next = some w → (store.get w).isSome) :
let first := if x = z then some { (store.get x).get hx with
next := ((store.get y).get hy).next, prev := some y } else store.get z
let second := if ((store.get y).get hy).next = some z then
first.map (fun (node : Node α (Fin n)) => { node with prev := some x }) else first
(sentinelInsert store x y hx hy hn hs).ret.get z =
if y = z then second.map (fun (node : Node α (Fin n)) =>
{ node with next := some x }) else second
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def sentinelInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Insert.lean:74
Uses 6 · Used by 1
TheoremSentinel insertion leaves other cells unchanged#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node, in which the cells \(x\) and \(y\) are allocated, the next pointer of \(y\) is not NIL, and the cell it points to is allocated. Let \(S'\) be the store returned by sentinel insertion of \(x\) after \(y\). If a cell \(z\) differs from \(x\) and from \(y\), and the next pointer of \(y\) in \(S\) is not \(z\), then \(z\) has the same content in \(S'\) as in \(S\).
public theorem sentinelInsert_frame {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y z : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ w, ((store.get y).get hy).next = some w → (store.get w).isSome)
(hxz : x ≠ z) (hyz : y ≠ z) (hsz : ((store.get y).get hy).next ≠ some z) :
(sentinelInsert store x y hx hy hn hs).ret.get z = store.get z
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def sentinelInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Insert.lean:74
Uses 6 · Used by 1
TheoremSentinel insertion preserves allocation#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node, in which the cells \(x\) and \(y\) are allocated, the next pointer of \(y\) is not NIL, and the cell it points to is allocated. Let \(S'\) be the store returned by sentinel insertion of \(x\) after \(y\). Then every cell \(z\) is allocated in \(S'\) if and only if it is allocated in \(S\).
No distinctness of \(x\), \(y\), \(z\) and the successor of \(y\) is assumed.
public theorem sentinelInsert_allocated {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y z : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ w, ((store.get y).get hy).next = some w → (store.get w).isSome) :
((sentinelInsert store x y hx hy hn hs).ret.get z).isSome =
(store.get z).isSome
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def sentinelInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Insert.lean:74
TheoremSentinel insertion preserves payloads#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node, in which the cells \(x\) and \(y\) are allocated, the next pointer of \(y\) is not NIL, and the cell it points to is allocated. Let \(S'\) be the store returned by sentinel insertion of \(x\) after \(y\). Then for every cell \(z\), either \(z\) is empty in both \(S\) and \(S'\), or it holds a node in both and the two nodes have the same payload, including keys and satellite data.
public theorem sentinelInsert_payload {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y z : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ w, ((store.get y).get hy).next = some w → (store.get w).isSome) :
((sentinelInsert store x y hx hy hn hs).ret.get z).map Node.payload =
(store.get z).map Node.payload
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
public def sentinelInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Insert.lean:74
payload : α
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:45
TheoremSentinel insertion preserves the circular list#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node, in which the cells \(x\) and \(y\) are allocated, the next pointer of \(y\) is not NIL, and the cell it points to is allocated. Suppose \(S\) circularly represents the list \(L\) of cells with sentinel \(s\), that \(L\) is the concatenation of lists \(B\) and \(A\), and that \(y\) is the last element of \(B\), or \(y = s\) if \(B\) is empty. If \(x \ne s\) and \(x\) does not occur in \(L\), then the store returned by sentinel insertion of \(x\) after \(y\) circularly represents, with sentinel \(s\), the list consisting of the elements of \(B\), then \(x\), then the elements of \(A\).
public theorem sentinelInsert_circular {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y s : Fin n)
(ids before after : List (Fin n))
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome)
(hc : CircularRepresents store s ids)
(hSplit : ids = before ++ after) (hPosition : y = before.getLastD s)
(hFreshSentinel : x ≠ s) (hFreshData : x ∉ ids) :
CircularRepresents (sentinelInsert store x y hx hy hn hs).ret s
(before ++ x :: after)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
public def sentinelInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Insert.lean:74
Uses 4
TheoremSentinel insertion preserves a disjoint circular list#✓fully proved
Let \(S\) be a store of \(n\) cells, each empty or holding a node, in which the cells \(x\) and \(y\) are allocated, the next pointer of \(y\) is not NIL, and the cell it points to is allocated. Suppose \(S\) circularly represents the list \(L\) with sentinel \(s\), that \(L\) is the concatenation of lists \(B\) and \(A\), and that \(y\) is the last element of \(B\), or \(y = s\) if \(B\) is empty. Suppose \(S\) also circularly represents the list \(M\) with sentinel \(t\), that no cell among \(s\) and the elements of \(L\) equals \(t\) or an element of \(M\), and that \(x \ne t\) and \(x\) does not occur in \(M\). Then the store returned by sentinel insertion of \(x\) after \(y\) still circularly represents \(M\) with sentinel \(t\).
public theorem sentinelInsert_disjoint_circular {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y s t : Fin n)
(ids before after other : List (Fin n))
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome)
(hc : CircularRepresents store s ids)
(hSplit : ids = before ++ after) (hPosition : y = before.getLastD s)
(hcOther : CircularRepresents store t other)
(hDisjoint : List.Disjoint (s :: ids) (t :: other))
(hFreshSentinel : x ≠ t) (hFreshData : x ∉ other) :
CircularRepresents (sentinelInsert store x y hx hy hn hs).ret t other
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
public def sentinelInsert {α : Type u} {n : Nat}
(store : Vector (Option (Node α (Fin n))) n) (x y : Fin n)
(hx : (store.get x).isSome) (hy : (store.get y).isSome)
(hn : ((store.get y).get hy).next.isSome)
(hs : ∀ z, ((store.get y).get hy).next = some z → (store.get z).isSome) :
TimeM Nat (Vector (Option (Node α (Fin n))) n) := do
let oldY := (store.get y).get hy
TimeM.tick 1
let nextX := { (store.get x).get hx with next := oldY.next }
let store1 := store.set x.val (some nextX) x.isLt
have hx1 := (allocated_set store x hx nextX x).trans hx
have hy1 := (allocated_set store x hx nextX y).trans hy
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Insert.lean:74
Uses 4
DefinitionSearch in a circular list with a sentinel#✓fully proved
Let a store have cells indexed by \(\{0, \dots, m-1\}\), each empty or holding a node that carries a key, satellite data and optional next and prev links, and let the cell \(s\) be the sentinel of some list of data nodes that the store circularly represents. Sentinel search for a key \(k\) first overwrites the key of the sentinel with \(k\), keeping its satellite data and links, then starts at the successor of \(s\) and follows next links until it reaches a node whose key is \(k\). It returns the modified store together with that node, or no node if the scan stopped at \(s\).
Only the sentinel key is overwritten, and its old key is not restored. The cost charges one unit for the key write, one for initializing the cursor, one for each key comparison, one for each advance past a mismatching node and one for the final test against \(s\); reads and result construction are free, and physical storage or bit operations are not counted.
Cslib.Algorithms.Lean.DoublyLinkedList.listSearchSentinel · 25 lines
public def listSearchSentinel {Key : Type u} {Satellite : Type v} {capacity : Nat} [DecidableEq Key]
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (valid : ∃ ids, CircularRepresents store s ids) :
TimeM Nat (Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity ×
Option (Fin capacity)) :=
let hs := onCircle_allocated store s s (valid_sentinel store s valid)
do
TimeM.tick 1
let updated := keyWrite store s key hs
have updatedValid : ∃ ids, CircularRepresents updated s ids := by
obtain ⟨ids, rep⟩ := valid
exact ⟨ids, keyWrite_circular store s key hs ids rep⟩
let allocated := onCircle_allocated updated s
let sentinel := (updated.get s).get (allocated s (valid_sentinel updated s updatedValid))
let nextProof := valid_initial updated s updatedValid
(allocated s (valid_sentinel updated s updatedValid))
TimeM.tick 1
let cursor := sentinel.next.get nextProof.choose
let hg := nextProof.choose_spec
let acc := onCircle_acc updated s key (keyWrite_match store s key hs) cursor hg
let scan := reachableLoop updated key (OnCircle updated s) allocated
(onCircle_next updated s key (keyWrite_match store s key hs)) cursor acc hg
TimeM.tick scan.time
TimeM.tick 1
pure (updated, if scan.ret = s then none else some scan.ret)
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
private lemma onCircle_allocated {Key : Type u} {Satellite : Type v} {capacity : Nat}
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s cursor : Fin capacity) (hg : OnCircle store s cursor) : (store.get cursor).isSome
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Search.lean:233
private lemma valid_sentinel {Key : Type u} {Satellite : Type v} {capacity : Nat}
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (valid : ∃ ids, CircularRepresents store s ids) : OnCircle store s s
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Search.lean:239
@[no_expose] private def keyWrite {Key : Type u} {Satellite : Type v} {capacity : Nat}
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (hs : (store.get s).isSome) :
Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity :=
let sentinel := (store.get s).get hs
store.set s.val (some { sentinel with payload := (key, sentinel.payload.2) }) s.isLt
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Search.lean:39
TheoremSentinel search returns the first matching node#✓fully proved
If a store circularly represents the list \(L\) of data nodes with sentinel \(s\), then sentinel search for a key \(k\) returns the first node of \(L\) (in list order, which starts at the successor of \(s\)) whose key in the input store is \(k\), and returns no node if no node of \(L\) has key \(k\). In particular, the sentinel is never returned.
public theorem listSearchSentinel_ret {Key : Type u} {Satellite : Type v} {capacity : Nat}
[DecidableEq Key]
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (ids : List (Fin capacity))
(rep : CircularRepresents store s ids) :
(listSearchSentinel store s key ⟨ids, rep⟩).ret.2 =
ids.find? (fun x => decide
((store.get x).map (fun node => node.payload.1) = some key))
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
public def listSearchSentinel {Key : Type u} {Satellite : Type v} {capacity : Nat} [DecidableEq Key]
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (valid : ∃ ids, CircularRepresents store s ids) :
TimeM Nat (Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity ×
Option (Fin capacity)) :=
let hs := onCircle_allocated store s s (valid_sentinel store s valid)
do
TimeM.tick 1
let updated := keyWrite store s key hs
have updatedValid : ∃ ids, CircularRepresents updated s ids := by
obtain ⟨ids, rep⟩ := valid
exact ⟨ids, keyWrite_circular store s key hs ids rep⟩
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Search.lean:290
Uses 3
TheoremCost of sentinel search#✓fully proved
If a store circularly represents the list \(L\) of \(n\) data nodes with sentinel \(s\), then sentinel search for a key \(k\) costs exactly \(2i + 4\), where \(i\) is the position, counting from \(0\), of the first node of \(L\) whose key is \(k\), and \(i = n\) if no node of \(L\) has key \(k\).
That is, two units for each mismatching data node before the match plus four fixed units; an unsuccessful search scans all \(n\) data nodes.
public theorem listSearchSentinel_time {Key : Type u} {Satellite : Type v} {capacity : Nat}
[DecidableEq Key]
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (ids : List (Fin capacity))
(rep : CircularRepresents store s ids) :
(listSearchSentinel store s key ⟨ids, rep⟩).time =
2 * (ids.findIdx? (fun x => decide
((store.get x).map (fun node => node.payload.1) = some key))).getD ids.length + 4
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
public def listSearchSentinel {Key : Type u} {Satellite : Type v} {capacity : Nat} [DecidableEq Key]
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (valid : ∃ ids, CircularRepresents store s ids) :
TimeM Nat (Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity ×
Option (Fin capacity)) :=
let hs := onCircle_allocated store s s (valid_sentinel store s valid)
do
TimeM.tick 1
let updated := keyWrite store s key hs
have updatedValid : ∃ ids, CircularRepresents updated s ids := by
obtain ⟨ids, rep⟩ := valid
exact ⟨ids, keyWrite_circular store s key hs ids rep⟩
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Search.lean:290
Uses 3
TheoremSentinel search changes only the sentinel's key#✓fully proved
Let \(s\) be the sentinel of some list of data nodes that a store circularly represents, and let \(k\) be a key. In the store returned by sentinel search for \(k\), the cell \(s\) holds the original sentinel node with its key replaced by \(k\) and its satellite data and next and prev links unchanged, and every cell \(z \ne s\) holds exactly what it held in the input store.
public theorem listSearchSentinel_get {Key : Type u} {Satellite : Type v} {capacity : Nat}
[DecidableEq Key]
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (valid : ∃ ids, CircularRepresents store s ids)
(z : Fin capacity) :
(listSearchSentinel store s key valid).ret.1.get z =
if z = s then
(store.get z).map (fun node => { node with payload := (key, node.payload.2) })
else store.get z
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
public def listSearchSentinel {Key : Type u} {Satellite : Type v} {capacity : Nat} [DecidableEq Key]
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (valid : ∃ ids, CircularRepresents store s ids) :
TimeM Nat (Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity ×
Option (Fin capacity)) :=
let hs := onCircle_allocated store s s (valid_sentinel store s valid)
do
TimeM.tick 1
let updated := keyWrite store s key hs
have updatedValid : ∃ ids, CircularRepresents updated s ids := by
obtain ⟨ids, rep⟩ := valid
exact ⟨ids, keyWrite_circular store s key hs ids rep⟩
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Search.lean:290
Uses 3
TheoremSentinel search preserves the circular list#✓fully proved
If a store circularly represents the list \(L\) of data nodes with sentinel \(s\), then for every key \(k\) the store returned by sentinel search for \(k\) still circularly represents \(L\) with sentinel \(s\).
public theorem listSearchSentinel_circular {Key : Type u} {Satellite : Type v} {capacity : Nat}
[DecidableEq Key]
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (ids : List (Fin capacity))
(rep : CircularRepresents store s ids) :
CircularRepresents (listSearchSentinel store s key ⟨ids, rep⟩).ret.1 s ids
public structure Node (α : Type u) (ι : Type v) where
/-- The full payload, including any key and satellite data. -/
payload : α
/-- Next node identity, or NIL at the tail. -/
next : Option ι
/-- Previous node identity, or NIL at the head. -/
prev : Option ι
deriving DecidableEq, Repr
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Basic.lean:43
def CircularRepresents {α : Type u} {capacity : Nat}
(store : Vector (Option (Node α (Fin capacity))) capacity)
(s : Fin capacity) (ids : List (Fin capacity)) : Prop :=
ids.Nodup ∧ s ∉ ids ∧
(∃ sentinel, store.get s = some sentinel ∧
sentinel.next = some (ids.headD s) ∧ sentinel.prev = some (ids.getLastD s)) ∧
∀ (before : List (Fin capacity)) (a : Fin capacity) (after : List (Fin capacity)),
ids = before ++ a :: after → ∃ node, store.get a = some node ∧
node.prev = some (before.getLastD s) ∧ node.next = some (after.headD s)
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Basic.lean:29
public def listSearchSentinel {Key : Type u} {Satellite : Type v} {capacity : Nat} [DecidableEq Key]
(store : Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity)
(s : Fin capacity) (key : Key) (valid : ∃ ids, CircularRepresents store s ids) :
TimeM Nat (Vector (Option (Node (Key × Satellite) (Fin capacity))) capacity ×
Option (Fin capacity)) :=
let hs := onCircle_allocated store s s (valid_sentinel store s valid)
do
TimeM.tick 1
let updated := keyWrite store s key hs
have updatedValid : ∃ ids, CircularRepresents updated s ids := by
obtain ⟨ids, rep⟩ := valid
exact ⟨ids, keyWrite_circular store s key hs ids rep⟩
⋯
CSLibExt/Algorithms/Lean/DataStructures/DoublyLinkedList/Sentinel/Search.lean:290
Uses 3
Loading dependency explorer…
The dependency explorer needs JavaScript and its graph data.