public void Test_S2PaddedCell_GetEntryExitVertices() { int kIters = 1000; for (int iter = 0; iter < kIters; ++iter) { S2CellId id = S2Testing.GetRandomCellId(); // Check that entry/exit vertices do not depend on padding. Assert.Equal(new S2PaddedCell(id, 0).GetEntryVertex(), new S2PaddedCell(id, 0.5).GetEntryVertex()); Assert.Equal(new S2PaddedCell(id, 0).GetExitVertex(), new S2PaddedCell(id, 0.5).GetExitVertex()); // Check that the exit vertex of one cell is the same as the entry vertex // of the immediately following cell. (This also tests wrapping from the // end to the start of the S2CellId curve with high probability.) Assert.Equal(new S2PaddedCell(id, 0).GetExitVertex(), new S2PaddedCell(id.NextWrap(), 0).GetEntryVertex()); // Check that the entry vertex of a cell is the same as the entry vertex // of its first child, and similarly for the exit vertex. if (!id.IsLeaf()) { Assert.Equal(new S2PaddedCell(id, 0).GetEntryVertex(), new S2PaddedCell(id.Child(0), 0).GetEntryVertex()); Assert.Equal(new S2PaddedCell(id, 0).GetExitVertex(), new S2PaddedCell(id.Child(3), 0).GetExitVertex()); } } }
public void Test_S2CellUnion_FromMinMax() { // Check the very first leaf cell and face cell. S2CellId face1_id = S2CellId.FromFace(0); TestFromMinMax(face1_id.RangeMin(), face1_id.RangeMin()); TestFromMinMax(face1_id.RangeMin(), face1_id.RangeMax()); // Check the very last leaf cell and face cell. S2CellId face5_id = S2CellId.FromFace(5); TestFromMinMax(face5_id.RangeMin(), face5_id.RangeMax()); TestFromMinMax(face5_id.RangeMax(), face5_id.RangeMax()); // Check random ranges of leaf cells. for (int iter = 0; iter < 100; ++iter) { S2CellId x = S2Testing.GetRandomCellId(S2.kMaxCellLevel); S2CellId y = S2Testing.GetRandomCellId(S2.kMaxCellLevel); if (x > y) { var tmp = x; x = y; y = tmp; } TestFromMinMax(x, y); } }
public void Test_GetCrossingCandidates_DegenerateEdgeOnCellVertexIsItsOwnCandidate() { for (int i = 0; i < 100; ++i) { List <(S2Point, S2Point)> edges = new(); S2Cell cell = new(S2Testing.GetRandomCellId()); edges.Add((cell.Vertex(0), cell.Vertex(0))); TestAllCrossings(edges); } }
public void Test_S2CellId_ExpandedByDistanceUV() { const double max_dist_degrees = 10; for (var iter = 0; iter < 100; ++iter) { S2CellId id = S2Testing.GetRandomCellId(); double dist_degrees = S2Testing.Random.UniformDouble(-max_dist_degrees, max_dist_degrees); TestExpandedByDistanceUV(id, S1Angle.FromDegrees(dist_degrees)); } }
public void Test_XYZToFaceSiTi() { // Check the conversion of random cells to center points and back. for (int level = 0; level <= S2.kMaxCellLevel; ++level) { for (int i = 0; i < 1000; ++i) { S2CellId id = S2Testing.GetRandomCellId(level); int actual_level = S2.XYZtoFaceSiTi(id.ToPoint(), out var face, out var si, out var ti); Assert.Equal(level, actual_level); S2CellId actual_id = S2CellId.FromFaceIJ(face, (int)(si / 2), (int)(ti / 2)).Parent(level); Assert.Equal(id, actual_id); // Now test a point near the cell center but not equal to it. S2Point p_moved = id.ToPoint() + new S2Point(1e-13, 1e-13, 1e-13); actual_level = S2.XYZtoFaceSiTi(p_moved, out var face_moved, out var si_moved, out var ti_moved); Assert.Equal(-1, actual_level); Assert.Equal(face, face_moved); Assert.Equal(si, si_moved); Assert.Equal(ti, ti_moved); // Finally, test some random (si,ti) values that may be at different // levels, or not at a valid level at all (for example, si == 0). int face_random = S2Testing.Random.Uniform(S2CellId.kNumFaces); uint si_random, ti_random; uint mask = ~0U << (S2.kMaxCellLevel - level); do { si_random = S2Testing.Random.Rand32() & mask; ti_random = S2Testing.Random.Rand32() & mask; } while (si_random > S2.kMaxSiTi || ti_random > S2.kMaxSiTi); S2Point p_random = S2.FaceSiTitoXYZ(face_random, si_random, ti_random); actual_level = S2.XYZtoFaceSiTi(p_random, out face, out si, out ti); if (face != face_random) { // The chosen point is on the edge of a top-level face cell. Assert.Equal(-1, actual_level); Assert.True(si == 0 || si == S2.kMaxSiTi || ti == 0 || ti == S2.kMaxSiTi); } else { Assert.Equal(si_random, si); Assert.Equal(ti_random, ti); if (actual_level >= 0) { Assert.Equal(p_random, S2CellId.FromFaceIJ(face, (int)(si / 2), (int)(ti / 2)).Parent(actual_level).ToPoint()); } } } } }
public void Test_S2CellId_Inverses() { // Check the conversion of random leaf cells to S2LatLngs and back. for (int i = 0; i < 200000; ++i) { S2CellId id = S2Testing.GetRandomCellId(S2.kMaxCellLevel); Assert.True(id.IsLeaf()); Assert.Equal(S2.kMaxCellLevel, id.Level()); S2LatLng center = id.ToLatLng(); Assert.Equal(id.Id, new S2CellId(center).Id); } }
public void Test_GetReferencePoint_PartiallyDegenerateLoops() { for (var iter = 0; iter < 100; ++iter) { // First we construct a long convoluted edge chain that follows the // S2CellId Hilbert curve. At some random point along the curve, we // insert a small triangular loop. List <List <S2Point> > loops = new(1) { new() }; var loop = loops[0]; int num_vertices = 100; var start = S2Testing.GetRandomCellId(S2.kMaxCellLevel - 1); var end = start.AdvanceWrap(num_vertices); var loop_cellid = start.AdvanceWrap( S2Testing.Random.Uniform(num_vertices - 2) + 1); var triangle = new List <S2Point>(); for (var cellid = start; cellid != end; cellid = cellid.NextWrap()) { if (cellid == loop_cellid) { // Insert a small triangular loop. We save the loop so that we can // test whether it contains the origin later. triangle.Add(cellid.Child(0).ToPoint()); triangle.Add(cellid.Child(1).ToPoint()); triangle.Add(cellid.Child(2).ToPoint()); loop.AddRange(triangle); loop.Add(cellid.Child(0).ToPoint()); } else { loop.Add(cellid.ToPoint()); } } // Now we retrace our steps, except that we skip the three edges that form // the triangular loop above. for (S2CellId cellid = end; cellid != start; cellid = cellid.PrevWrap()) { if (cellid == loop_cellid) { loop.Add(cellid.Child(0).ToPoint()); } else { loop.Add(cellid.ToPoint()); } } S2LaxPolygonShape shape = new(loops); S2Loop triangle_loop = new(triangle); var rp = shape.GetReferencePoint(); Assert.Equal(triangle_loop.Contains(rp.Point), rp.Contained); } } }
public void Test_S2CellIdSnapFunction_SnapPoint() { for (int iter = 0; iter < 1000; ++iter) { for (int level = 0; level <= S2.kMaxCellLevel; ++level) { // This checks that points are snapped to the correct level, since // S2CellId centers at different levels are always different. S2CellIdSnapFunction f = new(level); S2Point p = S2Testing.GetRandomCellId(level).ToPoint(); Assert.Equal(p, f.SnapPoint(p)); } } }
public void Test_S2Cell_GetMaxDistanceToCell() { for (int i = 0; i < 1000; i++) { S2Cell cell = new(S2Testing.GetRandomCellId()); S2Cell test_cell = new(S2Testing.GetRandomCellId()); S2CellId antipodal_leaf_id = new(-test_cell.Center()); S2Cell antipodal_test_cell = new(antipodal_leaf_id.Parent(test_cell.Level)); S1ChordAngle dist_from_min = S1ChordAngle.Straight - cell.Distance(antipodal_test_cell); S1ChordAngle dist_from_max = cell.MaxDistance(test_cell); Assert2.Near(dist_from_min.Radians(), dist_from_max.Radians(), 1e-8); } }
public void Test_S2Cell_ConsistentWithS2CellIdFromPoint() { // Construct many points that are nearly on an S2Cell edge, and verify that // S2Cell(S2CellId(p)).Contains(p) is always true. for (int iter = 0; iter < 1000; ++iter) { S2Cell cell = new(S2Testing.GetRandomCellId()); int i = S2Testing.Random.Uniform(4); S2Point v1 = cell.Vertex(i); S2Point v2 = S2Testing.SamplePoint( new S2Cap(cell.Vertex(i + 1), S1Angle.FromRadians(1e-15))); S2Point p = S2.Interpolate(S2Testing.Random.RandDouble(), v1, v2); Assert.True(new S2Cell(new S2CellId(p)).Contains(p)); } }
public void Test_S2RegionCoverer_RandomCells() { S2RegionCoverer.Options options = new() { MaxCells = 1 }; S2RegionCoverer coverer = new(options); // Test random cell ids at all levels. for (int i = 0; i < 10000; ++i) { S2CellId id = S2Testing.GetRandomCellId(); // var info = $"Iteration {i}, cell ID token {id.ToToken()}"; var covering = coverer.GetCovering(new S2Cell(id)).CellIds;//.Release(); Assert.Single(covering); Assert.Equal(id, covering[0]); } }
public void Test_S2PaddedCell_ShrinkToFit() { const int kIters = 1000; for (int iter = 0; iter < kIters; ++iter) { // Start with the desired result and work backwards. S2CellId result = S2Testing.GetRandomCellId(); R2Rect result_uv = result.BoundUV(); R2Point size_uv = result_uv.GetSize(); // Find the biggest rectangle that fits in "result" after padding. // (These calculations ignore numerical errors.) double max_padding = 0.5 * Math.Min(size_uv[0], size_uv[1]); double padding = max_padding * S2Testing.Random.RandDouble(); R2Rect max_rect = result_uv.Expanded(-padding); // Start with a random subset of the maximum rectangle. R2Point a = new(SampleInterval(max_rect[0]), SampleInterval(max_rect[1])); R2Point b = new(SampleInterval(max_rect[0]), SampleInterval(max_rect[1])); if (!result.IsLeaf()) { // If the result is not a leaf cell, we must ensure that no child of // "result" also satisfies the conditions of ShrinkToFit(). We do this // by ensuring that "rect" intersects at least two children of "result" // (after padding). int axis = S2Testing.Random.Uniform(2); double center = result.CenterUV()[axis]; // Find the range of coordinates that are shared between child cells // along that axis. R1Interval shared = new(center - padding, center + padding); double mid = SampleInterval(shared.Intersection(max_rect[axis])); a = a.SetAxis(axis, SampleInterval(new R1Interval(max_rect[axis].Lo, mid))); b = b.SetAxis(axis, SampleInterval(new R1Interval(mid, max_rect[axis].Hi))); } R2Rect rect = R2Rect.FromPointPair(a, b); // Choose an arbitrary ancestor as the S2PaddedCell. S2CellId initial_id = result.Parent(S2Testing.Random.Uniform(result.Level() + 1)); Assert.Equal(result, new S2PaddedCell(initial_id, padding).ShrinkToFit(rect)); } }
public void Test_S2ClosestCellQuery_EmptyTargetOptimized() { // Ensure that the optimized algorithm handles empty targets when a distance // limit is specified. S2CellIndex index = new(); for (int i = 0; i < 1000; ++i) { index.Add(S2Testing.GetRandomCellId(), i); } index.Build(); S2ClosestCellQuery query = new(index); query.Options_.MaxDistance = new S1ChordAngle(S1Angle.FromRadians(1e-5)); MutableS2ShapeIndex target_index = new(); S2ClosestCellQuery.ShapeIndexTarget target = new(target_index); Assert.Empty(query.FindClosestCells(target)); }
public void Test_S2Cell_RectBoundIsLargeEnough() { // Construct many points that are nearly on an S2Cell edge, and verify that // whenever the cell contains a point P then its bound contains S2LatLng(P). for (int iter = 0; iter < 1000; /* advanced in loop below */) { S2Cell cell = new(S2Testing.GetRandomCellId()); int i = S2Testing.Random.Uniform(4); S2Point v1 = cell.Vertex(i); S2Point v2 = S2Testing.SamplePoint( new S2Cap(cell.Vertex(i + 1), S1Angle.FromRadians(1e-15))); S2Point p = S2.Interpolate(S2Testing.Random.RandDouble(), v1, v2); if (new S2Loop(cell).Contains(p)) { Assert.True(cell.GetRectBound().Contains(new S2LatLng(p))); ++iter; } } }
public void Test_S2Cell_GetDistanceToPoint() { S2Testing.Random.Reset(S2Testing.Random.RandomSeed); for (int iter = 0; iter < 1000; ++iter) { _logger.WriteLine($"Iteration {iter}"); S2Cell cell = new(S2Testing.GetRandomCellId()); S2Point target = S2Testing.RandomPoint(); S1Angle expected_to_boundary = GetDistanceToPointBruteForce(cell, target).ToAngle(); S1Angle expected_to_interior = cell.Contains(target) ? S1Angle.Zero : expected_to_boundary; S1Angle expected_max = GetMaxDistanceToPointBruteForce(cell, target).ToAngle(); S1Angle actual_to_boundary = cell.BoundaryDistance(target).ToAngle(); S1Angle actual_to_interior = cell.Distance(target).ToAngle(); S1Angle actual_max = cell.MaxDistance(target).ToAngle(); // The error has a peak near Pi/2 for edge distance, and another peak near // Pi for vertex distance. Assert2.Near(expected_to_boundary.Radians, actual_to_boundary.Radians, 1e-12); Assert2.Near(expected_to_interior.Radians, actual_to_interior.Radians, 1e-12); Assert2.Near(expected_max.Radians, actual_max.Radians, 1e-12); if (expected_to_boundary.Radians <= Math.PI / 3) { Assert2.Near(expected_to_boundary.Radians, actual_to_boundary.Radians, S2.DoubleError); Assert2.Near(expected_to_interior.Radians, actual_to_interior.Radians, S2.DoubleError); } if (expected_max.Radians <= Math.PI / 3) { Assert2.Near(expected_max.Radians, actual_max.Radians, S2.DoubleError); } } }
public void Test_S2Cell_CellVsLoopRectBound() { // This test verifies that the S2Cell and S2Loop bounds contain each other // to within their maximum errors. // // The S2Cell and S2Loop calculations for the latitude of a vertex can differ // by up to 2 * S2Constants.DoubleEpsilon, therefore the S2Cell bound should never exceed // the S2Loop bound by more than this (the reverse is not true, because the // S2Loop code sometimes thinks that the maximum occurs along an edge). // Similarly, the longitude bounds can differ by up to 4 * S2Constants.DoubleEpsilon since // the S2Cell bound has an error of 2 * S2Constants.DoubleEpsilon and then expands by this // amount, while the S2Loop bound does no expansion at all. for (int iter = 0; iter < 1000; ++iter) { S2Cell cell = new(S2Testing.GetRandomCellId()); S2Loop loop = new(cell); S2LatLngRect cell_bound = cell.GetRectBound(); S2LatLngRect loop_bound = loop.GetRectBound(); Assert.True(loop_bound.Expanded(kCellError).Contains(cell_bound)); Assert.True(cell_bound.Expanded(kLoopError).Contains(loop_bound)); } }
public void Test_GetCrossingCandidates_CollinearEdgesOnCellBoundaries() { int kNumEdgeIntervals = 8; // 9*8/2 = 36 edges for (int level = 0; level <= S2.kMaxCellLevel; ++level) { S2Cell cell = new(S2Testing.GetRandomCellId(level)); int j2 = S2Testing.Random.Uniform(4); S2Point p1 = cell.VertexRaw(j2); S2Point p2 = cell.VertexRaw(j2 + 1); S2Point delta = (p2 - p1) / kNumEdgeIntervals; List <(S2Point, S2Point)> edges = new(); for (int i = 0; i <= kNumEdgeIntervals; ++i) { for (int j = 0; j < i; ++j) { edges.Add(((p1 + i * delta).Normalize(), (p1 + j * delta).Normalize())); } } TestAllCrossings(edges); } }
public void Test_S2CellId_Tokens() { // Test random cell ids at all levels. for (int i = 0; i < 10000; ++i) { var id = S2Testing.GetRandomCellId(); var token = id.ToToken(); Assert.True(token.Length <= 16); Assert.Equal(id, S2CellId.FromToken(token)); Assert.Equal(id, S2CellId.FromToken(token)); } // Check that invalid cell ids can be encoded, and round-trip is // the identity operation. string token2 = S2CellId.None.ToToken(); Assert.Equal(S2CellId.None, S2CellId.FromToken(token2)); Assert.Equal(S2CellId.None, S2CellId.FromToken(token2)); // Sentinel is invalid. token2 = S2CellId.Sentinel.ToToken(); Assert.Equal(S2CellId.FromToken(token2), S2CellId.Sentinel); Assert.Equal(S2CellId.FromToken(token2), S2CellId.Sentinel); // Check an invalid face. token2 = S2CellId.FromFace(7).ToToken(); Assert.Equal(S2CellId.FromToken(token2), S2CellId.FromFace(7)); Assert.Equal(S2CellId.FromToken(token2), S2CellId.FromFace(7)); // Check that supplying tokens with non-alphanumeric characters // returns S2CellId.None. Assert.Equal(S2CellId.None, S2CellId.FromToken("876b e99")); Assert.Equal(S2CellId.None, S2CellId.FromToken("876bee99\n")); Assert.Equal(S2CellId.None, S2CellId.FromToken("876[ee99")); Assert.Equal(S2CellId.None, S2CellId.FromToken(" 876bee99")); }
public void Test_S2Cell_GetDistanceToEdge() { S2Testing.Random.Reset(S2Testing.Random.RandomSeed); for (int iter = 0; iter < 1000; ++iter) { _logger.WriteLine($"Iteration {iter}"); S2Cell cell = new(S2Testing.GetRandomCellId()); ChooseEdgeNearCell(cell, out var a, out var b); S1Angle expected_min = GetDistanceToEdgeBruteForce(cell, a, b).ToAngle(); S1Angle expected_max = GetMaxDistanceToEdgeBruteForce(cell, a, b).ToAngle(); S1Angle actual_min = cell.Distance(a, b).ToAngle(); S1Angle actual_max = cell.MaxDistance(a, b).ToAngle(); // The error has a peak near Pi/2 for edge distance, and another peak near // Pi for vertex distance. if (expected_min.Radians > S2.M_PI_2) { // Max error for S1ChordAngle as it approaches Pi is about 2e-8. Assert2.Near(expected_min.Radians, actual_min.Radians, 2e-8); } else if (expected_min.Radians <= Math.PI / 3) { Assert2.Near(expected_min.Radians, actual_min.Radians, S2.DoubleError); } else { Assert2.Near(expected_min.Radians, actual_min.Radians, 1e-12); } Assert2.Near(expected_max.Radians, actual_max.Radians, 1e-12); if (expected_max.Radians <= Math.PI / 3) { Assert2.Near(expected_max.Radians, actual_max.Radians, S2.DoubleError); } } }
public void Test_S2PaddedCell_S2CellMethods() { // Test the S2PaddedCell methods that have approximate S2Cell equivalents. int kIters = 1000; for (int iter = 0; iter < kIters; ++iter) { S2CellId id = S2Testing.GetRandomCellId(); double padding = Math.Pow(1e-15, S2Testing.Random.RandDouble()); S2Cell cell = new(id); S2PaddedCell pcell = new(id, padding); CompareS2CellToPadded(cell, pcell, padding); if (!id.IsLeaf()) { var children = new S2Cell[4]; Assert.True(cell.Subdivide(children)); for (int pos = 0; pos < 4; ++pos) { pcell.GetChildIJ(pos, out var i, out var j); CompareS2CellToPadded(children[pos], new S2PaddedCell(pcell, i, j), padding); } } } }
public void Test_S2CellId_MaximumTile() { // This method is tested more thoroughly in s2cell_union_test.cc. for (int iter = 0; iter < 1000; ++iter) { S2CellId id = S2Testing.GetRandomCellId(10); // Check that "limit" is returned for tiles at or beyond "limit". Assert.Equal(id, id.MaximumTile(id)); Assert.Equal(id, id.Child(0).MaximumTile(id)); Assert.Equal(id, id.Child(1).MaximumTile(id)); Assert.Equal(id, id.Next().MaximumTile(id)); Assert.Equal(id.Child(0), id.MaximumTile(id.Child(0))); // Check that the tile size is increased when possible. Assert.Equal(id, id.Child(0).MaximumTile(id.Next())); Assert.Equal(id, id.Child(0).MaximumTile(id.Next().Child(0))); Assert.Equal(id, id.Child(0).MaximumTile(id.Next().Child(1).Child(0))); Assert.Equal(id, id.Child(0).Child(0).MaximumTile(id.Next())); Assert.Equal(id, id.Child(0).Child(0).Child(0).MaximumTile(id.Next())); // Check that the tile size is decreased when necessary. Assert.Equal(id.Child(0), id.MaximumTile(id.Child(0).Next())); Assert.Equal(id.Child(0), id.MaximumTile(id.Child(0).Next().Child(0))); Assert.Equal(id.Child(0), id.MaximumTile(id.Child(0).Next().Child(1))); Assert.Equal(id.Child(0).Child(0), id.MaximumTile(id.Child(0).Child(0).Next())); Assert.Equal(id.Child(0).Child(0).Child(0), id.MaximumTile(id.Child(0).Child(0).Child(0).Next())); // Check that the tile size is otherwise unchanged. Assert.Equal(id, id.MaximumTile(id.Next())); Assert.Equal(id, id.MaximumTile(id.Next().Child(0))); Assert.Equal(id, id.MaximumTile(id.Next().Child(1).Child(0))); } }
public void Test_S2CellId_Neighbors() { // Check the edge neighbors of face 1. var out_faces = new[] { 5, 3, 2, 0 }; var face_nbrs = new S2CellId[4]; S2CellId.FromFace(1).EdgeNeighbors(face_nbrs); for (int i = 0; i < 4; ++i) { Assert.True(face_nbrs[i].IsFace()); Assert.Equal(out_faces[i], (int)face_nbrs[i].Face()); } // Check the edge neighbors of the corner cells at all levels. This case is // trickier because it requires projecting onto adjacent faces. const int kMaxIJ = S2CellId.kMaxSize - 1; for (int level = 1; level <= S2.kMaxCellLevel; ++level) { S2CellId id2 = S2CellId.FromFaceIJ(1, 0, 0).Parent(level); var nbrs2 = new S2CellId[4]; id2.EdgeNeighbors(nbrs2); // These neighbors were determined manually using the face and axis // relationships defined in s2coords.cc. int size_ij = S2CellId.SizeIJ(level); Assert.Equal(S2CellId.FromFaceIJ(5, kMaxIJ, kMaxIJ).Parent(level), nbrs2[0]); Assert.Equal(S2CellId.FromFaceIJ(1, size_ij, 0).Parent(level), nbrs2[1]); Assert.Equal(S2CellId.FromFaceIJ(1, 0, size_ij).Parent(level), nbrs2[2]); Assert.Equal(S2CellId.FromFaceIJ(0, kMaxIJ, 0).Parent(level), nbrs2[3]); } // Check the vertex neighbors of the center of face 2 at level 5. var nbrs = new List <S2CellId>(); new S2CellId(new S2Point(0, 0, 1)).AppendVertexNeighbors(5, nbrs); nbrs.Sort(); for (int i = 0; i < 4; ++i) { Assert.Equal(S2CellId.FromFaceIJ( 2, (1 << 29) - ((i < 2) ? 1 : 0), (1 << 29) - ((i == 0 || i == 3) ? 1 : 0)) .Parent(5), nbrs[i]); } nbrs.Clear(); // Check the vertex neighbors of the corner of faces 0, 4, and 5. S2CellId id1 = S2CellId.FromFacePosLevel(0, 0, S2.kMaxCellLevel); id1.AppendVertexNeighbors(0, nbrs); nbrs.Sort(); Assert.Equal(3, nbrs.Count); Assert.Equal(S2CellId.FromFace(0), nbrs[0]); Assert.Equal(S2CellId.FromFace(4), nbrs[1]); Assert.Equal(S2CellId.FromFace(5), nbrs[2]); // Check that AppendAllNeighbors produces results that are consistent // with AppendVertexNeighbors for a bunch of random cells. for (var i = 0; i < 1000; ++i) { S2CellId id2 = S2Testing.GetRandomCellId(); if (id2.IsLeaf()) { id2 = id2.Parent(); } // TestAllNeighbors computes approximately 2**(2*(diff+1)) cell ids, // so it's not reasonable to use large values of "diff". int max_diff = Math.Min(5, S2.kMaxCellLevel - id2.Level() - 1); int level = id2.Level() + S2Testing.Random.Uniform(max_diff + 1); TestAllNeighbors(id2, level); } }