summaryrefslogtreecommitdiff
path: root/vendor/github.com/golang/geo/s1
diff options
context:
space:
mode:
authorFelix Hanley <felix@userspace.com.au>2017-03-19 15:19:42 +0000
committerFelix Hanley <felix@userspace.com.au>2017-03-19 15:19:42 +0000
commit8a541d499b6f117cd3a81e475ee779ba60fc0637 (patch)
tree7b3b5326235725ab93056b5ff4637d987fb0a7b6 /vendor/github.com/golang/geo/s1
parentfe847b2d01060044274d20d2c35ae01a684d4ee3 (diff)
downloadcrjw-maps-8a541d499b6f117cd3a81e475ee779ba60fc0637.tar.gz
crjw-maps-8a541d499b6f117cd3a81e475ee779ba60fc0637.tar.bz2
use golang dep tool for depsHEADmaster
Diffstat (limited to 'vendor/github.com/golang/geo/s1')
-rw-r--r--vendor/github.com/golang/geo/s1/LICENSE202
-rw-r--r--vendor/github.com/golang/geo/s1/angle_test.go169
-rw-r--r--vendor/github.com/golang/geo/s1/chordangle.go12
-rw-r--r--vendor/github.com/golang/geo/s1/chordangle_test.go226
-rw-r--r--vendor/github.com/golang/geo/s1/interval_test.go457
5 files changed, 861 insertions, 205 deletions
diff --git a/vendor/github.com/golang/geo/s1/LICENSE b/vendor/github.com/golang/geo/s1/LICENSE
deleted file mode 100644
index d645695..0000000
--- a/vendor/github.com/golang/geo/s1/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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.
diff --git a/vendor/github.com/golang/geo/s1/angle_test.go b/vendor/github.com/golang/geo/s1/angle_test.go
new file mode 100644
index 0000000..2dd2e58
--- /dev/null
+++ b/vendor/github.com/golang/geo/s1/angle_test.go
@@ -0,0 +1,169 @@
+/*
+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"
+)
+
+// float64Eq reports whether the two values are within the default epsilon.
+func float64Eq(x, y float64) bool {
+ return float64Near(x, y, epsilon)
+}
+
+// float64Near reports whether the two values are within the specified epsilon.
+func float64Near(x, y, eps float64) bool {
+ return math.Abs(x-y) <= eps
+}
+
+func TestEmptyValue(t *testing.T) {
+ var a Angle
+ if rad := a.Radians(); rad != 0 {
+ t.Errorf("Empty value of Angle was %v, want 0", rad)
+ }
+}
+
+func TestPiRadiansExactly180Degrees(t *testing.T) {
+ if rad := (math.Pi * Radian).Radians(); rad != math.Pi {
+ t.Errorf("(π * Radian).Radians() was %v, want π", rad)
+ }
+ if deg := (math.Pi * Radian).Degrees(); deg != 180 {
+ t.Errorf("(π * Radian).Degrees() was %v, want 180", deg)
+ }
+ if rad := (180 * Degree).Radians(); rad != math.Pi {
+ t.Errorf("(180 * Degree).Radians() was %v, want π", rad)
+ }
+ if deg := (180 * Degree).Degrees(); deg != 180 {
+ t.Errorf("(180 * Degree).Degrees() was %v, want 180", deg)
+ }
+
+ if deg := (math.Pi / 2 * Radian).Degrees(); deg != 90 {
+ t.Errorf("(π/2 * Radian).Degrees() was %v, want 90", deg)
+ }
+
+ // Check negative angles.
+ if deg := (-math.Pi / 2 * Radian).Degrees(); deg != -90 {
+ t.Errorf("(-π/2 * Radian).Degrees() was %v, want -90", deg)
+ }
+ if rad := (-45 * Degree).Radians(); rad != -math.Pi/4 {
+ t.Errorf("(-45 * Degree).Radians() was %v, want -π/4", rad)
+ }
+}
+
+func TestE5E6E7Representation(t *testing.T) {
+ // NOTE(dsymonds): This first test gives a variance in the 16th decimal place. I should track that down.
+ exp, act := (-45 * Degree).Radians(), (-4500000 * E5).Radians()
+ if math.Abs(exp-act) > 1e-15 {
+ t.Errorf("(-4500000 * E5).Radians() was %v, want %v", act, exp)
+ }
+ if exp, act := (-60 * Degree).Radians(), (-60000000 * E6).Radians(); exp != act {
+ t.Errorf("(-60000000 * E6).Radians() was %v, want %v", act, exp)
+ }
+ if exp, act := (75 * Degree).Radians(), (750000000 * E7).Radians(); exp != act {
+ t.Errorf("(-750000000 * E7).Radians() was %v, want %v", act, exp)
+ }
+
+ if exp, act := int32(-17256123), (-172.56123 * Degree).E5(); exp != act {
+ t.Errorf("(-172.56123°).E5() was %v, want %v", act, exp)
+ }
+ if exp, act := int32(12345678), (12.345678 * Degree).E6(); exp != act {
+ t.Errorf("(12.345678°).E6() was %v, want %v", act, exp)
+ }
+ if exp, act := int32(-123456789), (-12.3456789 * Degree).E7(); exp != act {
+ t.Errorf("(-12.3456789°).E7() was %v, want %v", act, exp)
+ }
+
+ roundingTests := []struct {
+ have Angle
+ want int32
+ }{
+ {0.500000001, 1},
+ {-0.500000001, -1},
+ {0.499999999, 0},
+ {-0.499999999, 0},
+ }
+ for _, test := range roundingTests {
+ if act := (test.have * 1e-5 * Degree).E5(); test.want != act {
+ t.Errorf("(%v°).E5() was %v, want %v", test.have, act, test.want)
+ }
+ if act := (test.have * 1e-6 * Degree).E6(); test.want != act {
+ t.Errorf("(%v°).E6() was %v, want %v", test.have, act, test.want)
+ }
+ if act := (test.have * 1e-7 * Degree).E7(); test.want != act {
+ t.Errorf("(%v°).E7() was %v, want %v", test.have, act, test.want)
+ }
+ }
+}
+
+func TestNormalizeCorrectlyCanonicalizesAngles(t *testing.T) {
+ tests := []struct {
+ in, want float64 // both in degrees
+ }{
+ {360, 0},
+ {-180, 180},
+ {180, 180},
+ {540, 180},
+ {-270, 90},
+ }
+ for _, test := range tests {
+ deg := (Angle(test.in) * Degree).Normalized().Degrees()
+ if deg != test.want {
+ t.Errorf("Normalized %.0f° = %v, want %v", test.in, deg, test.want)
+ }
+ }
+}
+
+func TestAngleString(t *testing.T) {
+ if s, exp := (180 * Degree).String(), "180.0000000"; s != exp {
+ t.Errorf("(180°).String() = %q, want %q", s, exp)
+ }
+}
+
+func TestDegreesVsRadians(t *testing.T) {
+ // This test tests the exactness of specific values between degrees and radians.
+ for k := -8; k <= 8; k++ {
+ if got, want := Angle(45*k)*Degree, Angle((float64(k)*math.Pi)/4)*Radian; got != want {
+ t.Errorf("45°*%d != (%d*π)/4 radians (%f vs %f)", k, k, got, want)
+ }
+
+ if got, want := (Angle(45*k) * Degree).Degrees(), float64(45*k); got != want {
+ t.Errorf("Angle(45°*%d).Degrees() != 45*%d, (%f vs %f)", k, k, got, want)
+ }
+ }
+
+ for k := uint64(0); k < 30; k++ {
+ m := 1 << k
+ n := float64(m)
+ for _, test := range []struct{ deg, rad float64 }{
+ {180, 1},
+ {60, 3},
+ {36, 5},
+ {20, 9},
+ {4, 45},
+ } {
+ if got, want := Angle(test.deg/n)*Degree, Angle(math.Pi/(test.rad*n))*Radian; got != want {
+ t.Errorf("%v°/%d != π/%v*%d rad (%f vs %f)", test.deg, m, test.rad, m, got, want)
+ }
+ }
+ }
+
+ // We also spot check a non-identity.
+ if got := (60 * Degree).Degrees(); float64Eq(got, 60) {
+ t.Errorf("Angle(60).Degrees() == 60, but should not (%f vs %f)", got, 60.0)
+ }
+}
diff --git a/vendor/github.com/golang/geo/s1/chordangle.go b/vendor/github.com/golang/geo/s1/chordangle.go
index 5f5832a..7fe06bb 100644
--- a/vendor/github.com/golang/geo/s1/chordangle.go
+++ b/vendor/github.com/golang/geo/s1/chordangle.go
@@ -164,8 +164,8 @@ func (c ChordAngle) Add(other ChordAngle) ChordAngle {
return ChordAngle(math.Min(4.0, x+y+2*math.Sqrt(x*y)))
}
-// Sub subtracts the other ChordAngle from this one and returns the resulting value.
-// This method assumes the ChordAngles are not special.
+// Sub subtracts the other ChordAngle from this one and returns the resulting
+// value. This method assumes the ChordAngles are not special.
func (c ChordAngle) Sub(other ChordAngle) ChordAngle {
if other == 0 {
return c
@@ -181,12 +181,18 @@ func (c ChordAngle) Sub(other ChordAngle) ChordAngle {
// Sin returns the sine of this chord angle. This method is more efficient
// than converting to Angle and performing the computation.
func (c ChordAngle) Sin() float64 {
+ return math.Sqrt(c.Sin2())
+}
+
+// Sin2 returns the square of the sine of this chord angle.
+// It is more efficient than Sin.
+func (c ChordAngle) Sin2() float64 {
// Let a be the (non-squared) chord length, and let A be the corresponding
// half-angle (a = 2*sin(A)). The formula below can be derived from:
// sin(2*A) = 2 * sin(A) * cos(A)
// cos^2(A) = 1 - sin^2(A)
// This is much faster than converting to an angle and computing its sine.
- return math.Sqrt(float64(c * (1 - 0.25*c)))
+ return float64(c * (1 - 0.25*c))
}
// Cos returns the cosine of this chord angle. This method is more efficient
diff --git a/vendor/github.com/golang/geo/s1/chordangle_test.go b/vendor/github.com/golang/geo/s1/chordangle_test.go
new file mode 100644
index 0000000..11c585d
--- /dev/null
+++ b/vendor/github.com/golang/geo/s1/chordangle_test.go
@@ -0,0 +1,226 @@
+/*
+Copyright 2015 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"
+)
+
+func TestChordAngleBasics(t *testing.T) {
+ var zeroChord ChordAngle
+ tests := []struct {
+ a, b ChordAngle
+ lessThan bool
+ equal bool
+ }{
+ {NegativeChordAngle, NegativeChordAngle, false, true},
+ {NegativeChordAngle, zeroChord, true, false},
+ {NegativeChordAngle, StraightChordAngle, true, false},
+ {NegativeChordAngle, InfChordAngle(), true, false},
+
+ {zeroChord, zeroChord, false, true},
+ {zeroChord, StraightChordAngle, true, false},
+ {zeroChord, InfChordAngle(), true, false},
+
+ {StraightChordAngle, StraightChordAngle, false, true},
+ {StraightChordAngle, InfChordAngle(), true, false},
+
+ {InfChordAngle(), InfChordAngle(), false, true},
+ {InfChordAngle(), InfChordAngle(), false, true},
+ }
+
+ for _, test := range tests {
+ if got := test.a < test.b; got != test.lessThan {
+ t.Errorf("%v should be less than %v", test.a, test.b)
+ }
+ if got := test.a == test.b; got != test.equal {
+ t.Errorf("%v should be equal to %v", test.a, test.b)
+ }
+ }
+}
+
+func TestChordAngleIsFunctions(t *testing.T) {
+ var zeroChord ChordAngle
+ tests := []struct {
+ have ChordAngle
+ isNegative bool
+ isZero bool
+ isInf bool
+ isSpecial bool
+ }{
+ {zeroChord, false, true, false, false},
+ {NegativeChordAngle, true, false, false, true},
+ {zeroChord, false, true, false, false},
+ {StraightChordAngle, false, false, false, false},
+ {InfChordAngle(), false, false, true, true},
+ }
+
+ for _, test := range tests {
+ if got := test.have < 0; got != test.isNegative {
+ t.Errorf("%v.isNegative() = %t, want %t", test.have, got, test.isNegative)
+ }
+ if got := test.have == 0; got != test.isZero {
+ t.Errorf("%v.isZero() = %t, want %t", test.have, got, test.isZero)
+ }
+ if got := test.have.isInf(); got != test.isInf {
+ t.Errorf("%v.isInf() = %t, want %t", test.have, got, test.isInf)
+ }
+ if got := test.have.isSpecial(); got != test.isSpecial {
+ t.Errorf("%v.isSpecial() = %t, want %t", test.have, got, test.isSpecial)
+ }
+ }
+}
+
+func TestChordAngleFromAngle(t *testing.T) {
+ for _, angle := range []float64{0, 1, -1, math.Pi} {
+ if got := ChordAngleFromAngle(Angle(angle)).Angle().Radians(); got != angle {
+ t.Errorf("ChordAngleFromAngle(Angle(%v)) = %v, want %v", angle, got, angle)
+ }
+ }
+
+ if got := ChordAngleFromAngle(Angle(math.Pi)); got != StraightChordAngle {
+ t.Errorf("a ChordAngle from an Angle of π = %v, want %v", got, StraightChordAngle)
+ }
+
+ if InfAngle() != ChordAngleFromAngle(InfAngle()).Angle() {
+ t.Errorf("converting infinite Angle to ChordAngle should yield infinite Angle")
+ }
+}
+
+func TestChordAngleArithmetic(t *testing.T) {
+ var (
+ zero ChordAngle
+ degree30 = ChordAngleFromAngle(30 * Degree)
+ degree60 = ChordAngleFromAngle(60 * Degree)
+ degree90 = ChordAngleFromAngle(90 * Degree)
+ degree120 = ChordAngleFromAngle(120 * Degree)
+ degree180 = StraightChordAngle
+ )
+
+ addTests := []struct {
+ a, b ChordAngle
+ want ChordAngle
+ }{
+ {zero, zero, zero},
+ {degree60, zero, degree60},
+ {zero, degree60, degree60},
+ {degree30, degree60, degree90},
+ {degree60, degree30, degree90},
+ {degree180, zero, degree180},
+ {degree60, degree30, degree90},
+ {degree90, degree90, degree180},
+ {degree120, degree90, degree180},
+ {degree120, degree120, degree180},
+ {degree30, degree180, degree180},
+ {degree180, degree180, degree180},
+ }
+
+ subTests := []struct {
+ a, b ChordAngle
+ want ChordAngle
+ }{
+ {zero, zero, zero},
+ {degree60, degree60, zero},
+ {degree180, degree180, zero},
+ {zero, degree60, zero},
+ {degree30, degree90, zero},
+ {degree90, degree30, degree60},
+ {degree90, degree60, degree30},
+ {degree180, zero, degree180},
+ }
+
+ for _, test := range addTests {
+ if got := float64(test.a.Add(test.b)); !float64Eq(got, float64(test.want)) {
+ t.Errorf("%v.Add(%v) = %0.24f, want %0.24f", test.a.Angle().Degrees(), test.b.Angle().Degrees(), got, test.want)
+ }
+ }
+ for _, test := range subTests {
+ if got := float64(test.a.Sub(test.b)); !float64Eq(got, float64(test.want)) {
+ t.Errorf("%v.Sub(%v) = %0.24f, want %0.24f", test.a.Angle().Degrees(), test.b.Angle().Degrees(), got, test.want)
+ }
+ }
+}
+
+func TestChordAngleTrigonometry(t *testing.T) {
+ // Because of the way the math works out, the 9/10th's case has slightly more
+ // difference than all the other computations, so this gets a more generous
+ // epsilon to deal with that.
+ const epsilon = 1e-14
+ const iters = 40
+ for iter := 0; iter <= iters; iter++ {
+ radians := math.Pi * float64(iter) / float64(iters)
+ angle := ChordAngleFromAngle(Angle(radians))
+ if !float64Near(math.Sin(radians), angle.Sin(), epsilon) {
+ t.Errorf("(%d/%d)*π. %v.Sin() = %v, want %v", iter, iters, angle, angle.Sin(), math.Sin(radians))
+ }
+ if !float64Near(math.Cos(radians), angle.Cos(), epsilon) {
+ t.Errorf("(%d/%d)*π. %v.Cos() = %v, want %v", iter, iters, angle, angle.Cos(), math.Cos(radians))
+ }
+ // Since tan(x) is unbounded near pi/4, we map the result back to an
+ // angle before comparing. The assertion is that the result is equal to
+ // the tangent of a nearby angle.
+ if !float64Near(math.Atan(math.Tan(radians)), math.Atan(angle.Tan()), 1e-14) {
+ t.Errorf("(%d/%d)*π. %v.Tan() = %v, want %v", iter, iters, angle, angle.Tan(), math.Tan(radians))
+ }
+ }
+
+ // Unlike Angle, ChordAngle can represent 90 and 180 degrees exactly.
+ angle90 := ChordAngleFromSquaredLength(2)
+ angle180 := ChordAngleFromSquaredLength(4)
+ if !float64Eq(1, angle90.Sin()) {
+ t.Errorf("%v.Sin() = %v, want 1", angle90, angle90.Sin())
+ }
+ if !float64Eq(0, angle90.Cos()) {
+ t.Errorf("%v.Cos() = %v, want 0", angle90, angle90.Cos())
+ }
+ if !math.IsInf(angle90.Tan(), 0) {
+ t.Errorf("%v.Tan() should be infinite, but was not.", angle90)
+ }
+ if !float64Eq(0, angle180.Sin()) {
+ t.Errorf("%v.Sin() = %v, want 0", angle180, angle180.Sin())
+ }
+ if !float64Eq(-1, angle180.Cos()) {
+ t.Errorf("%v.Cos() = %v, want -1", angle180, angle180.Cos())
+ }
+ if !float64Eq(0, angle180.Tan()) {
+ t.Errorf("%v.Tan() = %v, want 0", angle180, angle180.Tan())
+ }
+}
+
+func TestChordAngleExpanded(t *testing.T) {
+ var zero ChordAngle
+
+ tests := []struct {
+ have ChordAngle
+ add float64
+ want ChordAngle
+ }{
+ {NegativeChordAngle, 5, NegativeChordAngle.Expanded(5)},
+ {InfChordAngle(), -5, InfChordAngle()},
+ {StraightChordAngle, 5, ChordAngleFromSquaredLength(5)},
+ {zero, -5, zero},
+ {ChordAngleFromSquaredLength(1.25), 0.25, ChordAngleFromSquaredLength(1.5)},
+ {ChordAngleFromSquaredLength(0.75), 0.25, ChordAngleFromSquaredLength(1)},
+ }
+
+ for _, test := range tests {
+ if got := test.have.Expanded(test.add); got != test.want {
+ t.Errorf("%v.Expanded(%v) = %v, want %v", test.have, test.add, got, test.want)
+ }
+ }
+}
diff --git a/vendor/github.com/golang/geo/s1/interval_test.go b/vendor/github.com/golang/geo/s1/interval_test.go
new file mode 100644
index 0000000..9ec6ab3
--- /dev/null
+++ b/vendor/github.com/golang/geo/s1/interval_test.go
@@ -0,0 +1,457 @@
+/*
+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.
+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)
+ }
+}