public static Animation CreateDoubleCheckedLockingAnimation()
        {
            var ani = new Animation();
            var address1 = 0x100;

            var t = ani.Periodic(8.Seconds());

            var n = 14.0;
            var t_writeP1 = 1/n;
            var t_writeX =  2/n;
            var t_writeY =  3/n;
            var t_writeS =  4/n;
            var t_cacheY =  5/n;
            var t_cacheX =  6/n;
            var t_cacheS =  7/n;
            var t_uncacheS = 8 / n;
            var t_checkS = 9 / n;
            var t_readX = 10/n;
            var t_garbage = 11/n;

            var thread1Value = t.Proper.Select(e =>
                e < t_writeX ? new MemoryPoint { X = May<int>.NoValue, Y = May<int>.NoValue }
                : e < t_writeY ? new MemoryPoint { X = 1, Y = May<int>.NoValue }
                : new MemoryPoint { X = 1, Y = 1 });
            var cacheValue = t.Proper.Select(e =>
                e < t_cacheY ? new MemoryPoint { X = May<int>.NoValue, Y = May<int>.NoValue }
                : e < t_cacheX ? new MemoryPoint { X = May<int>.NoValue, Y = 1 }
                : new MemoryPoint { X = 1, Y = 1 });
            var thread2Value = t.Proper.Select(e =>
                new MemoryPoint { X = May<int>.NoValue, Y = May<int>.NoValue });

            var sideMargin = 10;
            var thread1X = sideMargin;
            var cacheX = 107;
            var thread2X = 210;
            var thread2X2 = 210+50;
            var rightX = thread2X2 + sideMargin*2;
            //var centerX = rightX / 2;

            //t.Add(new TextDesc(
            //    "No Acquire ⇒ Garbage",
            //    new Point(centerX, 5),
            //    new Point(0.5, 0),
            //    //fontWeight: FontWeights.SemiBold,
            //    fontSize: 18));
            var mainY = 16;
            var sectionLabelY = -15 + mainY;
            var dividerY3 = -16 + mainY;
            var dividerY1 = mainY;
            var pointerY = 15 + mainY;
            var valueY = 55 + mainY;
            var localPointerY = 110 + mainY;
            var resultY = 110 + mainY;
            var dividerY2 = 140 + mainY;
            var topInstructionY = 145 + mainY;

            var p1 = new Point(thread1X, valueY);
            var p2 = new Point(cacheX, valueY);
            var p3 = new Point(thread2X, valueY);

            var v1 = MakePointMemoryAnimator(address1)(thread1Value, true, p1);
            var v2 = MakePointMemoryAnimator(address1)(cacheValue, true, p2);
            var v3 = MakePointMemoryAnimator(address1)(thread2Value, true, p3);

            var b1 = v1.Item2.WithTopLeft(p1);
            var b2 = v2.Item2.WithTopLeft(p2);
            var b3 = v3.Item2.WithTopLeft(p3);

            var thread1LocalPointer = t.Proper.Combine(b1, (e, b) =>
                e < t_writeP1 ? Tuple.Create(0, b)
                : Tuple.Create(address1, b));
            var thread1Pointer = t.Proper.Combine(b1, (e, b) =>
                e < t_writeS ? Tuple.Create(0, b)
                : Tuple.Create(address1, b));
            var cachePointer = t.Proper.Combine(b2, (e, b) =>
                e < t_cacheS ? Tuple.Create(0, b)
                : Tuple.Create(address1, b));
            var thread2Pointer = t.Proper.Combine(b3, (e, b) =>
                e < t_uncacheS ? Tuple.Create(0, b)
                : Tuple.Create(address1, b));

            var thread2CopyX = t.Proper.Select(
                e => e < t_readX ? "-" : null);

            var t1LocPtrPt = new Point(thread1X, localPointerY);
            var t1PtrPt = new Point(thread1X, pointerY);
            var cachePtrPt = new Point(cacheX, pointerY);
            var t2PtrPt = new Point(thread2X, pointerY);
            var t2RetPt = new Point(thread2X, resultY);

            var v4 = MakePointerAnimator("p")(thread1LocalPointer, true, t1LocPtrPt);
            var v6 = MakePointerAnimator("s")(thread1Pointer, true, t1PtrPt);
            var v7 = MakePointerAnimator("s")(cachePointer, true, cachePtrPt);
            var v8 = MakePointerAnimator("s")(thread2Pointer, true, t2PtrPt);
            var v9 = MakeBoxValueAnimator("returned")(thread2CopyX, true, t2RetPt);

            var t1LocPtrR = v4.Item2.WithTopLeft(t1LocPtrPt);
            var t1PtrR = v6.Item2.WithTopLeft(t1PtrPt);
            var cachePtrR = v7.Item2.WithTopLeft(cachePtrPt);
            var t2PtrR = v8.Item2.WithTopLeft(t2PtrPt);
            var t2RetR = v9.Item2.WithTopLeft(t2RetPt);

            var times = new[] {
                Tuple.Create(t_writeP1, t1LocPtrR, b1, "p = new Point()", 4),
                Tuple.Create(t_writeX, b1, (Ani<Rect>)null, "p.X = 1", 5),
                Tuple.Create(t_writeY, b1, (Ani<Rect>)null, "p.Y = 1", 6),
                Tuple.Create(t_writeS, t1PtrR, t1LocPtrR, "s = p", 7),
                Tuple.Create(t_cacheY, b2, b1, "[write] s.Y", 8),
                Tuple.Create(t_cacheX, b2, b1, "[write] s.X", 8),
                Tuple.Create(t_cacheS, cachePtrR, t1PtrR, "[write] s", 8),
                Tuple.Create(t_uncacheS, t2PtrR, cachePtrR, "[read] s", -1),
                Tuple.Create(t_checkS, t2PtrR, (Ani<Rect>)null, "s != null", -1),
                Tuple.Create(t_readX, t2RetR, b3, "return s.X", -10),
                Tuple.Create(t_garbage, t2RetR, (Ani<Rect>)null, "[garbage]", 0),
                Tuple.Create(1.01, t2RetR, (Ani<Rect>)null, "[read] s.X", 0),
                Tuple.Create(1.01, t2RetR, (Ani<Rect>)null, "[read] s.Y", 0)
            };

            //var cur1 = 4;
            //var cur2 = 0;
            foreach (var i in times.Length.Range()) {
                var visible = t.Proper.Select(e => {
                    //var prevT = i == 0 ? 0 : times[i - 1].Item1;
                    var nextT = i == times.Length - 1 ? 1.01 : times[i + 1].Item1;
                    var curT = times[i].Item1;
                    var fade = 0.01;
                    if (e < curT-fade) return 0;
                    if (e >= curT && e < nextT - fade) return 1.0;
                    if (e > nextT) return 0;
                    if (e < curT) return (e - curT + fade) / fade;
                    return (nextT - e) / fade;
                });
                var margin = 0;

                //if (times[i].Item5 > 0) cur1 = times[i].Item5;
                //if (times[i].Item5 < 0) cur2 = -times[i].Item5;

                //var inst1 = cur1;
                //var inst2 = cur2;

                t.Add(new RectDesc(
                    times[i].Item2.Select(e => new Rect(e.X - margin, e.Y - margin, e.Width + margin * 2, e.Height + margin * 2)),
                    fill: visible.Select(e => (Brush)Brushes.Red.LerpToTransparent(0.5 + 0.5 * (1 - e)))));
                if (times[i].Item3 != null) {
                    var line = times[i].Item2.Combine(times[i].Item3, (r1, r2) => {
                        var c1 = r1.TopLeft.LerpTo(r1.BottomRight, 0.5);
                        var c2 = r2.TopLeft.LerpTo(r2.BottomRight, 0.5);
                        return c1.LerpTo(c2, 0.1).To(c2.LerpTo(c1, 0.1));
                    });
                    t.Add(
                        new LineSegmentDesc(
                            line,
                            visible.Select(e => (Brush)Brushes.Black.LerpToTransparent(1-e)),
                            1));
                    t.Add(
                        new LineSegmentDesc(
                            line.Select(e => e.Start.Sweep((e.Delta + e.Delta.PerpClockwise()).Normal()*10)),
                            visible.Select(e => (Brush)Brushes.Black.LerpToTransparent(1 - e)),
                            1));
                    t.Add(
                        new LineSegmentDesc(
                            line.Select(e => e.Start.Sweep((e.Delta + e.Delta.PerpCounterClockwise()).Normal() * 10)),
                            visible.Select(e => (Brush)Brushes.Black.LerpToTransparent(1 - e)),
                            1));

                    t.Add(new RectDesc(
                        times[i].Item3.Select(e => new Rect(e.X - margin, e.Y - margin, e.Width + margin * 2, e.Height + margin * 2)),
                        fill: visible.Select(e => (Brush)Brushes.Blue.LerpToTransparent(0.75+(1-e)*0.25))));
                }

                var localinsty = (i <= 6 ? i : i - 7) * 15 + topInstructionY;
                t.Add(
                    new TextDesc(times[i].Item4,
                        times[i].Item2.Select(e => new Point(e.X == thread2X ? thread2X2 : thread1X, localinsty)),
                        times[i].Item2.Select(e => new Point(e.X == thread2X ? 1 : 0, 0)),
                        fontSize: 12,
                        foreground: Brushes.Black));
                t.Add(
                    new RectDesc(
                        times[i].Item2.Select(e => new Rect(e.X == thread2X ? thread2X2 - 52 : thread1X, localinsty, e.X == thread2X ? 52 : 83, 15)),
                        fill: visible.Select(e => e > 0.5 ? (Brush)(Brushes.Black).LerpToTransparent(0.75) : Brushes.Transparent)));

                //if (cur1 > 0) {
                //    t.Add(new RectDesc(
                //        new Rect(cacheX, 200-15+inst1*15, 100, 15),
                //        fill: visible.Select(e => e > 0.5 ? (Brush)Brushes.Red.LerpToTransparent(0.5) : Brushes.Transparent)));
                //}
                //if (cur2 > 0) {
                //    t.Add(new RectDesc(
                //        new Rect(cacheX, 200 - 15 + inst2 * 15, 100, 15),
                //        fill: visible.Select(e => e > 0.5 ? (Brush)Brushes.Blue.LerpToTransparent(0.5) : Brushes.Transparent)));
                //}
            }

            //var codeY = 200;
            //var codeN = 0;
            //Func<Point> makeCodeP = () => new Point(cacheX, codeY + codeN++*15);

            t.Add(
                v1.Item1,
                v2.Item1,
                v3.Item1,
                v4.Item1,
                v6.Item1,
                v7.Item1,
                v8.Item1,
                v9.Item1,

                //new TextDesc("var p = s", makeCodeP(), new Point(0, 0)),
                //new TextDesc("if (p == nil) {", makeCodeP(), new Point(0, 0)),
                //new TextDesc("  lock {", makeCodeP(), new Point(0, 0)),
                //new TextDesc("    p = new Point()", makeCodeP(), new Point(0, 0)),
                //new TextDesc("    p.X = 1", makeCodeP(), new Point(0, 0)),
                //new TextDesc("    p.Y = 1", makeCodeP(), new Point(0, 0)),
                //new TextDesc("    s = p", makeCodeP(), new Point(0, 0)),
                //new TextDesc("  }", makeCodeP(), new Point(0, 0)),
                //new TextDesc("}", makeCodeP(), new Point(0, 0)),
                //new TextDesc("return p.X", makeCodeP(), new Point(0, 0)),

                new TextDesc("Producer", new Point(thread1X, sectionLabelY), new Point(0, 0), fontWeight: FontWeights.SemiBold, fontSize: 12),
                new TextDesc("Memory", new Point(cacheX + 28, sectionLabelY), new Point(0.5, 0), fontWeight: FontWeights.SemiBold, fontSize: 12),
                new TextDesc("Consumer", new Point(thread2X2, sectionLabelY), new Point(1, 0), fontWeight: FontWeights.SemiBold, fontSize: 12),

                new LineSegmentDesc(new Point(0, dividerY3).Sweep(new Vector(rightX, 0)), Brushes.Black, 1),
                new LineSegmentDesc(new Point(0, dividerY2).Sweep(new Vector(rightX, 0)), Brushes.Black, 1),
                new LineSegmentDesc(new Point(0, dividerY1).Sweep(new Vector(rightX, 0)), Brushes.Black, 1)
            );
            return ani;
        }
        private static Animation CreateVaryQSolveSlopeAnimation(Rect rect)
        {
            var size = 13;
            var yq = 10;
            var y0 = 5;

            var ani = new Animation { CreateGrid(size, rect) };
            var dt = 50.Milliseconds();
            var p = ani.Periodic(dt.Times(size - 1));
            for (var xq = 1; xq < size; xq++) {
                var slope = size.Range().Single(s => (y0 + s * xq) % size == yq);
                var q = p.LimitedNewTime(dt.Times(xq - 1), dt.Times(xq));
                q.Add(CreateLine(y0, slope, size, rect));
                q.Add(new PointDesc(GetP(0, y0, size, rect), fill: Brushes.Red, radius: 8));
                q.Add(new TextDesc(string.Format("Target = ({0}, {1}), Slope = {2}, Y-Intercept = {3}", xq, yq, slope, y0),
                    new Point(rect.Left + 67, rect.Bottom + 5),
                    new Point(0, 0), fontSize: 14));
                q.Add(new PointDesc(GetP(xq, yq, size, rect), fill: Brushes.Blue, radius: 8));
            }

            ani.Add(new TextDesc("Varying Target, Solving Slope", new Point(rect.Left + rect.Width / 2, rect.Top - 30), new Point(0.5, 0), fontSize: 18));

            return ani;
        }
 public static Animation ShowMatrixMultiplication(TimeSpan period,
                                             Rect pos,
                                             ComplexMatrix u,
                                             ComplexVector v,
                                             Ani<Brush> or,
                                             Ani<Brush> blu,
                                             Ani<Brush> bla)
 {
     var d = Math.Min(pos.Width/(u.Columns.Count + 2), pos.Height/u.Rows.Count)/2;
     pos = new Rect(pos.TopLeft, new Size(d * (u.Columns.Count + 2)*2, d*u.Rows.Count*2));
     var ur = d;
     var animation = new Animation {
         new RectDesc(new Rect(pos.X + d * 2, pos.Y, pos.Width - d * 4, pos.Height),
                      Brushes.Black,
                      strokeThickness: 0.6,
                      dashed: 5),
         new RectDesc(new Rect(pos.X, pos.Y, d * 2, pos.Height),
                      Brushes.Black,
                      strokeThickness: 0.6,
                      dashed: 5),
         new RectDesc(new Rect(pos.Right - d*2, pos.Y, d * 2, pos.Height),
                      Brushes.Black,
                      strokeThickness: 0.6,
                      dashed: 5)
     };
     var per = animation.Periodic(period);
     var s0 = per.LimitedSameTime(0.Seconds(), period.DividedBy(4));
     foreach (var r in u.Rows.Count.Range()) {
         var pp = s0.Proper;
         // input vector
         var p1 = pos.TopLeft + new Vector(d, d + r*d*2);
         var p2 = pos.TopLeft + new Vector(d*3 + r*d*2, 0);
         var p3 = pos.TopLeft + new Vector(d*3 + r*d*2, u.Rows.Count*d*2);
         s0.Add(ShowComplex(pp.Combine(blu, (p, b) => b.LerpToTransparent(p.SmoothTransition(0, 0, 1))),
                            pp.Combine(bla, (p, b) => b.LerpToTransparent(p.SmoothTransition(0, 0, 1))),
                            v.Values[r],
                            pp.Select(p => p.SmoothTransition(p1, p1, p2, p3)),
                            ur,
                            rotation: pp.Select(t => Turn.FromNaturalAngle(t.SmoothTransition(0, Math.PI/2, Math.PI/2)))));
         foreach (var c in u.Columns.Count.Range()) {
             // vector copied into matrix
             s0.Add(ShowComplex(pp.Combine(blu, (p, b) => b.LerpToTransparent(p.SmoothTransition(1, 1, 0))),
                                pp.Combine(bla, (p, b) => b.LerpToTransparent(p.SmoothTransition(1, 1, 0))),
                                v.Values[c],
                                (pos.TopLeft + new Vector(d*3 + c*d*2, d + r*d*2)),
                                ur,
                                Brushes.Transparent,
                                rotation: Turn.FromNaturalAngle(Math.PI/2)));
             // matrix
             s0.Add(ShowComplex(pp.Combine(or, (p, b) => b.LerpToTransparent(p.SmoothTransition(0, 0, 0))),
                                pp.Combine(bla, (p, b) => b.LerpToTransparent(p.SmoothTransition(0, 0, 0))),
                                u.Rows[r][c],
                                (pos.TopLeft + new Vector(d*3 + c*d*2, d + r*d*2)),
                                ur));
         }
     }
     var s1 = per.LimitedSameTime(period.Times(0.25), period.Times(0.5));
     foreach (var r in u.Rows.Count.Range()) {
         foreach (var c in u.Columns.Count.Range()) {
             s1.Add(
                 ShowComplexProduct(
                     blu,
                     or,
                     bla,
                     bla,
                     v.Values[c],
                     u.Rows[r][c],
                     (pos.TopLeft + new Vector(d*3 + c*d*2, d + r*d*2)),
                     ur,
                     s1.Proper));
         }
     }
     var s2 = per.LimitedSameTime(period.Times(0.5), period);
     foreach (var r in u.Rows.Count.Range()) {
         s2.Add(
             ShowComplexSum(
                 blu,
                 blu,
                 bla,
                 u.Rows[r].Count.Range()
                          .Select(e => u.Rows[r][e]*v.Values[e])
                          .Select(e => new ConstantAni<Complex>(e)),
                 (pos.TopLeft + new Vector(d*3, d + r*d*2)),
                 (pos.TopLeft + new Vector(d*3 + u.Columns.Count*d*2, d + r*d*2)),
                 new Vector(d*2, 0),
                 ur,
                 s2.Proper));
     }
     return animation;
 }
        private static Animation CreateVarySlopeSolveYAnimation(int size, Rect rect, int xq, int yq)
        {
            var ani = new Animation { CreateGrid(size, rect) };

            var dt = 50.Milliseconds();
            var p = ani.Periodic(dt.Times(size));
            for (var slope = 0; slope < size; slope++) {
                var y0 = size.Range().Single(y => (y + slope * xq) % size == yq);
                var q = p.LimitedNewTime(dt.Times(slope), dt.Times(slope + 1));
                q.Add(CreateLine(y0, slope, size, rect));
                q.Add(new PointDesc(GetP(0, y0, size, rect), fill: Brushes.Red, radius: 8));
                q.Add(new TextDesc(string.Format("Target = ({0}, {1}), Slope = {2}, Y-Intercept = {3}", xq, yq, slope, y0),
                    new Point(rect.Left + 67, rect.Bottom + 5),
                    new Point(0, 0), fontSize: 14));
                q.Add(new PointDesc(GetP(xq, yq, size, rect), fill: Brushes.Blue, radius: 8));
            }

            ani.Add(new TextDesc("Varying Slope, Solving Y-Intercept", new Point(rect.Left + rect.Width / 2, rect.Top - 30), new Point(0.5, 0), fontSize: 18));

            return ani;
        }
        private static Animation CreateAnimationOfOperations(Interval renderInterval, Interval targetInterval, params Operation[] operations)
        {
            var keyNodes = new Dictionary<int, Tuple<NestingDepthTreeNode, Interval, NestingDepthTreeNode>>();
            var activeOperation = default(Operation);

            var dt = 1.0;
            var ani = new Animation();

            ani.Add(new TextDesc("Using a Tree to Track Referenced Intervals", new Point(250, 5), new Point(0, 0), fontSize:20));
            ani.Add(new TextDesc("Referenced" + Environment.NewLine + "Intervals", new Point(10, 80)));
            ani.Add(new TextDesc("Tree", new Point(10, 220)));
            ani.Add(new TextDesc("Nesting" + Environment.NewLine + "Depth", new Point(10, 350)));
            ani.Add(new LineSegmentDesc(new Point(0, 30).Sweep(new Vector(10000, 0)), Brushes.Black, 1));
            ani.Add(new LineSegmentDesc(new Point(0, 100).Sweep(new Vector(10000, 0)), Brushes.Black, 1));
            ani.Add(new LineSegmentDesc(new Point(0, 300).Sweep(new Vector(10000, 0)), Brushes.Black, 1));
            ani.Add(new LineSegmentDesc(new Point(75, 0).Sweep(new Vector(0, 10000)), Brushes.Black, 1));

            var i = 0.0;
            var dealloced = new HashSet<int>();
            for (var i2 = 0; i2 < renderInterval.Length; i2++) {
                if (i2 + renderInterval.Offset < targetInterval.Offset || i2 + renderInterval.Offset >= targetInterval.Offset + targetInterval.Length) {
                    dealloced.Add(i2 + renderInterval.Offset);
                }
            }
            var x = 100;
            var y = 90;
            Func<double, Animation> addFrame = focus => {
                var roots = keyNodes.Select(e => NestingDepthTreeNode.RootOf(e.Value.Item1)).Distinct().ToArray();

                var a = new Animation();

                var s = 10;
                if (!double.IsNaN(focus) && !double.IsInfinity(focus)) {
                    var dx = focus - renderInterval.Offset;
                    a.Add(new LineSegmentDesc(new LineSegment(new Point(x + dx*s, 0), new Point(x + dx*s, 10000)), activeOperation.Interval.HasValue ? Brushes.Green : Brushes.Red, 1, 1.0));
                }

                foreach (var r in roots) a.Add(RenderTree(renderInterval, r, dealloced));
                ani.LimitedNewTime(i.Seconds(), (i + dt).Seconds()).Add(a);
                i += dt;

                var bb = 0;
                foreach (var e in keyNodes) {
                    Brush brush;
                    if (e.Key == activeOperation.Key && !double.IsInfinity(focus)) {
                        brush = activeOperation.Interval.HasValue ? Brushes.Green : Brushes.Red;
                    } else {
                        brush = Brushes.Yellow;
                    }
                    var inv = e.Value.Item2;
                    var dx = inv.Offset - renderInterval.Offset;
                    a.Add(new RectDesc(new Rect(x + dx * s, y - (bb+1) * s*1.1, s*inv.Length, s), fill: brush, stroke: Brushes.Black, strokeThickness: 1));

                    bb += 1;
                }

                return a;
            };

            foreach (var e in operations) {
                activeOperation = e;
                var affectedRoot =
                    e.Interval.HasValue ?
                    keyNodes.Select(n => NestingDepthTreeNode.RootOf(n.Value.Item1)).FirstOrDefault(n => NestingDepthTreeNode.GetInterval(n).Overlaps(e.Interval.Value))
                    : NestingDepthTreeNode.RootOf(keyNodes[e.Key].Item1);

                if (e.Interval.HasValue) {
                    keyNodes.Add(e.Key, Tuple.Create((NestingDepthTreeNode)null, e.Interval.Value, (NestingDepthTreeNode)null));

                    addFrame(double.NaN);
                    addFrame(e.Interval.Value.Offset);

                    var a1 = NestingDepthTreeNode.Include(affectedRoot, e.Interval.Value.Offset, +1, +1);
                    a1.AdjustedNode._fakeRefCount += 2;
                    keyNodes[e.Key] = Tuple.Create(a1.AdjustedNode, e.Interval.Value, (NestingDepthTreeNode)null);
                    addFrame(e.Interval.Value.Offset);

                    addFrame(e.Interval.Value.Offset + e.Interval.Value.Length);
                    var a2 = NestingDepthTreeNode.Include(a1.NewRoot, e.Interval.Value.Offset + e.Interval.Value.Length, -1, +1);
                    a2.AdjustedNode._fakeRefCount += 2;
                    keyNodes[e.Key] = Tuple.Create(a1.AdjustedNode, e.Interval.Value, a2.AdjustedNode);

                    addFrame(e.Interval.Value.Offset + e.Interval.Value.Length);
                    addFrame(double.PositiveInfinity);
                }
                else {
                    var xs = keyNodes[e.Key];

                    var r = NestingDepthTreeNode.RootOf(xs.Item1);
                    r = NestingDepthTreeNode.Include(r, xs.Item2.Offset + xs.Item2.Length, +1, 0).NewRoot;
                    r = NestingDepthTreeNode.Include(r, xs.Item2.Offset, -1, 0).NewRoot;
                    var hh = new HashSet<int>();
                    foreach (var ex in NestingDepthTreeNode.FindHolesIn(NestingDepthTreeNode.GetInterval(r), r)) {
                        for (var ii = ex.Offset; ii < ex.Offset + ex.Length; ii++) {
                            hh.Add(ii);
                        }
                    }
                    r = NestingDepthTreeNode.Include(r, xs.Item2.Offset + xs.Item2.Length, -1, 0).NewRoot;
                    r = NestingDepthTreeNode.Include(r, xs.Item2.Offset, +1, 0).NewRoot;

                    xs.Item3._fakeRefCount -= 1;
                    xs.Item3._fakeRefCount -= 1;
                    xs.Item1._fakeRefCount -= 1;
                    addFrame(xs.Item2.Offset + xs.Item2.Length);
                    var r1 = NestingDepthTreeNode.Include(r, xs.Item2.Offset + xs.Item2.Length, +1, -1);
                    addFrame(xs.Item2.Offset + xs.Item2.Length);

                    xs.Item1._fakeRefCount -= 1;
                    addFrame(xs.Item2.Offset);
                    var r2 = NestingDepthTreeNode.Include(r1.NewRoot, xs.Item2.Offset, -1, -1);
                    keyNodes.Remove(e.Key);
                    addFrame(double.NaN);

                    foreach (var xxx in hh) {
                        dealloced.Add(xxx);
                    }

                    NestingDepthTreeNode.PartitionAroundHoles(r2.NewRoot);

                    addFrame(double.NaN);
                }
            }

            var xx = new Animation();
            xx.Periodic(i.Seconds()).Add(ani);
            return xx;
        }