// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package s1

import (
	"math"
	"testing"
)

// Some standard intervals for use throughout the tests.
// These include the intervals spanning one or more "quadrants" which are
// numbered as follows:
//
//	quad1 == [0, π/2]
//	quad2 == [π/2, π]
//	quad3 == [-π, -π/2]
//	quad4 == [-π/2, 0]
var (
	empty = EmptyInterval()
	full  = FullInterval()
	// Single-point intervals:
	zero  = IntervalFromEndpoints(0, 0)
	pi2   = IntervalFromEndpoints(math.Pi/2, math.Pi/2)
	pi    = IntervalFromEndpoints(math.Pi, math.Pi)
	mipi  = IntervalFromEndpoints(-math.Pi, -math.Pi) // same as pi after normalization
	mipi2 = IntervalFromEndpoints(-math.Pi/2, -math.Pi/2)
	// Single quadrants:
	quad1 = IntervalFromEndpoints(0, math.Pi/2)
	quad2 = IntervalFromEndpoints(math.Pi/2, -math.Pi) // equivalent to (pi/2, pi)
	quad3 = IntervalFromEndpoints(math.Pi, -math.Pi/2)
	quad4 = IntervalFromEndpoints(-math.Pi/2, 0)
	// Quadrant pairs:
	quad12 = IntervalFromEndpoints(0, -math.Pi)
	quad23 = IntervalFromEndpoints(math.Pi/2, -math.Pi/2)
	quad34 = IntervalFromEndpoints(-math.Pi, 0)
	quad41 = IntervalFromEndpoints(-math.Pi/2, math.Pi/2)
	// Quadrant triples:
	quad123 = IntervalFromEndpoints(0, -math.Pi/2)
	quad234 = IntervalFromEndpoints(math.Pi/2, 0)
	quad341 = IntervalFromEndpoints(math.Pi, math.Pi/2)
	quad412 = IntervalFromEndpoints(-math.Pi/2, -math.Pi)
	// Small intervals around the midpoints between quadrants,
	// such that the center of each interval is offset slightly CCW from the midpoint.
	mid12 = IntervalFromEndpoints(math.Pi/2-0.01, math.Pi/2+0.02)
	mid23 = IntervalFromEndpoints(math.Pi-0.01, -math.Pi+0.02)
	mid34 = IntervalFromEndpoints(-math.Pi/2-0.01, -math.Pi/2+0.02)
	mid41 = IntervalFromEndpoints(-0.01, 0.02)
)

func TestConstructors(t *testing.T) {
	// Check that [-π,-π] is normalized to [π,π].
	if mipi.Lo != math.Pi {
		t.Errorf("mipi.Lo = %v, want π", mipi.Lo)
	}
	if mipi.Hi != math.Pi {
		t.Errorf("mipi.Hi = %v, want π", mipi.Lo)
	}

	var i Interval
	if !i.IsValid() {
		t.Errorf("Zero value Interval is not valid")
	}
}

func TestIntervalFromPointPair(t *testing.T) {
	tests := []struct {
		a, b float64
		want Interval
	}{
		{-math.Pi, math.Pi, pi},
		{math.Pi, -math.Pi, pi},
		{mid34.Hi, mid34.Lo, mid34},
		{mid23.Lo, mid23.Hi, mid23},
	}
	for _, test := range tests {
		got := IntervalFromPointPair(test.a, test.b)
		if got != test.want {
			t.Errorf("IntervalFromPointPair(%f, %f) = %v, want %v", test.a, test.b, got, test.want)
		}
	}
}

func TestSimplePredicates(t *testing.T) {
	if !zero.IsValid() || zero.IsEmpty() || zero.IsFull() {
		t.Errorf("Zero interval is invalid or empty or full")
	}
	if !empty.IsValid() || !empty.IsEmpty() || empty.IsFull() {
		t.Errorf("Empty interval is invalid or not empty or full")
	}
	if !empty.IsInverted() {
		t.Errorf("Empty interval is not inverted")
	}
	if !full.IsValid() || full.IsEmpty() || !full.IsFull() {
		t.Errorf("Full interval is invalid or empty or not full")
	}
	if !pi.IsValid() || pi.IsEmpty() || pi.IsInverted() {
		t.Errorf("pi is invalid or empty or inverted")
	}
	if !mipi.IsValid() || mipi.IsEmpty() || mipi.IsInverted() {
		t.Errorf("mipi is invalid or empty or inverted")
	}
}

