/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.lucene.spatial3d.geom;

import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.tests.geo.GeoTestUtil;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.junit.Test;

public class TestGeoBBox extends LuceneTestCase {

  protected static final double DEGREES_TO_RADIANS = Math.PI / 180.0;

  @Test
  public void testBBoxDegenerate() {
    GeoBBox box;
    int relationship;
    List<GeoPoint> points = new ArrayList<>();
    points.add(
        new GeoPoint(PlanetModel.SPHERE, -49 * DEGREES_TO_RADIANS, -176 * DEGREES_TO_RADIANS));
    points.add(
        new GeoPoint(PlanetModel.SPHERE, -11 * DEGREES_TO_RADIANS, 101 * DEGREES_TO_RADIANS));
    points.add(new GeoPoint(PlanetModel.SPHERE, 24 * DEGREES_TO_RADIANS, -30 * DEGREES_TO_RADIANS));
    GeoMembershipShape shape = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
    box =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE,
            -64 * DEGREES_TO_RADIANS,
            -64 * DEGREES_TO_RADIANS,
            -180 * DEGREES_TO_RADIANS,
            180 * DEGREES_TO_RADIANS);
    relationship = box.getRelationship(shape);
    assertEquals(GeoArea.CONTAINS, relationship);
    box =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE,
            -61.85 * DEGREES_TO_RADIANS,
            -67.5 * DEGREES_TO_RADIANS,
            -180 * DEGREES_TO_RADIANS,
            -168.75 * DEGREES_TO_RADIANS);
    // System.out.println("Shape = " + shape + " Rect = " + box);
    relationship = box.getRelationship(shape);
    assertEquals(GeoArea.CONTAINS, relationship);
  }

  @Test
  public void testBBoxPointWithin() {
    GeoBBox box;
    GeoPoint gp;

    // Standard normal Rect box, not crossing dateline
    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -1.0, 1.0);
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 0.0);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, 0.0);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 1.1);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.1);
    assertFalse(box.isWithin(gp));
    assertEquals(0.1, box.computeOutsideDistance(DistanceStyle.ARC, gp), 1e-2);
    assertEquals(0.1, box.computeOutsideDistance(DistanceStyle.NORMAL, gp), 1e-2);
    assertEquals(0.1, box.computeOutsideDistance(DistanceStyle.NORMAL, gp), 1e-2);

    // Standard normal Rect box, crossing dateline
    box =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, Math.PI - 1.0, -Math.PI + 1.0);
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, -Math.PI);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, (-Math.PI - 1.1) + Math.PI * 2.0);
    assertFalse(box.isWithin(gp));

    // Latitude zone rectangle
    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -Math.PI, Math.PI);
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, -Math.PI);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, (-Math.PI - 1.1) + Math.PI * 2.0);
    assertTrue(box.isWithin(gp));

    // World
    box =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, Math.PI);
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, -Math.PI);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, (-Math.PI - 1.1) + Math.PI * 2.0);
    assertTrue(box.isWithin(gp));
  }

  @Test
  public void testBBoxExpand() {
    GeoBBox box;
    GeoPoint gp;
    // Standard normal Rect box, not crossing dateline
    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -1.0, 1.0);
    box = box.expand(0.1);
    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.0);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, 0.15, 0.0);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.05, 0.0);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.15, 0.0);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.05);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.15);
    assertFalse(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 1.05);
    assertTrue(box.isWithin(gp));
    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 1.15);
    assertFalse(box.isWithin(gp));
  }

  @Test
  public void testBBoxBounds() {
    GeoBBox c;
    LatLonBounds b;
    XYZBounds xyzb;
    GeoArea solid;
    GeoPoint point;
    int relationship;

    c =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE,
            0.7570958596622309,
            -0.7458670829264561,
            -0.9566079379002148,
            1.4802570961901191);
    solid =
        GeoAreaFactory.makeGeoArea(
            PlanetModel.SPHERE,
            0.10922258701604912,
            0.1248184603754517,
            -0.8172414690802067,
            0.9959041483215542,
            -0.6136586624726926,
            0.6821740363641521);
    point = new GeoPoint(PlanetModel.SPHERE, 0.3719987557178081, 1.4529582778845198);
    assertTrue(c.isWithin(point));
    assertTrue(solid.isWithin(point));
    relationship = solid.getRelationship(c);
    assertTrue(
        relationship == GeoArea.OVERLAPS
            || relationship == GeoArea.CONTAINS
            || relationship == GeoArea.WITHIN);

    c =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE,
            0.006607096847842122,
            -0.002828135860810422,
            -0.0012934461873348349,
            0.006727418645092394);
    solid =
        GeoAreaFactory.makeGeoArea(
            PlanetModel.SPHERE,
            0.9999995988328008,
            1.0000000002328306,
            -0.0012934708508166816,
            0.006727393021214471,
            -0.002828157275369464,
            0.006607074060760007);
    point = new GeoPoint(PlanetModel.SPHERE, -5.236470872437899E-4, 3.992578692654256E-4);
    assertTrue(c.isWithin(point));
    assertTrue(solid.isWithin(point));
    relationship = solid.getRelationship(c);
    assertTrue(
        relationship == GeoArea.OVERLAPS
            || relationship == GeoArea.CONTAINS
            || relationship == GeoArea.WITHIN);

    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.25, -Math.PI * 0.25, -1.0, 1.0);
    b = new LatLonBounds();
    c.getBounds(b);
    xyzb = new XYZBounds();
    c.getBounds(xyzb);
    assertFalse(b.checkNoLongitudeBound());
    assertFalse(b.checkNoTopLatitudeBound());
    assertFalse(b.checkNoBottomLatitudeBound());
    assertEquals(-1.0, b.getLeftLongitude(), 0.000001);
    assertEquals(1.0, b.getRightLongitude(), 0.000001);
    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
    assertEquals(Math.PI * 0.25, b.getMaxLatitude(), 0.000001);
    assertEquals(0.382051, xyzb.getMinimumX(), 0.000001);
    assertEquals(1.0, xyzb.getMaximumX(), 0.000001);
    assertEquals(-0.841471, xyzb.getMinimumY(), 0.000001);
    assertEquals(0.841471, xyzb.getMaximumY(), 0.000001);
    assertEquals(-0.707107, xyzb.getMinimumZ(), 0.000001);
    assertEquals(0.707107, xyzb.getMaximumZ(), 0.000001);

    GeoArea area =
        GeoAreaFactory.makeGeoArea(
            PlanetModel.SPHERE,
            xyzb.getMinimumX() - 2.0 * Vector.MINIMUM_RESOLUTION,
            xyzb.getMaximumX() + 2.0 * Vector.MINIMUM_RESOLUTION,
            xyzb.getMinimumY() - 2.0 * Vector.MINIMUM_RESOLUTION,
            xyzb.getMaximumY() + 2.0 * Vector.MINIMUM_RESOLUTION,
            xyzb.getMinimumZ() - 2.0 * Vector.MINIMUM_RESOLUTION,
            xyzb.getMaximumZ() + 2.0 * Vector.MINIMUM_RESOLUTION);
    assertEquals(GeoArea.WITHIN, area.getRelationship(c));

    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -1.0, 1.0);
    b = new LatLonBounds();
    c.getBounds(b);
    xyzb = new XYZBounds();
    c.getBounds(xyzb);
    assertFalse(b.checkNoLongitudeBound());
    assertFalse(b.checkNoTopLatitudeBound());
    assertFalse(b.checkNoBottomLatitudeBound());
    assertEquals(-1.0, b.getLeftLongitude(), 0.000001);
    assertEquals(1.0, b.getRightLongitude(), 0.000001);
    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
    assertEquals(0.0, b.getMaxLatitude(), 0.000001);
    assertEquals(0.382051, xyzb.getMinimumX(), 0.000001);
    assertEquals(1.0, xyzb.getMaximumX(), 0.000001);
    assertEquals(-0.841471, xyzb.getMinimumY(), 0.000001);
    assertEquals(0.841471, xyzb.getMaximumY(), 0.000001);
    assertEquals(-0.707107, xyzb.getMinimumZ(), 0.000001);
    assertEquals(0.0, xyzb.getMaximumZ(), 0.000001);

    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, 1.0, -1.0);

    b = new LatLonBounds();
    c.getBounds(b);
    xyzb = new XYZBounds();
    c.getBounds(xyzb);
    assertTrue(b.checkNoLongitudeBound());
    assertFalse(b.checkNoTopLatitudeBound());
    assertFalse(b.checkNoBottomLatitudeBound());
    // assertEquals(1.0,b.getLeftLongitude(),0.000001);
    // assertEquals(-1.0,b.getRightLongitude(),0.000001);
    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
    assertEquals(0.0, b.getMaxLatitude(), 0.000001);
    assertEquals(-1.0, xyzb.getMinimumX(), 0.000001);
    assertEquals(0.540303, xyzb.getMaximumX(), 0.000001);
    assertEquals(-1.0, xyzb.getMinimumY(), 0.000001);
    assertEquals(1.0, xyzb.getMaximumY(), 0.000001);
    assertEquals(-0.707107, xyzb.getMinimumZ(), 0.000001);
    assertEquals(0.0, xyzb.getMaximumZ(), 0.000001);

    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -1.0, 1.0);

    b = new LatLonBounds();
    c.getBounds(b);
    xyzb = new XYZBounds();
    c.getBounds(xyzb);
    assertTrue(b.checkNoLongitudeBound());
    assertTrue(b.checkNoTopLatitudeBound());
    assertTrue(b.checkNoBottomLatitudeBound());
    // assertEquals(-1.0, b.getLeftLongitude(), 0.000001);
    // assertEquals(1.0, b.getRightLongitude(), 0.000001);
    assertEquals(0.0, xyzb.getMinimumX(), 0.000001);
    assertEquals(1.0, xyzb.getMaximumX(), 0.000001);
    assertEquals(-0.841471, xyzb.getMinimumY(), 0.000001);
    assertEquals(0.841471, xyzb.getMaximumY(), 0.000001);
    assertEquals(-1.0, xyzb.getMinimumZ(), 0.000001);
    assertEquals(1.0, xyzb.getMaximumZ(), 0.000001);

    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 1.0, -1.0);

    b = new LatLonBounds();
    c.getBounds(b);
    xyzb = new XYZBounds();
    c.getBounds(xyzb);
    assertTrue(b.checkNoLongitudeBound());
    assertTrue(b.checkNoTopLatitudeBound());
    assertTrue(b.checkNoBottomLatitudeBound());
    // assertEquals(1.0,b.getLeftLongitude(),0.000001);
    // assertEquals(-1.0,b.getRightLongitude(),0.000001);
    assertEquals(-1.0, xyzb.getMinimumX(), 0.000001);
    assertEquals(0.540303, xyzb.getMaximumX(), 0.000001);
    assertEquals(-1.0, xyzb.getMinimumY(), 0.000001);
    assertEquals(1.0, xyzb.getMaximumY(), 0.000001);
    assertEquals(-1.0, xyzb.getMinimumZ(), 0.000001);
    assertEquals(1.0, xyzb.getMaximumZ(), 0.000001);

    // Check wide variants of rectangle and longitude slice

    c =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -Math.PI + 0.1, Math.PI - 0.1);

    b = new LatLonBounds();
    c.getBounds(b);
    assertTrue(b.checkNoLongitudeBound());
    assertFalse(b.checkNoTopLatitudeBound());
    assertFalse(b.checkNoBottomLatitudeBound());
    // assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001);
    // assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001);
    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
    assertEquals(0.0, b.getMaxLatitude(), 0.000001);

    c =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, Math.PI - 0.1, -Math.PI + 0.1);

    b = new LatLonBounds();
    c.getBounds(b);
    assertFalse(b.checkNoLongitudeBound());
    assertFalse(b.checkNoTopLatitudeBound());
    assertFalse(b.checkNoBottomLatitudeBound());
    assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.000001);
    assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001);
    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
    assertEquals(0.0, b.getMaxLatitude(), 0.000001);

    c =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI + 0.1, Math.PI - 0.1);

    b = new LatLonBounds();
    c.getBounds(b);
    assertTrue(b.checkNoLongitudeBound());
    assertTrue(b.checkNoTopLatitudeBound());
    assertTrue(b.checkNoBottomLatitudeBound());
    // assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001);
    // assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001);

    c =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, Math.PI - 0.1, -Math.PI + 0.1);

    b = new LatLonBounds();
    c.getBounds(b);
    assertTrue(b.checkNoLongitudeBound());
    assertTrue(b.checkNoTopLatitudeBound());
    assertTrue(b.checkNoBottomLatitudeBound());
    // assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.000001);
    // assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001);

    // Check latitude zone
    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 1.0, -1.0, -Math.PI, Math.PI);

    b = new LatLonBounds();
    c.getBounds(b);
    assertTrue(b.checkNoLongitudeBound());
    assertFalse(b.checkNoTopLatitudeBound());
    assertFalse(b.checkNoBottomLatitudeBound());
    assertEquals(-1.0, b.getMinLatitude(), 0.000001);
    assertEquals(1.0, b.getMaxLatitude(), 0.000001);

    // Now, combine a few things to test the bounds object
    GeoBBox c1;
    GeoBBox c2;

    c1 =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0);
    c2 =
        GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI);

    b = new LatLonBounds();
    c1.getBounds(b);
    c2.getBounds(b);
    assertTrue(b.checkNoLongitudeBound());
    assertTrue(b.checkNoTopLatitudeBound());
    assertTrue(b.checkNoBottomLatitudeBound());

    c1 =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0);
    c2 =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI * 0.5);

    b = new LatLonBounds();
    c1.getBounds(b);
    c2.getBounds(b);
    assertTrue(b.checkNoLongitudeBound());
    assertTrue(b.checkNoTopLatitudeBound());
    assertTrue(b.checkNoBottomLatitudeBound());
    // assertEquals(-Math.PI,b.getLeftLongitude(),0.000001);
    // assertEquals(Math.PI*0.5,b.getRightLongitude(),0.000001);

    c1 =
        GeoBBoxFactory.makeGeoBBox(
            PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI * 0.5, 0.0);
    c2 =
        GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI);

    b = new LatLonBounds();
    c1.getBounds(b);
    c2.getBounds(b);
    assertTrue(b.checkNoLongitudeBound());
    assertTrue(b.checkNoTopLatitudeBound());
    assertTrue(b.checkNoBottomLatitudeBound());
    // assertEquals(-Math.PI * 0.5,b.getLeftLongitude(),0.000001);
    // assertEquals(Math.PI,b.getRightLongitude(),0.000001);

  }

  @Test
  public void testFailureCase1() {
    final GeoPoint point =
        new GeoPoint(-0.017413370801260174, -2.132522881412925E-18, 0.9976113450663769);
    final GeoBBox box =
        new GeoNorthRectangle(
            PlanetModel.WGS84, 0.35451471030934045, 9.908337057950734E-15, 2.891004593509811E-11);
    final XYZBounds bounds = new XYZBounds();
    box.getBounds(bounds);
    final XYZSolid solid =
        XYZSolidFactory.makeXYZSolid(
            PlanetModel.WGS84,
            bounds.getMinimumX(),
            bounds.getMaximumX(),
            bounds.getMinimumY(),
            bounds.getMaximumY(),
            bounds.getMinimumZ(),
            bounds.getMaximumZ());

    assertTrue(box.isWithin(point) ? solid.isWithin(point) : true);
  }

  @Test
  public void testFailureCase2() {
    // final GeoPoint point = new GeoPoint(-0.7375647084975573, -2.3309121299774915E-10,
    // 0.6746626163258577);
    final GeoPoint point =
        new GeoPoint(-0.737564708579924, -9.032562595264542E-17, 0.6746626165197899);
    final GeoBBox box =
        new GeoRectangle(
            PlanetModel.WGS84,
            0.7988584710911523,
            0.25383311815493353,
            -1.2236144735575564E-12,
            7.356011300929654E-49);
    final XYZBounds bounds = new XYZBounds();
    box.getBounds(bounds);
    final XYZSolid solid =
        XYZSolidFactory.makeXYZSolid(
            PlanetModel.WGS84,
            bounds.getMinimumX(),
            bounds.getMaximumX(),
            bounds.getMinimumY(),
            bounds.getMaximumY(),
            bounds.getMinimumZ(),
            bounds.getMaximumZ());

    // System.out.println("Is within Y value? "+(point.y >= bounds.getMinimumY() && point.y <=
    // bounds.getMaximumY()));
    // System.out.println("Shape = "+box+" is within? "+box.isWithin(point));
    // System.out.println("XYZBounds = "+bounds+" is within? "+solid.isWithin(point)+"
    // solid="+solid);
    assertTrue(box.isWithin(point) == solid.isWithin(point));
  }

  @Test
  public void testLUCENE10508() {
    double minX = Geo3DUtil.fromDegrees(GeoTestUtil.nextLongitude());
    double maxX = Geo3DUtil.fromDegrees(GeoTestUtil.nextLongitude());
    double minY = -Math.PI * 0.5;
    double maxY = -Math.PI * 0.5 + 1e-8;
    assertNotNull(GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, maxY, minY, minX, maxX));
  }

  @Test
  public void testBBoxRandomDegenerate() {
    for (int i = 0; i < 100; i++) {
      double minX = Geo3DUtil.fromDegrees(GeoTestUtil.nextLongitude());
      double maxX = Math.nextUp(minX + i * Vector.MINIMUM_ANGULAR_RESOLUTION);
      double minY = Geo3DUtil.fromDegrees(GeoTestUtil.nextLatitude());
      double maxY = Math.nextUp(minY + i * Vector.MINIMUM_ANGULAR_RESOLUTION);
      assertNotNull(GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, maxY, minY, minX, maxX));
    }
  }

  @Test
  public void testBBoxLatDegenerate() {
    double minX = Geo3DUtil.fromDegrees(-180.0);
    double maxX = Geo3DUtil.fromDegrees(-174.37500008381903);
    double minY = Geo3DUtil.fromDegrees(89.99999765306711);
    double maxY = Geo3DUtil.fromDegrees(89.99999794643372);
    assertNotNull(GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, maxY, minY, minX, maxX));
  }

  @Test
  public void testBBoxRandomLatDegenerate() {
    for (int i = 0; i < 100; i++) {
      double minX = Geo3DUtil.fromDegrees(GeoTestUtil.nextLongitude());
      double maxX = Geo3DUtil.fromDegrees(GeoTestUtil.nextLongitude());
      double minY = Geo3DUtil.fromDegrees(GeoTestUtil.nextLatitude());
      double maxY = Math.nextUp(minY + i * Vector.MINIMUM_ANGULAR_RESOLUTION);
      assertNotNull(GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, maxY, minY, minX, maxX));
    }
  }

  @Test
  public void testBBoxRandomLonDegenerate() {
    for (int i = 0; i < 100; i++) {
      double minX = Geo3DUtil.fromDegrees(GeoTestUtil.nextLongitude());
      double maxX = Math.nextUp(minX + i * Vector.MINIMUM_ANGULAR_RESOLUTION);
      double minY = Geo3DUtil.fromDegrees(GeoTestUtil.nextLatitude());
      double maxY = Geo3DUtil.fromDegrees(GeoTestUtil.nextLatitude());
      if (minY > maxY) {
        double temp = minY;
        minY = maxY;
        maxY = temp;
      }
      assertNotNull(GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, maxY, minY, minX, maxX));
    }
  }
}
