Documentation

ToMathlib.Data.IndexedBinaryTree.Basic

Inductive Indexed Binary Trees #

A Note about Process/API when working with this #

I am trying to follow a process of making simp theorems and procedures that decompose the trees as much as possible.

  1. Use casework to take a tree of a known structure and specify the parts of that structure
  2. Push maps or other functions on this down to the bottom of the (tree / syntax tree)
  3. I then have e.g. FullData.internal value1 left1 right1 = FullData.internal value2 left2 right2
  4. and then I can congruence the values and subtrees

Notes & TODOs #

Inductive data type for a binary tree skeleton. A skeleton is a binary tree without values, used to represent the structure of the tree.

Instances For

    Depth of a Skeleton, measured as the maximum number of edges from the root to a leaf. A standalone leaf has depth 0, and an internal node has depth one more than the deeper child.

    Instances For

      Number of leaves of a Skeleton. A leaf contributes one, and an internal node sums the leaf counts of its children.

      Instances For

        Type of indices of leaves of a skeleton

        Instances For

          Type of indices of internal nodes of a skeleton

          Instances For

            Type of indices of any node of a skeleton

            Instances For

              This section contains predicates about indices determined by their neighborhood in the tree.

              Check whether a leaf index is the root of its tree.

              Instances For

                Check whether an internal-node index is the root of its tree.

                Instances For

                  Check whether a node index is the root of its tree.

                  Instances For

                    Check whether a node index points to a leaf of the tree.

                    Instances For
                      inductive BinaryTree.LeafData (α : Type u_1) :
                      SkeletonType u_1

                      A binary tree with values stored at leaves.

                      Instances For
                        def BinaryTree.instReprLeafData.repr {α✝ : Type u_1} {a✝ : Skeleton} [Repr α✝] :
                        LeafData α✝ a✝Std.Format
                        Instances For
                          @[implicit_reducible]
                          instance BinaryTree.instReprLeafData {α✝ : Type u_1} {a✝ : Skeleton} [Repr α✝] :
                          Repr (LeafData α✝ a✝)
                          inductive BinaryTree.InternalData (α : Type u_1) :
                          SkeletonType u_1

                          A binary tree with values stored in internal nodes.

                          Instances For
                            def BinaryTree.instReprInternalData.repr {α✝ : Type u_1} {a✝ : Skeleton} [Repr α✝] :
                            InternalData α✝ a✝Std.Format
                            Instances For
                              @[implicit_reducible]
                              instance BinaryTree.instReprInternalData {α✝ : Type u_1} {a✝ : Skeleton} [Repr α✝] :
                              Repr (InternalData α✝ a✝)
                              inductive BinaryTree.FullData (α : Type u_1) :
                              SkeletonType u_1

                              A binary tree with values stored at both leaves and internal nodes.

                              Instances For
                                def BinaryTree.instReprFullData.repr {α✝ : Type u_1} {a✝ : Skeleton} [Repr α✝] :
                                FullData α✝ a✝Std.Format
                                Instances For
                                  @[implicit_reducible]
                                  instance BinaryTree.instReprFullData {α✝ : Type u_1} {a✝ : Skeleton} [Repr α✝] :
                                  Repr (FullData α✝ a✝)
                                  def BinaryTree.LeafData.leftSubtree {α : Type u_1} {s_left s_right : Skeleton} (tree : LeafData α (s_left.internal s_right)) :
                                  LeafData α s_left

                                  Get the left subtree of a LeafData.

                                  Instances For
                                    def BinaryTree.LeafData.rightSubtree {α : Type u_1} {s_left s_right : Skeleton} (tree : LeafData α (s_left.internal s_right)) :
                                    LeafData α s_right

                                    Get the right subtree of a LeafData.

                                    Instances For
                                      def BinaryTree.InternalData.leftSubtree {α : Type u_1} {s_left s_right : Skeleton} (tree : InternalData α (s_left.internal s_right)) :
                                      InternalData α s_left

                                      Get the left subtree of a InternalData.

                                      Instances For
                                        def BinaryTree.InternalData.rightSubtree {α : Type u_1} {s_left s_right : Skeleton} (tree : InternalData α (s_left.internal s_right)) :
                                        InternalData α s_right

                                        Get the right subtree of a InternalData.

                                        Instances For
                                          def BinaryTree.FullData.leftSubtree {α : Type u_1} {s_left s_right : Skeleton} (tree : FullData α (s_left.internal s_right)) :
                                          FullData α s_left

                                          Get the left subtree of a FullData.

                                          Instances For
                                            def BinaryTree.FullData.rightSubtree {α : Type u_1} {s_left s_right : Skeleton} (tree : FullData α (s_left.internal s_right)) :
                                            FullData α s_right

                                            Get the right subtree of a FullData.

                                            Instances For
                                              @[simp]
                                              theorem BinaryTree.LeafData.leftSubtree_internal {α : Type u_1} {s_left s_right : Skeleton} (left : LeafData α s_left) (right : LeafData α s_right) :
                                              (left.internal right).leftSubtree = left
                                              @[simp]
                                              theorem BinaryTree.LeafData.rightSubtree_internal {α : Type u_1} {s_left s_right : Skeleton} (left : LeafData α s_left) (right : LeafData α s_right) :
                                              (left.internal right).rightSubtree = right
                                              @[simp]
                                              theorem BinaryTree.InternalData.leftSubtree_internal {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : InternalData α s_left) (right : InternalData α s_right) :
                                              (internal value left right).leftSubtree = left
                                              @[simp]
                                              theorem BinaryTree.InternalData.rightSubtree_internal {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : InternalData α s_left) (right : InternalData α s_right) :
                                              (internal value left right).rightSubtree = right
                                              @[simp]
                                              theorem BinaryTree.FullData.leftSubtree_internal {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : FullData α s_left) (right : FullData α s_right) :
                                              (internal value left right).leftSubtree = left
                                              @[simp]
                                              theorem BinaryTree.FullData.rightSubtree_internal {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : FullData α s_left) (right : FullData α s_right) :
                                              (internal value left right).rightSubtree = right
                                              def BinaryTree.LeafData.get {s : Skeleton} {α : Type u_1} (tree : LeafData α s) (idx : SkeletonLeafIndex s) :
                                              α

                                              Get a value of a LeafData at an index.

                                              Instances For
                                                def BinaryTree.InternalData.get {s : Skeleton} {α : Type u_1} (tree : InternalData α s) (idx : SkeletonInternalIndex s) :
                                                α

                                                Get a value of a InternalData at an index.

                                                Instances For
                                                  def BinaryTree.FullData.get {s : Skeleton} {α : Type u_1} (tree : FullData α s) (idx : SkeletonNodeIndex s) :
                                                  α

                                                  Get a value of a FullData at an index.

                                                  Instances For
                                                    @[simp]
                                                    @[simp]
                                                    theorem BinaryTree.LeafData.get_ofLeft {s_left s_right : Skeleton} {α : Type u_1} (tree : LeafData α (s_left.internal s_right)) (idxLeft : SkeletonLeafIndex s_left) :
                                                    tree.get idxLeft.ofLeft = tree.leftSubtree.get idxLeft
                                                    @[simp]
                                                    theorem BinaryTree.LeafData.get_ofRight {s_left s_right : Skeleton} {α : Type u_1} (tree : LeafData α (s_left.internal s_right)) (idxRight : SkeletonLeafIndex s_right) :
                                                    tree.get idxRight.ofRight = tree.rightSubtree.get idxRight
                                                    @[simp]
                                                    theorem BinaryTree.LeafData.get_internal_ofLeft {α : Type u_1} {s_left s_right : Skeleton} (left : LeafData α s_left) (right : LeafData α s_right) (idxLeft : SkeletonLeafIndex s_left) :
                                                    (left.internal right).get idxLeft.ofLeft = left.get idxLeft
                                                    @[simp]
                                                    theorem BinaryTree.LeafData.get_internal_ofRight {α : Type u_1} {s_left s_right : Skeleton} (left : LeafData α s_left) (right : LeafData α s_right) (idxRight : SkeletonLeafIndex s_right) :
                                                    (left.internal right).get idxRight.ofRight = right.get idxRight
                                                    @[simp]
                                                    @[simp]
                                                    theorem BinaryTree.InternalData.get_internal {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : InternalData α s_left) (right : InternalData α s_right) :
                                                    @[simp]
                                                    theorem BinaryTree.InternalData.get_ofLeft {s_left s_right : Skeleton} {α : Type u_1} (tree : InternalData α (s_left.internal s_right)) (idxLeft : SkeletonInternalIndex s_left) :
                                                    tree.get idxLeft.ofLeft = tree.leftSubtree.get idxLeft
                                                    @[simp]
                                                    theorem BinaryTree.InternalData.get_ofRight {s_left s_right : Skeleton} {α : Type u_1} (tree : InternalData α (s_left.internal s_right)) (idxRight : SkeletonInternalIndex s_right) :
                                                    tree.get idxRight.ofRight = tree.rightSubtree.get idxRight
                                                    @[simp]
                                                    theorem BinaryTree.InternalData.get_internal_ofLeft {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : InternalData α s_left) (right : InternalData α s_right) (idxLeft : SkeletonInternalIndex s_left) :
                                                    (internal value left right).get idxLeft.ofLeft = left.get idxLeft
                                                    @[simp]
                                                    theorem BinaryTree.InternalData.get_internal_ofRight {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : InternalData α s_left) (right : InternalData α s_right) (idxRight : SkeletonInternalIndex s_right) :
                                                    (internal value left right).get idxRight.ofRight = right.get idxRight
                                                    @[simp]
                                                    theorem BinaryTree.FullData.get_internal {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : FullData α s_left) (right : FullData α s_right) :
                                                    (internal value left right).get SkeletonNodeIndex.ofInternal = value
                                                    @[simp]
                                                    theorem BinaryTree.FullData.get_ofLeft {s_left s_right : Skeleton} {α : Type u_1} (tree : FullData α (s_left.internal s_right)) (idxLeft : SkeletonNodeIndex s_left) :
                                                    tree.get idxLeft.ofLeft = tree.leftSubtree.get idxLeft
                                                    @[simp]
                                                    theorem BinaryTree.FullData.get_ofRight {s_left s_right : Skeleton} {α : Type u_1} (tree : FullData α (s_left.internal s_right)) (idxRight : SkeletonNodeIndex s_right) :
                                                    tree.get idxRight.ofRight = tree.rightSubtree.get idxRight
                                                    @[simp]
                                                    theorem BinaryTree.FullData.get_internal_ofLeft {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : FullData α s_left) (right : FullData α s_right) (idxLeft : SkeletonNodeIndex s_left) :
                                                    (internal value left right).get idxLeft.ofLeft = left.get idxLeft
                                                    @[simp]
                                                    theorem BinaryTree.FullData.get_internal_ofRight {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : FullData α s_left) (right : FullData α s_right) (idxRight : SkeletonNodeIndex s_right) :
                                                    (internal value left right).get idxRight.ofRight = right.get idxRight
                                                    def BinaryTree.FullData.toLeafData {α : Type u_1} {s : Skeleton} (tree : FullData α s) :

                                                    Convert a FullData to a LeafData by dropping the internal node values.

                                                    Instances For
                                                      def BinaryTree.FullData.toInternalData {α : Type u_1} {s : Skeleton} (tree : FullData α s) :

                                                      Convert a FullData to a InternalData by dropping the leaf node values.

                                                      Instances For
                                                        @[simp]
                                                        theorem BinaryTree.FullData.toLeafData_leftSubtree {α : Type u_1} {s_left s_right : Skeleton} (tree : FullData α (s_left.internal s_right)) :
                                                        @[simp]
                                                        theorem BinaryTree.FullData.toLeafData_rightSubtree {α : Type u_1} {s_left s_right : Skeleton} (tree : FullData α (s_left.internal s_right)) :
                                                        theorem BinaryTree.FullData.toLeafData_eq_leaf {α : Type u_1} (a : α) (tree : FullData α Skeleton.leaf) (h : LeafData.leaf a = tree.toLeafData) :
                                                        tree = leaf a
                                                        @[simp]
                                                        theorem BinaryTree.FullData.toLeafData_internal {α : Type u_1} {s_left s_right : Skeleton} (value : α) (left : FullData α s_left) (right : FullData α s_right) :
                                                        (internal value left right).toLeafData = left.toLeafData.internal right.toLeafData

                                                        Get the root index of a skeleton.

                                                        Instances For
                                                          def BinaryTree.FullData.getRootValue {s : Skeleton} {α : Type u_1} (tree : FullData α s) :
                                                          α

                                                          Get the root value of a FullData.

                                                          Instances For
                                                            @[simp]
                                                            theorem BinaryTree.FullData.getRootValue_leaf {α : Type u_1} (a : α) :
                                                            @[simp]
                                                            theorem BinaryTree.FullData.internal_getRootValue {s_left s_right : Skeleton} {α : Type u_1} (value : α) (left : FullData α s_left) (right : FullData α s_right) :
                                                            (internal value left right).getRootValue = value

                                                            Depth of a SkeletonLeafIndex

                                                            Instances For

                                                              Depth of a SkeletonInternalIndex

                                                              Instances For

                                                                Depth of a SkeletonNodeIndex

                                                                Instances For

                                                                  Get the parent of a SkeletonNodeIndex, or none if the index is the root.

                                                                  Instances For

                                                                    Return the sibling node index of a given SkeletonNodeIndex. Or none if the node is the root.

                                                                    Instances For

                                                                      Return the left child of a SkeletonNodeIndex, or none if the index is a leaf.

                                                                      Instances For

                                                                        Return the right child of a SkeletonNodeIndex, or none if the index is a leaf.

                                                                        Instances For

                                                                          Given a Skeleton, and a node index of the skeleton, return a list of node indices which are the ancestors of the node, starting with the root node, and going down to but not including the node itself.

                                                                          Instances For
                                                                            def BinaryTree.FullData.copath {α : Type u_1} {s : Skeleton} (cache_tree : FullData α s) :

                                                                            Find the siblings of a node and its ancestors, starting with the child of the root

                                                                            Instances For
                                                                              def BinaryTree.LeafData.map {α : Type u_1} {β : Type u_2} (f : αβ) {s : Skeleton} (tree : LeafData α s) :

                                                                              Apply a function to every value stored in a LeafData.

                                                                              Instances For
                                                                                def BinaryTree.InternalData.map {α : Type u_1} {β : Type u_2} (f : αβ) {s : Skeleton} (tree : InternalData α s) :

                                                                                Apply a function to every value stored in an InternalData.

                                                                                Instances For
                                                                                  def BinaryTree.FullData.map {α : Type u_1} {β : Type u_2} (f : αβ) {s : Skeleton} (tree : FullData α s) :

                                                                                  Apply a function to every value stored in a FullData.

                                                                                  Instances For
                                                                                    @[simp]
                                                                                    theorem BinaryTree.FullData.map_leaf {α : Type u_1} {β : Type u_2} (f : αβ) (a : α) :
                                                                                    map f (leaf a) = leaf (f a)
                                                                                    @[simp]
                                                                                    theorem BinaryTree.FullData.map_internal {α : Type u_1} {β : Type u_2} {s_left s_right : Skeleton} (f : αβ) (value : α) (left : FullData α s_left) (right : FullData α s_right) :
                                                                                    map f (internal value left right) = internal (f value) (map f left) (map f right)
                                                                                    @[simp]
                                                                                    theorem BinaryTree.LeafData.map_leaf {α : Type u_1} {β : Type u_2} (f : αβ) (a : α) :
                                                                                    map f (leaf a) = leaf (f a)
                                                                                    @[simp]
                                                                                    theorem BinaryTree.LeafData.map_internal {α : Type u_1} {β : Type u_2} {s_left s_right : Skeleton} (f : αβ) (left : LeafData α s_left) (right : LeafData α s_right) :
                                                                                    map f (left.internal right) = (map f left).internal (map f right)
                                                                                    theorem BinaryTree.FullData.map_getRootValue {α : Type u_1} {β : Type u_2} {s : Skeleton} (f : αβ) (tree : FullData α s) :
                                                                                    (map f tree).getRootValue = f tree.getRootValue

                                                                                    Building full trees #

                                                                                    Two dual constructions of FullData trees:

                                                                                    def BinaryTree.populateUp {α : Type u_1} {s : Skeleton} (leaf_data_tree : LeafData α s) (compose : ααα) :

                                                                                    Bottom-up tree population: fill internal nodes by composing child subtrees' root values via compose.

                                                                                    Instances For
                                                                                      @[simp]
                                                                                      theorem BinaryTree.populateUp_leaf {α : Type u_1} (a : α) (compose : ααα) :
                                                                                      @[simp]
                                                                                      theorem BinaryTree.populateUp_internal {α : Type u_1} {s_left s_right : Skeleton} (left : LeafData α s_left) (right : LeafData α s_right) (compose : ααα) :
                                                                                      populateUp (left.internal right) compose = FullData.internal (compose (populateUp left compose).getRootValue (populateUp right compose).getRootValue) (populateUp left compose) (populateUp right compose)
                                                                                      @[simp]
                                                                                      theorem BinaryTree.populateUp_getRootValue {α : Type u_1} {s_left s_right : Skeleton} (left : LeafData α s_left) (right : LeafData α s_right) (compose : ααα) :
                                                                                      (populateUp (left.internal right) compose).getRootValue = compose (populateUp left compose).getRootValue (populateUp right compose).getRootValue
                                                                                      def BinaryTree.populateDown {α : Type u_1} (s : Skeleton) (children : αα × α) (root : α) :

                                                                                      Top-down tree population: fill nodes by recursively expanding the input root into a pair of child values via children.

                                                                                      Instances For
                                                                                        @[simp]
                                                                                        theorem BinaryTree.populateDown_leaf {β : Type u_1} (children : ββ × β) (root : β) :

                                                                                        For a leaf skeleton, populateDown returns a single-leaf FullData carrying root.

                                                                                        @[simp]
                                                                                        theorem BinaryTree.populateDown_internal_def {β : Type u_1} {sl sr : Skeleton} (children : ββ × β) (root : β) :
                                                                                        populateDown (sl.internal sr) children root = FullData.internal root (populateDown sl children (children root).fst) (populateDown sr children (children root).snd)

                                                                                        For an internal skeleton, populateDown unfolds to a FullData.internal whose subtrees are recursively populated from the projections of children root.

                                                                                        This holds by rfl thanks to Prod-eta: the let ⟨l, r⟩ := children root in the definition is Prod.casesOn (children root) _, and Prod-eta makes Prod.casesOn p f = f p.1 p.2 definitional.

                                                                                        @[simp]
                                                                                        theorem BinaryTree.populateDown_getRootValue {β : Type u_1} {s : Skeleton} (children : ββ × β) (root : β) :
                                                                                        (populateDown s children root).getRootValue = root

                                                                                        The root value of populateDown is the input root.

                                                                                        theorem BinaryTree.populateDown_none_get_eq_none {α : Type u_1} {s : Skeleton} (children : Option αOption α × Option α) (h_none : children none = (none, none)) (idx : SkeletonNodeIndex s) :
                                                                                        (populateDown s children none).get idx = none

                                                                                        If children maps none ↦ (none, none), then populateDown started from none has value none at every node index.

                                                                                        def BinaryTree.Option.doubleBind {α β γ : Type u_1} (f : αβOption γ) (x : Option α) (y : Option β) :

                                                                                        Lift a binary function through two Option arguments.

                                                                                        Instances For
                                                                                          def BinaryTree.Option.bindPair {α : Type u_1} {β : Type u_2} (f : αOption (β × β)) (x : Option α) :

                                                                                          Lift a partial child-decomposition function α → Option (β × β) through Option, returning a pair of Options. If the input or f fails, both projections are none.

                                                                                          Instances For
                                                                                            @[simp]
                                                                                            theorem BinaryTree.Option.bindPair_some {α : Type u_1} {β : Type u_2} (f : αOption (β × β)) (a : α) :
                                                                                            @[simp]
                                                                                            theorem BinaryTree.Option.bindPair_none {α : Type u_1} {β : Type u_2} (f : αOption (β × β)) :
                                                                                            def BinaryTree.optionPopulateUp {α : Type u_1} {s : Skeleton} (leaf_data_tree : LeafData α s) (compose : ααOption α) :

                                                                                            Build a tree while allowing failures in the composition function.

                                                                                            Instances For
                                                                                              @[simp]
                                                                                              theorem BinaryTree.optionPopulateUp_leaf {α : Type u_1} (a : α) (compose : ααOption α) :
                                                                                              @[simp]
                                                                                              theorem BinaryTree.optionPopulateUp_internal {α : Type u_1} {s_left s_right : Skeleton} (left : LeafData α s_left) (right : LeafData α s_right) (compose : ααOption α) :
                                                                                              optionPopulateUp (left.internal right) compose = FullData.internal (Option.doubleBind compose (optionPopulateUp left compose).getRootValue (optionPopulateUp right compose).getRootValue) (optionPopulateUp left compose) (optionPopulateUp right compose)
                                                                                              def BinaryTree.optionPopulateDown {α : Type u_1} (s : Skeleton) (children : αOption (α × α)) (root : α) :

                                                                                              Build a tree top-down while allowing failures in the children-decomposition function. children may return none to indicate failure; failure propagates to all descendants in the corresponding subtree.

                                                                                              Instances For
                                                                                                @[simp]
                                                                                                theorem BinaryTree.optionPopulateDown_leaf {α : Type u_1} (children : αOption (α × α)) (root : α) :
                                                                                                @[simp]
                                                                                                theorem BinaryTree.optionPopulateDown_internal {α : Type u_1} {sl sr : Skeleton} (children : αOption (α × α)) (root : α) :
                                                                                                optionPopulateDown (sl.internal sr) children root = FullData.internal (some root) (populateDown sl (Option.bindPair children) (Option.map Prod.fst (children root))) (populateDown sr (Option.bindPair children) (Option.map Prod.snd (children root)))
                                                                                                @[simp]
                                                                                                theorem BinaryTree.optionPopulateDown_getRootValue {α : Type u_1} {s : Skeleton} (children : αOption (α × α)) (root : α) :
                                                                                                (optionPopulateDown s children root).getRootValue = some root