/**
  * Tests PFXUtils::searchArray().
  */
 public function testSearchArray()
 {
     $searchArray = array('1', 'aa', 'bar', 'baz', 'zee');
     $this->assertEquals(3, PFXUtils::searchArray('baz', $searchArray));
     $searchArray[] = 'zeee';
     $this->assertEquals(3, PFXUtils::searchArray('baz', $searchArray));
     array_unshift($searchArray, '0');
     $this->assertEquals(4, PFXUtils::searchArray('baz', $searchArray));
     /* Loose matching is problematic due to the fact that, for example, "1"
        compares as less than "bar", while 1 compares as greater than "bar".
        For this reason, it's not supported. */
     $this->assertFalse(PFXUtils::searchArray(1, $searchArray));
     $this->assertEquals(1, PFXUtils::searchArray("1", $searchArray));
     $this->assertFalse(PFXUtils::searchArray('foo', $searchArray));
     // We can find the next lowest or highest value
     $this->assertEquals(2, PFXUtils::searchArray('az', $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTLOWEST));
     $this->assertEquals(4, PFXUtils::searchArray('bax', $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTHIGHEST));
     // Of course, exact matches are still preferred
     $this->assertEquals(3, PFXUtils::searchArray('bar', $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTLOWEST));
     $this->assertEquals(4, PFXUtils::searchArray('baz', $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTHIGHEST));
     /* If there is no next lowest or next highest match, the corresponding
        search types do not indicate a match. */
     $this->assertFalse(PFXUtils::searchArray('-1', $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTLOWEST));
     $this->assertFalse(PFXUtils::searchArray('zz', $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTHIGHEST));
     $this->assertSame(0, PFXUtils::searchArray('-1', $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTHIGHEST));
     $this->assertSame(count($searchArray) - 1, PFXUtils::searchArray('zz', $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTLOWEST));
     // Try a bigger array
     $searchArray = array();
     for ($i = 0; $i < 1000; $i++) {
         $searchArray[] = $i * 3;
     }
     /* I'm only going from 1 to 999 because I have some assertions around
        finding the next lowest and next highest items. */
     for ($i = 1; $i < 999; $i++) {
         $searchVal = $i * 3;
         $this->assertEquals($searchVal / 3, PFXUtils::searchArray($searchVal, $searchArray));
         $this->assertFalse(PFXUtils::searchArray((string) $searchVal, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_STRICTMATCH));
         $this->assertEquals($searchVal / 3 + 1, PFXUtils::searchArray($searchVal + 1, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTHIGHEST));
         $this->assertEquals($searchVal / 3, PFXUtils::searchArray($searchVal + 1, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTLOWEST));
         $this->assertEquals($searchVal / 3 - 1, PFXUtils::searchArray($searchVal - 1, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTLOWEST));
         $this->assertEquals($searchVal / 3, PFXUtils::searchArray($searchVal - 1, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTHIGHEST));
     }
     // We can also use a custom getter to examine the value
     $searchArray = array();
     for ($i = 0; $i < 20; $i++) {
         $obj = new stdClass();
         $obj->prop = $i * 3;
         $searchArray[] = $obj;
     }
     $this->assertFalse(PFXUtils::searchArray('12', $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_STRICTMATCH, function ($obj) {
         return $obj->prop;
     }));
     $this->assertEquals(4, PFXUtils::searchArray(12, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_STRICTMATCH, function ($obj) {
         return $obj->prop;
     }));
     $this->assertFalse(PFXUtils::searchArray(13, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_STRICTMATCH, function ($obj) {
         return $obj->prop;
     }));
     $this->assertEquals(4, PFXUtils::searchArray(13, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTLOWEST, function ($obj) {
         return $obj->prop;
     }));
     $this->assertEquals(5, PFXUtils::searchArray(13, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTHIGHEST, function ($obj) {
         return $obj->prop;
     }));
     // Try a test with floats
     $searchArray = array();
     for ($i = 0; $i < 1000; $i++) {
         $obj = new stdClass();
         $obj->prop = $i + mt_rand(0, 999999) / 1000000;
         $searchArray[] = $obj;
     }
     for ($i = 1; $i < 1000; $i++) {
         /* I'm not going to test strict match searching here, because as
            these are floats, it may or may not succeed. Searching for the next
            highest or lowest value is the way to go here. */
         $this->assertEquals($i, PFXUtils::searchArray($i, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTHIGHEST, function ($obj) {
             return $obj->prop;
         }));
         $this->assertEquals($i - 1, PFXUtils::searchArray($i, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_NEXTLOWEST, function ($obj) {
             return $obj->prop;
         }));
     }
     // Various bad arguments will cause an exception to be thrown
     $this->assertThrows('InvalidArgumentException', array('PFXUtils', 'searchArray'), array(null, $searchArray));
     $this->assertThrows('InvalidArgumentException', array('PFXUtils', 'searchArray'), array(array('foo', 'bar'), $searchArray));
     /* This array does not appear to be numerically indexed, although it
        would be trivial to fool the method into using an associative array by
        just adding the key 0. */
     $this->assertThrows('InvalidArgumentException', array('PFXUtils', 'searchArray'), array(1, array('foo' => 'bar')));
     // Not a valid callable
     $this->assertThrows('InvalidArgumentException', array('PFXUtils', 'searchArray'), array(1, $searchArray, 0, null, PFXUtils::ARRAY_SEARCH_STRICTMATCH, 'asdf'));
     // Bad search type
     $this->assertThrows('InvalidArgumentException', array('PFXUtils', 'searchArray'), array(1, $searchArray, 0, null, 'asdf'));
 }