{"id":855,"date":"2010-04-26T10:47:53","date_gmt":"2010-04-26T10:47:53","guid":{"rendered":"http:\/\/scientopia.org\/blogs\/goodmath\/2010\/04\/26\/finger-trees-done-right-i-hope\/"},"modified":"2010-04-26T10:47:53","modified_gmt":"2010-04-26T10:47:53","slug":"finger-trees-done-right-i-hope","status":"publish","type":"post","link":"http:\/\/www.goodmath.org\/blog\/2010\/04\/26\/finger-trees-done-right-i-hope\/","title":{"rendered":"Finger Trees Done Right (I hope)"},"content":{"rendered":"<p> A while ago, I wrote a <a href=\"http:\/\/scienceblogs.com\/goodmath\/2009\/05\/finally_finger_trees.php\">couple of posts<\/a> that claimed to talk about finger trees. Unfortunately, I really botched it. I&#8217;d read a bunch of data structure papers, and managed to get myself thoroughly scrambled. What I wrote about was <em>distantly<\/em> related to finger trees, and it was useful to help understand how fingertrees work &#8211; but it was not, in any way, shape, or form, actually a description of fingertrees. Since then, I&#8217;ve been meaning to write a proper post explaining finger trees &#8211; but with the work on my book, and with chaos at work, I just haven&#8217;t had the time. This time, in order to do my damnedest to make sure that I don&#8217;t screw it up again, I&#8217;m basically go to describe finger trees over a couple of posts by walking through the best finger-tree paper that I could find. The paper is <a href=\"http:\/\/www.soi.city.ac.uk\/~ross\/papers\/FingerTree.html\">&#8220;Finger Trees: a simple general-purpose data structure&#8221;, by Ralf Hinze and Ross Patterson.<\/a>  This <em>might<\/em> by the paper that introduced the structure, but I&#8217;m not sure.<\/p>\n<p> The point of finger trees is pretty simple. It&#8217;s very similar to the point of zippers. Programming in functional languages is terrific. As I&#8217;ve described before, there are a lot of advantages to writing functional code. But there are also a lot of places where a naive implementation of an algorithm using a functional data structure is dreadfully inefficient. Functional code may be prettier, more maintainable, and more reusable &#8211; but imperative code is frequently much more efficient. When you&#8217;re doing an operation that, conceptually, modifies a piece of a complex data structure, then functional code can really suck. Finger trees give you a way around that &#8211; for many common updatabale data structures, you can build finger-tree versions that are very close to or fully as good as imperative, updating structures.<\/p>\n<p><!--more--><\/p>\n<p> For example, imagine some numerical code that operates on an array. If you use a naive array, and you update one element of the array, then in a functional program, you need to copy <em>the entire array<\/em> to create a new array with the modified element. Of course, in a real program, you probably wouldn&#8217;t do anything quite that naive &#8211; you can usually find some way of representing things to reduce the cost (or you can use a compiler that can do copy-to-update transformations when it&#8217;s possible). But you are, at least conceptually, stuck making copies of <em>something<\/em> in order to do a functional update. So what you want to do is to minimize that cost.<\/p>\n<p> The simplest example of that is something like a gap buffer. To modify something at the gap, you only need to copy a couple of pointers, and you can re-use the vast majority of your original structure. Similarly, in a zipper, you can modify something at the focal point of the zipper very quickly, and you can move the focus reasonably quickly. What a finger tree does is generalize the concept of zipper, so that you end up with a structure that&#8217;s sort of like a tree with multiple zippers. It&#8217;s almost like taking a zipper &#8211; which splits a tree into (left, path-to-current, right) &#8211; and then taking the left and right subtrees, and turning <em>them<\/em> into zippers; and taking <em>their<\/em> left and right subtrees, and turning <em>them<\/em> into zippers. You end up with a structure where you&#8217;ve got multiple pointers into the depths of the tree &#8211; and you can modify the tree just by modifying the zipper-like structure <em>closest<\/em> to the value you want to update.<\/p>\n<p> To explain a finger tree in detail is pretty complicated. The structure itself isn&#8217;t that bad &#8211; but to understand <em>why<\/em> it has the performance characteristics that it does is rather tricky. It relies on the fact that certain operations are done lazily &#8211; and that the lazy evaluation guarantees that no more than a certain amount of time will need to be spent on any structure change.<\/p>\n<p> But we&#8217;ll come back to that later. Let&#8217;s start by looking at a simple structure, which we&#8217;ll gradually turn into a finger tree. To start off with, we want to represent a sequence. We can do that with a 2\/3 tree like the following Haskell code.<\/p>\n<pre>\ndata Tree t = Zero t | Succ (Tree (Node t))\ndata Node s = Node2 s s | Node3 s s s\n<\/pre>\n<p> Just this initial code is a bit tricky, so I&#8217;ll explain it in a bit of detail. It&#8217;s creating a 2-3 tree that has a semi-numerical structure to it. Each level of the tree is basically a distinct type. In this tree, a node is an <em>internal<\/em> non-leaf node of the tree. Only leafs contain values; internal nodes are just for maintaining structure.<\/p>\n<p> The trick to understanding this is in the definition of <code>Tree a<\/code>. What this does is effectively create a <em>distinct type<\/em> for each tree level. A leaf of a tree of ts is a value of type t, wrapped by the <code>Zero<\/code> constructor. So a leaf node of a tree of integers could be <code>Zero 23<\/code>. A level one node of a <code>Tree t<\/code> is a value of type <code>Tree (Node t)<\/code> wrapped by the <code>Succ<\/code> constructor &#8212; so, for example, it could be <code>Succ( Zero (Node2 23 34))<\/code><\/p>\n<p>. A level two node is a value of type <code>Tree (Node (Tree (Node tt))<\/code> &#8211; like <code>Succ((Zero(Node2 (Succ (Zero (Node2 23 32))) (Succ (Zero (Node2 43 45))))))<\/code>.<\/p>\n<p> Just this much is interesting: the type itself is expressing one of the structural constraints of the tree! 2-3 trees are always balanced &#8211; the length of the path from root to leaf is always the same for all nodes. This type declaration means that it&#8217;s a <em>type error<\/em> to have a level 1 tree be a child of a level 3 tree.<\/p>\n<p> And we haven&#8217;t even gotten to the finger tree yet.<\/p>\n<p> To get to a finger tree, we need to know what a <em>finger<\/em> is. A finger is a structure that provides a reference to a node deep in a tree. In a zipper, the zipper-path is a finger: it&#8217;s a trail down the tree that gives you fast access to a particular node of the tree, and which you can then use for fast updates or traversals of the nodes around the finger. A finger tree is a tree structure of fingers superimposed on a conventional balanced tree. In general, the internal nodes of the tree are annotated with monoids (a la my botched original article) in order to help you find the part of the tree that you want.<\/p>\n<p> Before we get to anything like a type declaration, let&#8217;s look at a picture. Here&#8217;s a 2-3 tree of integers.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_418.png?resize=465%2C171\" width=\"465\" height=\"171\" alt=\"initialtwothree.png\" \/><\/p>\n<p> To make it a finger tree, what you do, in theory, is reach down to the leftmost and and rightmost internal nodes of the tree &#8211; in our case, the parents of (1,2) and (8, 9, 10). Then you <em>pick up<\/em> the tree by those two nodes, and let the rest dangle down. Like this:<\/p>\n<div><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_419.png?resize=334%2C236\" width=\"334\" height=\"236\" alt=\"finger-dangled.png\" \/><\/div>\n<p> If you look at this, you&#8217;ve got a finger for the node (1,2), and a finger for the node (8,9,10). In between them, you&#8217;ve got a bunch of stuff dangling. But if you look at the dangling stuff: it&#8217;s a finger tree. It&#8217;s got a finger for its leftmost internal node, and a finger for its rightmost internal node, and in between, it&#8217;s got some stuff dangling. <em>That<\/em> stuff is, in this case, an empty finger tree.<\/p>\n<p> That&#8217;s basically the structure of a finger tree: you&#8217;ve got fingers for the head and tail parts of the tree; and you&#8217;ve got a finger tree in between them.<\/p>\n<p> When you make the tree a bit bigger, you can see some more of the basic properties of the tree. Take this tree:<\/p>\n<div><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_420.png?resize=588%2C393\" width=\"588\" height=\"393\" alt=\"big-twothree.png\" \/><\/div>\n<p> And dangle it:<\/p>\n<div><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_421.png?resize=507%2C360\" width=\"507\" height=\"360\" alt=\"dangled-big.png\" \/><\/div>\n<p> The finger on the outermost tree contains only leaf nodes. The finger on the next inner tree contains level one nodes. And the next one contains level 2 nodes. And so on. You&#8217;ve got fingers for the first and last nodes at each depth level of the tree. the tree.<\/p>\n<p> We&#8217;ll see a bit more about that as we look at how to build some structures with a finger tree. We&#8217;ll start by looking at a generic finger tree. It&#8217;s roughly what you would expect from the description above; a finger tree is either a single node; a <em>empty<\/em> node (like the root in the dangled tree above); or a left finger, a right finger, and a finger tree in between. It&#8217;s built on the 2-3 tree, so we&#8217;ll reuse the <code>Node<\/code> type from that.<\/p>\n<pre>\ndata FingerTree a = Empty\n|  Single a\n| Deep (Digit a) (FingerTree (Node a)) (Digit a)\ndata Digit a = [ a ]\n<\/pre>\n<p> The <code>Digit<\/code> type is new. I&#8217;d prefer to call it a finger, but Hinze and Paterson use <code>Digit<\/code>. The <code>Digit<\/code> is basically a buffer that contains the members of the node of the 2\/3 tree that was replaced by the digit. The reason that we make that switch &#8211; from a node using a constructor with fixed numbers of children is because we&#8217;re going to cheat. You&#8217;ll see the details later.<\/p>\n<p> An important thing to notice here: this uses the same node type trick as the two-three tree: each time you shift to a deeper level of finger tree, the <em>type<\/em> of the arguments to <code>FingerTree<\/code> enforce the idea that the fingers on each level point to higher-order nodes. At the top of the finger tree, we&#8217;ve got a digit of <code>a<\/code>s; at the next level, we&#8217;ve got digits of <code>Node a<\/code>s; climbing down the central spine, the third level has digits of <code>Node<sup>2<\/sup> a<\/code>s; in general, at level N+1, the digits contain <code>Node<sup>N<\/sup> a<\/code>s. <\/p>\n<p> We&#8217;ve already got enough to start seeing a bit of what makes the tree interesting. Suppose we want to find the value at position x. Even though the number of nodes per branch is variable, we still know that the value as position x will be either in, or very close to, the nodes at level lg(x).<\/p>\n<p> To actually write the implementation, we need a bunch of general reduction operations. In Haskell, the whole idea of reduction is really deeply engraved in the language &#8211; so there&#8217;s a type-class for defining reduction. So what we do is just provide the instance definition of reductions that we&#8217;ll need for the tree.<\/p>\n<p> We&#8217;ll start with nodes:<\/p>\n<pre>\ninstance Reduce Node where\nreducer (-&lt;) (Node2 a b) z = a -&lt; (b -&lt; z)\nreducer (-&lt;) (Node3 a b c) z = a -&lt; (b -&lt; (c -&lt; z))\nreducer (&gt;-) (Node2 b a) = (z &gt;- b) &gt;- a\nreducer (&gt;-) (Node3 c b a) = ((z &gt;- c) &gt;- b) &gt;- a\n<\/pre>\n<p> Reduction on trees is kind of strange until you wrap your head around it. It uses a technique called <em>lifting<\/em>. Basically, reduction on a tree is based on reduction on a node &#8211; the operation of reducing the tree is, in fact, doing a reduce on the tree using reduce on a node as the reduction operation:<\/p>\n<pre>\ninstance Reduce FingerTree where\nreducer (-&lt;) Empty zero = zero\nreducer (-&lt;) (Single x) zero = x -&lt; zero\nreducer (-&lt;) Deep left mid right zero = left -&lt;' (mid -&lt;'' (right -&lt;' zero))\nwhere (-&lt;') = reducer (-&lt;)\n(-&lt;'') = reducer (reducer (-&lt;))\nreducel (&gt;-) zero Empty = zero\nreducel (&gt;-) zero (Single x) = zero &gt;- x\nreducel (&gt;-) zero (Deep left mid right) = ((zero &gt;-' left) &gt;-'' mid) &gt;-' right\nwhere (&gt;-') = reducel (&gt;-)\n(&gt;-'') = reducel (reducel (&gt;-))\n<\/pre>\n<p> It&#8217;s a bit subtle &#8211; but it does really make sense. A finger tree is a collection of nodes on the left, a tree of nodes in the middle, and a collection of nodes on the right. So to reduce a finger tree, you need to reduce the left-hand nodes to a list of values, and then reduce those values &#8211; in other words, do a reduction of the list of nodes using the reduction of nodes as the operator. And so on. Trace it through, and it makes a lot of sense. (But I would probably never have come up with it myself.)<\/p>\n<p> Now, let&#8217;s get a look at an example of something you can do with a finger tree that shows it off. We&#8217;re going to build a double-ended queue. Building a single-ended queue is easy; it&#8217;s no less efficient in a functional language than in an imperative one. But a double-ended queue is a problem. Traditional implementations would end up with roughly O(lg n) time. With a finger tree, we can get amortized time of O(1) &#8211; the same performance as imperative. That&#8217;s damned impressive!<\/p>\n<p> So how does it work? Well, for starters, we need to munge the structure a bit. Theoretically, our finger-tree is based on a 2-3 tree. But if we always perfectly maintained the two-three tree structure, we wouldn&#8217;t be able to get the performance we want. That&#8217;s why our definition of <code>Digit<\/code> is a list of values: sometimes we&#8217;ll temporarily have either one or four in a digit. We&#8217;ll always eventually restore the proper 2-3 tree, but sometimes we&#8217;ll defer it for a while.<\/p>\n<p> Let&#8217;s consider the push operations. In a deque, there are two different push operations; one to push at the front, and one at the back. In the H&amp;P paper, they define these using some pretty symbols; but it&#8217;s much easier for me to type text than symbols in HTML, so I&#8217;m going to name them push_front and push_back:<\/p>\n<pre>\npush_front :: a -&gt; FingerTree a -&gt; FingerTree a\npush_front a Empty = Single a\npush_front a (Single b) = Deep [a] Empty [b]\npush_front a (Deep [b, c, d, e] mid right) = Deep [a, b] (push_front (Node3 c d e) mid) right\npush_front (Deep left mid right) a = Deep ([a] ++ left) mid right\n<\/pre>\n<p> So far, this is pretty simple:<\/p>\n<ol>\n<li> Push onto an empty queue-tree, and you get a tree with a single value.<\/li>\n<li> Push onto a tree with one value, and you get a finger tree where the left finger is the first value, the right finger is the second value, and in between them is an empty tree.<\/li>\n<li> Push into a non-empty finger tree with a four-value left digit, and you fix the two-three structure; you take one value from the four in the left digit, and join it with the new entry; take the remaining three, and turn them into a node; then insert the new node into the finger tree in the center.<\/li>\n<\/ol>\n<p> To do a push at the end, you just take the mirror image of the code above:<\/p>\n<pre>\npush_back :: FingerTree a -&gt; a -&gt; FingerTree a\npush_back Empty a = Single a\npush_back (Single b) a = Deep [b] Empty [a]\npush_back (Deep left mid [e, d, c, b]) a = Deep left (push_back mid (Node3 e d c)) [b,a]\npush_back left mid right = Deep left mid (right ++ [a])\n<\/pre>\n<p> This is really beautiful. We&#8217;re working with this remarkably clever structure &#8211; and yet an insert operation isn&#8217;t any more complicated than the insert on a simpler structure.<\/p>\n<p> So we can push things into trees. We need to be able to create deques from lists of values; and we need to be able to remove values from the trees. To build those operations, we need a bit of infrastructure. In particular, we need lifted versions of the push operations.<\/p>\n<pre>\npush_front' :: (Reduce f) =&gt; f a -&gt; FingerTree a -&gt; FingerTree a\npush_front' = reducer push_front\npush_back' :: (Reduce  f) =&gt; FingerTree a -&gt; f a -&gt; FingerTree a\npush_back' = reducel push_back\n<\/pre>\n<p> With these lifted operators, we can create a finger tree from <em>any<\/em> reducable collection. Basically, you can create a finger tree based on a reduction operator by just lifting that operator with push-front.<\/p>\n<pre>\ntoTree :: (Reduce f) =&gt; f a -&gt; FingerTree a\ntoTree s = push_front' s Empty\n<\/pre>\n<p> Now we&#8217;re getting to the point where things start to get tricky. This isn&#8217;t the way that I would have written it &#8211; but they provide a way of defining a <em>view<\/em> of the fingertree as a list. That lets you take the fingertree, and call a function which can <em>lazily<\/em> convert your tree into a traditional cons-list:<\/p>\n<pre>\ndata LeftView s a = LeftNil | LeftCons a (s a)\nleftView :: FingerTree a -&gt; LeftView FingerTree a\nleftView Empty = LeftNil\nleftView (Single x) = LeftCons x Empty\nleftView (Deep left mid right) = LeftCons (head left) (leftDeep (tail prefix) mid right)\n<\/pre>\n<p> Now, you&#8217;ll notice that in the last, recursive case of <code>leftView<\/code>, we call &#8220;<code>leftDeep<\/code>&#8220;. Normally, in code like this, that would be the &#8220;<code>Deep<\/code>&#8221; constructor of the fingertree. But sometimes, there will be only one element in the left digit; so when we use <code>leftView<\/code>, it will invoke the constructor with the tail of the left digit, which is empty. That&#8217;s not valid in the <code>Deep<\/code> constructor of the finger tree. But instead of making the cases of <code>leftView<\/code> more complicated, we provide a pseudo-constructor:<\/p>\n<pre>\nleftDeep :: [a] -&gt; FingerTree (Node a) -&gt; Digit a -&gt; FingerTree a\nleftDeep [] mid right = case leftView mid of\nLeftNil -&gt; toTree right\nLeftCons a mid' = Deep (toList a) mid' right\nleftDeep left mid right = Deep left mid right\n<\/pre>\n<p> You can create a <code>rightView<\/code> by mirroring <code>leftView<\/code> and <code>leftDeep<\/code> exactly the way we mirrored <code>push_front<\/code>  from <code>push_back<\/code>.<\/p>\n<p> These view functions are the heart of how we do pops from the deque. They do all of the work:<\/p>\n<ul>\n<li> To check if the deque is empty, compare its <code>leftView<\/code> to <code>LeftNil<\/code>.<\/li>\n<li> To retrieve the front element, take its <code>leftView<\/code> and extract the first parameter of <code>LeftCons<\/code>.<\/li>\n<li> To get the deque with the first element removed, take its <code>leftView<\/code>, and extract the second parameter of its <code>LeftCons<\/code>.<\/li>\n<\/ul>\n<p> (And, of course, mirror all of the above for the right-end of the deque.)<\/p>\n<p> To make things simpler, you can provide functions for all of this:<\/p>\n<pre>\nleft_peek :: FingerTree a -&gt; Maybe a\nleft_peek tree = case (leftView tree) of\nLeftNil -&gt; Nothing\nLeftCons h t -&gt; Just h\nleft_pop :: FingerTree a -&gt; FingerTree a\nleft_pop tree = case (leftView tree) of\nLeftNil -&gt; LeftNil\nLeftCons h t -&gt; t\n<\/pre>\n<p> In terms of performance, there is a bit of subtlety to this. First and most importantly, you need to remember laziness. When you call <code>leftView<\/code>, <em>nothing<\/em> is actually calculated until you extract and use the values. So while when you look at the code, you see the full construction of the remainder deque whenever you peek at the head or tail of the deque, it <em>doesn&#8217;t<\/em> happen until you use it. And even then, it doesn&#8217;t do the whole thing: it does <em>just enough<\/em> to give you the values that you need.<\/p>\n<p> To get the O(1) bound, you need to do an amortized analysis. I&#8217;m not going to go into depth about it &#8211; check the paper if you&#8217;re interested. But most of the time, there&#8217;s a non-empty list in the finger. In those cases, it&#8217;s obviously O(1). Less frequently, you need to do a bit of shuffling between the finger and the middle tree. Even less frequently &#8211; less frequently by a factor of O(lg N), while doing the shuffling on the first inner tree, you&#8217;ll need to do some shuffling on the next inner tree. And so on. Each level of shuffling becomes less likely by a factor of O(lg N). That O(lg n) factor works out as an implication of the structure of the tree: the fingers on each inner tree get bigger, making it less likely that you&#8217;ll need to push further into the tree. <\/p>\n<p> Anyway &#8211; this is getting long and complicated, so I&#8217;m going to end this post here. Next, we&#8217;ll look at more structures that you can build using finger trees as a foundation. Just from this much, the finger tree is incredibly cool &#8211; but it gets better.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A while ago, I wrote a couple of posts that claimed to talk about finger trees. Unfortunately, I really botched it. I&#8217;d read a bunch of data structure papers, and managed to get myself thoroughly scrambled. What I wrote about was distantly related to finger trees, and it was useful to help understand how fingertrees [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[83],"tags":[],"class_list":["post-855","post","type-post","status-publish","format-standard","hentry","category-data-structures"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4lzZS-dN","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/855","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/comments?post=855"}],"version-history":[{"count":0,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/855\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/media?parent=855"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/categories?post=855"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/tags?post=855"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}