private static void gatherStats(S2Cell cell)
        {
            var    s = levelStats[cell.Level];
            var    exactArea = cell.ExactArea();
            var    approxArea = cell.ApproxArea();
            double minEdge = 100, maxEdge = 0, avgEdge = 0;
            double minDiag = 100, maxDiag = 0;
            double minWidth = 100, maxWidth = 0;
            double minAngleSpan = 100, maxAngleSpan = 0;

            for (var i = 0; i < 4; ++i)
            {
                var edge = cell.GetVertexRaw(i).Angle(cell.GetVertexRaw((i + 1) & 3));
                minEdge  = Math.Min(edge, minEdge);
                maxEdge  = Math.Max(edge, maxEdge);
                avgEdge += 0.25 * edge;
                var mid   = cell.GetVertexRaw(i) + cell.GetVertexRaw((i + 1) & 3);
                var width = S2.PiOver2 - mid.Angle(cell.GetEdgeRaw(i ^ 2));
                minWidth = Math.Min(width, minWidth);
                maxWidth = Math.Max(width, maxWidth);
                if (i < 2)
                {
                    var diag = cell.GetVertexRaw(i).Angle(cell.GetVertexRaw(i ^ 2));
                    minDiag = Math.Min(diag, minDiag);
                    maxDiag = Math.Max(diag, maxDiag);
                    var angleSpan = cell.GetEdgeRaw(i).Angle(
                        -cell.GetEdgeRaw(i ^ 2));
                    minAngleSpan = Math.Min(angleSpan, minAngleSpan);
                    maxAngleSpan = Math.Max(angleSpan, maxAngleSpan);
                }
            }
            s.count        += 1;
            s.minArea       = Math.Min(exactArea, s.minArea);
            s.maxArea       = Math.Max(exactArea, s.maxArea);
            s.avgArea      += exactArea;
            s.minWidth      = Math.Min(minWidth, s.minWidth);
            s.maxWidth      = Math.Max(maxWidth, s.maxWidth);
            s.avgWidth     += 0.5 * (minWidth + maxWidth);
            s.minEdge       = Math.Min(minEdge, s.minEdge);
            s.maxEdge       = Math.Max(maxEdge, s.maxEdge);
            s.avgEdge      += avgEdge;
            s.maxEdgeAspect = Math.Max(maxEdge / minEdge, s.maxEdgeAspect);
            s.minDiag       = Math.Min(minDiag, s.minDiag);
            s.maxDiag       = Math.Max(maxDiag, s.maxDiag);
            s.avgDiag      += 0.5 * (minDiag + maxDiag);
            s.maxDiagAspect = Math.Max(maxDiag / minDiag, s.maxDiagAspect);
            s.minAngleSpan  = Math.Min(minAngleSpan, s.minAngleSpan);
            s.maxAngleSpan  = Math.Max(maxAngleSpan, s.maxAngleSpan);
            s.avgAngleSpan += 0.5 * (minAngleSpan + maxAngleSpan);
            var approxRatio = approxArea / exactArea;

            s.minApproxRatio = Math.Min(approxRatio, s.minApproxRatio);
            s.maxApproxRatio = Math.Max(approxRatio, s.maxApproxRatio);
        }
        public void testFaces()
        {
            IDictionary <S2Point, int> edgeCounts   = new Dictionary <S2Point, int>();
            IDictionary <S2Point, int> vertexCounts = new Dictionary <S2Point, int>();

            for (var face = 0; face < 6; ++face)
            {
                var id   = S2CellId.FromFacePosLevel(face, 0, 0);
                var cell = new S2Cell(id);
                JavaAssert.Equal(cell.Id, id);
                JavaAssert.Equal(cell.Face, face);
                JavaAssert.Equal(cell.Level, 0);
                // Top-level faces have alternating orientations to get RHS coordinates.
                JavaAssert.Equal(cell.Orientation, face & S2.SwapMask);
                Assert.True(!cell.IsLeaf);
                for (var k = 0; k < 4; ++k)
                {
                    if (edgeCounts.ContainsKey(cell.GetEdgeRaw(k)))
                    {
                        edgeCounts[cell.GetEdgeRaw(k)] = edgeCounts[cell
                                                                    .GetEdgeRaw(k)] + 1;
                    }
                    else
                    {
                        edgeCounts[cell.GetEdgeRaw(k)] = 1;
                    }

                    if (vertexCounts.ContainsKey(cell.GetVertexRaw(k)))
                    {
                        vertexCounts[cell.GetVertexRaw(k)] = vertexCounts[cell
                                                                          .GetVertexRaw(k)] + 1;
                    }
                    else
                    {
                        vertexCounts[cell.GetVertexRaw(k)] = 1;
                    }
                    assertDoubleNear(cell.GetVertexRaw(k).DotProd(cell.GetEdgeRaw(k)), 0);
                    assertDoubleNear(cell.GetVertexRaw((k + 1) & 3).DotProd(
                                         cell.GetEdgeRaw(k)), 0);
                    assertDoubleNear(S2Point.Normalize(
                                         S2Point.CrossProd(cell.GetVertexRaw(k), cell
                                                           .GetVertexRaw((k + 1) & 3))).DotProd(cell.GetEdge(k)), 1.0);
                }
            }
            // Check that edges have multiplicity 2 and vertices have multiplicity 3.
            foreach (var i in edgeCounts.Values)
            {
                JavaAssert.Equal(i, 2);
            }
            foreach (var i in vertexCounts.Values)
            {
                JavaAssert.Equal(i, 3);
            }
        }
        public void testSubdivide(S2Cell cell)
        {
            gatherStats(cell);
            if (cell.IsLeaf)
            {
                return;
            }

            var children = new S2Cell[4];

            for (var i = 0; i < children.Length; ++i)
            {
                children[i] = new S2Cell();
            }
            Assert.True(cell.Subdivide(children));
            var    childId     = cell.Id.ChildBegin;
            double exactArea   = 0;
            double approxArea  = 0;
            double averageArea = 0;

            for (var i = 0; i < 4; ++i, childId = childId.Next)
            {
                exactArea   += children[i].ExactArea();
                approxArea  += children[i].ApproxArea();
                averageArea += children[i].AverageArea();

                // Check that the child geometry is consistent with its cell id.
                JavaAssert.Equal(children[i].Id, childId);
                Assert.True(children[i].Center.ApproxEquals(childId.ToPoint(), 1e-15));
                var direct = new S2Cell(childId);
                JavaAssert.Equal(children[i].Face, direct.Face);
                JavaAssert.Equal(children[i].Level, direct.Level);
                JavaAssert.Equal(children[i].Orientation, direct.Orientation);
                JavaAssert.Equal(children[i].CenterRaw, direct.CenterRaw);
                for (var k = 0; k < 4; ++k)
                {
                    JavaAssert.Equal(children[i].GetVertexRaw(k), direct.GetVertexRaw(k));
                    JavaAssert.Equal(children[i].GetEdgeRaw(k), direct.GetEdgeRaw(k));
                }

                // Test Contains() and MayIntersect().
                Assert.True(cell.Contains(children[i]));
                Assert.True(cell.MayIntersect(children[i]));
                Assert.True(!children[i].Contains(cell));
                Assert.True(cell.Contains(children[i].CenterRaw));
                for (var j = 0; j < 4; ++j)
                {
                    Assert.True(cell.Contains(children[i].GetVertexRaw(j)));
                    if (j != i)
                    {
                        Assert.True(!children[i].Contains(children[j].CenterRaw));
                        Assert.True(!children[i].MayIntersect(children[j]));
                    }
                }

                // Test GetCapBound and GetRectBound.
                var parentCap  = cell.CapBound;
                var parentRect = cell.RectBound;
                if (cell.Contains(new S2Point(0, 0, 1)) ||
                    cell.Contains(new S2Point(0, 0, -1)))
                {
                    Assert.True(parentRect.Lng.IsFull);
                }
                var childCap  = children[i].CapBound;
                var childRect = children[i].RectBound;
                Assert.True(childCap.Contains(children[i].Center));
                Assert.True(childRect.Contains(children[i].CenterRaw));
                Assert.True(parentCap.Contains(children[i].Center));
                Assert.True(parentRect.Contains(children[i].CenterRaw));
                for (var j = 0; j < 4; ++j)
                {
                    Assert.True(childCap.Contains(children[i].GetVertex(j)));
                    Assert.True(childRect.Contains(children[i].GetVertex(j)));
                    Assert.True(childRect.Contains(children[i].GetVertexRaw(j)));
                    Assert.True(parentCap.Contains(children[i].GetVertex(j)));
                    if (!parentRect.Contains(children[i].GetVertex(j)))
                    {
                        Console.WriteLine("cell: " + cell + " i: " + i + " j: " + j);
                        Console.WriteLine("Children " + i + ": " + children[i]);
                        Console.WriteLine("Parent rect: " + parentRect);
                        Console.WriteLine("Vertex raw(j) " + children[i].GetVertex(j));
                        Console.WriteLine("Latlng of vertex: " + new S2LatLng(children[i].GetVertex(j)));
                        Console.WriteLine("RectBound: " + cell.RectBound);
                    }
                    Assert.True(parentRect.Contains(children[i].GetVertex(j)));
                    if (!parentRect.Contains(children[i].GetVertexRaw(j)))
                    {
                        Console.WriteLine("cell: " + cell + " i: " + i + " j: " + j);
                        Console.WriteLine("Children " + i + ": " + children[i]);
                        Console.WriteLine("Parent rect: " + parentRect);
                        Console.WriteLine("Vertex raw(j) " + children[i].GetVertexRaw(j));
                        Console.WriteLine("Latlng of vertex: " + new S2LatLng(children[i].GetVertexRaw(j)));
                        Console.WriteLine("RectBound: " + cell.RectBound);
                    }
                    Assert.True(parentRect.Contains(children[i].GetVertexRaw(j)));
                    if (j != i)
                    {
                        // The bounding caps and rectangles should be tight enough so that
                        // they exclude at least two vertices of each adjacent cell.
                        var capCount  = 0;
                        var rectCount = 0;
                        for (var k = 0; k < 4; ++k)
                        {
                            if (childCap.Contains(children[j].GetVertex(k)))
                            {
                                ++capCount;
                            }
                            if (childRect.Contains(children[j].GetVertexRaw(k)))
                            {
                                ++rectCount;
                            }
                        }
                        Assert.True(capCount <= 2);
                        if (childRect.LatLo.Radians > -S2.PiOver2 &&
                            childRect.LatHi.Radians < S2.PiOver2)
                        {
                            // Bounding rectangles may be too large at the poles because the
                            // pole itself has an arbitrary fixed longitude.
                            Assert.True(rectCount <= 2);
                        }
                    }
                }

                // Check all children for the first few levels, and then sample randomly.
                // Also subdivide one corner cell, one edge cell, and one center cell
                // so that we have a better chance of sample the minimum metric values.
                var forceSubdivide = false;
                var center         = S2Projections.GetNorm(children[i].Face);
                var edge           = center + S2Projections.GetUAxis(children[i].Face);
                var corner         = edge + S2Projections.GetVAxis(children[i].Face);
                for (var j = 0; j < 4; ++j)
                {
                    var p = children[i].GetVertexRaw(j);
                    if (p.Equals(center) || p.Equals(edge) || p.Equals(corner))
                    {
                        forceSubdivide = true;
                    }
                }
                if (forceSubdivide || cell.Level < (DEBUG_MODE ? 5 : 6) ||
                    random(DEBUG_MODE ? 10 : 4) == 0)
                {
                    testSubdivide(children[i]);
                }
            }

            // Check sum of child areas equals parent area.
            //
            // For ExactArea(), the best relative error we can expect is about 1e-6
            // because the precision of the unit vector coordinates is only about 1e-15
            // and the edge length of a leaf cell is about 1e-9.
            //
            // For ApproxArea(), the areas are accurate to within a few percent.
            //
            // For AverageArea(), the areas themselves are not very accurate, but
            // the average area of a parent is exactly 4 times the area of a child.

            Assert.True(Math.Abs(Math.Log(exactArea / cell.ExactArea())) <= Math
                        .Abs(Math.Log(1 + 1e-6)));
            Assert.True(Math.Abs(Math.Log(approxArea / cell.ApproxArea())) <= Math
                        .Abs(Math.Log(1.03)));
            Assert.True(Math.Abs(Math.Log(averageArea / cell.AverageArea())) <= Math
                        .Abs(Math.Log(1 + 1e-15)));
        }
 private static void gatherStats(S2Cell cell)
 {
     var s = levelStats[cell.Level];
     var exactArea = cell.ExactArea();
     var approxArea = cell.ApproxArea();
     double minEdge = 100, maxEdge = 0, avgEdge = 0;
     double minDiag = 100, maxDiag = 0;
     double minWidth = 100, maxWidth = 0;
     double minAngleSpan = 100, maxAngleSpan = 0;
     for (var i = 0; i < 4; ++i)
     {
         var edge = cell.GetVertexRaw(i).Angle(cell.GetVertexRaw((i + 1) & 3));
         minEdge = Math.Min(edge, minEdge);
         maxEdge = Math.Max(edge, maxEdge);
         avgEdge += 0.25*edge;
         var mid = cell.GetVertexRaw(i) + cell.GetVertexRaw((i + 1) & 3);
         var width = S2.PiOver2 - mid.Angle(cell.GetEdgeRaw(i ^ 2));
         minWidth = Math.Min(width, minWidth);
         maxWidth = Math.Max(width, maxWidth);
         if (i < 2)
         {
             var diag = cell.GetVertexRaw(i).Angle(cell.GetVertexRaw(i ^ 2));
             minDiag = Math.Min(diag, minDiag);
             maxDiag = Math.Max(diag, maxDiag);
             var angleSpan = cell.GetEdgeRaw(i).Angle(
                 -cell.GetEdgeRaw(i ^ 2));
             minAngleSpan = Math.Min(angleSpan, minAngleSpan);
             maxAngleSpan = Math.Max(angleSpan, maxAngleSpan);
         }
     }
     s.count += 1;
     s.minArea = Math.Min(exactArea, s.minArea);
     s.maxArea = Math.Max(exactArea, s.maxArea);
     s.avgArea += exactArea;
     s.minWidth = Math.Min(minWidth, s.minWidth);
     s.maxWidth = Math.Max(maxWidth, s.maxWidth);
     s.avgWidth += 0.5*(minWidth + maxWidth);
     s.minEdge = Math.Min(minEdge, s.minEdge);
     s.maxEdge = Math.Max(maxEdge, s.maxEdge);
     s.avgEdge += avgEdge;
     s.maxEdgeAspect = Math.Max(maxEdge/minEdge, s.maxEdgeAspect);
     s.minDiag = Math.Min(minDiag, s.minDiag);
     s.maxDiag = Math.Max(maxDiag, s.maxDiag);
     s.avgDiag += 0.5*(minDiag + maxDiag);
     s.maxDiagAspect = Math.Max(maxDiag/minDiag, s.maxDiagAspect);
     s.minAngleSpan = Math.Min(minAngleSpan, s.minAngleSpan);
     s.maxAngleSpan = Math.Max(maxAngleSpan, s.maxAngleSpan);
     s.avgAngleSpan += 0.5*(minAngleSpan + maxAngleSpan);
     var approxRatio = approxArea/exactArea;
     s.minApproxRatio = Math.Min(approxRatio, s.minApproxRatio);
     s.maxApproxRatio = Math.Max(approxRatio, s.maxApproxRatio);
 }
        public void testFaces()
        {
            IDictionary<S2Point, int> edgeCounts = new Dictionary<S2Point, int>();
            IDictionary<S2Point, int> vertexCounts = new Dictionary<S2Point, int>();
            for (var face = 0; face < 6; ++face)
            {
                var id = S2CellId.FromFacePosLevel(face, 0, 0);
                var cell = new S2Cell(id);
                JavaAssert.Equal(cell.Id, id);
                JavaAssert.Equal(cell.Face, face);
                JavaAssert.Equal(cell.Level, 0);
                // Top-level faces have alternating orientations to get RHS coordinates.
                JavaAssert.Equal(cell.Orientation, face & S2.SwapMask);
                Assert.True(!cell.IsLeaf);
                for (var k = 0; k < 4; ++k)
                {
                    if (edgeCounts.ContainsKey(cell.GetEdgeRaw(k)))
                    {
                        edgeCounts[cell.GetEdgeRaw(k)] = edgeCounts[cell
                                                                        .GetEdgeRaw(k)] + 1;
                    }
                    else
                    {
                        edgeCounts[cell.GetEdgeRaw(k)] = 1;
                    }

                    if (vertexCounts.ContainsKey(cell.GetVertexRaw(k)))
                    {
                        vertexCounts[cell.GetVertexRaw(k)] = vertexCounts[cell
                                                                              .GetVertexRaw(k)] + 1;
                    }
                    else
                    {
                        vertexCounts[cell.GetVertexRaw(k)] = 1;
                    }
                    assertDoubleNear(cell.GetVertexRaw(k).DotProd(cell.GetEdgeRaw(k)), 0);
                    assertDoubleNear(cell.GetVertexRaw((k + 1) & 3).DotProd(
                        cell.GetEdgeRaw(k)), 0);
                    assertDoubleNear(S2Point.Normalize(
                        S2Point.CrossProd(cell.GetVertexRaw(k), cell
                                                                    .GetVertexRaw((k + 1) & 3))).DotProd(cell.GetEdge(k)), 1.0);
                }
            }
            // Check that edges have multiplicity 2 and vertices have multiplicity 3.
            foreach (var i in edgeCounts.Values)
            {
                JavaAssert.Equal(i, 2);
            }
            foreach (var i in vertexCounts.Values)
            {
                JavaAssert.Equal(i, 3);
            }
        }
        public void testSubdivide(S2Cell cell)
        {
            gatherStats(cell);
            if (cell.IsLeaf)
            {
                return;
            }

            var children = new S2Cell[4];
            for (var i = 0; i < children.Length; ++i)
            {
                children[i] = new S2Cell();
            }
            Assert.True(cell.Subdivide(children));
            var childId = cell.Id.ChildBegin;
            double exactArea = 0;
            double approxArea = 0;
            double averageArea = 0;
            for (var i = 0; i < 4; ++i, childId = childId.Next)
            {
                exactArea += children[i].ExactArea();
                approxArea += children[i].ApproxArea();
                averageArea += children[i].AverageArea();

                // Check that the child geometry is consistent with its cell id.
                JavaAssert.Equal(children[i].Id, childId);
                Assert.True(children[i].Center.ApproxEquals(childId.ToPoint(), 1e-15));
                var direct = new S2Cell(childId);
                JavaAssert.Equal(children[i].Face, direct.Face);
                JavaAssert.Equal(children[i].Level, direct.Level);
                JavaAssert.Equal(children[i].Orientation, direct.Orientation);
                JavaAssert.Equal(children[i].CenterRaw, direct.CenterRaw);
                for (var k = 0; k < 4; ++k)
                {
                    JavaAssert.Equal(children[i].GetVertexRaw(k), direct.GetVertexRaw(k));
                    JavaAssert.Equal(children[i].GetEdgeRaw(k), direct.GetEdgeRaw(k));
                }

                // Test Contains() and MayIntersect().
                Assert.True(cell.Contains(children[i]));
                Assert.True(cell.MayIntersect(children[i]));
                Assert.True(!children[i].Contains(cell));
                Assert.True(cell.Contains(children[i].CenterRaw));
                for (var j = 0; j < 4; ++j)
                {
                    Assert.True(cell.Contains(children[i].GetVertexRaw(j)));
                    if (j != i)
                    {
                        Assert.True(!children[i].Contains(children[j].CenterRaw));
                        Assert.True(!children[i].MayIntersect(children[j]));
                    }
                }

                // Test GetCapBound and GetRectBound.
                var parentCap = cell.CapBound;
                var parentRect = cell.RectBound;
                if (cell.Contains(new S2Point(0, 0, 1))
                    || cell.Contains(new S2Point(0, 0, -1)))
                {
                    Assert.True(parentRect.Lng.IsFull);
                }
                var childCap = children[i].CapBound;
                var childRect = children[i].RectBound;
                Assert.True(childCap.Contains(children[i].Center));
                Assert.True(childRect.Contains(children[i].CenterRaw));
                Assert.True(parentCap.Contains(children[i].Center));
                Assert.True(parentRect.Contains(children[i].CenterRaw));
                for (var j = 0; j < 4; ++j)
                {
                    Assert.True(childCap.Contains(children[i].GetVertex(j)));
                    Assert.True(childRect.Contains(children[i].GetVertex(j)));
                    Assert.True(childRect.Contains(children[i].GetVertexRaw(j)));
                    Assert.True(parentCap.Contains(children[i].GetVertex(j)));
                    if (!parentRect.Contains(children[i].GetVertex(j)))
                    {
                        Console.WriteLine("cell: " + cell + " i: " + i + " j: " + j);
                        Console.WriteLine("Children " + i + ": " + children[i]);
                        Console.WriteLine("Parent rect: " + parentRect);
                        Console.WriteLine("Vertex raw(j) " + children[i].GetVertex(j));
                        Console.WriteLine("Latlng of vertex: " + new S2LatLng(children[i].GetVertex(j)));
                        Console.WriteLine("RectBound: " + cell.RectBound);
                    }
                    Assert.True(parentRect.Contains(children[i].GetVertex(j)));
                    if (!parentRect.Contains(children[i].GetVertexRaw(j)))
                    {
                        Console.WriteLine("cell: " + cell + " i: " + i + " j: " + j);
                        Console.WriteLine("Children " + i + ": " + children[i]);
                        Console.WriteLine("Parent rect: " + parentRect);
                        Console.WriteLine("Vertex raw(j) " + children[i].GetVertexRaw(j));
                        Console.WriteLine("Latlng of vertex: " + new S2LatLng(children[i].GetVertexRaw(j)));
                        Console.WriteLine("RectBound: " + cell.RectBound);
                    }
                    Assert.True(parentRect.Contains(children[i].GetVertexRaw(j)));
                    if (j != i)
                    {
                        // The bounding caps and rectangles should be tight enough so that
                        // they exclude at least two vertices of each adjacent cell.
                        var capCount = 0;
                        var rectCount = 0;
                        for (var k = 0; k < 4; ++k)
                        {
                            if (childCap.Contains(children[j].GetVertex(k)))
                            {
                                ++capCount;
                            }
                            if (childRect.Contains(children[j].GetVertexRaw(k)))
                            {
                                ++rectCount;
                            }
                        }
                        Assert.True(capCount <= 2);
                        if (childRect.LatLo.Radians > -S2.PiOver2
                            && childRect.LatHi.Radians < S2.PiOver2)
                        {
                            // Bounding rectangles may be too large at the poles because the
                            // pole itself has an arbitrary fixed longitude.
                            Assert.True(rectCount <= 2);
                        }
                    }
                }

                // Check all children for the first few levels, and then sample randomly.
                // Also subdivide one corner cell, one edge cell, and one center cell
                // so that we have a better chance of sample the minimum metric values.
                var forceSubdivide = false;
                var center = S2Projections.GetNorm(children[i].Face);
                var edge = center + S2Projections.GetUAxis(children[i].Face);
                var corner = edge + S2Projections.GetVAxis(children[i].Face);
                for (var j = 0; j < 4; ++j)
                {
                    var p = children[i].GetVertexRaw(j);
                    if (p.Equals(center) || p.Equals(edge) || p.Equals(corner))
                    {
                        forceSubdivide = true;
                    }
                }
                if (forceSubdivide || cell.Level < (DEBUG_MODE ? 5 : 6)
                    || random(DEBUG_MODE ? 10 : 4) == 0)
                {
                    testSubdivide(children[i]);
                }
            }

            // Check sum of child areas equals parent area.
            //
            // For ExactArea(), the best relative error we can expect is about 1e-6
            // because the precision of the unit vector coordinates is only about 1e-15
            // and the edge length of a leaf cell is about 1e-9.
            //
            // For ApproxArea(), the areas are accurate to within a few percent.
            //
            // For AverageArea(), the areas themselves are not very accurate, but
            // the average area of a parent is exactly 4 times the area of a child.

            Assert.True(Math.Abs(Math.Log(exactArea/cell.ExactArea())) <= Math
                                                                              .Abs(Math.Log(1 + 1e-6)));
            Assert.True(Math.Abs(Math.Log(approxArea/cell.ApproxArea())) <= Math
                                                                                .Abs(Math.Log(1.03)));
            Assert.True(Math.Abs(Math.Log(averageArea/cell.AverageArea())) <= Math
                                                                                  .Abs(Math.Log(1 + 1e-15)));
        }