Documentation

CslibFrontier.DataStructure.StateTransition

State-transition specifications and adaptive clients #

Specifications describe mathematical states, typed operations, relational transitions, and input admissibility. Clients use Quang Dao's upstream PFunctor.FreeM; continuations may depend on earlier answers. The state interpreter and its transcripts have no computational-model or performance claim. In particular, pure Lean computation in continuations is not charged as a machine instruction.

The relational semantics is demonic for callers and angelic for implementations:

Main definitions #

Main results #

References #

structure Cslib.DataStructure.StateTransitionSpec :
Type (max (max (u + 1) (v + 1)) (w + 1))

A relational abstract state machine with operation-dependent observations. step s o s' a holds when transitioning from s to s' and returning a : Result o is a permitted outcome of operation o. Only admissible operations (valid s o) are constrained by refinement certificates.

  • State : Type u

    Mathematical states, independent of a storage representation.

  • Op : Type v

    Operations available to clients.

  • Result : self.Op → Type w

    The observation type returned by each operation.

  • step (s : self.State) (o : self.Op) : self.State → self.Result o → Prop

    Relational state transition and observation specification.

  • valid : self.State → self.Op → Prop

    Preconditions under which an operation's implementation must satisfy its contract.

Instances For

    The set of permitted (nextState, result) outcomes of operation o from state s.

    Instances For
      @[simp]

      Membership in D.outcomes s o unfolds to D.step s o.

      A specification is total on admissible inputs if every valid (s, o) admits at least one permitted outcome (s', a).

      • step_total (s : D.State) (o : D.Op) : D.valid s o → ∃ (s' : D.State) (a : D.Result o), D.step s o s' a

        Every admissible operation call has at least one permitted post-state and result.

      Instances
        theorem Cslib.DataStructure.StateTransitionSpec.exists_step (D : StateTransitionSpec) [D.Total] {s : D.State} {o : D.Op} (hv : D.valid s o) :
        ∃ (s' : D.State) (a : D.Result o), D.step s o s' a

        Obtain a permitted outcome (s', a) for an admissible operation under [D.Total].

        A specification is deterministic if each (s, o) admits at most one outcome (s', a).

        • step_deterministic {s : D.State} {o : D.Op} {s₁ : D.State} {a₁ : D.Result o} {s₂ : D.State} {a₂ : D.Result o} : D.step s o s₁ a₁ → D.step s o s₂ a₂ → s₁ = s₂ ∧ a₁ = a₂

          Any two permitted outcomes for the same state and operation coincide.

        Instances
          @[reducible, inline]

          The polynomial operation signature, shared with upstream effectful programs.

          Instances For
            @[reducible, inline]

            An adaptive client: each continuation may depend on the observed answer. Inductive clients have no infinite execution branch; branching need not be finite.

            Instances For
              @[reducible, inline]

              Invoke a single operation o : D.Op in an adaptive D.Client.

              Instances For
                @[reducible, inline]

                An input-output transition label pairing an operation o : D.Op with its result a.

                Instances For

                  View a stateful specification as a labelled transition system (Cslib.LTS) labelled by input-output pairs ⟨o, a⟩ : Σ o : D.Op, D.Result o.

                  Instances For
                    @[simp]
                    theorem Cslib.DataStructure.StateTransitionSpec.toLTS_tr (D : StateTransitionSpec) (s s' : D.State) (lbl : D.Label) :
                    D.toLTS.Tr s lbl s' ↔ D.step s lbl.fst s' lbl.snd

                    A transition in D.toLTS is a step of D.

                    Transcripts, non-adaptive operation sequences, and cost accounting #

                    One recorded step of a specification: the issued operation op, its returned observation result, and the resulting post-state nextState.

                    • op : D.Op

                      The operation invoked at this step.

                    • result : D.Result self.op

                      The observation returned by op.

                    • nextState : D.State

                      The abstract post-state reached after op.

                    Instances For
                      @[reducible, inline]

                      A finite execution transcript is a list of recorded steps.

                      Instances For

                        Extract the underlying sequence of operations from a transcript.

                        Instances For

                          Extract the Cslib.LTS input-output labels from a transcript.

                          Instances For
                            @[simp]

                            D.ValidTranscript s tr s' holds when tr is a valid step-by-step execution starting at s and ending at s', where each step is admissible (D.valid) and permitted by D.step.

                            Instances For
                              @[simp]

                              The empty transcript is valid iff the initial and final states coincide.

                              @[simp]

                              A nonempty transcript is valid iff its head step is admissible and permitted and its tail is valid from st.nextState.

                              theorem Cslib.DataStructure.StateTransitionSpec.ValidTranscript.append {D : StateTransitionSpec} {s s₁ s₂ : D.State} {tr₁ tr₂ : D.Transcript} (h₁ : D.ValidTranscript s tr₁ s₁) (h₂ : D.ValidTranscript s₁ tr₂ s₂) :
                              D.ValidTranscript s (tr₁ ++ tr₂) s₂

                              Valid transcripts compose across concatenation.

                              Every valid transcript projects to a multi-step transition in D.toLTS.

                              Demonic admissibility of a non-adaptive operation sequence ops : List D.Op from s: the head operation is admissible at s, and the tail sequence remains admissible from every permitted post-state s' of the head operation.

                              Instances For
                                @[simp]
                                theorem Cslib.DataStructure.StateTransitionSpec.validOpSeq_cons (D : StateTransitionSpec) (s : D.State) (o : D.Op) (ops : List D.Op) :
                                D.ValidOpSeq s (o :: ops) ↔ D.valid s o ∧ ∀ (s' : D.State) (a : D.Result o), D.step s o s' a → D.ValidOpSeq s' ops
                                theorem Cslib.DataStructure.StateTransitionSpec.validOpSeq_of_forall_valid (D : StateTransitionSpec) (h : ∀ (s : D.State) (o : D.Op), D.valid s o) (s : D.State) (ops : List D.Op) :
                                D.ValidOpSeq s ops

                                On a specification where every operation is valid in every state, every operation sequence is demonically admissible.

                                theorem Cslib.DataStructure.StateTransitionSpec.validOpSeq_append {D : StateTransitionSpec} {s : D.State} {xs ys : List D.Op} (h : D.ValidOpSeq s (xs ++ ys)) :
                                D.ValidOpSeq s xs ∧ ∀ {tr : D.Transcript} {s' : D.State}, tr.ops = xs → D.ValidTranscript s tr s' → D.ValidOpSeq s' ys

                                Demonic operation-sequence admissibility splits across concatenation across all valid transcripts of the prefix.

                                Under [D.Total], every demonically admissible operation sequence admits at least one valid transcript.

                                State-dependent charges summed along a transcript.

                                Instances For

                                  Outcome-dependent charges summed along a transcript, where each step charge may also inspect the post-state st.nextState and result st.result.

                                  Instances For
                                    @[simp]
                                    theorem Cslib.DataStructure.StateTransitionSpec.totalCostTranscript_cons (D : StateTransitionSpec) (charge : D.State → D.Op → ℕ) (s : D.State) (st : D.Step) (tr : D.Transcript) :
                                    D.totalCostTranscript charge s (st :: tr) = charge s st.op + D.totalCostTranscript charge st.nextState tr
                                    @[simp]
                                    theorem Cslib.DataStructure.StateTransitionSpec.totalCostOutcome_nil (D : StateTransitionSpec) (charge : D.State → (o : D.Op) → D.State → D.Result o → ℕ) (s : D.State) :
                                    D.totalCostOutcome charge s [] = 0
                                    @[simp]
                                    theorem Cslib.DataStructure.StateTransitionSpec.totalCostOutcome_cons (D : StateTransitionSpec) (charge : D.State → (o : D.Op) → D.State → D.Result o → ℕ) (s : D.State) (st : D.Step) (tr : D.Transcript) :
                                    D.totalCostOutcome charge s (st :: tr) = charge s st.op st.nextState st.result + D.totalCostOutcome charge st.nextState tr
                                    theorem Cslib.DataStructure.StateTransitionSpec.totalCostTranscript_eq_totalCostOutcome (D : StateTransitionSpec) (charge : D.State → D.Op → ℕ) (s : D.State) (tr : D.Transcript) :
                                    D.totalCostTranscript charge s tr = D.totalCostOutcome (fun (s : D.State) (o : D.Op) (x : D.State) (x_1 : D.Result o) => charge s o) s tr

                                    totalCostTranscript is the special case of totalCostOutcome that ignores the post-state and result.

                                    theorem Cslib.DataStructure.StateTransitionSpec.totalCostTranscript_append {D : StateTransitionSpec} (charge : D.State → D.Op → ℕ) {s s₁ : D.State} {tr₁ : D.Transcript} (h₁ : D.ValidTranscript s tr₁ s₁) (tr₂ : D.Transcript) :
                                    D.totalCostTranscript charge s (tr₁ ++ tr₂) = D.totalCostTranscript charge s tr₁ + D.totalCostTranscript charge s₁ tr₂

                                    Along a valid transcript prefix D.ValidTranscript s tr₁ s₁, totalCostTranscript splits additively at the intermediate state s₁.

                                    theorem Cslib.DataStructure.StateTransitionSpec.totalCostTranscript_le_length_mul {D : StateTransitionSpec} (charge : D.State → D.Op → ℕ) (T : ℕ) (h : ∀ (s : D.State) (o : D.Op), D.valid s o → charge s o ≤ T) {s s' : D.State} {tr : D.Transcript} (hv : D.ValidTranscript s tr s') :

                                    Uniform per-operation bounds scale by the transcript length on any valid transcript.

                                    A constant charge K per operation totals tr.length * K.

                                    A unit charge per operation totals the transcript length.

                                    Adaptive clients (ClientRun and DemonicValid) #

                                    D.ClientRun s p v s' tr holds when running the adaptive client p : D.Client α from initial state s can produce return value v : α, final state s' : D.State, and transcript tr by making admissible and permitted transitions at each operation call.

                                    Instances For
                                      theorem Cslib.DataStructure.StateTransitionSpec.ClientRun.validTranscript {α : Type y} {D : StateTransitionSpec} {s s' : D.State} {p : D.Client α} {v : α} {tr : D.Transcript} (h : D.ClientRun s p v s' tr) :
                                      D.ValidTranscript s tr s'

                                      Every ClientRun produces a valid transcript.

                                      theorem Cslib.DataStructure.StateTransitionSpec.clientRun_pure_iff {α : Type y} {D : StateTransitionSpec} (s : D.State) (a v : α) (s' : D.State) (tr : D.Transcript) :
                                      D.ClientRun s (PFunctor.FreeM.pure a) v s' tr ↔ v = a ∧ s' = s ∧ tr = []
                                      theorem Cslib.DataStructure.StateTransitionSpec.clientRun_liftBind_iff {α : Type y} {D : StateTransitionSpec} (s : D.State) (o : D.Op) (k : D.Result o → D.Client α) (v : α) (s₂ : D.State) (tr : D.Transcript) :
                                      D.ClientRun s (PFunctor.FreeM.liftBind o k) v s₂ tr ↔ D.valid s o ∧ ∃ (a : D.Result o) (s₁ : D.State) (tr' : D.Transcript), D.step s o s₁ a ∧ D.ClientRun s₁ (k a) v s₂ tr' ∧ tr = { op := o, result := a, nextState := s₁ } :: tr'
                                      theorem Cslib.DataStructure.StateTransitionSpec.ClientRun.bind {α β : Type y} {D : StateTransitionSpec} {s s₁ s₂ : D.State} {p : D.Client α} {k : α → D.Client β} {a : α} {b : β} {tr₁ tr₂ : D.Transcript} (h₁ : D.ClientRun s p a s₁ tr₁) (h₂ : D.ClientRun s₁ (k a) b s₂ tr₂) :
                                      D.ClientRun s (PFunctor.FreeM.bind p k) b s₂ (tr₁ ++ tr₂)

                                      Sequential client runs compose by concatenating their transcripts.

                                      theorem Cslib.DataStructure.StateTransitionSpec.clientRun_bind_iff {α β : Type y} {D : StateTransitionSpec} {s s₂ : D.State} {p : D.Client α} {k : α → D.Client β} {b : β} {tr : D.Transcript} :
                                      D.ClientRun s (PFunctor.FreeM.bind p k) b s₂ tr ↔ ∃ (a : α) (s₁ : D.State) (tr₁ : D.Transcript) (tr₂ : D.Transcript), D.ClientRun s p a s₁ tr₁ ∧ D.ClientRun s₁ (k a) b s₂ tr₂ ∧ tr = tr₁ ++ tr₂

                                      A client run of p.bind k splits into a run of p and a run of k from the intermediate result and state.

                                      Demonic admissibility of an adaptive client p from state s:

                                      • .pure a is always admissible;
                                      • .liftBind o k requires D.valid s o now, and for every permitted outcome (s', a) of o from s, the continuation k a must be demonically admissible from s'.
                                      Instances For
                                        theorem Cslib.DataStructure.StateTransitionSpec.demonicValid_liftBind_iff {α : Type y} {D : StateTransitionSpec} (s : D.State) (o : D.Op) (k : D.Result o → D.Client α) :
                                        D.DemonicValid s (PFunctor.FreeM.liftBind o k) ↔ D.valid s o ∧ ∀ (s' : D.State) (a : D.Result o), D.step s o s' a → D.DemonicValid s' (k a)
                                        theorem Cslib.DataStructure.StateTransitionSpec.demonicValid_of_forall_valid (D : StateTransitionSpec) {α : Type y} (h : ∀ (s : D.State) (o : D.Op), D.valid s o) (s : D.State) (p : D.Client α) :

                                        On a specification where every operation is valid in every state, every client is demonically admissible.

                                        theorem Cslib.DataStructure.StateTransitionSpec.DemonicValid.exists_clientRun {α : Type y} {D : StateTransitionSpec} [D.Total] {s : D.State} {p : D.Client α} (hv : D.DemonicValid s p) :
                                        ∃ (v : α) (s' : D.State) (tr : D.Transcript), D.ClientRun s p v s' tr

                                        Under [D.Total], every demonically admissible client admits at least one valid ClientRun.

                                        theorem Cslib.DataStructure.StateTransitionSpec.DemonicValid.bind {α β : Type y} {D : StateTransitionSpec} {s : D.State} {p : D.Client α} {k : α → D.Client β} (hp : D.DemonicValid s p) (hk : ∀ (a : α) (s' : D.State) (tr : D.Transcript), D.ClientRun s p a s' tr → D.DemonicValid s' (k a)) :

                                        Demonic admissibility composes across bind when the continuation is demonically admissible after every valid run of the first client.

                                        theorem Cslib.DataStructure.StateTransitionSpec.DemonicValid.of_bind {α β : Type y} {D : StateTransitionSpec} {s : D.State} {p : D.Client α} {k : α → D.Client β} (h : D.DemonicValid s (PFunctor.FreeM.bind p k)) :
                                        D.DemonicValid s p ∧ ∀ (a : α) (s' : D.State) (tr : D.Transcript), D.ClientRun s p a s' tr → D.DemonicValid s' (k a)

                                        Demonic admissibility of p.bind k splits: p is demonically admissible, and so is the continuation after every valid run of p. This is the converse of DemonicValid.bind.

                                        Specifications given by a transition function (StateTransitionSpec.ofFn, #

                                        StateTransitionSpec.HasStepFn) and functional helpers

                                        @[reducible, inline]
                                        abbrev Cslib.DataStructure.StateTransitionSpec.ofFn {State : Type u} {Op : Type v} {Result : Op → Type w} (stepFn : State → (o : Op) → State × Result o) (valid : State → Op → Prop := fun (x : State) (x_1 : Op) => True) :

                                        Construct a deterministic StateTransitionSpec from a pure transition function stepFn : (s : State) → (o : Op) → State × Result o and an optional admissibility predicate valid.

                                        Instances For
                                          @[simp]
                                          theorem Cslib.DataStructure.StateTransitionSpec.ofFn_state {State : Type u} {Op : Type v} {Result : Op → Type w} (stepFn : State → (o : Op) → State × Result o) (valid : State → Op → Prop := fun (x : State) (x_1 : Op) => True) :
                                          (ofFn stepFn valid).State = State
                                          @[simp]
                                          theorem Cslib.DataStructure.StateTransitionSpec.ofFn_op {State : Type u} {Op : Type v} {Result : Op → Type w} (stepFn : State → (o : Op) → State × Result o) (valid : State → Op → Prop := fun (x : State) (x_1 : Op) => True) :
                                          (ofFn stepFn valid).Op = Op
                                          @[simp]
                                          theorem Cslib.DataStructure.StateTransitionSpec.ofFn_result {State : Type u} {Op : Type v} {Result : Op → Type w} (stepFn : State → (o : Op) → State × Result o) (valid : State → Op → Prop := fun (x : State) (x_1 : Op) => True) :
                                          (ofFn stepFn valid).Result = Result
                                          @[simp]
                                          theorem Cslib.DataStructure.StateTransitionSpec.ofFn_step_iff {State : Type u} {Op : Type v} {Result : Op → Type w} (stepFn : State → (o : Op) → State × Result o) (valid : State → Op → Prop := fun (x : State) (x_1 : Op) => True) (s : State) (o : Op) (s' : State) (a : Result o) :
                                          (ofFn stepFn valid).step s o s' a ↔ (s', a) = stepFn s o
                                          @[simp]
                                          theorem Cslib.DataStructure.StateTransitionSpec.ofFn_valid {State : Type u} {Op : Type v} {Result : Op → Type w} (stepFn : State → (o : Op) → State × Result o) (valid : State → Op → Prop := fun (x : State) (x_1 : Op) => True) (s : State) (o : Op) :
                                          (ofFn stepFn valid).valid s o ↔ valid s o

                                          Typeclass witnessing that a specification D : StateTransitionSpec is governed by a pure transition function f : (s : D.State) → (o : D.Op) → D.State × D.Result o: the only outcome of o from s is f s o, whether or not D.valid s o holds.

                                          • step_iff (s : D.State) (o : D.Op) (s' : D.State) (a : D.Result o) : D.step s o s' a ↔ (s', a) = f s o

                                            A transition D.step s o s' a holds if and only if (s', a) = f s o.

                                          Instances
                                            instance Cslib.DataStructure.StateTransitionSpec.instHasStepFnOfFn {State : Type u} {Op : Type v} {Result : Op → Type w} (f : State → (o : Op) → State × Result o) (valid : State → Op → Prop) :
                                            (ofFn f valid).HasStepFn f
                                            @[reducible, inline]
                                            abbrev Cslib.DataStructure.StateTransitionSpec.stepFn (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (o : D.Op) :

                                            The functional transition (s : D.State) → (o : D.Op) → D.State × D.Result o of a specification D with [D.HasStepFn f].

                                            Instances For
                                              theorem Cslib.DataStructure.StateTransitionSpec.step_iff_stepFn (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (o : D.Op) (s' : D.State) (a : D.Result o) :
                                              D.step s o s' a ↔ (s', a) = D.stepFn s o
                                              theorem Cslib.DataStructure.StateTransitionSpec.step_stepFn (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (o : D.Op) :
                                              D.step s o (D.stepFn s o).1 (D.stepFn s o).2

                                              Forget typed observations to view state transitions as a functional labelled transition system.

                                              Instances For
                                                @[simp]
                                                theorem Cslib.DataStructure.StateTransitionSpec.toFLTS_tr (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (o : D.Op) :
                                                D.toFLTS.tr s o = (D.stepFn s o).1

                                                The transition function of D.toFLTS is the state component of D.stepFn.

                                                theorem Cslib.DataStructure.StateTransitionSpec.toLTS_tr_iff_toFLTS (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s s' : D.State) (o : D.Op) (a : D.Result o) :
                                                D.toLTS.Tr s ⟨o, a⟩ s' ↔ s' = D.toFLTS.tr s o ∧ a = (D.stepFn s o).2

                                                Transitions in D.toLTS agree with D.toFLTS on states and (D.stepFn s o).2 on observations.

                                                The final state and complete typed observation trace of a history.

                                                Instances For

                                                  Each operation is admissible in the state reached by its predecessors.

                                                  Instances For
                                                    def Cslib.DataStructure.StateTransitionSpec.totalCost (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (charge : D.State → D.Op → ℕ) (s : D.State) :
                                                    List D.Op → ℕ

                                                    State-dependent charges summed along the abstract execution.

                                                    Instances For
                                                      @[simp]
                                                      theorem Cslib.DataStructure.StateTransitionSpec.run_nil (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) :
                                                      D.run s [] = (s, [])

                                                      Running the empty history keeps the state and observes nothing.

                                                      @[simp]
                                                      theorem Cslib.DataStructure.StateTransitionSpec.run_cons (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (o : D.Op) (os : List D.Op) :
                                                      D.run s (o :: os) = ((D.run (D.stepFn s o).1 os).1, ⟨o, (D.stepFn s o).2⟩ :: (D.run (D.stepFn s o).1 os).2)

                                                      Running a nonempty history steps once and prepends the typed observation.

                                                      theorem Cslib.DataStructure.StateTransitionSpec.fst_run_eq_toFLTS_mtr (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (os : List D.Op) :
                                                      (D.run s os).1 = D.toFLTS.mtr s os

                                                      The final state of D.run agrees with the multi-step transition of D.toFLTS.

                                                      theorem Cslib.DataStructure.StateTransitionSpec.toLTS_mtr_run (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (os : List D.Op) :
                                                      D.toLTS.MTr s (D.run s os).2 (D.toFLTS.mtr s os)

                                                      Multi-step execution D.run produces a valid multi-step trace in D.toLTS ending at D.toFLTS.mtr s os.

                                                      @[simp]

                                                      The empty history is admissible in every state.

                                                      @[simp]
                                                      theorem Cslib.DataStructure.StateTransitionSpec.validHistory_cons (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (o : D.Op) (os : List D.Op) :
                                                      D.ValidHistory s (o :: os) ↔ D.valid s o ∧ D.ValidHistory (D.stepFn s o).1 os

                                                      A nonempty history is admissible when its first operation is valid and the tail is admissible from the next state.

                                                      theorem Cslib.DataStructure.StateTransitionSpec.validHistory_of_forall_valid (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (h : ∀ (s : D.State) (o : D.Op), D.valid s o) (s : D.State) (os : List D.Op) :

                                                      On a specification where every operation is valid in every state, every history is admissible.

                                                      @[simp]
                                                      theorem Cslib.DataStructure.StateTransitionSpec.totalCost_nil (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (charge : D.State → D.Op → ℕ) (s : D.State) :
                                                      D.totalCost charge s [] = 0

                                                      The empty history costs nothing.

                                                      @[simp]
                                                      theorem Cslib.DataStructure.StateTransitionSpec.totalCost_cons (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (charge : D.State → D.Op → ℕ) (s : D.State) (o : D.Op) (os : List D.Op) :
                                                      D.totalCost charge s (o :: os) = charge s o + D.totalCost charge (D.stepFn s o).1 os

                                                      The cost of a nonempty history adds the first operation's charge to the tail cost from the next state.

                                                      theorem Cslib.DataStructure.StateTransitionSpec.run_append (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (xs ys : List D.Op) :
                                                      D.run s (xs ++ ys) = ((D.run (D.run s xs).1 ys).1, (D.run s xs).2 ++ (D.run (D.run s xs).1 ys).2)

                                                      Concatenation preserves both the state and the observation order.

                                                      theorem Cslib.DataStructure.StateTransitionSpec.totalCost_append (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (charge : D.State → D.Op → ℕ) (s : D.State) (xs ys : List D.Op) :
                                                      D.totalCost charge s (xs ++ ys) = D.totalCost charge s xs + D.totalCost charge (D.run s xs).1 ys

                                                      Charges compose at the intermediate abstract state. The element type D.Op is not indexed, so simp also uses this for a concrete specification such as Memory.spec w, whose Op reduces.

                                                      theorem Cslib.DataStructure.StateTransitionSpec.totalCost_le_length_mul (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (charge : D.State → D.Op → ℕ) (T : ℕ) (h : ∀ (s : D.State) (o : D.Op), D.valid s o → charge s o ≤ T) {s : D.State} {os : List D.Op} (hv : D.ValidHistory s os) :
                                                      D.totalCost charge s os ≤ os.length * T

                                                      Uniform operation bounds imply a bound proportional to history length.

                                                      theorem Cslib.DataStructure.StateTransitionSpec.totalCost_const (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (K : ℕ) (s : D.State) (os : List D.Op) :
                                                      D.totalCost (fun (x : D.State) (x_1 : D.Op) => K) s os = os.length * K

                                                      A constant charge K per operation totals K times the length of the history.

                                                      theorem Cslib.DataStructure.StateTransitionSpec.totalCost_one (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (os : List D.Op) :
                                                      D.totalCost (fun (x : D.State) (x_1 : D.Op) => 1) s os = os.length

                                                      A unit charge per operation totals the length of the history.

                                                      def Cslib.DataStructure.StateTransitionSpec.runClient (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) :
                                                      D.Client α → α × D.State × List D.Op

                                                      Interpret a client mathematically, retaining its chosen operation history.

                                                      Instances For
                                                        @[reducible, inline]
                                                        abbrev Cslib.DataStructure.StateTransitionSpec.clientResult (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (p : D.Client α) :
                                                        α

                                                        Value returned by a deterministic client run D.runClient s p.

                                                        Instances For
                                                          @[reducible, inline]
                                                          abbrev Cslib.DataStructure.StateTransitionSpec.clientState (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (p : D.Client α) :

                                                          Final abstract state reached by a deterministic client run D.runClient s p.

                                                          Instances For
                                                            @[reducible, inline]
                                                            abbrev Cslib.DataStructure.StateTransitionSpec.clientHistory (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (p : D.Client α) :

                                                            Operation history selected by a deterministic client run D.runClient s p.

                                                            Instances For
                                                              @[simp]
                                                              theorem Cslib.DataStructure.StateTransitionSpec.runClient_pure (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (a : α) :

                                                              Interpreting a pure client returns its value, preserves the state, and records no operations.

                                                              @[simp]
                                                              theorem Cslib.DataStructure.StateTransitionSpec.runClient_pure' (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (a : α) :
                                                              D.runClient s (pure a) = (a, s, [])

                                                              Interpreting a pure client returns its value, preserves the state, and records no operations.

                                                              @[simp]
                                                              theorem Cslib.DataStructure.StateTransitionSpec.runClient_lift (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (o : D.Op) :
                                                              D.runClient s (PFunctor.FreeM.lift o) = ((D.stepFn s o).2, (D.stepFn s o).1, [o])

                                                              Interpreting a single lifted operation steps the specification once.

                                                              @[simp]
                                                              theorem Cslib.DataStructure.StateTransitionSpec.runClient_liftBind (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (o : D.Op) (k : D.Result o → D.Client α) :
                                                              D.runClient s (PFunctor.FreeM.liftBind o k) = match D.runClient (D.stepFn s o).1 (k (D.stepFn s o).2) with | (v, s', os) => (v, s', o :: os)

                                                              Interpreting an operation call steps the specification and continues from the resulting state and answer.

                                                              @[simp]
                                                              theorem Cslib.DataStructure.StateTransitionSpec.runClient_lift_bind (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (o : D.Op) (k : D.Result o → D.Client α) :
                                                              D.runClient s ((PFunctor.FreeM.lift o).bind k) = match D.runClient (D.stepFn s o).1 (k (D.stepFn s o).2) with | (v, s', os) => (v, s', o :: os)

                                                              Interpreting a lifted operation bound to a continuation steps the specification and continues from the resulting state and answer.

                                                              @[simp]
                                                              theorem Cslib.DataStructure.StateTransitionSpec.runClient_lift_bind' (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type w} (s : D.State) (o : D.Op) (k : D.Result o → D.Client α) :
                                                              D.runClient s (PFunctor.FreeM.lift o >>= k) = match D.runClient (D.stepFn s o).1 (k (D.stepFn s o).2) with | (v, s', os) => (v, s', o :: os)

                                                              Interpreting a lifted operation monadically bound to a continuation steps the specification and continues from the resulting state and answer.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.run_runClient (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (p : D.Client α) (s : D.State) :
                                                              (D.run s (D.clientHistory s p)).1 = D.clientState s p

                                                              The actual history chosen by an adaptive client has its computed final state.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.validHistory_append {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (xs ys : List D.Op) :
                                                              D.ValidHistory s (xs ++ ys) ↔ D.ValidHistory s xs ∧ D.ValidHistory (D.run s xs).1 ys

                                                              Admissibility splits at the mathematical state reached by the first history. The element type D.Op is not indexed, as in totalCost_append.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.runClient_bind {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α β : Type y} (p : D.Client α) (k : α → D.Client β) (s : D.State) :
                                                              D.runClient s (PFunctor.FreeM.bind p k) = match D.runClient s p with | (v₁, s₁, os₁) => match D.runClient s₁ (k v₁) with | (v₂, s₂, os₂) => (v₂, s₂, os₁ ++ os₂)

                                                              Sequential interpretation concatenates the histories actually selected by the client.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.clientResult_bind {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α β : Type y} (p : D.Client α) (k : α → D.Client β) (s : D.State) :
                                                              D.clientResult s (p >>= k) = D.clientResult (D.clientState s p) (k (D.clientResult s p))

                                                              A sequential client answers as its continuation, run from where the first client stopped.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.clientState_bind {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α β : Type y} (p : D.Client α) (k : α → D.Client β) (s : D.State) :
                                                              D.clientState s (p >>= k) = D.clientState (D.clientState s p) (k (D.clientResult s p))

                                                              A sequential client stops where its continuation, run from where the first client stopped, stops.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.clientHistory_bind {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α β : Type y} (p : D.Client α) (k : α → D.Client β) (s : D.State) :

                                                              The history of a sequential client is the first client's history, then its continuation's.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.clientResult_pure {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (a : α) :
                                                              D.clientResult s (pure a) = a

                                                              A client that only returns a value answers with it.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.clientState_pure {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (a : α) :
                                                              D.clientState s (pure a) = s

                                                              A client that only returns a value leaves the state unchanged.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.clientHistory_pure {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (a : α) :

                                                              A client that only returns a value issues no operation.

                                                              clientState and clientHistory unfold to a match on D.runClient s p. Under its metavariables simp cannot reduce that match, so it would index clientState_bind and clientHistory_bind under a key that no goal has; in a goal it sees (D.runClient s p).2.1. The following restatements have that form, and put the simp set client_simps in the normal form of clientState and clientHistory.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.fst_snd_runClient_bind {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α β : Type y} (p : D.Client α) (k : α → D.Client β) (s : D.State) :
                                                              (D.runClient s (p >>= k)).2.1 = D.clientState (D.clientState s p) (k (D.clientResult s p))

                                                              clientState_bind, stated in the form that simp indexes.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.snd_snd_runClient_bind {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α β : Type y} (p : D.Client α) (k : α → D.Client β) (s : D.State) :
                                                              (D.runClient s (p >>= k)).2.2 = D.clientHistory s p ++ D.clientHistory (D.clientState s p) (k (D.clientResult s p))

                                                              clientHistory_bind, stated in the form that simp indexes.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.fst_snd_runClient_pure {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (a : α) :
                                                              (D.runClient s (pure a)).2.1 = s

                                                              clientState_pure, stated in the form that simp indexes.

                                                              theorem Cslib.DataStructure.StateTransitionSpec.snd_snd_runClient_pure {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (a : α) :
                                                              (D.runClient s (pure a)).2.2 = []

                                                              clientHistory_pure, stated in the form that simp indexes.

                                                              @[simp]

                                                              Under [D.HasStepFn f], demonic operation-sequence admissibility ValidOpSeq coincides with D.ValidHistory.

                                                              The unique canonical transcript determined by a specification D with [D.HasStepFn f] on an operation list os from state s.

                                                              Instances For
                                                                @[simp]
                                                                theorem Cslib.DataStructure.StateTransitionSpec.canonicalTranscript_cons {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (o : D.Op) (os : List D.Op) :
                                                                D.canonicalTranscript s (o :: os) = { op := o, result := (D.stepFn s o).2, nextState := (D.stepFn s o).1 } :: D.canonicalTranscript (D.stepFn s o).1 os
                                                                @[simp]

                                                                The underlying operation list of D.canonicalTranscript s os is os.

                                                                @[simp]

                                                                The labels of D.canonicalTranscript s os coincide with (D.run s os).2.

                                                                @[simp]
                                                                theorem Cslib.DataStructure.StateTransitionSpec.totalCostTranscript_canonicalTranscript {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (charge : D.State → D.Op → ℕ) (s : D.State) (os : List D.Op) :
                                                                D.totalCostTranscript charge s (D.canonicalTranscript s os) = D.totalCost charge s os

                                                                totalCostTranscript along D.canonicalTranscript s os equals D.totalCost charge s os.

                                                                Every admissible operation history executes canonically to (D.run s os).1.

                                                                theorem Cslib.DataStructure.StateTransitionSpec.validTranscript_iff_run {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (tr : D.Transcript) (s' : D.State) :
                                                                D.ValidTranscript s tr s' ↔ D.ValidHistory s tr.ops ∧ s' = (D.run s tr.ops).1 ∧ tr = D.canonicalTranscript s tr.ops

                                                                Under [D.HasStepFn f], ValidTranscript s tr s' collapses uniquely to D.ValidHistory s tr.ops, s' = (D.run s tr.ops).1, and tr = D.canonicalTranscript s tr.ops.

                                                                Under [D.HasStepFn f], demonic client admissibility DemonicValid s p is equivalent to D.ValidHistory s (D.clientHistory s p).

                                                                theorem Cslib.DataStructure.StateTransitionSpec.clientRun_canonical {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (p : D.Client α) (hv : D.ValidHistory s (D.clientHistory s p)) :

                                                                Every client with an admissible deterministic history executes canonically.

                                                                theorem Cslib.DataStructure.StateTransitionSpec.clientRun_iff_runClient {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (p : D.Client α) (v : α) (s' : D.State) (tr : D.Transcript) :
                                                                D.ClientRun s p v s' tr ↔ D.ValidHistory s (D.clientHistory s p) ∧ v = D.clientResult s p ∧ s' = D.clientState s p ∧ tr = D.canonicalTranscript s (D.clientHistory s p)

                                                                Under [D.HasStepFn f], ClientRun collapses uniquely to D.clientResult, D.clientState, D.clientHistory, and D.ValidHistory.

                                                                def Cslib.DataStructure.eval {α : Type y} (D : StateTransitionSpec) {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (p : D.Client α) (s : D.State) :
                                                                α × D.State

                                                                Observe results and state without displaying the operation history.

                                                                Instances For
                                                                  @[simp]
                                                                  theorem Cslib.DataStructure.eval_pure {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (a : α) :

                                                                  Evaluating a pure client returns its value and leaves the state unchanged.

                                                                  @[simp]
                                                                  theorem Cslib.DataStructure.eval_pure' {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (a : α) :
                                                                  eval D (pure a) s = (a, s)

                                                                  Evaluating a pure client returns its value and leaves the state unchanged.

                                                                  @[simp]
                                                                  theorem Cslib.DataStructure.eval_lift {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] (s : D.State) (o : D.Op) :
                                                                  eval D (PFunctor.FreeM.lift o) s = ((D.stepFn s o).2, (D.stepFn s o).1)

                                                                  Evaluating a single lifted operation steps the state and returns its answer.

                                                                  @[simp]
                                                                  theorem Cslib.DataStructure.eval_liftBind {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (o : D.Op) (k : D.Result o → D.Client α) :
                                                                  eval D (PFunctor.FreeM.liftBind o k) s = eval D (k (D.stepFn s o).2) (D.stepFn s o).1

                                                                  Evaluating an operation call steps the state and continues with the observed answer.

                                                                  @[simp]
                                                                  theorem Cslib.DataStructure.eval_lift_bind {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type y} (s : D.State) (o : D.Op) (k : D.Result o → D.Client α) :
                                                                  eval D ((PFunctor.FreeM.lift o).bind k) s = eval D (k (D.stepFn s o).2) (D.stepFn s o).1

                                                                  Evaluating a lifted operation bound to a continuation steps the state and continues with the observed answer.

                                                                  @[simp]
                                                                  theorem Cslib.DataStructure.eval_lift_bind' {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type w} (s : D.State) (o : D.Op) (k : D.Result o → D.Client α) :
                                                                  eval D (PFunctor.FreeM.lift o >>= k) s = eval D (k (D.stepFn s o).2) (D.stepFn s o).1

                                                                  Evaluating a lifted operation monadically bound to a continuation steps the state and continues with the observed answer.

                                                                  theorem Cslib.DataStructure.eval_bind {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α β : Type y} (p : D.Client α) (k : α → D.Client β) (s : D.State) :
                                                                  eval D (PFunctor.FreeM.bind p k) s = eval D (k (eval D p s).1) (eval D p s).2

                                                                  Evaluating p.bind k evaluates p, then the continuation k at its result from the state p reaches.

                                                                  theorem Cslib.DataStructure.eval_eq_liftM {D : StateTransitionSpec} {f : D.State → (o : D.Op) → D.State × D.Result o} [D.HasStepFn f] {α : Type u} (p : D.Client α) (s : D.State) :
                                                                  StateT.run (PFunctor.FreeM.liftM (fun (o : D.signature.A) (s : D.State) => ((D.stepFn s o).2, (D.stepFn s o).1)) p) s = eval D p s

                                                                  eval agrees with interpreting the free monad into StateM D.State via PFunctor.FreeM.liftM when states, operation answers, and client results share a universe.

                                                                  @[reducible, inline]

                                                                  Two independent abstract components. A backend must separately justify their independence. The answer types are Sum.elim D.Result E.Result, so the signature of a product depends only on the signatures of its components: clients of product D E are clients of product D' E' when D, D' and E, E' differ only in their states' admissibility.

                                                                  Instances For
                                                                    @[simp]
                                                                    theorem Cslib.DataStructure.product_step_inl (D E : StateTransitionSpec) (s s' : D.State × E.State) (o : D.Op) (a : D.Result o) :
                                                                    (product D E).step s (Sum.inl o) s' a ↔ D.step s.1 o s'.1 a ∧ s'.2 = s.2

                                                                    Stepping the left component of a product specification steps the left state and preserves the right state.

                                                                    @[simp]
                                                                    theorem Cslib.DataStructure.product_step_inr (D E : StateTransitionSpec) (s s' : D.State × E.State) (o : E.Op) (a : E.Result o) :
                                                                    (product D E).step s (Sum.inr o) s' a ↔ s'.1 = s.1 ∧ E.step s.2 o s'.2 a

                                                                    Stepping the right component of a product specification preserves the left state and steps the right state.

                                                                    @[simp]
                                                                    theorem Cslib.DataStructure.product_valid_inl (D E : StateTransitionSpec) (s : D.State × E.State) (o : D.Op) :
                                                                    (product D E).valid s (Sum.inl o) ↔ D.valid s.1 o

                                                                    Admissibility of a left operation in a product specification depends only on the left state.

                                                                    @[simp]
                                                                    theorem Cslib.DataStructure.product_valid_inr (D E : StateTransitionSpec) (s : D.State × E.State) (o : E.Op) :
                                                                    (product D E).valid s (Sum.inr o) ↔ E.valid s.2 o

                                                                    Admissibility of a right operation in a product specification depends only on the right state.

                                                                    Instances on products #

                                                                    product is an abbrev, so an instance stated on product D E would be indexed by the unfolded structure, whose state type is the projection D.State × E.State. A nested product such as product (product D E) F has state type (D.State × E.State) × F.State after unfolding, which that index does not match. The product instances therefore index their specification with no_index, and products of any depth find them by unification.

                                                                    Product of two total specifications is total.

                                                                    Product of two deterministic specifications is deterministic.

                                                                    @[reducible, inline]
                                                                    abbrev Cslib.DataStructure.productStepFn (D E : StateTransitionSpec) (fD : D.State → (o : D.Op) → D.State × D.Result o) (fE : E.State → (o : E.Op) → E.State × E.Result o) (s : (product D E).State) (o : (product D E).Op) :
                                                                    (product D E).State × (product D E).Result o

                                                                    The canonical transition function of a product of two specifications with transition functions fD and fE.

                                                                    Instances For
                                                                      instance Cslib.DataStructure.instHasStepFnProduct (D E : StateTransitionSpec) {fD : D.State → (o : D.Op) → D.State × D.Result o} {fE : E.State → (o : E.Op) → E.State × E.Result o} [D.HasStepFn fD] [E.HasStepFn fE] :
                                                                      (product D E).HasStepFn (productStepFn D E fD fE)

                                                                      A product of specifications with HasStepFn has HasStepFn with the componentwise transition function, at any nesting depth.

                                                                      instance Cslib.DataStructure.instHasStepFnProductOfFn {State₁ : Type u} {Op₁ : Type v} {Result₁ : Op₁ → Type w} {State₂ : Type u} {Op₂ : Type v} {Result₂ : Op₂ → Type w} (fD : State₁ → (o : Op₁) → State₁ × Result₁ o) (validD : State₁ → Op₁ → Prop) (fE : State₂ → (o : Op₂) → State₂ × Result₂ o) (validE : State₂ → Op₂ → Prop) :

                                                                      The product of two ofFn specifications, with their transition functions named. It is tried before instHasStepFnProduct, so such products keep this form of the transition function.

                                                                      @[simp]
                                                                      theorem Cslib.DataStructure.product_stepFn_inl (D E : StateTransitionSpec) {fD : D.State → (o : D.Op) → D.State × D.Result o} {fE : E.State → (o : E.Op) → E.State × E.Result o} [D.HasStepFn fD] [E.HasStepFn fE] (s : D.State × E.State) (o : D.Op) :
                                                                      (product D E).stepFn s (Sum.inl o) = (((D.stepFn s.1 o).1, s.2), (D.stepFn s.1 o).2)
                                                                      @[simp]
                                                                      theorem Cslib.DataStructure.product_stepFn_inr (D E : StateTransitionSpec) {fD : D.State → (o : D.Op) → D.State × D.Result o} {fE : E.State → (o : E.Op) → E.State × E.Result o} [D.HasStepFn fD] [E.HasStepFn fE] (s : D.State × E.State) (o : E.Op) :
                                                                      (product D E).stepFn s (Sum.inr o) = ((s.1, (E.stepFn s.2 o).1), (E.stepFn s.2 o).2)