Documentation

Complexitylib.Classes.Containments.Internal.PPAssemble

Assembling the counting loop's body #

⚠️ Unreviewed by Bolton

The loop invariant names every tape: the counter and the two tallies carry their numbers, the horizon and the wipe's register carry theirs, and everything else rests blank. This file records what that bank looks like at each named index and proves the body's stages against it.

Main results #

def Complexity.NTM.bodyBank (k N H v a r : ) :

The bank the loop invariant pins at count v with tallies a and r.

Equations
Instances For
    theorem Complexity.NTM.bodyBank_cIdx (k N H v a r : ) :
    bodyBank k N H v a r (cIdx k) = natTape v
    theorem Complexity.NTM.bodyBank_aIdx (k N H v a r : ) :
    bodyBank k N H v a r (aIdx k) = natTape a
    theorem Complexity.NTM.bodyBank_rIdx (k N H v a r : ) :
    bodyBank k N H v a r (rIdx k) = natTape r
    theorem Complexity.NTM.bodyBank_rest (k N H v a r : ) (j : Fin (bodyTapes k)) (hc : j cIdx k) (ha : j aIdx k) (hr : j rIdx k) :
    bodyBank k N H v a r j = bodyRest k N H j
    theorem Complexity.NTM.bodyBank_parked (k N H v a r : ) (j : Fin (bodyTapes k)) :
    TM.Parked (bodyBank k N H v a r j)
    theorem Complexity.NTM.bodyBank_cells_zero (k N H v a r : ) (j : Fin (bodyTapes k)) :
    (bodyBank k N H v a r j).cells 0 = Γ.start
    theorem Complexity.NTM.bodyBank_startInvariant (k N H v a r : ) (j : Fin (bodyTapes k)) :
    (bodyBank k N H v a r j).StartInvariant
    theorem Complexity.NTM.bodyBank_wipeTarget (k N H v a r : ) (j : Fin (bodyTapes k)) (hj : j < k j = vIdx k) :
    bodyBank k N H v a r j = TM.blankTape

    Every tape the body wipes rests blank in the loop's bank.

    theorem Complexity.NTM.bodyBank_zIdx_read (k N H v a r : ) :
    (bodyBank k N H v a r (zIdx k)).read = Γ.blank

    The permanently blank register reads a blank, which is what makes it usable as the source of a blanking write.

    theorem Complexity.NTM.blankSlot_hoareTime (k N H v a r : ) (I : Tape) (hI : TM.Parked I) :
    (TM.writeOutputBitTM (zIdx k)).HoareTime (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = I work = bodyBank k N H v a r ∃ (s : Γw), s Γw.one out = outSlot s) (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = I work = bodyBank k N H v a r out = outSlot Γw.blank) 1

    The body's first stage: blank the verdict slot. The loop returns to its start state with the previous check's verdict still in the slot; the wipe that ends the body needs it blank, and so does the simulation, whose real output tape must start blank.

    theorem Complexity.NTM.bodyBank_eq_of (k N H v a r : ) (W : Fin (bodyTapes k)Tape) (hc : W (cIdx k) = natTape v) (ha : W (aIdx k) = natTape a) (hr : W (rIdx k) = natTape r) (hn : W (nIdx k) = natTape N) (hreg : W (regIdx k) = TM.regTape H) (hrest : jwipeTargets k, j cIdx kj aIdx kj rIdx kj nIdx kj regIdx kW j = TM.blankTape) :
    (fun (j : Fin (bodyTapes k)) => if j wipeTargets k then TM.blankTape else W j) = bodyBank k N H v a r

    The bridge back to the loop invariant. After the body's wipe, a bank that carries the right numbers on the named registers and rests blank elsewhere is the bank the loop invariant pins at the next index.

    theorem Complexity.NTM.simCfg_entry {k : } (tm : NTM k) (x : List Bool) (N H v a r : ) (extras : Fin (0 + (k + 1) + 6)Tape) (hex : ∀ (j : Fin (0 + (k + 1) + 6)), extras j = bodyBank k N H v a r j.castSucc) :
    tm.simCfg 6 extras (tm.simEntry x v) = { state := (tm.simTM 6).qstart, input := (Tape.init (List.map Γ.ofBool x)).move Dir3.right, work := bodyBank k N H v a r, output := TM.blankTape }

    The stage's entry configuration is the loop's own bank. Placing the simulation beside the registers and redirecting its output puts exactly the tapes the loop invariant names where the stage expects them: the machine's own tapes blank, the counter at v, the registers untouched, and the verdict tape blank.

    theorem Complexity.NTM.bodyBank_head (k N H v a r : ) (j : Fin (bodyTapes k)) :
    (bodyBank k N H v a r j).head = 1

    Every tape of the loop's bank is parked at cell one.

    The real input tape, parked at cell one, with x on it.

    Equations
    Instances For
      def Complexity.NTM.afterSim (k N H v a r T : ) (I : Tape) (b : Bool) :

      The tape state the simulation stage leaves the body in. Nothing is pinned but the registers: the simulated machine's tapes and the verdict tape hold whatever the run put there, and the heads are only bounded. What survives is enough — the counter's digits, the registers, the verdict bit, and a blank real output.

      Equations
      • One or more equations did not get rendered due to their size.
      Instances For
        theorem Complexity.NTM.simTM_hoareTime {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (T v : ) (hT : 1 T) (hfT : f x.length T) (N H a r : ) :
        (tm.simTM 6).HoareTime (fun (inp : Tape) (work : Fin (0 + (k + 1) + 6 + 1)Tape) (out : Tape) => inp = bodyInput x work = bodyBank k N H v a r out = TM.blankTape) (afterSim k N H v a r T (bodyInput x) (tm.acceptsAt x T v)) T

        The simulation stage's contract. From the loop's bank with a blank output slot, the stage halts within the horizon and leaves the body in NTM.afterSim, carrying the acceptance bit of path v.

        def Complexity.NTM.afterPark (k N H v a r T : ) (I : Tape) (b : Bool) :

        The tape state after the body's cleanup. Every head is parked, the counter has been rewound and reads as v again, and the verdict tape is at cell one so its bit can be published. The simulated machine's own tapes still hold whatever the run left; the wipe deals with them.

        Equations
        • One or more equations did not get rendered due to their size.
        Instances For
          theorem Complexity.NTM.parkStage_hoareTime (k N H v a r T : ) (I : Tape) (b : Bool) (hIhead : I.head = 1) (hcne : cIdx k vIdx k) :
          (TM.parkRewindTM [cIdx k, vIdx k]).HoareTime (afterSim k N H v a r T I b) (afterPark k N H v a r T I b) (1 + 1 + (2 * (max (1 + T + 2) (2 * (1 + T + 3) + 1) + 1) + 1))

          The body's cleanup stage. It asks only for start-invariance, which is all a halted simulation guarantees, and returns the counter and the verdict tape at cell one.

          def Complexity.NTM.afterPublish (k N H v a r T : ) (I : Tape) (b : Bool) :

          The tape state once the verdict is published. The slot now holds a symbol that is 1 exactly when the path accepted, which is what TM.ifTM branches on.

          Equations
          • One or more equations did not get rendered due to their size.
          Instances For
            theorem Complexity.NTM.publishStage_hoareTime (k N H v a r T : ) (I : Tape) (b : Bool) (hI : TM.Parked I) :
            (TM.writeOutputBitTM (vIdx k)).HoareTime (afterPark k N H v a r T I b) (afterPublish k N H v a r T I b) 1

            The body's publishing stage. One transition copies the verdict tape's cell into the output slot; nothing else on any tape moves.

            def Complexity.NTM.afterBump (k N H v a r T : ) (I : Tape) (b : Bool) :

            The tape state after the tallies are bumped. The count has advanced, the chosen tally has grown by one, and the verdict slot is blank again — ready for the wipe, which needs it so.

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For
              theorem Complexity.NTM.bumpStage_hoareTime (k N H v a r T : ) (I : Tape) (b : Bool) (hI : TM.Parked I) (hIz : I.cells 0 = Γ.start) (hT : 1 T) :
              (TM.tallyBumpTM (cIdx k) (aIdx k) (rIdx k) (zIdx k)).HoareTime (afterPublish k N H v a r T I b) (afterBump k N H v a r T I b) (3 * (max (1 + 1 + max (TM.binarySuccTime a) (TM.binarySuccTime r) + 5) (TM.binarySuccTime v) + 1) + 1)

              The body's arithmetic stage. The verdict in the slot picks which tally grows; the count grows too, and the slot is blanked on the way out.

              theorem Complexity.NTM.wipeStage_hoareTime (k N T v a r : ) (I : Tape) (b : Bool) (hIsi : I.StartInvariant) (hIp : TM.Parked I) :
              (TM.wipeRewindTM (wipeTargets k) (regIdx k)).HoareTime (afterBump k N (1 + T) v a r T I b) (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = I work = bodyBank k N (1 + T) (v + 1) (a + if b = true then 1 else 0) (r + if b = true then 0 else 1) out = outSlot Γw.blank) ((wipeTargets k).length * (1 + T + 4) + (1 + T) * 4 + 8 + 1 + ((wipeTargets k).length * (1 + T + 4) + 1))

              The body's last stage. Blanking the simulated machine's tapes and the verdict tape returns the bank to exactly the shape the loop invariant names — at the next index, with the tallies advanced. This is where the body closes.

              theorem Complexity.NTM.afterSim_trans (k N H v a r T : ) (I : Tape) (b : Bool) (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) (h : afterSim k N H v a r T I b inp work out) :
              afterSim k N H v a r T I b (TM.transitionInput inp) (fun (i : Fin (bodyTapes k)) => TM.transitionTape (work i)) (TM.transitionTape out)

              The post-simulation state survives a phase boundary. It is stated in terms of cells and head bounds rather than exact tapes, which is what makes it stable: a transition preserves every cell and never pushes a head outward. A predicate that pinned the tapes could not survive here, because a halted simulation may leave a head on the marker, which the boundary then moves.

              theorem Complexity.NTM.afterPark_trans (k N H v a r T : ) (I : Tape) (b : Bool) (hI : TM.Parked I) (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) (h : afterPark k N H v a r T I b inp work out) :
              afterPark k N H v a r T I b (TM.transitionInput inp) (fun (i : Fin (bodyTapes k)) => TM.transitionTape (work i)) (TM.transitionTape out)

              The pinned states between the body's later stages survive a phase boundary too, and for the easy reason: every tape they name is parked, so the boundary is the identity.

              theorem Complexity.NTM.afterPublish_trans (k N H v a r T : ) (I : Tape) (b : Bool) (hI : TM.Parked I) (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) (h : afterPublish k N H v a r T I b inp work out) :
              afterPublish k N H v a r T I b (TM.transitionInput inp) (fun (i : Fin (bodyTapes k)) => TM.transitionTape (work i)) (TM.transitionTape out)

              The same, once the verdict is in the slot.

              theorem Complexity.NTM.afterBump_trans (k N H v a r T : ) (I : Tape) (b : Bool) (hI : TM.Parked I) (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) (h : afterBump k N H v a r T I b inp work out) :
              afterBump k N H v a r T I b (TM.transitionInput inp) (fun (i : Fin (bodyTapes k)) => TM.transitionTape (work i)) (TM.transitionTape out)

              The same, once the tallies have been bumped.

              def Complexity.NTM.bodyTM {k : } (tm : NTM k) :

              The counting loop's body. Blank the verdict slot, simulate one path, put the heads back, publish the verdict, bump the tallies and the count, and wipe the scratch tapes.

              Equations
              • One or more equations did not get rendered due to their size.
              Instances For
                def Complexity.NTM.bodyTime (k T v a r : ) :

                The body's running time: the six stages plus the five transitions between them.

                Equations
                • One or more equations did not get rendered due to their size.
                Instances For
                  theorem Complexity.NTM.bodyTM_hoareTime {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (T v : ) (hT : 1 T) (hfT : f x.length T) (N a r : ) :
                  tm.bodyTM.HoareTime (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = bodyInput x work = bodyBank k N (1 + T) v a r ∃ (s : Γw), s Γw.one out = outSlot s) (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = bodyInput x work = bodyBank k N (1 + T) (v + 1) (a + if tm.acceptsAt x T v = true then 1 else 0) (r + if tm.acceptsAt x T v = true then 0 else 1) out = outSlot Γw.blank) (bodyTime k T v a r)

                  The body's contract. One pass advances the count by one and the accepting or rejecting tally by one, according to whether the path selected by the counter accepts.

                  def Complexity.NTM.bodyMid (k N T : ) (x : List Bool) (tm : NTM k) (v : ) :

                  The tape state the body hands to the test: the bank at the next index, verdict slot blank.

                  Equations
                  • One or more equations did not get rendered due to their size.
                  Instances For
                    theorem Complexity.NTM.bodyTM_hoareTime_mid {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (T : ) (hT : 1 T) (hfT : f x.length T) (N bBody v : ) (hbound : bodyTime k T v (tally (tm.acceptsAt x T) v) (tally (fun (u : ) => !tm.acceptsAt x T u) v) bBody) :
                    tm.bodyTM.HoareTime (tallyPre (cIdx k) (aIdx k) (rIdx k) (bodyInput x) (bodyRest k N (1 + T)) (tm.acceptsAt x T) v) (bodyMid k N T x tm v) bBody

                    The body meets the loop rule's obligation. Its contract is the tally step: the count advances and exactly one of the two tallies grows.

                    theorem Complexity.NTM.tallyLoop_full {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (T N : ) (hT : 1 T) (hfT : f x.length T) (hN : 1 N) (bBody bTest B : ) (hbBody : v < N, bodyTime k T v (tally (tm.acceptsAt x T) v) (tally (fun (u : ) => !tm.acceptsAt x T u) v) bBody) (hbTest : wN, TM.binaryEqTime w.bits N.bits + 1 + (3 * (max (3 * (B + 3) + 1) (TM.resetBinaryWorkTime B 1) + 1) + 1) bTest) (hBw : wN, 1 + 1 + TM.binaryEqTime w.bits N.bits B) :
                    (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).HoareTime (tallyPre (cIdx k) (aIdx k) (rIdx k) (bodyInput x) (bodyRest k N (1 + T)) (tm.acceptsAt x T) 0) (tallyPost (cIdx k) (aIdx k) (rIdx k) (bodyInput x) (bodyRest k N (1 + T)) (tm.acceptsAt x T) N N) (N * (bBody + bTest + 5))

                    The counting loop. Body and test together run the tally to its horizon: the counter walks from 0 to N, and the two tallies end holding how many of the paths accepted and how many did not.

                    The comparison's running time is uniform over the loop. Every count the loop compares is at most the horizon, and a smaller number has no more binary digits, so one bound serves every iteration — which is what the loop rule demands.

                    theorem Complexity.NTM.tally_le (P : Bool) (v : ) :
                    tally P v v

                    A tally over [0, v) counts at most v things.

                    The body's running time with every value-dependent part replaced by its bound at the horizon.

                    Equations
                    • One or more equations did not get rendered due to their size.
                    Instances For
                      theorem Complexity.NTM.bodyTime_le (k T N v : ) (P : Bool) (hv : v N) :
                      bodyTime k T v (tally P v) (tally (fun (u : ) => !P u) v) bodyTimeBound k T N

                      The body's running time is uniform over the loop. Only three parts of it depend on the iteration — the three counter increments — and each is bounded by the width of the horizon, since neither the count nor either tally ever exceeds it.

                      The head bound the test's rewinds need: enough for the horizon's digits.

                      Equations
                      Instances For

                        The test's running time at the horizon.

                        Equations
                        • One or more equations did not get rendered due to their size.
                        Instances For
                          theorem Complexity.NTM.tallyLoop_full_bounded {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (T N : ) (hT : 1 T) (hfT : f x.length T) (hN : 1 N) :
                          (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).HoareTime (tallyPre (cIdx k) (aIdx k) (rIdx k) (bodyInput x) (bodyRest k N (1 + T)) (tm.acceptsAt x T) 0) (tallyPost (cIdx k) (aIdx k) (rIdx k) (bodyInput x) (bodyRest k N (1 + T)) (tm.acceptsAt x T) N N) (N * (bodyTimeBound k T N + testTimeBound N + 5))

                          The counting loop with concrete bounds. Both running times are now written in terms of the horizon alone, which is what a polynomial space bound will need.

                          theorem Complexity.NTM.bodyTM_keepsWindowOn {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (T v : ) (hT : 1 T) (hfT : f x.length T) (N a r : ) :
                          tm.bodyTM.KeepsWindowOn (fun (c : Cfg (bodyTapes k) tm.bodyTM.Q) => c.state = tm.bodyTM.qstart c.input = bodyInput x c.work = bodyBank k N (1 + T) v a r ∃ (s : Γw), s Γw.one c.output = outSlot s) x.length (1 + bodyTime k T v a r)

                          The body keeps a window. Its running time bounds how far any head can drift during one pass, and every tape it starts from is parked at cell one, so the whole pass stays inside a window of that width — independent of which iteration it is.

                          theorem Complexity.NTM.tallyLoop_keepsWindow_bounded {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (T N : ) (hT : 1 T) (hfT : f x.length T) (hN : 1 N) (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) :
                          tallyPre (cIdx k) (aIdx k) (rIdx k) (bodyInput x) (bodyRest k N (1 + T)) (tm.acceptsAt x T) 0 inp work out∀ (c : Cfg (bodyTapes k) (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).Q), (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).reaches { state := (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).qstart, input := inp, work := work, output := out } cc.WithinDecisionSpace x.length (1 + (bodyTimeBound k T N + testTimeBound N + 5))

                          The counting loop's space bound at the concrete layout. Every configuration the loop ever reaches fits inside a window one iteration wide — and one iteration's width is a polynomial in the horizon, not in the exponentially many iterations.

                          theorem Complexity.NTM.lt_iff_succ_sub_eq_zero (r a : ) :
                          r < a r + 1 - a = 0

                          The comparison the epilogue actually performs. Testing r < a directly would need a strict comparator; testing (r + 1) - a = 0 needs only truncated subtraction and an equality test against zero, both of which the subroutine library already has — and it avoids having to complement the verdict afterwards.

                          The counting machine's epilogue. Bump the rejecting tally, subtract the accepting one from it, clear the counter to reuse as scratch, test the difference against zero, and publish the answer. The counter's value is spent by this point, which is what frees a register for the test's result.

                          Equations
                          • One or more equations did not get rendered due to their size.
                          Instances For

                            The epilogue's tail: put the equality test's answer at cell one and publish it.

                            Equations
                            Instances For

                              The counting machine's epilogue. Bump the rejecting tally, subtract the accepting one from it, clear the counter to reuse as scratch, test the difference against zero, and publish the answer.

                              Equations
                              • One or more equations did not get rendered due to their size.
                              Instances For
                                def Complexity.NTM.epilogueBank (k N H a r : ) :

                                The bank the epilogue's arithmetic leaves behind.

                                Equations
                                • One or more equations did not get rendered due to their size.
                                Instances For
                                  theorem Complexity.NTM.epiloguePreTM_hoareTime (k N H a r : ) (I O : Tape) (hI : TM.Parked I) (hO : TM.Parked O) :
                                  (epiloguePreTM k).HoareTime (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = I work = bodyBank k N H N a r out = O) (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = I work = epilogueBank k N H a r out = O) (3 * (max (max (TM.binarySuccTime r) (TM.binaryRippleSubTime (r + 1) a)) (TM.resetBinaryWorkTime 1 N.bits.length) + 1) + 1)

                                  The epilogue's arithmetic, contracted. Three pinned stages: bump the rejecting tally, subtract the accepting one from it, and clear the counter — whose value is spent — to free a register for the comparison that follows.

                                  def Complexity.NTM.afterEq (k : ) (b : Bool) (I : Tape) (B : ) :

                                  The state between the epilogue's comparison and its publication: the answer sits on the counter tape, which the comparison used as its result register.

                                  Equations
                                  • One or more equations did not get rendered due to their size.
                                  Instances For
                                    theorem Complexity.NTM.epiloguePostTM_hoareTime (k : ) (b : Bool) (I : Tape) (B : ) (hI : TM.Parked I) :
                                    (epiloguePostTM k).HoareTime (afterEq k b I B) (fun (_inp : Tape) (_work : Fin (bodyTapes k)Tape) (out : Tape) => out = outSlot (TM.readBackWrite (Γ.ofBool b))) (1 * (B + 3) + 1 + 1 + 1)

                                    The epilogue's publication. Rewind the register holding the comparison's answer and copy its bit into the output slot, where the surrounding obligation reads it.

                                    theorem Complexity.NTM.epilogueBank_resIdx (k N H a r : ) (hsc : resIdx k cIdx k) :
                                    epilogueBank k N H a r (resIdx k) = natTape (r + 1 - a)
                                    theorem Complexity.NTM.epilogueBank_parked (k N H a r : ) (j : Fin (bodyTapes k)) :
                                    TM.Parked (epilogueBank k N H a r j)
                                    theorem Complexity.NTM.epilogueEq_hoareTime (k N H a r : ) (I : Tape) (hI : TM.Parked I) (hIsi : I.StartInvariant) :
                                    (TM.binaryEqTM (resIdx k) (zIdx k) (cIdx k)).HoareTime (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = I work = epilogueBank k N H a r out = outSlot Γw.one) (afterEq k (decide (r < a)) I 2) (TM.binaryEqTime (r + 1 - a).bits (Nat.bits 0))

                                    The epilogue's comparison. Testing the difference against zero decides r < a; the answer lands on the counter tape, which the arithmetic stage cleared for exactly this purpose.

                                    theorem Complexity.NTM.afterEq_trans (k : ) (b : Bool) (I : Tape) (B : ) (hI : TM.Parked I) (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) (h : afterEq k b I B inp work out) :
                                    afterEq k b I B (TM.transitionInput inp) (fun (i : Fin (bodyTapes k)) => TM.transitionTape (work i)) (TM.transitionTape out)

                                    The state between the epilogue's comparison and its publication survives a phase boundary: every tape it names is parked, so the boundary is the identity.

                                    The epilogue's running time: its three stages and the two transitions between them.

                                    Equations
                                    • One or more equations did not get rendered due to their size.
                                    Instances For
                                      theorem Complexity.NTM.epilogueTM_hoareTime (k N H a r : ) (I : Tape) (hI : TM.Parked I) (hIsi : I.StartInvariant) :
                                      (epilogueTM k).HoareTime (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = I work = bodyBank k N H N a r out = outSlot Γw.one) (fun (_inp : Tape) (_work : Fin (bodyTapes k)Tape) (out : Tape) => out = outSlot (TM.readBackWrite (Γ.ofBool (decide (r < a))))) (epilogueTime N a r)

                                      The epilogue's contract. From the bank the loop leaves — the counter at the horizon, the two tallies holding their counts — the machine writes 1 into the verdict slot exactly when the rejecting tally is smaller than the accepting one.

                                      The epilogue keeps a window. It runs for a bounded time from tapes parked at cell one, so no head can leave a window of that width.

                                      The register-arithmetic subroutines state their contracts in the TM.EmitPred shape, whose output component is an accumulator of emitted bits. With nothing emitted that is just the blank tape, so those contracts are pinned after all.

                                      theorem Complexity.NTM.hoareTime_emit_of_pinned {m : } {tm : TM m} {inp₀ : Tape} {W W' : Fin mTape} {b : } (h : tm.HoareTime (fun (inp : Tape) (work : Fin mTape) (out : Tape) => inp = inp₀ work = W out = TM.blankTape) (fun (inp : Tape) (work : Fin mTape) (out : Tape) => inp = inp₀ work = W' out = TM.blankTape) b) :
                                      tm.HoareTime (TM.EmitPred inp₀ W []) (TM.EmitPred inp₀ W' []) b

                                      A pinned contract with a blank output is an TM.EmitPred contract with nothing emitted.

                                      theorem Complexity.NTM.bodyInput_eq (x : List Bool) :
                                      bodyInput x = { head := 1, cells := (Tape.init (List.map Γ.ofBool x)).cells }

                                      The machine's own input tape, in the shape the register subroutines name it.

                                      The counting machine's prologue. Measure the input, evaluate the horizon polynomial on a unary register, turn that register into the binary horizon with one increment, bump the wipe height, and clear the scratch.

                                      Equations
                                      • One or more equations did not get rendered due to their size.
                                      Instances For

                                        The bank the prologue's five stages leave behind.

                                        Equations
                                        • One or more equations did not get rendered due to their size.
                                        Instances For
                                          theorem Complexity.NTM.prologueBank_eq (k T lx : ) :
                                          Function.update (prologueBank k T lx) (resIdx k) (TM.regTape 0) = bodyBank k (2 ^ T) (1 + T) 0 0 0

                                          The prologue lands on the loop's starting bank. The horizon sits on nIdx, the wipe height on regIdx, and everything else — the counter, both tallies, the scratch registers — is blank, which is what NTM.bodyBank at index zero says.

                                          A bound covering the input length and every Horner accumulator the prologue forms.

                                          Equations
                                          Instances For

                                            The prologue's running time.

                                            Equations
                                            • One or more equations did not get rendered due to their size.
                                            Instances For
                                              theorem Complexity.NTM.prologueTM_hoareTime (k : ) (p : Polynomial ) (x : List Bool) :
                                              (prologueTM k p).HoareTime (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = { head := 1, cells := (Tape.init (List.map Γ.ofBool x)).cells } (work = fun (x : Fin (bodyTapes k)) => TM.blankTape) out = TM.blankTape) (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = { head := 1, cells := (Tape.init (List.map Γ.ofBool x)).cells } work = bodyBank k (2 ^ Polynomial.eval x.length p) (1 + Polynomial.eval x.length p) 0 0 0 out = TM.blankTape) (prologueTime p x.length)

                                              The prologue's contract. From the blank bank it lands on the loop's starting bank: the horizon on nIdx, the wipe height on regIdx, everything else blank.

                                              The prologue keeps a window. It runs for a bounded time from blank tapes parked at cell one, so nothing travels beyond a window of that width.

                                              def Complexity.NTM.ppMachine (k : ) (tm : NTM k) (p : Polynomial ) :

                                              The whole counting machine. Park the heads off the left marker, set up the horizon and the wipe height, run the tally to its horizon, then compare the two tallies and publish.

                                              Equations
                                              • One or more equations did not get rendered due to their size.
                                              Instances For
                                                theorem Complexity.NTM.ppPark_hoareTime (k : ) (x : List Bool) :
                                                TM.skipTM.HoareTime (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = Tape.init (List.map Γ.ofBool x) (work = fun (x : Fin (bodyTapes k)) => Tape.init []) out = Tape.init []) (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = { head := 1, cells := (Tape.init (List.map Γ.ofBool x)).cells } (work = fun (x : Fin (bodyTapes k)) => TM.blankTape) out = TM.blankTape) 1

                                                The parking step. One transition off the initial configuration puts every head at cell one, which is where every stage of the machine expects to be entered.

                                                def Complexity.NTM.ppTime (k : ) (tm : NTM k) (p : Polynomial ) (x : List Bool) :

                                                The whole machine's running time.

                                                Equations
                                                • One or more equations did not get rendered due to their size.
                                                Instances For
                                                  theorem Complexity.NTM.ppMachine_hoareTime (k : ) (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (p : Polynomial ) (hT : 1 Polynomial.eval x.length p) (hfT : f x.length Polynomial.eval x.length p) :
                                                  (ppMachine k tm p).HoareTime (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = Tape.init (List.map Γ.ofBool x) (work = fun (x : Fin (bodyTapes k)) => Tape.init []) out = Tape.init []) (fun (_inp : Tape) (_work : Fin (bodyTapes k)Tape) (out : Tape) => out = outSlot (TM.readBackWrite (Γ.ofBool (decide (tally (fun (u : ) => !tm.acceptsAt x (Polynomial.eval x.length p) u) (2 ^ Polynomial.eval x.length p) < tally (tm.acceptsAt x (Polynomial.eval x.length p)) (2 ^ Polynomial.eval x.length p)))))) (ppTime k tm p x)

                                                  The counting machine's contract. From its initial configuration it halts with the verdict slot holding 1 exactly when the accepting paths outnumber the rejecting ones.

                                                  theorem Complexity.NTM.tallyLoop_keepsWindowOn {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (T N : ) (hT : 1 T) (hfT : f x.length T) (hN : 1 N) :
                                                  (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).KeepsWindowOn (fun (c : Cfg (bodyTapes k) (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).Q) => c.state = (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).qstart tallyPre (cIdx k) (aIdx k) (rIdx k) (bodyInput x) (bodyRest k N (1 + T)) (tm.acceptsAt x T) 0 c.input c.work c.output) x.length (1 + (bodyTimeBound k T N + testTimeBound N + 5))

                                                  The counting loop's space bound, packaged as a conditional window contract so it can be composed with the machine's other parts.

                                                  theorem Complexity.NTM.tallyPre_cfg_ok (k N H v a r : ) (x : List Bool) (S : ) (hS : 1 S) {Q : Type} (c : Cfg (bodyTapes k) Q) (h : c.input = bodyInput x c.work = bodyBank k N H v a r ∃ (s : Γw), s Γw.one c.output = outSlot s) :

                                                  The loop's starting configuration is inside any window at least one cell wide, and carries its left markers.

                                                  theorem Complexity.NTM.loopEpilogue_keepsWindowOn {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (T N : ) (hT : 1 T) (hfT : f x.length T) (hN : 1 N) (S : ) (hS1 : 1 + (bodyTimeBound k T N + testTimeBound N + 5) S) (hS2 : 1 + epilogueTime N (tally (tm.acceptsAt x T) N) (tally (fun (u : ) => !tm.acceptsAt x T u) N) S) :
                                                  ((tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).seqTM (epilogueTM k)).KeepsWindowOn (fun (c : Cfg (bodyTapes k) ((tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).seqTM (epilogueTM k)).Q) => ∃ (d : Cfg (bodyTapes k) (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).Q), (d.state = (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).qstart tallyPre (cIdx k) (aIdx k) (rIdx k) (bodyInput x) (bodyRest k N (1 + T)) (tm.acceptsAt x T) 0 d.input d.work d.output) c = (tm.bodyTM.loopTM (TM.tallyTestTM (cIdx k) (nIdx k) (resIdx k))).phase1Wrap (epilogueTM k) d) x.length S

                                                  The loop and the epilogue, composed in space.

                                                  theorem Complexity.NTM.prologueRest_keepsWindowOn {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (p : Polynomial ) (T N : ) (hTdef : T = Polynomial.eval x.length p) (hNdef : N = 2 ^ T) (hT : 1 T) (hfT : f x.length T) (hN : 1 N) (S : ) (hS0 : 1 + prologueTime p x.length S) (hS1 : 1 + (bodyTimeBound k T N + testTimeBound N + 5) S) (hS2 : 1 + epilogueTime N (tally (tm.acceptsAt x T) N) (tally (fun (u : ) => !tm.acceptsAt x T u) N) S) :

                                                  The prologue joined to the rest, in space.

                                                  theorem Complexity.NTM.ppMachine_keepsWindow {k : } (tm : NTM k) (x : List Bool) (hne : tm.qstart tm.qhalt) {f : } (hall : tm.AllPathsHaltIn f) (p : Polynomial ) (T N : ) (hTdef : T = Polynomial.eval x.length p) (hNdef : N = 2 ^ T) (hT : 1 T) (hfT : f x.length T) (hN : 1 N) (S : ) (hS : 1 S) (hS0 : 1 + prologueTime p x.length S) (hS1 : 1 + (bodyTimeBound k T N + testTimeBound N + 5) S) (hS2 : 1 + epilogueTime N (tally (tm.acceptsAt x T) N) (tally (fun (u : ) => !tm.acceptsAt x T u) N) S) (c' : Cfg (bodyTapes k) (ppMachine k tm p).Q) :
                                                  (ppMachine k tm p).reaches ((ppMachine k tm p).initCfg x) c'c'.WithinDecisionSpace x.length S

                                                  The whole machine keeps a polynomial window. Every configuration it reaches from its initial one fits inside S cells.

                                                  theorem Complexity.NTM.size_horizon (T : ) :
                                                  (2 ^ T).size = T + 1

                                                  The horizon's width is the exponent plus one. Every bound the machine's parts state in terms of the horizon's number of digits is therefore a bound in terms of the exponent — which is what makes them polynomial in the input length rather than exponential.

                                                  The Horner cap, as a polynomial. The space bound the surrounding obligation asks for must be a Polynomial, so each arithmetic expression the machine's parts are bounded by has to be mirrored by a polynomial whose evaluation reproduces it.

                                                  Equations
                                                  Instances For

                                                    The operation budget, as a polynomial.

                                                    Equations
                                                    Instances For

                                                      The layer budget, as a polynomial.

                                                      Equations
                                                      Instances For
                                                        theorem Complexity.NTM.size_horizon_pred (T : ) :
                                                        (2 ^ T - 1).size = T

                                                        The predecessor of the horizon has exactly T digits, all of them ones.

                                                        The prologue's running time, as a polynomial. Only a bound is possible — the running time involves max, which no polynomial reproduces — so the maxima are replaced by sums.

                                                        Equations
                                                        • One or more equations did not get rendered due to their size.
                                                        Instances For
                                                          theorem Complexity.NTM.tally_size_le (P : Bool) (N : ) :
                                                          (tally P N).size N.size

                                                          A tally has no more digits than its horizon. Every time bound the epilogue states in terms of the tallies' widths is therefore a bound in terms of the exponent.

                                                          theorem Complexity.NTM.tally_size_horizon_le (P : Bool) (T : ) :
                                                          (tally P (2 ^ T)).size T + 1
                                                          theorem Complexity.NTM.tally_succ_size_horizon_le (P : Bool) (T : ) :
                                                          (tally P (2 ^ T) + 1).size T + 2

                                                          One more than a tally still has no more than one extra digit.

                                                          theorem Complexity.NTM.loopWidth_le (k T : ) :
                                                          1 + (bodyTimeBound k T (2 ^ T) + testTimeBound (2 ^ T) + 5) 2 * ((wipeTargets k).length * (T + 5)) + (40 * T + 300)

                                                          The loop's window is linear in the exponent. Its width is stated through the digit counts of the horizon and of the two tallies; each of those is T + O(1), so the width is too — even though the horizon itself is 2 ^ T.

                                                          theorem Complexity.NTM.epilogueWidth_le (T : ) (P : Bool) :
                                                          1 + epilogueTime (2 ^ T) (tally P (2 ^ T)) (tally (fun (u : ) => !P u) (2 ^ T)) 40 * T + 200

                                                          The epilogue's window is linear in the exponent too. Its cost is stated through the digit counts of the two tallies and of their difference, all of which are T + O(1).

                                                          The machine's space bound, as a polynomial. The sum of the three parts' widths: the prologue's, the loop's, and the epilogue's.

                                                          Equations
                                                          • One or more equations did not get rendered due to their size.
                                                          Instances For
                                                            theorem Complexity.NTM.epilogue_le_ppSpacePoly (k : ) (p : Polynomial ) (n : ) (P : Bool) :
                                                            1 + epilogueTime (2 ^ Polynomial.eval n p) (tally P (2 ^ Polynomial.eval n p)) (tally (fun (u : ) => !P u) (2 ^ Polynomial.eval n p)) Polynomial.eval n (ppSpacePoly k p)
                                                            def Complexity.NTM.ppCond {k : } (tm : NTM k) (p : Polynomial ) (x : List Bool) :

                                                            The comparison the surrounding obligation names: after 2 ^ p |x| tally steps, does the accepting component exceed the rejecting one?

                                                            Equations
                                                            • One or more equations did not get rendered due to their size.
                                                            Instances For
                                                              theorem Complexity.NTM.ppMachine_decides (k : ) (tm : NTM k) {f : } (hall : tm.AllPathsHaltIn f) (p : Polynomial ) (hle : ∀ (n : ), f n Polynomial.eval n p) (hne : tm.qstart tm.qhalt) (x : List Bool) :
                                                              ∃ (c' : Cfg (bodyTapes k) (ppMachine k tm (p + 1)).Q), (ppMachine k tm (p + 1)).reaches ((ppMachine k tm (p + 1)).initCfg x) c' (ppMachine k tm (p + 1)).halted c' (tm.ppCond p xc'.output.cells 1 = Γ.one) (¬tm.ppCond p xc'.output.cells 1 = Γ.zero)

                                                              The counting machine decides the PP comparison. It runs at the horizon p.eval |x| + 1 — one more than the specification names, so that the horizon is never zero, which the simulation needs — and NTM.cmp_horizon_iff' says the comparison is the same either way.

                                                              theorem Complexity.NTM.ppMachine_space (k : ) (tm : NTM k) {f : } (hall : tm.AllPathsHaltIn f) (p : Polynomial ) (hle : ∀ (n : ), f n Polynomial.eval n p) (hne : tm.qstart tm.qhalt) (x : List Bool) (c' : Cfg (bodyTapes k) (ppMachine k tm (p + 1)).Q) (hreach : (ppMachine k tm (p + 1)).reaches ((ppMachine k tm (p + 1)).initCfg x) c') :

                                                              The counting machine runs in polynomial space. Every configuration it reaches fits inside NTM.ppSpacePoly cells — a polynomial in the input length, even though the machine's own running time is exponential.

                                                              Two is the smallest numeral whose low digit is zero. A blank register incremented twice therefore reads 0, which is how the trivial machine below produces a 0 to publish — the alphabet offers no other way to name one.

                                                              The trivial machine: it writes 0 and halts. This is what serves for a source machine that starts halted, where no path can accept and the comparison is always false.

                                                              Equations
                                                              • One or more equations did not get rendered due to their size.
                                                              Instances For

                                                                The trivial machine's running time.

                                                                Equations
                                                                Instances For
                                                                  theorem Complexity.NTM.zeroTM_hoareTime (k : ) (x : List Bool) :
                                                                  (zeroTM k).HoareTime (fun (inp : Tape) (work : Fin (bodyTapes k)Tape) (out : Tape) => inp = Tape.init (List.map Γ.ofBool x) (work = fun (x : Fin (bodyTapes k)) => Tape.init []) out = Tape.init []) (fun (_inp : Tape) (_work : Fin (bodyTapes k)Tape) (out : Tape) => out = outSlot Γw.zero) zeroTime

                                                                  The trivial machine's contract. It halts with 0 in the verdict slot.

                                                                  The trivial machine's space bound, as a polynomial: a constant.

                                                                  Equations
                                                                  Instances For

                                                                    The trivial machine keeps a constant window. Its running time is constant, so the heads cannot travel far enough to leave one.

                                                                    theorem Complexity.NTM.zeroTM_decides (k : ) (x : List Bool) :
                                                                    ∃ (c' : Cfg (bodyTapes k) (zeroTM k).Q), (zeroTM k).reaches ((zeroTM k).initCfg x) c' (zeroTM k).halted c' c'.output.cells 1 = Γ.zero

                                                                    The trivial machine publishes 0.

                                                                    theorem Complexity.NTM.not_ppCond_of_qstart_eq_qhalt (k : ) (tm : NTM k) (heq : tm.qstart = tm.qhalt) (p : Polynomial ) (x : List Bool) :
                                                                    ¬tm.ppCond p x

                                                                    A source that starts halted fails the comparison. No path accepts, so the accepting tally is zero and cannot exceed the rejecting one — which is why the trivial machine, publishing 0 unconditionally, decides this case.