func r2PointSlicesApproxEqual(a, b []r2.Point, epsilon float64) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if !r2PointsApproxEqual(a[i], b[i], epsilon) {
			return false
		}
	}
	return true
}
r2PointSlicesApproxEqual reports whether corresponding elements of the slices are approximately equal.
package s2
import (
	"flag"
	"io"
	"math"
	"math/rand"
	"os"
	"github.com/golang/geo/r1"
	"github.com/golang/geo/r2"
	"github.com/golang/geo/s1"
)
var (
	benchmarkBruteForce = flag.Bool("benchmark_brute_force", false,
		"When set, use brute force algorithms in benchmarking.")
)
func float64Eq(x, y float64) bool { return float64Near(x, y, epsilon) }
func float64Near(x, y, ε float64) bool {
	return math.Abs(x-y) <= ε
}
const earthRadiusKm = 6371.01
func kmToAngle(km float64) s1.Angle {
	return s1.Angle(km / earthRadiusKm)
}
func randomBits(num uint32) uint64 {
	if num > 63 {
		num = 63
	}
	return uint64(rand.Int63()) & ((1 << num) - 1)
}
func randomUint64() uint64 {
	return uint64(rand.Int63() | (rand.Int63() << 63))
}
func randomUint32() uint32 {
	return uint32(randomBits(32))
}
func randomFloat64() float64 {
	const randomFloatBits = 53
	return math.Ldexp(float64(randomBits(randomFloatBits)), -randomFloatBits)
}
func randomUniformInt(n int) int {
	return int(randomFloat64() * float64(n))
}
func randomUniformFloat64(min, max float64) float64 {
	return min + randomFloat64()*(max-min)
}
func oneIn(n int) bool {
	return randomUniformInt(n) == 0
}
func randomPoint() Point {
	return PointFromCoords(randomUniformFloat64(-1, 1),
		randomUniformFloat64(-1, 1), randomUniformFloat64(-1, 1))
}
func randomFrame() *matrix3x3 {
	return randomFrameAtPoint(randomPoint())
}
func randomFrameAtPoint(z Point) *matrix3x3 {
	x := Point{z.Cross(randomPoint().Vector).Normalize()}
	y := Point{z.Cross(x.Vector).Normalize()}
	m := &matrix3x3{}
	m.setCol(0, x)
	m.setCol(1, y)
	m.setCol(2, z)
	return m
}
func randomCellIDForLevel(level int) CellID {
	face := randomUniformInt(numFaces)
	pos := randomUint64() & uint64((1<<posBits)-1)
	return CellIDFromFacePosLevel(face, pos, level)
}
func randomCellID() CellID {
	return randomCellIDForLevel(randomUniformInt(maxLevel + 1))
}
func randomCellUnion(n int) CellUnion {
	var cu CellUnion
	for i := 0; i < n; i++ {
		cu = append(cu, randomCellID())
	}
	return cu
}
func concentricLoopsPolygon(center Point, numLoops, verticesPerLoop int) *Polygon {
	var loops []*Loop
	for li := 0; li < numLoops; li++ {
		radius := s1.Angle(0.005 * float64(li+1) / float64(numLoops))
		loops = append(loops, RegularLoop(center, radius, verticesPerLoop))
	}
	return PolygonFromLoops(loops)
}
func skewedInt(maxLog int) int {
	base := uint32(rand.Int31n(int32(maxLog + 1)))
	return int(randomBits(31) & ((1 << base) - 1))
}
func randomCap(minArea, maxArea float64) Cap {
	capArea := maxArea * math.Pow(minArea/maxArea, randomFloat64())
	return CapFromCenterArea(randomPoint(), capArea)
}
func latLngsApproxEqual(a, b LatLng, epsilon float64) bool {
	return float64Near(float64(a.Lat), float64(b.Lat), epsilon) &&
		float64Near(float64(a.Lng), float64(b.Lng), epsilon)
}
func pointsApproxEqual(a, b Point, epsilon float64) bool {
	return float64(a.Vector.Angle(b.Vector)) <= epsilon
}
func pointSlicesApproxEqual(a, b []Point, epsilon float64) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if !pointsApproxEqual(a[i], b[i], epsilon) {
			return false
		}
	}
	return true
}
func r1IntervalsApproxEqual(a, b r1.Interval, epsilon float64) bool {
	if a.IsEmpty() {
		return b.Length() <= 2*epsilon
	}
	if b.IsEmpty() {
		return a.Length() <= 2*epsilon
	}
	return math.Abs(b.Lo-a.Lo) <= epsilon &&
		math.Abs(b.Hi-a.Hi) <= epsilon
}
var (
	rectErrorLat = 10 * dblEpsilon
	rectErrorLng = dblEpsilon
)
func r2PointsApproxEqual(a, b r2.Point, epsilon float64) bool {
	return float64Near(a.X, b.X, epsilon) && float64Near(a.Y, b.Y, epsilon)
}