Documentation

Complexitylib.Classes.Containments.Internal.BinArith

Binary addition and comparison inside the polynomial-time algebra #

⚠️ Unreviewed by Bolton

Complexitylib.Classes.Containments.Internal.SavitchBits builds a fixed-width counter in the algebra — increment and overflow. A search that accumulates counts needs more: to add two numbers and to compare them. Both are the same shape of computation, a single left-to-right scan over the two operands, and both are written twice here for the same reason the counter was: as a plain recursion, where the arithmetic is proved, and as a scan on a packed state, which is the shape Cobham.iterate_mem_FP iterates.

Numbers are little-endian, least significant bit first, and the two operands are the same width. Addition returns the carry out separately, so nothing is lost to wraparound.

Main definitions #

Main results #

The bit operations #

def Complexity.majB (c b d : Bool) :

The carry out of a full adder.

Equations
Instances For
    def Complexity.sumB (c b d : Bool) :

    The sum bit of a full adder.

    Equations
    Instances For
      theorem Complexity.majB_sumB (c b d : Bool) :
      (sumB c b d).toNat + 2 * (majB c b d).toNat = c.toNat + b.toNat + d.toNat

      Exclusive or of two flags.

      Equations
      Instances For

        The carry out, on flags.

        Equations
        Instances For

          The sum bit, on flags.

          Equations
          Instances For
            @[simp]
            theorem Complexity.xorBit_flag (c d : Bool) :
            xorBit [c] [d] = [c ^^ d]
            @[simp]
            theorem Complexity.majBit_flag (c b d : Bool) :
            majBit [c] [b] [d] = [majB c b d]
            @[simp]
            theorem Complexity.sumBit_flag (c b d : Bool) :
            sumBit [c] [b] [d] = [sumB c b d]

            The leading bit of a string, as a flag, reading past the end as zero.

            Equations
            Instances For
              @[simp]
              theorem Complexity.bit1_cons (d : Bool) (t : List Bool) :
              bit1 (d :: t) = [d]

              Addition as a recursion #

              Add two little-endian bitstrings with a carry in: the carry out and the sum bits. The second operand is read through headD/drop, so the two are stepped in lockstep.

              Equations
              Instances For
                @[simp]
                theorem Complexity.addBitsLE_cons {d : Bool} (c b : Bool) (tu tv : List Bool) :
                addBitsLE c (b :: tu) (d :: tv) = ((addBitsLE (majB c b d) tu tv).1, sumB c b d :: (addBitsLE (majB c b d) tu tv).2)
                @[simp]
                theorem Complexity.addBitsLE_length (c : Bool) (u v : List Bool) :
                (addBitsLE c u v).2.length = u.length
                theorem Complexity.addBitsLE_binValLE (c : Bool) (u v : List Bool) (h : v.length = u.length) :
                binValLE (addBitsLE c u v).2 + (addBitsLE c u v).1.toNat * 2 ^ u.length = binValLE u + binValLE v + c.toNat

                The adder is correct.

                Addition as a scan #

                One step of the addition scan: the carry, the bits emitted so far, and the two operands still to read.

                Equations
                Instances For
                  @[simp]
                  theorem Complexity.addStep_nil (c acc v : List Bool) :
                  addStep (c, acc, [], v) = (c, acc, [], v)
                  theorem Complexity.addStep_flag (c b : Bool) (acc tu v : List Bool) :
                  addStep ([c], acc, b :: tu, v) = ([majB c b (v.headD false)], acc ++ [sumB c b (v.headD false)], tu, List.drop 1 v)
                  theorem Complexity.addStep_iterate_run (c : Bool) (acc u v : List Bool) (h : v.length = u.length) :
                  addStep^[u.length] ([c], acc, u, v) = ([(addBitsLE c u v).1], acc ++ (addBitsLE c u v).2, [], [])

                  The scan computes the sum.

                  theorem Complexity.addStep_iterate_length (c : Bool) (acc u v : List Bool) (n : ) :
                  (addStep^[n] ([c], acc, u, v)).1.length = 1 (addStep^[n] ([c], acc, u, v)).2.1.length + (addStep^[n] ([c], acc, u, v)).2.2.1.length acc.length + u.length (addStep^[n] ([c], acc, u, v)).2.2.2.length v.length

                  The scan never has more in hand than it started with.

                  The packed scan #

                  def Complexity.addPack (c acc ru rv : List Bool) :

                  The packed scan state.

                  Equations
                  Instances For
                    @[simp]
                    theorem Complexity.addPack_length (c acc ru rv : List Bool) :
                    (addPack c acc ru rv).length = 2 * c.length + 2 * acc.length + 2 * ru.length + rv.length + 6

                    One step of the packed scan.

                    Equations
                    • One or more equations did not get rendered due to their size.
                    Instances For
                      theorem Complexity.addStepP_pack (c acc ru rv : List Bool) :
                      addStepP (addPack c acc ru rv) = addPack (addStep (c, acc, ru, rv)).1 (addStep (c, acc, ru, rv)).2.1 (addStep (c, acc, ru, rv)).2.2.1 (addStep (c, acc, ru, rv)).2.2.2

                      The packed step is the unpacked step.

                      theorem Complexity.addStepP_iterate (s : List Bool × List Bool × List Bool × List Bool) (n : ) :
                      addStepP^[n] (addPack s.1 s.2.1 s.2.2.1 s.2.2.2) = addPack (addStep^[n] s).1 (addStep^[n] s).2.1 (addStep^[n] s).2.2.1 (addStep^[n] s).2.2.2

                      The packed iteration is the unpacked one.

                      theorem Complexity.addStepP_iterate_args (c acc ru rv : List Bool) (n : ) :
                      addStepP^[n] (addPack c acc ru rv) = addPack (addStep^[n] (c, acc, ru, rv)).1 (addStep^[n] (c, acc, ru, rv)).2.1 (addStep^[n] (c, acc, ru, rv)).2.2.1 (addStep^[n] (c, acc, ru, rv)).2.2.2

                      The verdicts #

                      The packed addition run to completion.

                      Equations
                      Instances For

                        The sum bits of u and v.

                        Equations
                        Instances For

                          The carry out of u + v.

                          Equations
                          Instances For
                            @[simp]
                            theorem Complexity.addBits_eq (u v : List Bool) (h : v.length = u.length) :
                            addBits u v = (addBitsLE false u v).2
                            @[simp]
                            theorem Complexity.addCarry_eq (u v : List Bool) (h : v.length = u.length) :

                            What the packed addition computes.

                            Addition is polynomial-time #

                            theorem Complexity.addRunFn_mem_FP {a b : List BoolList Bool} (ha : a FP) (hb : b FP) :
                            (fun (z : List Bool) => addRun (a z) (b z)) FP
                            theorem Complexity.addBitsFn_mem_FP {a b : List BoolList Bool} (ha : a FP) (hb : b FP) :
                            (fun (z : List Bool) => addBits (a z) (b z)) FP
                            theorem Complexity.addCarryFn_mem_FP {a b : List BoolList Bool} (ha : a FP) (hb : b FP) :
                            (fun (z : List Bool) => addCarry (a z) (b z)) FP

                            Comparison as a recursion #

                            Scan two little-endian bitstrings from the bottom, keeping the verdict of the highest position at which they have differed so far.

                            Equations
                            Instances For
                              @[simp]
                              theorem Complexity.ltBitsLE_nil (f : Bool) (v : List Bool) :
                              ltBitsLE f [] v = f
                              theorem Complexity.ltBitsLE_cons (f b : Bool) (tu v : List Bool) :
                              ltBitsLE f (b :: tu) v = ltBitsLE (if b = v.headD false then f else v.headD false) tu (List.drop 1 v)

                              The comparator is correct.

                              Comparison as a scan #

                              @[simp]

                              The scan computes the comparison.

                              The packed comparison #

                              The packed comparison state.

                              Equations
                              Instances For
                                @[simp]
                                theorem Complexity.ltPack_length (f ru rv : List Bool) :
                                (ltPack f ru rv).length = 2 * f.length + 2 * ru.length + rv.length + 4

                                One step of the packed comparison.

                                Equations
                                • One or more equations did not get rendered due to their size.
                                Instances For
                                  theorem Complexity.ltStepP_pack (f ru rv : List Bool) :
                                  ltStepP (ltPack f ru rv) = ltPack (ltStep (f, ru, rv)).1 (ltStep (f, ru, rv)).2.1 (ltStep (f, ru, rv)).2.2
                                  theorem Complexity.ltStepP_iterate (s : List Bool × List Bool × List Bool) (n : ) :
                                  ltStepP^[n] (ltPack s.1 s.2.1 s.2.2) = ltPack (ltStep^[n] s).1 (ltStep^[n] s).2.1 (ltStep^[n] s).2.2
                                  theorem Complexity.ltStepP_iterate_args (f ru rv : List Bool) (n : ) :
                                  ltStepP^[n] (ltPack f ru rv) = ltPack (ltStep^[n] (f, ru, rv)).1 (ltStep^[n] (f, ru, rv)).2.1 (ltStep^[n] (f, ru, rv)).2.2

                                  The packed comparison run to completion.

                                  Equations
                                  Instances For

                                    Is u below v, as a flag.

                                    Equations
                                    Instances For

                                      The flag decides the comparison.

                                      Comparison is polynomial-time #

                                      theorem Complexity.ltFlagFn_mem_FP {a b : List BoolList Bool} (ha : a FP) (hb : b FP) :
                                      (fun (z : List Bool) => ltFlag (a z) (b z)) FP

                                      The larger of two numbers #

                                      The larger of two equal-width numbers.

                                      Equations
                                      Instances For
                                        theorem Complexity.maxBitsFn_mem_FP {a b : List BoolList Bool} (ha : a FP) (hb : b FP) :
                                        (fun (z : List Bool) => maxBits (a z) (b z)) FP

                                        Arithmetic without overflow #

                                        Adding below the width is exact.

                                        Powers of two as bitstrings #

                                        The bitstring of 2 ^ t, one bit wider than t bits so that it can be compared with a doubled t + 1-bit count.

                                        Equations
                                        Instances For
                                          theorem Complexity.two_pow_lt_two_mul_iff (t : ) (w : List Bool) (hw : w.length = t + 1) :

                                          The threshold comparison, as the algebra performs it. A count w of t + 1 bits exceeds half of 2 ^ t exactly when the doubled count is above 2 ^ t.

                                          Selection on a literal flag #

                                          Emptiness and the leading bit #

                                          Is the string empty, as a flag.

                                          Equations
                                          Instances For
                                            theorem Complexity.emptyFlagFn_mem_FP {a : List BoolList Bool} (ha : a FP) :
                                            (fun (z : List Bool) => emptyFlag (a z)) FP

                                            Drop the leading bit.

                                            Equations
                                            Instances For
                                              theorem Complexity.dropOneFn_mem_FP {a : List BoolList Bool} (ha : a FP) :
                                              (fun (z : List Bool) => dropOne (a z)) FP

                                              The strings of a given length #

                                              The bitstrings of length exactly .

                                              Equations
                                              Instances For
                                                @[simp]
                                                theorem Complexity.mem_strsOfLen { : } {l : List Bool} :
                                                l strsOfLen l.length =

                                                The bitstrings of length at most m.

                                                Equations
                                                Instances For
                                                  @[simp]
                                                  theorem Complexity.mem_strsLe {m : } {l : List Bool} :

                                                  Enumerating every short string with one counter #

                                                  A search that sums over all strings of length at most m must visit each exactly once. One counter suffices: order the strings by length and then by value, so the successor of the all-ones string of length is the all-zeros string of length ℓ + 1. The position of a string in that order is Complexity.strIdx, and it increases by one at every step, which is what makes the enumeration a bijection.

                                                  The position of a string in the order: by length first, then by value.

                                                  Equations
                                                  Instances For

                                                    The counter steps one place along the order.

                                                    theorem Complexity.strIdx_injective {w w' : List Bool} (h : strIdx w = strIdx w') :
                                                    w = w'
                                                    theorem Complexity.strIdx_lt_iff (m : ) (w : List Bool) :
                                                    strIdx w < 2 ^ (m + 1) - 1 w.length m
                                                    theorem Complexity.strsLe_eq_image (m : ) :
                                                    strsLe m = Finset.image (fun (i : ) => nextStr^[i] []) (Finset.range (2 ^ (m + 1) - 1))

                                                    The counter enumerates every short string exactly once.

                                                    theorem Complexity.nextStr_injOn (m : ) :
                                                    Set.InjOn (fun (i : ) => nextStr^[i] []) (Finset.range (2 ^ (m + 1) - 1))

                                                    The counter enumerates the strings of one length too, starting from the zeros.

                                                    theorem Complexity.bumpBits_injOn ( : ) :
                                                    Set.InjOn (fun (i : ) => bumpBits^[i] (List.replicate false)) (Finset.range (2 ^ ))
                                                    theorem Complexity.nextStrFn_mem_FP {a : List BoolList Bool} (ha : a FP) :
                                                    (fun (z : List Bool) => nextStr (a z)) FP