func TestAlmostFullOrEmpty(t *testing.T) {
	// Test that rounding errors don't cause intervals that are almost empty or
	// full to be considered empty or full.  The following value is the greatest
	// representable value less than Pi.
	almostPi := math.Pi - 2*dblEpsilon

	i := Interval{-almostPi, math.Pi}
	if i.IsFull() {
		t.Errorf("%v.IsFull should not be true", i)
	}

	i = Interval{-math.Pi, almostPi}
	if i.IsFull() {
		t.Errorf("%v.IsFull should not be true", i)
	}

	i = Interval{math.Pi, -almostPi}
	if i.IsEmpty() {
		t.Errorf("%v.IsEmpty should not be true", i)
	}

	i = Interval{almostPi, -math.Pi}
	if i.IsEmpty() {
		t.Errorf("%v.IsEmpty should not be true", i)
	}
}

func TestCenter(t *testing.T) {
	tests := []struct {
		interval Interval
		want     float64
	}{
		{quad12, math.Pi / 2},
		{IntervalFromEndpoints(3.1, 2.9), 3 - math.Pi},
		{IntervalFromEndpoints(-2.9, -3.1), math.Pi - 3},
		{IntervalFromEndpoints(2.1, -2.1), math.Pi},
		{pi, math.Pi},
		{mipi, math.Pi},
		// TODO(dsymonds): The C++ test for quad23 uses fabs. Why?
		{quad23, math.Pi},
		// TODO(dsymonds): The C++ test for quad123 uses EXPECT_DOUBLE_EQ. Why?
		{quad123, 0.75 * math.Pi},
	}
	for _, test := range tests {
		got := test.interval.Center()
		// TODO(dsymonds): Some are inaccurate in the 16th decimal place. Track it down.
		if math.Abs(got-test.want) > 1e-15 {
			t.Errorf("%v.Center() = %v, want %v", test.interval, got, test.want)
		}
	}
}

func TestLength(t *testing.T) {
	tests := []struct {
		interval Interval
		want     float64
	}{
		{quad12, math.Pi},
		{pi, 0},
		{mipi, 0},
		// TODO(dsymonds): The C++ test for quad123 uses DOUBLE_EQ. Why?
		{quad123, 1.5 * math.Pi},
		// TODO(dsymonds): The C++ test for quad23 uses fabs. Why?
		{quad23, math.Pi},
		{full, 2 * math.Pi},
	}
	for _, test := range tests {
		if l := test.interval.Length(); l != test.want {
			t.Errorf("%v.Length() got %v, want %v", test.interval, l, test.want)
		}
	}
	if l := empty.Length(); l >= 0 {
		t.Errorf("empty interval has non-negative length %v", l)
	}
}

