N-ary rooted trees (rose trees) #
An n-ary rooted tree (rose tree) over α: a distinguished root carrying a
value in α, and an ordered list of child subtrees. A leaf is node a [].
This is the n-ary generalization of BinaryTree (Mathlib.Data.Tree.Basic),
matching Haskell's Data.RoseTree (Node a [RoseTree a]).
The children are an ordered List, not a Multiset or a WType branching
family: List-valued children are positivity-clean, keep the type computable,
and give the ergonomic map/traversal API that the unordered (Multiset) and
WType encodings do not. The unordered rooted tree — the carrier of the
free pre-Lie algebra and the Connes–Kreimer Hopf algebra — is a quotient of
this type by child permutation, built downstream; it does not belong at this
data-structure layer.
The recursion principle #
The type is nested through List, so the auto-generated recursor hands a
per-List motive rather than a ∀ c ∈ children, motive c hypothesis. The
RoseTree.rec' eliminator (registered @[induction_eliminator]) packages the
(tree, list-of-trees) shape once, so downstream map/depth/numNodes
recurse and prove with a single List-shaped induction hypothesis instead of a
hand-written mutual block per operation.
Upstreaming #
Intended shape for the reserved Mathlib.Data.Tree n-ary Tree slot (freed by
mathlib's Tree → BinaryTree rename). Self-contained: no linguistics, no order/command
imports. Dependency-light, sorry-free, no noncomputable.
Equations
- instReprRoseTree = { reprPrec := instReprRoseTree.repr }
Projections #
The ordered list of child subtrees at the root.
Equations
- (RoseTree.node a a_1).children = a_1
Instances For
Decidable equality #
deriving DecidableEq does not fire through the nested List occurrence (still
true as of Lean v4.32.0-rc1), so the instance is built by mutual recursion on the
tree and its child list.
Decidable equality on trees (mutual with the child-list case).
Equations
- (RoseTree.node a cs).decEq (RoseTree.node b ds) = if hab : a = b then match RoseTree.decEqList cs ds with | isTrue h => isTrue ⋯ | isFalse h => isFalse ⋯ else isFalse ⋯
Instances For
Decidable equality on child lists (mutual with the tree case).
Equations
- One or more equations did not get rendered due to their size.
- RoseTree.decEqList [] [] = isTrue ⋯
- RoseTree.decEqList [] (head :: tail) = isFalse ⋯
- RoseTree.decEqList (head :: tail) [] = isFalse ⋯
Instances For
Equations
Size #
The recursion principle #
Structural induction for RoseTree: to prove motive t for all t, prove
it for node a cs given motive c for every child c ∈ cs. Packages the
nested-List recursion so downstream defs/proofs use a single List-shaped
hypothesis.
Equations
- RoseTree.rec' node (RoseTree.node a a_1) = node a a_1 fun (c : RoseTree α) (_hc : c ∈ a_1) => RoseTree.rec' node c
Instances For
Catamorphism #
fold f is the workhorse: every structural operation (map, numNodes,
height, …) is a one-line fold specialization, and their reduction lemmas fall
out of fold_node.
Catamorphism: replace each node a cs by f a (folded children).
Equations
- RoseTree.fold f (RoseTree.node a a_1) = f a (RoseTree.foldList f a_1)
Instances For
Auxiliary: fold across a list of children.
Equations
- RoseTree.foldList f [] = []
- RoseTree.foldList f (c :: cs) = RoseTree.fold f c :: RoseTree.foldList f cs
Instances For
Functoriality #
Relabel every node by f, preserving shape.
Equations
- RoseTree.map f = RoseTree.fold fun (a : α) (cs : List (RoseTree β)) => RoseTree.node (f a) cs
Instances For
Traversal #
Effectful traversal: act on the root, then the children left-to-right — the
Traversable action for RoseTree.
Traverse a tree with an applicative action, root then children in order.
Equations
- RoseTree.traverse f (RoseTree.node a a_1) = RoseTree.node <$> f a <*> RoseTree.traverseList f a_1
Instances For
Auxiliary: traverse a list of child subtrees.
Equations
- RoseTree.traverseList f [] = pure []
- RoseTree.traverseList f (c :: cs) = (fun (x1 : RoseTree β) (x2 : List (RoseTree β)) => x1 :: x2) <$> RoseTree.traverse f c <*> RoseTree.traverseList f cs
Instances For
Counting #
The total number of nodes (vertices). A leaf counts as 1.
Equations
- RoseTree.numNodes = RoseTree.fold fun (x : α) (ns : List ℕ) => 1 + ns.sum
Instances For
The number of leaves (childless nodes). A single leaf counts as 1.
Equations
- RoseTree.numLeaves = RoseTree.fold fun (x : α) (ns : List ℕ) => 1 ⊔ ns.sum
Instances For
The (offset, size) leaf-spans of the nodes satisfying p, in
left-to-right traversal order: the offset counts leaves strictly to
the node's left, the size its own leaves.
Equations
- RoseTree.spansOf p t = RoseTree.spansOf.go p 0 t
Instances For
Equations
- RoseTree.spansOf.go p off (RoseTree.node a a_1) = (if p a = true then [(off, (RoseTree.node a a_1).numLeaves)] else []) ++ RoseTree.spansOf.goList p off a_1
Instances For
Equations
- RoseTree.spansOf.goList p off [] = []
- RoseTree.spansOf.goList p off (c :: cs) = RoseTree.spansOf.go p off c ++ RoseTree.spansOf.goList p (off + c.numLeaves) cs
Instances For
The height (length of the longest root-to-leaf path in edges): a leaf has
height 0, an internal node is one more than the maximum child height.
Equations
- RoseTree.height = RoseTree.fold fun (x : α) (ds : List ℕ) => List.foldr max 0 (List.map (fun (x : ℕ) => x + 1) ds)
Instances For
Arity and the leaf test #
Smart constructors #
A binary node: two ordered children.
Equations
- RoseTree.binary a l r = RoseTree.node a [l, r]
Instances For
depth — longest root-to-leaf path in vertices #
depth = height + 1: a leaf has depth 1 (height counts edges, depth
counts the vertices on the longest path).
Equations
- RoseTree.depth = RoseTree.fold fun (x : α) (ds : List ℕ) => 1 + List.foldr max 0 ds
Instances For
Instances #
Equations
- RoseTree.instInhabited = { default := RoseTree.leaf default }