code: /*******************************************************************************
   * Copyright (c) 2015 Voyager Search and MITRE
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Apache License, Version 2.0 which
   * accompanies this distribution and is available at
   *    http://www.apache.org/licenses/LICENSE-2.0.txt
   ******************************************************************************/
  
  package org.locationtech.spatial4j.shape;
  
  import org.locationtech.spatial4j.TestLog;
  import org.locationtech.spatial4j.context.SpatialContext;
  import org.locationtech.spatial4j.shape.impl.InfBufLine;
  import org.locationtech.spatial4j.shape.impl.PointImpl;
  
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  import static org.locationtech.spatial4j.shape.SpatialRelation.CONTAINS;
  import static org.locationtech.spatial4j.shape.SpatialRelation.DISJOINT;
  
  public abstract class RectIntersectionTestHelper<S extends Shape> extends RandomizedShapeTest {
  
    public RectIntersectionTestHelper(SpatialContext ctx) {
      super(ctx);
    }
  
    /** Override to return true if generateRandomShape is essentially a Rectangle. */
    protected boolean isRandomShapeRectangular() {
      return false;
    }
  
    protected abstract S generateRandomShape(Point nearP);
  
    /** shape has no area; return a point in it */
    protected abstract Point randomPointInEmptyShape(S shape);
  
    // Minimum distribution of relationships
  
    // Each shape has different characteristics, so we don't expect (for instance) shapes that
    // are likely to be long and thin to contain as many rectangles as those that
    // short and fat.
  
    protected int getContainsMinimum(int laps) {
      return laps/1000;
    }
  
    protected int getIntersectsMinimum(int laps) {
      return laps/1000;
    }
  
    protected int getWithinMinimum(int laps) {
      return laps/1000;
    }
  
    protected int getDisjointMinimum(int laps) {
      return laps/1000;
    }
  
    protected int getBoundingMinimum(int laps) {
      return laps/1000;
    }
  
    @SuppressWarnings("unchecked")
    @Override
    protected Point randomPointInOrNull(Shape shape) {
      if (!shape.hasArea()) {
        final Point pt = randomPointInEmptyShape((S) shape);
        assert shape.relate(pt).intersects() : "faulty randomPointInEmptyShape";
        return pt;
      }
      return super.randomPointInOrNull(shape);
    }
  
    public void testRelateWithRectangle() {
      //counters for the different intersection cases
      int i_C = 0, i_I = 0, i_W = 0, i_D = 0, i_bboxD = 0;
      int laps = 0;
      final int MINLAPS = scaledRandomIntBetween(20000, 200000);
      while(i_C < getContainsMinimum(MINLAPS) || i_I < getIntersectsMinimum(MINLAPS) || i_W < getWithinMinimum(MINLAPS)
              || (!isRandomShapeRectangular() && i_D < getDisjointMinimum(MINLAPS)) || i_bboxD < getBoundingMinimum(MINLAPS)) {
        laps++;
  
        TestLog.clear();
  
        if (laps > MINLAPS) {
          fail("Did not find enough contains/within/intersection/disjoint/bounds cases in a reasonable number" +
                  " of random attempts. CWIDbD: " +
                  i_C + "("+getContainsMinimum(MINLAPS)+")," +
                  i_W + "("+getWithinMinimum(MINLAPS)+")," +
                  i_I + "("+getIntersectsMinimum(MINLAPS)+")," +
                  i_D + "("+getDisjointMinimum(MINLAPS)+")," +
                  i_bboxD + "("+getBoundingMinimum(MINLAPS)+")"
                  + "  Laps exceeded " + MINLAPS);
        }
  
        Point nearP = randomPointIn(ctx.getWorldBounds());
  
        S s = generateRandomShape(nearP);
  
        Rectangle r = randomRectangle(s.getBoundingBox().getCenter());
  
        SpatialRelation ic = s.relate(r);
  
        TestLog.log("S-R Rel: {}, Shape {}, Rectangle {}", ic, s, r);
  
        if (ic != DISJOINT) {
          assertTrue("if not disjoint then the shape's bbox shouldn't be disjoint",
                  s.getBoundingBox().relate(r).intersects());
        }
  
        try {
          int MAX_TRIES = scaledRandomIntBetween(10, 100);
          switch (ic) {
            case CONTAINS:
              i_C++;
              for (int j = 0; j < MAX_TRIES; j++) {
                Point p = randomPointIn(r);
                assertRelation(null, CONTAINS, s, p);
              }
              break;
  
            case WITHIN:
              i_W++;
              for (int j = 0; j < MAX_TRIES; j++) {
                Point p = randomPointInOrNull(s);
                if (p == null) {//couldn't find a random point in shape
                  break;
                }
                assertRelation(null, CONTAINS, r, p);
              }
              break;
  
            case DISJOINT:
              if (!s.getBoundingBox().relate(r).intersects()) {//bboxes are disjoint
                i_bboxD++;
                if (i_bboxD >= getBoundingMinimum(MINLAPS))
                  break;
              } else {
                i_D++;
              }
              for (int j = 0; j < MAX_TRIES; j++) {
                Point p = randomPointIn(r);
                assertRelation(null, DISJOINT, s, p);
              }
              break;
  
            case INTERSECTS:
              i_I++;
              SpatialRelation pointR = null;//set once
              Rectangle randomPointSpace = null;
              MAX_TRIES = 1000;//give many attempts
              for (int j = 0; j < MAX_TRIES; j++) {
                Point p;
                if (j < 4) {
                  p = new PointImpl(0, 0, ctx);
                  InfBufLine.cornerByQuadrant(r, j + 1, p);
                } else {
                  if (randomPointSpace == null) {
                    if (pointR == DISJOINT) {
                      randomPointSpace = intersectRects(r,s.getBoundingBox());
                    } else {//CONTAINS
                      randomPointSpace = r;
                    }
                  }
                  p = randomPointIn(randomPointSpace);
                }
                SpatialRelation pointRNew = s.relate(p);
                if (pointR == null) {
                  pointR = pointRNew;
                } else if (pointR != pointRNew) {
                  break;
                }
              }
  
              break;
  
            default: fail(""+ic);
          } // switch
        } catch (AssertionError e) {
          onAssertFail(e, s, r, ic);
        }
  
      } // while loop
  
      System.out.println("Laps: "+laps + " CWIDbD: "+i_C+","+i_W+","+i_I+","+i_D+","+i_bboxD);
    }
  
    protected void onAssertFail(AssertionError e, S s, Rectangle r, SpatialRelation ic) {
      throw e;
    }
  
    private Rectangle intersectRects(Rectangle r1, Rectangle r2) {
      assert r1.relate(r2).intersects();
      final double minX, maxX;
      if (r1.relateXRange(r2.getMinX(),r2.getMinX()).intersects()) {
        minX = r2.getMinX();
      } else {
        minX = r1.getMinX();
      }
      if (r1.relateXRange(r2.getMaxX(),r2.getMaxX()).intersects()) {
        maxX = r2.getMaxX();
      } else {
        maxX = r1.getMaxX();
      }
      final double minY, maxY;
      if (r1.relateYRange(r2.getMinY(),r2.getMinY()).intersects()) {
        minY = r2.getMinY();
      } else {
        minY = r1.getMinY();
      }
      if (r1.relateYRange(r2.getMaxY(),r2.getMaxY()).intersects()) {
        maxY = r2.getMaxY();
      } else {
        maxY = r1.getMaxY();
      }
      return ctx.makeRectangle(minX, maxX, minY, maxY);
    }
  
  }
  