func TestContains(t *testing.T) {
	tests := []struct {
		interval  Interval
		in, out   []float64 // points that should be inside/outside the interval
		iIn, iOut []float64 // points that should be inside/outside the interior
	}{
		{empty, nil, []float64{0, math.Pi, -math.Pi}, nil, []float64{math.Pi, -math.Pi}},
		{full, []float64{0, math.Pi, -math.Pi}, nil, []float64{math.Pi, -math.Pi}, nil},
		{quad12, []float64{0, math.Pi, -math.Pi}, nil,
			[]float64{math.Pi / 2}, []float64{0, math.Pi, -math.Pi}},
		{quad23, []float64{math.Pi / 2, -math.Pi / 2, math.Pi, -math.Pi}, []float64{0},
			[]float64{math.Pi, -math.Pi}, []float64{math.Pi / 2, -math.Pi / 2, 0}},
		{pi, []float64{math.Pi, -math.Pi}, []float64{0}, nil, []float64{math.Pi, -math.Pi}},
		{mipi, []float64{math.Pi, -math.Pi}, []float64{0}, nil, []float64{math.Pi, -math.Pi}},
		{zero, []float64{0}, nil, nil, []float64{0}},
	}
	for _, test := range tests {
		for _, p := range test.in {
			if !test.interval.Contains(p) {
				t.Errorf("%v should contain %v", test.interval, p)
			}
		}
		for _, p := range test.out {
			if test.interval.Contains(p) {
				t.Errorf("%v should not contain %v", test.interval, p)
			}
		}
		for _, p := range test.iIn {
			if !test.interval.InteriorContains(p) {
				t.Errorf("interior of %v should contain %v", test.interval, p)
			}
		}
		for _, p := range test.iOut {
			if test.interval.InteriorContains(p) {
				t.Errorf("interior %v should not contain %v", test.interval, p)
			}
		}
	}
}

