Documentation

Mathlib.Combinatorics.Enumerative.DyckWord

Dyck words #

A Dyck word is a sequence consisting of an equal number n of symbols of two types such that for all prefixes one symbol occurs at least as many times as the other. If the symbols are ( and ) the latter restriction is equivalent to balanced brackets; if they are U = (1, 1) and D = (1, -1) the sequence is a lattice path from (0, 0) to (0, 2n) and the restriction requires the path to never go below the x-axis.

This file defines Dyck words and constructs their bijection with rooted binary trees, one consequence being that the number of Dyck words with length 2 * n is catalan n.

Main definitions #

Main results #

Implementation notes #

While any two-valued type could have been used for DyckStep, a new enumerated type is used here to emphasise that the definition of a Dyck word does not depend on that underlying type.

inductive DyckStep :

A DyckStep is either U or D, corresponding to ( and ) respectively.

Instances For
    @[implicit_reducible]
    instance instInhabitedDyckStep :
    Inhabited DyckStep
    @[implicit_reducible]
    instance instDecidableEqDyckStep :
    DecidableEq DyckStep
    theorem DyckStep.dichotomy (s : DyckStep) :
    s = U โˆจ s = D

    Named in analogy to Bool.dichotomy.

    structure DyckWord :

    A Dyck word is a list of DyckSteps with as many Us as Ds and with every prefix having at least as many Us as Ds.

    • toList : List DyckStep

      The underlying list

    • count_U_eq_count_D : List.count DyckStep.U โ†‘self = List.count DyckStep.D โ†‘self

      There are as many Us as Ds

    • count_D_le_count_U (i : โ„•) : List.count DyckStep.D (List.take i โ†‘self) โ‰ค List.count DyckStep.U (List.take i โ†‘self)

      Each prefix has at least as many Us as Ds

    Instances For
      theorem DyckWord.ext {x y : DyckWord} (toList : โ†‘x = โ†‘y) :
      x = y
      theorem DyckWord.ext_iff {x y : DyckWord} :
      x = y โ†” โ†‘x = โ†‘y
      def instDecidableEqDyckWord.decEq (xโœ xโœยน : DyckWord) :
      Decidable (xโœ = xโœยน)
      Instances For
        @[implicit_reducible]
        instance instDecidableEqDyckWord :
        DecidableEq DyckWord
        @[implicit_reducible]
        @[implicit_reducible]
        @[implicit_reducible]
        instance instZeroDyckWord :
        @[implicit_reducible]

        Dyck words form an additive cancellative monoid under concatenation, with the empty word as 0.

        theorem DyckWord.toList_eq_nil {p : DyckWord} :
        โ†‘p = [] โ†” p = 0
        theorem DyckWord.toList_ne_nil {p : DyckWord} :
        โ†‘p โ‰  [] โ†” p โ‰  0
        @[implicit_reducible]

        The only Dyck word that is an additive unit is the empty word.

        theorem DyckWord.head_eq_U (p : DyckWord) (h : โ†‘p โ‰  []) :
        (โ†‘p).head h = DyckStep.U

        The first element of a nonempty Dyck word is U.

        theorem DyckWord.getLast_eq_D (p : DyckWord) (h : โ†‘p โ‰  []) :
        (โ†‘p).getLast h = DyckStep.D

        The last element of a nonempty Dyck word is D.

        theorem DyckWord.cons_tail_dropLast_concat {p : DyckWord} (h : p โ‰  0) :
        DyckStep.U :: (โ†‘p).dropLast.tail ++ [DyckStep.D] = โ†‘p
        def DyckWord.take (p : DyckWord) (i : โ„•) (hi : List.count DyckStep.U (List.take i โ†‘p) = List.count DyckStep.D (List.take i โ†‘p)) :

        Prefix of a Dyck word as a Dyck word, given that the count of Us and Ds in it are equal.

        Instances For
          def DyckWord.drop (p : DyckWord) (i : โ„•) (hi : List.count DyckStep.U (List.take i โ†‘p) = List.count DyckStep.D (List.take i โ†‘p)) :

          Suffix of a Dyck word as a Dyck word, given that the count of Us and Ds in the prefix are equal.

          Instances For

            Nest p in one pair of brackets, i.e. x becomes (x).

            Instances For
              @[simp]
              theorem DyckWord.nest_ne_zero {p : DyckWord} :
              p.nest โ‰  0

              A property stating that p is nonempty and strictly positive in its interior, i.e. is of the form (x) with x a Dyck word.

              Instances For

                Denest p, i.e. (x) becomes x, given that p.IsNested.

                Instances For
                  def DyckWord.semilength (p : DyckWord) :
                  โ„•

                  The semilength of a Dyck word is half of the number of DyckSteps in it, or equivalently its number of Us.

                  Instances For
                    @[simp]
                    theorem DyckWord.two_mul_semilength_eq_length {p : DyckWord} :
                    2 * p.semilength = (โ†‘p).length
                    def DyckWord.firstReturn (p : DyckWord) :
                    โ„•

                    p.firstReturn is 0 if p = 0 and the index of the D matching the initial U otherwise.

                    Instances For
                      theorem DyckWord.firstReturn_lt_length {p : DyckWord} (h : p โ‰  0) :
                      p.firstReturn < (โ†‘p).length
                      theorem DyckWord.count_take_firstReturn_add_one {p : DyckWord} (h : p โ‰  0) :
                      List.count DyckStep.U (List.take (p.firstReturn + 1) โ†‘p) = List.count DyckStep.D (List.take (p.firstReturn + 1) โ†‘p)
                      theorem DyckWord.count_D_lt_count_U_of_lt_firstReturn {p : DyckWord} {i : โ„•} (hi : i < p.firstReturn) :
                      List.count DyckStep.D (List.take (i + 1) โ†‘p) < List.count DyckStep.U (List.take (i + 1) โ†‘p)
                      @[simp]
                      theorem DyckWord.firstReturn_add {p q : DyckWord} :
                      (p + q).firstReturn = if p = 0 then q.firstReturn else p.firstReturn
                      @[simp]
                      theorem DyckWord.firstReturn_nest {p : DyckWord} :
                      p.nest.firstReturn = (โ†‘p).length + 1

                      The left part of the Dyck word decomposition, inside the U, D pair that firstReturn refers to. insidePart 0 = 0.

                      Instances For

                        The right part of the Dyck word decomposition, outside the U, D pair that firstReturn refers to. outsidePart 0 = 0.

                        Instances For
                          @[simp]
                          theorem DyckWord.insidePart_add {p q : DyckWord} (h : p โ‰  0) :
                          @[simp]
                          theorem DyckWord.outsidePart_add {p q : DyckWord} (h : p โ‰  0) :
                          (p + q).outsidePart = p.outsidePart + q
                          @[irreducible]
                          theorem DyckWord.le_add_self (p q : DyckWord) :
                          q โ‰ค p + q
                          theorem DyckWord.infix_of_le {p q : DyckWord} (h : p โ‰ค q) :
                          โ†‘p <:+: โ†‘q
                          theorem DyckWord.le_of_suffix {p q : DyckWord} (h : โ†‘p <:+ โ†‘q) :
                          @[implicit_reducible]

                          Partial order on Dyck words: p โ‰ค q if a (possibly empty) sequence of insidePart and outsidePart operations can turn q into p.

                          theorem DyckWord.pos_iff_ne_zero {p : DyckWord} :
                          0 < p โ†” p โ‰  0
                          @[irreducible]
                          def DyckWord.toTree (p : DyckWord) :
                          Tree Unit

                          Convert a Dyck word to a binary rooted tree.

                          f(0) = nil. For a nonzero word find the D that matches the initial U, which has index p.firstReturn, then let x be everything strictly between said U and D, and y be everything strictly after said D. p = x.nest + y with x, y (possibly empty) Dyck words. f(p) = f(x) โ–ณ f(y), where โ–ณ (defined in Mathlib/Data/Tree/Basic.lean) joins two subtrees to a new root node.

                          Instances For
                            def DyckWord.ofTree :
                            Tree Unit โ†’ DyckWord

                            Convert a binary rooted tree to a Dyck word.

                            g(nil) = 0. A nonempty tree with left subtree l and right subtree r is sent to g(l).nest + g(r).

                            Instances For

                              Equivalence between Dyck words and rooted binary trees.

                              Instances For
                                @[simp]
                                theorem DyckWord.equivTree_symm_apply (aโœ : Tree Unit) :
                                equivTree.symm aโœ = ofTree aโœ
                                @[deprecated DyckWord.numNodes_toTree (since := "2026-02-03")]

                                Alias of DyckWord.numNodes_toTree.

                                def DyckWord.equivTreesOfNumNodesEq (n : โ„•) :
                                { p : DyckWord // p.semilength = n } โ‰ƒ โ†ฅ(Tree.treesOfNumNodesEq n)

                                Equivalence between Dyck words of semilength n and rooted binary trees with n internal nodes.

                                Instances For
                                  @[simp]
                                  theorem DyckWord.equivTreesOfNumNodesEq_apply_coe (n : โ„•) (a : { a : DyckWord // (fun (p : DyckWord) => p.semilength = n) a }) :
                                  โ†‘((equivTreesOfNumNodesEq n) a) = (โ†‘a).toTree
                                  @[implicit_reducible]

                                  There are catalan n Dyck words of semilength n (or length 2 * n).

                                  Extension for the positivity tactic: p.firstReturn is positive if p is nonzero.

                                  Instances For