/**
   * Try several searches and see if they all produce good results. Just fine the nearest-neighbor
   */
  @Test
  public void findClosest_basic_1() {
    KdTreeSearchN alg = createAlg();

    KdTree tree = StandardKdTreeSearch1Tests.createTreeA();
    alg.setTree(tree);
    alg.setMaxDistance(Double.MAX_VALUE);

    // the first decision will be incorrect and it will need to back track
    found.reset();
    alg.findNeighbor(new double[] {11, 8}, 1, found);
    assertEquals(1, found.size);
    assertTrue(found.data[0].node == tree.root.right.right);

    // the root will be the best
    found.reset();
    alg.findNeighbor(new double[] {1.001, 1.99999}, 1, found);
    assertTrue(found.data[0].node == tree.root);

    // a point on the left branch will be a perfect fit
    found.reset();
    alg.findNeighbor(new double[] {2, 0.8}, 1, found);
    assertTrue(found.data[0].node == tree.root.left.right);

    // a point way outside the tree's bounds
    found.reset();
    alg.findNeighbor(new double[] {-10000, 0.5}, 1, found);
    assertTrue(found.data[0].node == tree.root.left.left);
  }
  /** Randomly generate several searches and check the result */
  @Test
  public void randomTests() {
    KdTreeSearchN alg = createAlg();

    KdTree tree = StandardKdTreeSearch1Tests.createTreeA();
    alg.setTree(tree);

    List<double[]> data = new ArrayList<double[]>();
    flattenTree(tree.root, data);

    for (int i = 0; i < 100; i++) {
      int searchN = rand.nextInt(data.size() + 5) + 1;

      double[] target = data.get(rand.nextInt(data.size()));

      double maxDistance = rand.nextDouble() * 10;

      List<double[]> expected = findNeighbors(data, target, maxDistance, searchN);

      found.reset();
      alg.setMaxDistance(maxDistance);
      alg.findNeighbor(target, searchN, found);
      assertEquals(expected.size(), found.size);

      for (int j = 0; j < expected.size(); j++) {
        checkContains(expected.get(j));
      }
    }
  }
  /** See if max distance is being respected */
  @Test
  public void findClosest_maxDistance() {
    KdTree tree = new KdTree(2);
    tree.root = new KdTree.Node(new double[] {1, 2}, null);

    KdTreeSearchN alg = createAlg();
    alg.setTree(tree);
    alg.setMaxDistance(2);

    found.reset();
    alg.findNeighbor(new double[] {11, 8}, 1, found);
    assertEquals(0, found.size);
    found.reset();
    alg.findNeighbor(new double[] {1, 1.5}, 1, found);
    assertEquals(1, found.size);
    assertTrue(found.data[0].node == tree.root);
  }
  /** The tree is empty and it should always fail */
  @Test
  public void findClosest_empty() {
    KdTreeSearchN alg = createAlg();
    alg.setTree(new KdTree(2));

    found.reset();
    alg.findNeighbor(new double[] {11, 8}, 2, found);
    assertEquals(0, found.size());
  }
  /** See if it can handle a null leaf */
  @Test
  public void findClosest_nullLeaf() {
    KdTreeSearchN alg = createAlg();

    KdTree tree = StandardKdTreeSearch1Tests.createTreeWithNull();
    alg.setTree(tree);
    alg.setMaxDistance(Double.MAX_VALUE);

    // the first decision will be incorrect and it will need to back track
    found.reset();
    alg.findNeighbor(new double[] {2, 3}, 1, found);
    assertTrue(found.get(0).node == tree.root);
  }
  /** Make sure the distance it returns is correct */
  @Test
  public void checkDistance() {
    KdTreeSearchN alg = createAlg();

    KdTree tree = StandardKdTreeSearch1Tests.createTreeA();
    alg.setTree(tree);
    alg.setMaxDistance(Double.MAX_VALUE);

    double[] pt = new double[] {11.5, 8.2};
    found.reset();
    alg.findNeighbor(pt, 1, found);

    assertEquals(1, found.size);
    double d0 = found.get(0).node.point[0] - pt[0];
    double d1 = found.get(0).node.point[1] - pt[1];

    assertEquals(d0 * d0 + d1 * d1, found.get(0).distance, 1e-8);
  }
  /** See of it can handle duplicate values correctly */
  @Test
  public void checkDuplicates() {
    KdTreeSearchN alg = createAlg();

    KdTree tree = createTreeDuplicates();
    alg.setTree(tree);
    alg.setMaxDistance(Double.MAX_VALUE);

    double[] pt = new double[] {1, 2};
    found.reset();
    alg.findNeighbor(pt, 3, found);
    assertEquals(3, found.size);

    // make sure each instance is unique
    for (int i = 0; i < 3; i++) {
      double[] a = found.get(i).node.point;
      for (int j = i + 1; j < 3; j++) {
        assertTrue(found.get(j).node.point != a);
      }
    }
  }