func TestIntervalOperations(t *testing.T) {
	quad12eps := IntervalFromEndpoints(quad12.Lo, mid23.Hi)
	quad2hi := IntervalFromEndpoints(mid23.Lo, quad12.Hi)
	quad412eps := IntervalFromEndpoints(mid34.Lo, quad12.Hi)
	quadeps12 := IntervalFromEndpoints(mid41.Lo, quad12.Hi)
	quad1lo := IntervalFromEndpoints(quad12.Lo, mid41.Hi)
	quad2lo := IntervalFromEndpoints(quad23.Lo, mid12.Hi)
	quad3hi := IntervalFromEndpoints(mid34.Lo, quad23.Hi)
	quadeps23 := IntervalFromEndpoints(mid12.Lo, quad23.Hi)
	quad23eps := IntervalFromEndpoints(quad23.Lo, mid34.Hi)
	quadeps123 := IntervalFromEndpoints(mid41.Lo, quad23.Hi)

	// This massive list of test cases is ported directly from the C++ test case.
	tests := []struct {
		x, y                               Interval
		xContainsY, xInteriorContainsY     bool
		xIntersectsY, xInteriorIntersectsY bool
		wantUnion, wantIntersection        Interval
	}{
		// 0
		{empty, empty, true, true, false, false, empty, empty},
		{empty, full, false, false, false, false, full, empty},
		{empty, zero, false, false, false, false, zero, empty},
		{empty, pi, false, false, false, false, pi, empty},
		{empty, mipi, false, false, false, false, mipi, empty},

		// 5
		{full, empty, true, true, false, false, full, empty},
		{full, full, true, true, true, true, full, full},
		{full, zero, true, true, true, true, full, zero},
		{full, pi, true, true, true, true, full, pi},
		{full, mipi, true, true, true, true, full, mipi},
		{full, quad12, true, true, true, true, full, quad12},
		{full, quad23, true, true, true, true, full, quad23},

		// 12
		{zero, empty, true, true, false, false, zero, empty},
		{zero, full, false, false, true, false, full, zero},
		{zero, zero, true, false, true, false, zero, zero},
		{zero, pi, false, false, false, false, IntervalFromEndpoints(0, math.Pi), empty},
		{zero, pi2, false, false, false, false, quad1, empty},
		{zero, mipi, false, false, false, false, quad12, empty},
		{zero, mipi2, false, false, false, false, quad4, empty},
		{zero, quad12, false, false, true, false, quad12, zero},
		{zero, quad23, false, false, false, false, quad123, empty},

		// 21
		{pi2, empty, true, true, false, false, pi2, empty},
		{pi2, full, false, false, true, false, full, pi2},
		{pi2, zero, false, false, false, false, quad1, empty},
		{pi2, pi, false, false, false, false, IntervalFromEndpoints(math.Pi/2, math.Pi), empty},
		{pi2, pi2, true, false, true, false, pi2, pi2},
		{pi2, mipi, false, false, false, false, quad2, empty},
		{pi2, mipi2, false, false, false, false, quad23, empty},
		{pi2, quad12, false, false, true, false, quad12, pi2},
		{pi2, quad23, false, false, true, false, quad23, pi2},

		// 30
		{pi, empty, true, true, false, false, pi, empty},
		{pi, full, false, false, true, false, full, pi},
		{pi, zero, false, false, false, false, IntervalFromEndpoints(math.Pi, 0), empty},
		{pi, pi, true, false, true, false, pi, pi},
		{pi, pi2, false, false, false, false, IntervalFromEndpoints(math.Pi/2, math.Pi), empty},
		{pi, mipi, true, false, true, false, pi, pi},
		{pi, mipi2, false, false, false, false, quad3, empty},
		{pi, quad12, false, false, true, false, IntervalFromEndpoints(0, math.Pi), pi},
		{pi, quad23, false, false, true, false, quad23, pi},

		// 39
		{mipi, empty, true, true, false, false, mipi, empty},
		{mipi, full, false, false, true, false, full, mipi},
		{mipi, zero, false, false, false, false, quad34, empty},
		{mipi, pi, true, false, true, false, mipi, mipi},
		{mipi, pi2, false, false, false, false, quad2, empty},
		{mipi, mipi, true, false, true, false, mipi, mipi},
		{mipi, mipi2, false, false, false, false, IntervalFromEndpoints(-math.Pi, -math.Pi/2), empty},
		{mipi, quad12, false, false, true, false, quad12, mipi},
		{mipi, quad23, false, false, true, false, quad23, mipi},

		// 48
		{quad12, empty, true, true, false, false, quad12, empty},
		{quad12, full, false, false, true, true, full, quad12},
		{quad12, zero, true, false, true, false, quad12, zero},
		{quad12, pi, true, false, true, false, quad12, pi},
		{quad12, mipi, true, false, true, false, quad12, mipi},
		{quad12, quad12, true, false, true, true, quad12, quad12},
		{quad12, quad23, false, false, true, true, quad123, quad2},
		{quad12, quad34, false, false, true, false, full, quad12},

		// 56
		{quad23, empty, true, true, false, false, quad23, empty},
		{quad23, full, false, false, true, true, full, quad23},
		{quad23, zero, false, false, false, false, quad234, empty},
		{quad23, pi, true, true, true, true, quad23, pi},
		{quad23, mipi, true, true, true, true, quad23, mipi},
		{quad23, quad12, false, false, true, true, quad123, quad2},
		{quad23, quad23, true, false, true, true, quad23, quad23},
		{quad23, quad34, false, false, true, true, quad234, IntervalFromEndpoints(-math.Pi, -math.Pi/2)},

		// 64
		{quad1, quad23, false, false, true, false, quad123, IntervalFromEndpoints(math.Pi/2, math.Pi/2)},
		{quad2, quad3, false, false, true, false, quad23, mipi},
		{quad3, quad2, false, false, true, false, quad23, pi},
		{quad2, pi, true, false, true, false, quad2, pi},
		{quad2, mipi, true, false, true, false, quad2, mipi},
		{quad3, pi, true, false, true, false, quad3, pi},
		{quad3, mipi, true, false, true, false, quad3, mipi},

		// 71
		{quad12, mid12, true, true, true, true, quad12, mid12},
		{mid12, quad12, false, false, true, true, quad12, mid12},

		// 73
		{quad12, mid23, false, false, true, true, quad12eps, quad2hi},
		{mid23, quad12, false, false, true, true, quad12eps, quad2hi},

		// This test checks that the union of two disjoint intervals is the smallest
		// interval that contains both of them.  Note that the center of "mid34"
		// slightly CCW of -Pi/2 so that there is no ambiguity about the result.
		// 75
		{quad12, mid34, false, false, false, false, quad412eps, empty},
		{mid34, quad12, false, false, false, false, quad412eps, empty},

		// 77
		{quad12, mid41, false, false, true, true, quadeps12, quad1lo},
		{mid41, quad12, false, false, true, true, quadeps12, quad1lo},

		// 79
		{quad23, mid12, false, false, true, true, quadeps23, quad2lo},
		{mid12, quad23, false, false, true, true, quadeps23, quad2lo},
		{quad23, mid23, true, true, true, true, quad23, mid23},
		{mid23, quad23, false, false, true, true, quad23, mid23},
		{quad23, mid34, false, false, true, true, quad23eps, quad3hi},
		{mid34, quad23, false, false, true, true, quad23eps, quad3hi},
		{quad23, mid41, false, false, false, false, quadeps123, empty},
		{mid41, quad23, false, false, false, false, quadeps123, empty},
	}
	should := func(b bool) string {
		if b {
			return "should"
		}
		return "should not"
	}
	for _, test := range tests {
		if test.x.ContainsInterval(test.y) != test.xContainsY {
			t.Errorf("%v %s contain %v", test.x, should(test.xContainsY), test.y)
		}
		if test.x.InteriorContainsInterval(test.y) != test.xInteriorContainsY {
			t.Errorf("interior of %v %s contain %v", test.x, should(test.xInteriorContainsY), test.y)
		}
		if test.x.Intersects(test.y) != test.xIntersectsY {
			t.Errorf("%v %s intersect %v", test.x, should(test.xIntersectsY), test.y)
		}
		if test.x.InteriorIntersects(test.y) != test.xInteriorIntersectsY {
			t.Errorf("interior of %v %s intersect %v", test.x, should(test.xInteriorIntersectsY), test.y)
		}
		if u := test.x.Union(test.y); u != test.wantUnion {
			t.Errorf("%v ∪ %v was %v, want %v", test.x, test.y, u, test.wantUnion)
		}
		if u := test.x.Intersection(test.y); u != test.wantIntersection {
			t.Errorf("%v ∩ %v was %v, want %v", test.x, test.y, u, test.wantIntersection)
		}
	}
}

