Exemplo n.º 1
0
        public RangedArray(int numberOfItems, TheFanciestMemory mem)
        {
            if (mem == null) throw new ArgumentNullException("mem");
            _mem = mem;
            if (numberOfItems == 0) {
                _refNode = null;
                return;
            }

            _memInterval = mem.Malloc(numberOfItems);
            var root = NestingDepthTreeNode.Include(null, _memInterval.Offset, +1, +1).NewRoot;
            _refNode = NestingDepthTreeNode.Include(root, _memInterval.Offset + _memInterval.Length, -1, +1).AdjustedNode;
        }
Exemplo n.º 2
0
        public RangedArray(RangedArray other, Interval interval, TheFanciestMemory mem)
        {
            if (mem == null) throw new ArgumentNullException("mem");
            _mem = mem;
            if (other._disposed) throw new ObjectDisposedException("other");
            if (interval.Offset < 0) throw new ArgumentOutOfRangeException();
            if (interval.Length < 0) throw new ArgumentOutOfRangeException();
            if (interval.Offset + interval.Length > other._memInterval.Length) throw new ArgumentOutOfRangeException();
            if (interval.Length == 0) {
                _refNode = null;
                return;
            }

            _memInterval = new Interval(other._memInterval.Offset + interval.Offset, interval.Length);
            var root = NestingDepthTreeNode.RootOf(other._refNode);
            root = NestingDepthTreeNode.Include(root, _memInterval.Offset, +1, +1).NewRoot;
            _refNode = NestingDepthTreeNode.Include(root, _memInterval.Offset + _memInterval.Length, -1, +1).AdjustedNode;
        }
        ///<summary>Returns the areas that the tree has covered; that aren't holes in the nesting depth.</summary>
        public static IReadOnlyList<Interval> FindCoveredIntervals(NestingDepthTreeNode root)
        {
            var coveredStart = (int?)null;
            var results = new List<Interval>();

            FindAndCallbackHoleTransitionsInOrder(
                root,
                0,
                (node, isIntoHole) => {
                    if (coveredStart.HasValue != isIntoHole) {
                        throw new InvalidOperationException("Invariant violated: in-out-repeat");
                    }
                    if (coveredStart.HasValue) {
                        results.Add(new Interval(coveredStart.Value, node._offset - coveredStart.Value));
                        coveredStart = null;
                    } else {
                        coveredStart = node._offset;
                    }
                });

            return results;
        }
 /// <summary>
 /// Numbers with more power-of-2-ness are superior.
 /// Also, power-of-2-ness makes a nice balanced binary structure: 1317131F1317131...
 /// This is not optimal, because nodes may be sparse w.r.t. the range, but it's good enough for an experiment.
 /// </summary>
 private bool ShouldBeParentOf(NestingDepthTreeNode other)
 {
     if (other == null) return false;
     return PowerOf2Ness(other._offset) < PowerOf2Ness(this._offset);
 }
 ///<summary>Sets the child in the given direction (negative -> lesser child, positive -> larger child).</summary>
 private void SetChild(int childSign, NestingDepthTreeNode value)
 {
     if (childSign < 0) _less = value;
     else if (childSign > 0) _more = value;
     else throw new ArgumentException("childSign == 0");
 }
        /// <summary>Continues cutting the tree in two, assuming that child nodes have been handled.</summary>
        private static void PartitionUpwardToParentSideOf(NestingDepthTreeNode n, int d, NestingDepthTreeNode orphan)
        {
            if (n == null) return;
            n.RecomputeAggregates();

            var p = n._parent;
            if (n.DirToParent() == d) {
                // going from n to p crosses the cut line, so we must disconnect them
                n._parent = null;

                // the orphan node takes n's place
                p.SetChild(-d, orphan);
                if (orphan != null) orphan._parent = p;

                // switch directions back towards the cut, and onward with our new orphan n!
                PartitionUpwardToParentSideOf(p, -d, n);
            } else {
                // didn't pass over the cut line, keep going upwards
                PartitionUpwardToParentSideOf(p, d, orphan);
            }
        }
 /// <summary>Cuts the tree in two, to the left of the given node if d is negative and to the right if d is positive.</summary>
 private static void PartitionToSideOfNode(NestingDepthTreeNode n, int d)
 {
     var orphan = n.Child(d);
     if (orphan != null) orphan._parent = null;
     n.SetChild(d, null);
     PartitionUpwardToParentSideOf(n, d, orphan);
 }
        /// <summary>Scans the tree for transitions into and out of holes, running a callback for each one.</summary>
        private static void FindAndCallbackHoleTransitionsInOrder(NestingDepthTreeNode node,
                                                                  int initialNestingDepth,
                                                                  Action<NestingDepthTreeNode, bool> callback)
        {
            if (node == null) return;

            // we can skip this whole subtree if it's guaranteed to stay above water the whole time
            var lowestNestingDepth = node._subTreeRelativeMinimum + initialNestingDepth;
            if (initialNestingDepth > 0 && lowestNestingDepth > 0) return;

            // scan left subtree
            FindAndCallbackHoleTransitionsInOrder(node._less, initialNestingDepth, callback);

            // check for transition here
            var nestingDepthJustBeforeNode = initialNestingDepth + GetTotalAdjust(node._less);
            var nestingDepthJustAfterNode = nestingDepthJustBeforeNode + node._adjust;
            var wasInHole = nestingDepthJustBeforeNode <= 0;
            var nowInHole = nestingDepthJustAfterNode <= 0;
            if (wasInHole != nowInHole) {
                callback(node, nowInHole);
            }

            // scan right subtree
            FindAndCallbackHoleTransitionsInOrder(node._more, nestingDepthJustAfterNode, callback);
        }
        ///<summary>Returns the nesting depth at a particular index, as determined by the tree rooted at the given node.</summary>
        public static int QueryNestingDepthAt(NestingDepthTreeNode root, int index)
        {
            if (root == null) return 0;

            var pre = QueryNestingDepthAt(root._less, index);
            if (root._offset > index) return pre;

            var on = pre + root._adjust;
            if (root._offset == index) return on;

            var post = on + QueryNestingDepthAt(root._more, index);
            return post;
        }
        /// <summary>
        /// When the nesting depth hits zero, we can actually split the tree in two around that hole and make future queries more efficient.
        /// This only works because arrays only care about the segment they're in, as opposed to the global state.
        /// </summary>
        public static void PartitionAroundHoles(NestingDepthTreeNode root)
        {
            var partitionsToPerform = new List<Action>();

            // find the nodes that have a hole to their left or right, so we can cut there
            FindAndCallbackHoleTransitionsInOrder(
                root,
                0,
                (node, isIntoHole) => partitionsToPerform.Add(() => PartitionToSideOfNode(node, isIntoHole ? +1 : -1)));

            // only do the partitioning after the search is done, so we don't interfere with it
            foreach (var e in partitionsToPerform) {
                e.Invoke();
            }
        }
        public static IncludeResult IncludeHelper(NestingDepthTreeNode root, int index, int adjust, int refAdjust)
        {
            // Do we need to create a new node?
            if (root == null) {
                return new IncludeResult(new NestingDepthTreeNode(index, adjust, refAdjust));
            }

            // It is understood that the caller will fixup our parent afterwards; we must act independently of the tree above us
            root._parent = null;

            // Is this node the one we need to adjust?
            if (index == root._offset) {
                root._adjust += adjust;
                root._refCount += refAdjust;
                root.RecomputeAggregates();

                // nodes can be removed when they are not referenced and have no effect on the totals
                if (root._adjust == 0 && root._refCount == 0) {
                    return new IncludeResult(null, root.Implode());
                }

                return new IncludeResult(root);
            }

            // Pick the subtree the node has to end up and recurse the inclusion that-a-way
            var d = index.CompareTo(root._offset);
            var subtree = root.Child(d);
            var preTotal = GetTotalAdjust(subtree);
            var subResult = IncludeHelper(subtree, index, adjust, refAdjust);
            var postTotal = GetTotalAdjust(subResult.NewRoot);
            if (preTotal + adjust != postTotal) {
                throw new InvalidOperationException("Invariant violated: total adjustment did not shift by the included adjustment.");
            }

            // Great! Now we just need to fixup so the new subtree is linked in
            var c = subResult.NewRoot;
            root.SetChild(d, c);
            if (c != null) c._parent = root;
            root.RecomputeAggregates();

            // Oh, and do a token effort to keep things balanced using our hacky hierarchical ordering over the indexes
            // Can we get away with not rotating the new child above ourselves to keep things sorta balanced?
            if (c == null || !c.ShouldBeParentOf(root)) {
                return new IncludeResult(subResult.AdjustedNode, root);
            }

            // darn, need to rotate
            var s = c.Child(-d);
            c.SetChild(-d, root);
            root.SetChild(d, s);

            // fixup
            c._parent = null;
            root._parent = c;
            if (s != null) s._parent = root;
            root.RecomputeAggregates();
            c.RecomputeAggregates();

            // finally
            return new IncludeResult(subResult.AdjustedNode, c);
        }
        /// <summary>
        /// Adds an adjustment to the tree rooted at the given node.
        /// The nesting depth will be perceived as the given adjust higher after the given index.
        /// Can also adjust reference counts.
        /// This can create, modify, or remove a node in the tree.
        /// </summary>
        public static IncludeResult Include(NestingDepthTreeNode root, int index, int adjust, int refAdjust)
        {
            if (root != null && root._parent != null) throw new ArgumentException("root.Parent != null");

            var preTotal = GetTotalAdjust(root);
            var result = IncludeHelper(root, index, adjust, refAdjust);
            var postTotal = GetTotalAdjust(result.NewRoot);
            if (preTotal + adjust != postTotal) {
                throw new InvalidOperationException("Invariant violated: total adjustment did not shift by the included adjustment.");
            }
            return result;
        }
 ///<summary>Determines how much the nesting depth changes as you cross the tree rooted at the given node.</summary>
 public static int GetTotalAdjust(NestingDepthTreeNode root)
 {
     return root == null ? 0 : root._subTreeTotalAdjust;
 }
 /// <summary>Determines the range of indexes spanned by the sub tree rooted at the given node.</summary>
 public static Interval GetInterval(NestingDepthTreeNode root)
 {
     if (root == null) return default(Interval);
     var min = root;
     var max = root;
     while (min._less != null) min = min._less;
     while (max._more != null) max = max._more;
     return new Interval(min._offset, max._offset - min._offset);
 }
 public IncludeResult(NestingDepthTreeNode newRootAndAdjustedNode)
 {
     AdjustedNode = NewRoot = newRootAndAdjustedNode;
 }
 ///<summary>Travels upwards from the given node until the root of the tree containing it is found.</summary>
 public static NestingDepthTreeNode RootOf(NestingDepthTreeNode node)
 {
     if (node == null) return null;
     if (node._parent == null) return node;
     return RootOf(node._parent);
 }
 public IncludeResult(NestingDepthTreeNode adjustedNode, NestingDepthTreeNode newRoot)
 {
     AdjustedNode = adjustedNode;
     NewRoot = newRoot;
 }
        public List<Tuple<NestingDepthTreeNode, int>> GetCuts(NestingDepthTreeNode root)
        {
            var partitionsToPerform = new List<Tuple<NestingDepthTreeNode, int>>();

            // find the nodes that have a hole to their left or right, so we can cut there
            FindAndCallbackHoleTransitionsInOrder(
                root,
                0,
                (node, isIntoHole) => partitionsToPerform.Add(Tuple.Create(node, isIntoHole ? +1 : -1)));

            return partitionsToPerform;
        }
        ///<summary>Returns all of the areas in the given interval where the tree would return zero if you queried the nesting depth there.</summary>
        public static IReadOnlyList<Interval> FindHolesIn(Interval interval, NestingDepthTreeNode root)
        {
            var relevantSegments =
                FindCoveredIntervals(root)
                    .SkipWhile(e => !e.Overlaps(interval))
                    .TakeWhile(e => e.Overlaps(interval))
                    .ToArray();

            var holeStarts =
                new[] {interval.Offset}
                    .Concat(relevantSegments
                                .Select(e => e.Offset + e.Length));
            var holeEnds =
                relevantSegments
                    .Select(e => e.Offset)
                    .Concat(new[] {interval.Offset + interval.Length});

            return holeStarts
                .Zip(holeEnds, (start, end) => new Interval(start, end - start))
                .Where(e => e.Length > 0)
                .ToArray();
        }
        private static Animation RenderTree(Interval renderInterval, NestingDepthTreeNode root, HashSet<int> dealloced)
        {
            var v = renderInterval;
            var ani = new Animation();
            var r = 10;
            var query = new Dictionary<int, int>();
            var x = 100;
            var y = 350;

            for (var i = 0; i < v.Length; i++) {
                var h = NestingDepthTreeNode.QueryNestingDepthAt(root, i+v.Offset);
                query[i+v.Offset] = h;
                if (h== -1) throw new Exception();
                ani.Add(new RectDesc(new Rect(x + i * r, y - h * r - 1, r, h * r + 1), fill: Brushes.Black, stroke: Brushes.Black, strokeThickness: 0));
                if (dealloced.Contains(i+v.Offset)) {
                    ani.Add(new RectDesc(new Rect(x + i * r, y - 0.5 * r - 1, r, 1 * r + 1), fill: Brushes.Red, stroke: Brushes.Orange, strokeThickness: 1));
                }
            }

            var y2 = 125;
            Action<NestingDepthTreeNode, int> paintNode = null;
            paintNode = (n, l) => {
                if (n == null) return;

                var p = new Point(x + (n._offset-v.Offset)*r, y2 + l*r*5);
                if (n._parent != null) {
                    var q = new Point(x + (n._parent._offset - v.Offset) * r, y2 + (l - 1) * r * 5);
                    var b1 = false;
                    var b2 = false;
                    for (var i = Math.Min(n._offset, n._parent._offset); i < Math.Max(n._offset, n._parent._offset); i++) {
                        b1 |= query[i] != 0;
                        b2 |= query[i] == 0;
                    }
                    ani.Add(new LineSegmentDesc(new LineSegment(p, q), b1 && b2 ? Brushes.Red : b2 ? Brushes.Red : Brushes.Black, 1));
                }
                if (n._less != null) paintNode(n._less, l + 1);
                if (n._more != null) paintNode(n._more, l + 1);
                ani.Add(new RectDesc(new Rect(new Point(p.X - 18, p.Y - 22), new Size(36, 44)), n._fakeRefCount > 1 ? Brushes.Black : Brushes.Red, n._fakeRefCount > 0 ? Brushes.Gray : Brushes.Red, 1));
                var s = "d=" + n._adjust +Environment.NewLine + "t=" + n._subTreeTotalAdjust + Environment.NewLine + "m=" + n._subTreeRelativeMinimum;
                ani.Add(new TextDesc(s, new Point(p.X - 16, p.Y - 20), new Point(0, 0)));
                //var s = (n._adjust > 0 ? "+" : "") + n._adjust;
                //ani.Add(new PointDesc(p, n._fakeRefCount > 1 ? Brushes.Black : Brushes.Red, n._fakeRefCount > 0 ? Brushes.Gray : Brushes.Red, 15, 1));
                //ani.Add(new TextDesc(s, new Point(p.X - 8, p.Y - 8), new Point(0, 0)));
            };
            paintNode(root, 0);

            return ani;
        }