func TestAddPoint(t *testing.T) {
	tests := []struct {
		interval Interval
		points   []float64
		want     Interval
	}{
		{empty, []float64{0}, zero},
		{empty, []float64{math.Pi}, pi},
		{empty, []float64{-math.Pi}, mipi},
		{empty, []float64{math.Pi, -math.Pi}, pi},
		{empty, []float64{-math.Pi, math.Pi}, mipi},
		{empty, []float64{mid12.Lo, mid12.Hi}, mid12},
		{empty, []float64{mid23.Lo, mid23.Hi}, mid23},

		{quad1, []float64{-0.9 * math.Pi, -math.Pi / 2}, quad123},
		{full, []float64{0}, full},
		{full, []float64{math.Pi}, full},
		{full, []float64{-math.Pi}, full},
	}
	for _, test := range tests {
		got := test.interval
		for _, point := range test.points {
			got = got.AddPoint(point)
		}
		want := test.want
		if math.Abs(got.Lo-want.Lo) > 1e-15 || math.Abs(got.Hi-want.Hi) > 1e-15 {
			t.Errorf("%v.AddPoint(%v) = %v, want %v", test.interval, test.points, got, want)
		}
	}
}

func TestExpanded(t *testing.T) {
	tests := []struct {
		interval Interval
		margin   float64
		want     Interval
	}{
		{empty, 1, empty},
		{full, 1, full},
		{zero, 1, Interval{-1, 1}},
		{mipi, 0.01, Interval{math.Pi - 0.01, -math.Pi + 0.01}},
		{pi, 27, full},
		{pi, math.Pi / 2, quad23},
		{pi2, math.Pi / 2, quad12},
		{mipi2, math.Pi / 2, quad34},

		{empty, -1, empty},
		{full, -1, full},
		{quad123, -27, empty},
		{quad234, -27, empty},
		{quad123, -math.Pi / 2, quad2},
		{quad341, -math.Pi / 2, quad4},
		{quad412, -math.Pi / 2, quad1},
	}
	for _, test := range tests {
		if got, want := test.interval.Expanded(test.margin), test.want; math.Abs(got.Lo-want.Lo) > 1e-15 || math.Abs(got.Hi-want.Hi) > 1e-15 {
			t.Errorf("%v.Expanded(%v) = %v, want %v", test.interval, test.margin, got, want)
		}
	}
}

func TestIntervalString(t *testing.T) {
	if s, exp := pi.String(), "[3.1415927, 3.1415927]"; s != exp {
		t.Errorf("pi.String() = %q, want %q", s, exp)
	}
}

func TestIntervalApproxEqual(t *testing.T) {
	// Choose two values lo and hi such that it's okay to shift an endpoint by
	// lo (i.e., the resulting interval is equivalent) but not by hi.
	lo := 4 * dblEpsilon // < epsilon default
	hi := 6 * dblEpsilon // > epsilon default

	tests := []struct {
		a, b Interval
		want bool
	}{
		// Empty intervals.
		{a: empty, b: empty, want: true},
		{a: zero, b: empty, want: true},
		{a: empty, b: zero, want: true},
		{a: pi, b: empty, want: true},
		{a: empty, b: pi, want: true},
		{a: mipi, b: empty, want: true},
		{a: empty, b: mipi, want: true},
		{a: empty, b: full, want: false},
		{a: empty, b: Interval{1, 1 + 2*lo}, want: true},
		{a: empty, b: Interval{1, 1 + 2*hi}, want: false},
		{a: Interval{math.Pi - lo, -math.Pi + lo}, b: empty, want: true},

		// Full intervals.
		{a: full, b: full, want: true},
		{a: full, b: empty, want: false},
		{a: full, b: zero, want: false},
		{a: full, b: pi, want: false},
		{a: full, b: Interval{lo, -lo}, want: true},
		{a: full, b: Interval{2 * hi, 0}, want: false},
		{a: Interval{-math.Pi + lo, math.Pi - lo}, b: full, want: true},
		{a: Interval{-math.Pi, math.Pi - 2*hi}, b: full, want: false},

		// Singleton intervals.
		{a: pi, b: pi, want: true},
		{a: mipi, b: pi, want: true},
		{a: pi, b: Interval{math.Pi - lo, math.Pi - lo}, want: true},
		{a: pi, b: Interval{math.Pi - hi, math.Pi - hi}, want: false},
		{a: pi, b: Interval{math.Pi - lo, -math.Pi + lo}, want: true},
		{a: pi, b: Interval{math.Pi - hi, -math.Pi}, want: false},
		{a: zero, b: pi, want: false},
		{a: pi.Union(mid12).Union(zero), b: quad12, want: true},
		{a: quad2.Intersection(quad3), b: pi, want: true},
		{a: quad3.Intersection(quad2), b: pi, want: true},

		// Intervals whose corresponding endpoints are nearly the same but where the
		// endpoints are in opposite order (i.e., inverted intervals).
		{a: Interval{0, lo}, b: Interval{lo, 0}, want: false},
		{a: Interval{math.Pi - 0.5*lo, -math.Pi + 0.5*lo}, b: Interval{-math.Pi + 0.5*lo, math.Pi - 0.5*lo}, want: false},

		// Other intervals.
		{a: Interval{1 - lo, 2 + lo}, b: Interval{1, 2}, want: true},
		{a: Interval{1 + lo, 2 - lo}, b: Interval{1, 2}, want: true},
		{a: Interval{2 - lo, 1 + lo}, b: Interval{2, 1}, want: true},
		{a: Interval{2 + lo, 1 - lo}, b: Interval{2, 1}, want: true},
		{a: Interval{1 - hi, 2 + lo}, b: Interval{1, 2}, want: false},
		{a: Interval{1 + hi, 2 - lo}, b: Interval{1, 2}, want: false},
		{a: Interval{2 - hi, 1 + lo}, b: Interval{2, 1}, want: false},
		{a: Interval{2 + hi, 1 - lo}, b: Interval{2, 1}, want: false},
		{a: Interval{1 - lo, 2 + hi}, b: Interval{1, 2}, want: false},
		{a: Interval{1 + lo, 2 - hi}, b: Interval{1, 2}, want: false},
		{a: Interval{2 - lo, 1 + hi}, b: Interval{2, 1}, want: false},
		{a: Interval{2 + lo, 1 - hi}, b: Interval{2, 1}, want: false},
	}

	for _, test := range tests {
		if got := test.a.ApproxEqual(test.b); got != test.want {
			t.Errorf("%v.ApproxEqual(%v) = %t, want %t", test.a, test.b, got, test.want)
		}
	}
}

func TestIntervalComplement(t *testing.T) {
	if !EmptyInterval().Complement().IsFull() {
		t.Error("empty interval's complement is not full, but should be")
	}
	if !FullInterval().Complement().IsEmpty() {
		t.Error("full interval's complement is not empty, but should be")
	}
	if !pi.Complement().IsFull() {
		t.Errorf("pi's (%v) complement is not full, but should be", pi)
	}
	if !mipi.Complement().IsFull() {
		t.Errorf("mipi's (%v) complement is not full, but should be", mipi)
	}
	if !zero.Complement().IsFull() {
		t.Errorf("zero's (%v) complement is not full, but should be", zero)
	}

	if got := quad12.Complement(); !got.ApproxEqual(quad34) {
		t.Errorf("%v.Complement = %v, want %v", quad12, got, quad34)
	}
	if got := quad34.Complement(); !got.ApproxEqual(quad12) {
		t.Errorf("%v.Complement = %v, want %v", quad34, got, quad12)
	}
	if got := quad123.Complement(); !got.ApproxEqual(quad4) {
		t.Errorf("%v.Complement = %v, want %v", quad123, got, quad4)
	}
}

func TestIntervalDirectedHausdorff(t *testing.T) {
	in := Interval{3.0, -3.0}
	tests := []struct {
		i, y Interval
		want Angle
	}{
		{Interval{-0.139626, 0.349066}, Interval{0.139626, 0.139626}, 0.279252 * Radian},
		{Interval{0.2, 0.4}, Interval{0.1, 0.5}, 0 * Radian},
		{Interval{0, 0}, EmptyInterval(), math.Pi * Radian},
		{empty, empty, 0.0},
		{empty, mid12, 0.0},
		{mid12, empty, math.Pi},
		{quad12, quad123, 0.0},
		{Interval{-0.1, 0.2}, in, 3.0},
		{Interval{0.1, 0.2}, in, 3.0 - 0.1},
		{Interval{-0.2, -0.1}, in, 3.0 - 0.1},
	}

	for _, test := range tests {
		got := test.i.DirectedHausdorffDistance(test.y)
		if math.Abs(float64(got-test.want)) > 1e-15 {
			t.Errorf("%v.DirectedHausdorffDistance(%v) = %v, want %v", test.i, test.y, got.Radians(), test.want.Radians())
		}
	}
}

func TestIntervalProject(t *testing.T) {
	r := IntervalFromEndpoints(-math.Pi, -math.Pi)
	r1 := IntervalFromEndpoints(0, math.Pi)
	r2 := IntervalFromEndpoints(math.Pi-0.1, -math.Pi+0.1)
	tests := []struct {
		interval Interval
		have     float64
		want     float64
	}{
		{r, -math.Pi, math.Pi},
		{r, 0, math.Pi},

		{r1, 0.1, 0.1},
		{r1, -math.Pi/2 + 1e-15, 0},
		{r1, -math.Pi/2 - 1e-15, math.Pi},
		{r2, math.Pi, math.Pi},
		{r2, 1e-15, math.Pi - 0.1},
		{r2, -1e-15, -math.Pi + 0.1},
		{full, 0, 0},
		{full, math.Pi, math.Pi},
		{full, -math.Pi, math.Pi},
	}

	for _, test := range tests {
		if got := test.interval.Project(test.have); !float64Eq(got, test.want) {
			t.Errorf("%v.Project(%v) = %v, want %v", test.interval, test.have, got, test.want)
		}
	}
}
