php-toolbox/tests/Ranges/IntegerRangeTest.php

1261 lines
53 KiB
PHP
Raw Normal View History

2024-07-26 20:02:22 +00:00
<?php
/**
* Joby's PHP Toolbox: https://code.byjoby.com/php-toolbox/
* MIT License: Copyright (c) 2024 Joby Elliott
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Joby\Toolbox\Sorting;
use Joby\Toolbox\Ranges\IntegerRange;
use PHPUnit\Framework\TestCase;
class IntegerRangeTest extends TestCase
{
public function testInstantiation()
{
// test fully bounded
$range = new IntegerRange(1, 10);
$this->assertInstanceOf(IntegerRange::class, $range);
$this->assertEquals(1, $range->start());
$this->assertEquals(10, $range->end());
// test open start
$range = new IntegerRange(null, 10);
$this->assertInstanceOf(IntegerRange::class, $range);
$this->assertNull($range->start());
$this->assertEquals(10, $range->end());
// test open end
$range = new IntegerRange(1, null);
$this->assertInstanceOf(IntegerRange::class, $range);
$this->assertEquals(1, $range->start());
$this->assertNull($range->end());
// test open both
$range = new IntegerRange(null, null);
$this->assertInstanceOf(IntegerRange::class, $range);
$this->assertNull($range->start());
$this->assertNull($range->end());
}
public function testEquals()
{
// fully bounded
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => false,
'intersecting open end center' => false,
'intersecting open start left' => false,
'intersecting open start right' => false,
'intersecting open start center' => false,
'disjoint open start' => false,
'disjoint open end' => false,
'disjoint left' => false,
'disjoint right' => false,
'adjacent open start' => false,
'adjacent open end' => false,
'adjacent left' => false,
'adjacent right' => false,
'contained' => false,
'contained same start' => false,
'contained same end' => false,
'containing' => false,
'containing unbounded start' => false,
'containing unbounded end' => false,
'containing same start' => false,
'containing same end' => false,
'containing same start unbounded end' => false,
'containing same end unbounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, 20),
'equals',
)
2024-07-26 20:02:22 +00:00
);
// open start
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'unbounded' => false,
'intersecting open start left' => false,
'intersecting open start right' => false,
'intersecting bounded start left' => false,
'intersecting bounded start right' => false,
'intersecting bounded start center' => false,
'intersecting open end same' => false,
'intersecting open end left' => false,
'intersecting bounded end same' => false,
'intersecting bounded end left' => false,
'adjacent open end' => false,
'adjacent bounded end' => false,
'disjoint open end' => false,
'disjoint bounded end' => false,
],
$this->scenarioResults(
new IntegerRange(null, 20),
'equals',
)
2024-07-26 20:02:22 +00:00
);
// open end
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => false,
'intersecting bounded end left' => false,
'intersecting bounded end right' => false,
'intersecting bounded end center' => false,
'intersecting open start same' => false,
'intersecting open start right' => false,
'intersecting bounded start same' => false,
'intersecting bounded start right' => false,
'adjacent open start' => false,
'adjacent bounded start' => false,
'disjoint open start' => false,
'disjoint bounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, null),
'equals',
)
2024-07-26 20:02:22 +00:00
);
// fully unbounded
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'open start' => false,
'open end' => false,
'bounded' => false,
],
$this->scenarioResults(
new IntegerRange(null, null),
'equals',
)
2024-07-26 20:02:22 +00:00
);
}
public function testIntersects()
{
// fully bounded
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'unbounded' => true,
'intersecting open end left' => true,
'intersecting open end right' => true,
'intersecting open end center' => true,
'intersecting open start left' => true,
'intersecting open start right' => true,
'intersecting open start center' => true,
'disjoint open start' => false,
'disjoint open end' => false,
'disjoint left' => false,
'disjoint right' => false,
'adjacent open start' => false,
'adjacent open end' => false,
'adjacent left' => false,
'adjacent right' => false,
'contained' => true,
'contained same start' => true,
'contained same end' => true,
'containing' => true,
'containing unbounded start' => true,
'containing unbounded end' => true,
'containing same start' => true,
'containing same end' => true,
'containing same start unbounded end' => true,
'containing same end unbounded start' => true,
],
$this->scenarioResults(
new IntegerRange(10, 20),
'intersects',
)
2024-07-26 20:02:22 +00:00
);
// open start
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'unbounded' => true,
'intersecting open start left' => true,
'intersecting open start right' => true,
'intersecting bounded start left' => true,
'intersecting bounded start right' => true,
'intersecting bounded start center' => true,
'intersecting open end same' => true,
'intersecting open end left' => true,
'intersecting bounded end same' => true,
'intersecting bounded end left' => true,
'adjacent open end' => false,
'adjacent bounded end' => false,
'disjoint open end' => false,
'disjoint bounded end' => false,
],
$this->scenarioResults(
new IntegerRange(null, 20),
'intersects',
)
2024-07-26 20:02:22 +00:00
);
// open end
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'unbounded' => true,
'intersecting open end left' => true,
'intersecting open end right' => true,
'intersecting bounded end left' => true,
'intersecting bounded end right' => true,
'intersecting bounded end center' => true,
'intersecting open start same' => true,
'intersecting open start right' => true,
'intersecting bounded start same' => true,
'intersecting bounded start right' => true,
'adjacent open start' => false,
'adjacent bounded start' => false,
'disjoint open start' => false,
'disjoint bounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, null),
'intersects',
)
2024-07-26 20:02:22 +00:00
);
// fully unbounded
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'open start' => true,
'open end' => true,
'bounded' => true,
],
$this->scenarioResults(
new IntegerRange(null, null),
'intersects',
)
2024-07-26 20:02:22 +00:00
);
}
public function testContains()
{
// fully bounded
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => false,
'intersecting open end center' => false,
'intersecting open start left' => false,
'intersecting open start right' => false,
'intersecting open start center' => false,
'disjoint open start' => false,
'disjoint open end' => false,
'disjoint left' => false,
'disjoint right' => false,
'adjacent open start' => false,
'adjacent open end' => false,
'adjacent left' => false,
'adjacent right' => false,
'contained' => true,
'contained same start' => true,
'contained same end' => true,
'containing' => false,
'containing unbounded start' => false,
'containing unbounded end' => false,
'containing same start' => false,
'containing same end' => false,
'containing same start unbounded end' => false,
'containing same end unbounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, 20),
'contains',
)
2024-07-26 20:02:22 +00:00
);
// open start
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'unbounded' => false,
'intersecting open start left' => true,
'intersecting open start right' => false,
'intersecting bounded start left' => true,
'intersecting bounded start right' => false,
'intersecting bounded start center' => true,
'intersecting open end same' => false,
'intersecting open end left' => false,
'intersecting bounded end same' => false,
'intersecting bounded end left' => false,
'adjacent open end' => false,
'adjacent bounded end' => false,
'disjoint open end' => false,
'disjoint bounded end' => false,
],
$this->scenarioResults(
new IntegerRange(null, 20),
'contains',
)
2024-07-26 20:02:22 +00:00
);
// open end
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => true,
'intersecting bounded end left' => false,
'intersecting bounded end right' => true,
'intersecting bounded end center' => true,
'intersecting open start same' => false,
'intersecting open start right' => false,
'intersecting bounded start same' => false,
'intersecting bounded start right' => false,
'adjacent open start' => false,
'adjacent bounded start' => false,
'disjoint open start' => false,
'disjoint bounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, null),
'contains',
)
2024-07-26 20:02:22 +00:00
);
2024-07-29 17:06:00 +00:00
// fully unbounded contains anything
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => true,
'open start' => true,
'open end' => true,
'bounded' => true,
],
$this->scenarioResults(
new IntegerRange(null, null),
'contains',
)
2024-07-26 20:02:22 +00:00
);
}
2024-07-29 18:29:02 +00:00
public function testAdjacentRightOf()
2024-07-29 17:06:00 +00:00
{
// fully bounded only abuts the end of things that are adjacent left
$this->assertEquals(
[
'same' => false,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => false,
'intersecting open end center' => false,
'intersecting open start left' => false,
'intersecting open start right' => false,
'intersecting open start center' => false,
'disjoint open start' => false,
'disjoint open end' => false,
'disjoint left' => false,
'disjoint right' => false,
'adjacent open start' => true,
'adjacent open end' => false,
'adjacent left' => true,
'adjacent right' => false,
'contained' => false,
'contained same start' => false,
'contained same end' => false,
'containing' => false,
'containing unbounded start' => false,
'containing unbounded end' => false,
'containing same start' => false,
'containing same end' => false,
'containing same start unbounded end' => false,
'containing same end unbounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, 20),
2024-07-29 18:29:02 +00:00
'adjacentRightOf',
2024-07-29 17:06:00 +00:00
)
);
// open start can't abut the end of anything
$this->assertEquals(
[
'same' => false,
'unbounded' => false,
'intersecting open start left' => false,
'intersecting open start right' => false,
'intersecting bounded start left' => false,
'intersecting bounded start right' => false,
'intersecting bounded start center' => false,
'intersecting open end same' => false,
'intersecting open end left' => false,
'intersecting bounded end same' => false,
'intersecting bounded end left' => false,
'adjacent open end' => false,
'adjacent bounded end' => false,
'disjoint open end' => false,
'disjoint bounded end' => false,
],
$this->scenarioResults(
new IntegerRange(null, 20),
2024-07-29 18:29:02 +00:00
'adjacentRightOf',
2024-07-29 17:06:00 +00:00
)
);
// open end only abuts the end of things that are adjacent left
$this->assertEquals(
[
'same' => false,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => false,
'intersecting bounded end left' => false,
'intersecting bounded end right' => false,
'intersecting bounded end center' => false,
'intersecting open start same' => false,
'intersecting open start right' => false,
'intersecting bounded start same' => false,
'intersecting bounded start right' => false,
'adjacent open start' => true,
'adjacent bounded start' => true,
'disjoint open start' => false,
'disjoint bounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, null),
2024-07-29 18:29:02 +00:00
'adjacentRightOf',
2024-07-29 17:06:00 +00:00
)
);
// fully unbounded can't abut anything
$this->assertEquals(
[
'same' => false,
'open start' => false,
'open end' => false,
'bounded' => false,
],
$this->scenarioResults(
new IntegerRange(null, null),
2024-07-29 18:29:02 +00:00
'adjacentRightOf',
2024-07-29 17:06:00 +00:00
)
);
}
2024-07-26 20:02:22 +00:00
2024-07-29 18:29:02 +00:00
public function testAdjacentLeftOf()
2024-07-26 20:02:22 +00:00
{
2024-07-29 17:06:00 +00:00
// fully bounded only abuts the start of things that are adjacent right
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => false,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => false,
'intersecting open end center' => false,
'intersecting open start left' => false,
'intersecting open start right' => false,
'intersecting open start center' => false,
'disjoint open start' => false,
'disjoint open end' => false,
'disjoint left' => false,
'disjoint right' => false,
'adjacent open start' => false,
'adjacent open end' => true,
'adjacent left' => false,
'adjacent right' => true,
'contained' => false,
'contained same start' => false,
'contained same end' => false,
'containing' => false,
'containing unbounded start' => false,
'containing unbounded end' => false,
'containing same start' => false,
'containing same end' => false,
'containing same start unbounded end' => false,
'containing same end unbounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, 20),
2024-07-29 18:29:02 +00:00
'adjacentLeftOf',
)
2024-07-26 20:02:22 +00:00
);
2024-07-29 17:06:00 +00:00
// open start only abuts the start of things that are adjacent right
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => false,
'unbounded' => false,
'intersecting open start left' => false,
'intersecting open start right' => false,
'intersecting bounded start left' => false,
'intersecting bounded start right' => false,
'intersecting bounded start center' => false,
'intersecting open end same' => false,
'intersecting open end left' => false,
'intersecting bounded end same' => false,
'intersecting bounded end left' => false,
'adjacent open end' => true,
'adjacent bounded end' => true,
'disjoint open end' => false,
'disjoint bounded end' => false,
],
$this->scenarioResults(
new IntegerRange(null, 20),
2024-07-29 18:29:02 +00:00
'adjacentLeftOf',
)
2024-07-26 20:02:22 +00:00
);
2024-07-29 17:06:00 +00:00
// open end can't abut the start of anything
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => false,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => false,
'intersecting bounded end left' => false,
'intersecting bounded end right' => false,
'intersecting bounded end center' => false,
'intersecting open start same' => false,
'intersecting open start right' => false,
'intersecting bounded start same' => false,
'intersecting bounded start right' => false,
'adjacent open start' => false,
'adjacent bounded start' => false,
'disjoint open start' => false,
'disjoint bounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, null),
2024-07-29 18:29:02 +00:00
'adjacentLeftOf',
)
2024-07-26 20:02:22 +00:00
);
2024-07-29 17:06:00 +00:00
// fully unbounded can't abut anything
$this->assertEquals(
2024-07-26 20:02:22 +00:00
[
'same' => false,
'open start' => false,
'open end' => false,
'bounded' => false,
],
$this->scenarioResults(
new IntegerRange(null, null),
2024-07-29 18:29:02 +00:00
'adjacentLeftOf',
)
2024-07-26 20:02:22 +00:00
);
}
2024-07-29 18:29:02 +00:00
public function testAdjacent()
2024-07-29 17:06:00 +00:00
{
// fully bounded only abuts things that are adjacent
$this->assertEquals(
[
'same' => false,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => false,
'intersecting open end center' => false,
'intersecting open start left' => false,
'intersecting open start right' => false,
'intersecting open start center' => false,
'disjoint open start' => false,
'disjoint open end' => false,
'disjoint left' => false,
'disjoint right' => false,
'adjacent open start' => true,
'adjacent open end' => true,
'adjacent left' => true,
'adjacent right' => true,
'contained' => false,
'contained same start' => false,
'contained same end' => false,
'containing' => false,
'containing unbounded start' => false,
'containing unbounded end' => false,
'containing same start' => false,
'containing same end' => false,
'containing same start unbounded end' => false,
'containing same end unbounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, 20),
2024-07-29 18:29:02 +00:00
'adjacent',
2024-07-29 17:06:00 +00:00
)
);
// open start abuts adjacent right
$this->assertEquals(
[
'same' => false,
'unbounded' => false,
'intersecting open start left' => false,
'intersecting open start right' => false,
'intersecting bounded start left' => false,
'intersecting bounded start right' => false,
'intersecting bounded start center' => false,
'intersecting open end same' => false,
'intersecting open end left' => false,
'intersecting bounded end same' => false,
'intersecting bounded end left' => false,
'adjacent open end' => true,
'adjacent bounded end' => true,
'disjoint open end' => false,
'disjoint bounded end' => false,
],
$this->scenarioResults(
new IntegerRange(null, 20),
2024-07-29 18:29:02 +00:00
'adjacent',
2024-07-29 17:06:00 +00:00
)
);
// open end abuts adjacent left
$this->assertEquals(
[
'same' => false,
'unbounded' => false,
'intersecting open end left' => false,
'intersecting open end right' => false,
'intersecting bounded end left' => false,
'intersecting bounded end right' => false,
'intersecting bounded end center' => false,
'intersecting open start same' => false,
'intersecting open start right' => false,
'intersecting bounded start same' => false,
'intersecting bounded start right' => false,
'adjacent open start' => true,
'adjacent bounded start' => true,
'disjoint open start' => false,
'disjoint bounded start' => false,
],
$this->scenarioResults(
new IntegerRange(10, null),
2024-07-29 18:29:02 +00:00
'adjacent',
2024-07-29 17:06:00 +00:00
)
);
// fully unbounded can't abut anything
$this->assertEquals(
[
'same' => false,
'open start' => false,
'open end' => false,
'bounded' => false,
],
$this->scenarioResults(
new IntegerRange(null, null),
2024-07-29 18:29:02 +00:00
'adjacent',
2024-07-29 17:06:00 +00:00
)
);
}
public function testBooleanAnd()
{
// fully bounded
$this->assertEquals(
[
'same' => '10,20',
'unbounded' => '10,20',
'intersecting open end left' => '10,20',
2024-07-29 18:29:02 +00:00
'intersecting open end right' => '11,20',
2024-07-29 17:06:00 +00:00
'intersecting open end center' => '10,20',
2024-07-29 18:29:02 +00:00
'intersecting open start left' => '10,19',
2024-07-29 17:06:00 +00:00
'intersecting open start right' => '10,20',
'intersecting open start center' => '10,20',
'disjoint open start' => null,
'disjoint open end' => null,
'disjoint left' => null,
'disjoint right' => null,
'adjacent open start' => null,
'adjacent open end' => null,
'adjacent left' => null,
'adjacent right' => null,
'contained' => '11,19',
'contained same start' => '10,19',
'contained same end' => '11,20',
'containing' => '10,20',
'containing unbounded start' => '10,20',
'containing unbounded end' => '10,20',
'containing same start' => '10,20',
'containing same end' => '10,20',
'containing same start unbounded end' => '10,20',
'containing same end unbounded start' => '10,20',
],
$this->scenarioResults(
new IntegerRange(10, 20),
'booleanAnd',
)
);
// open start
$this->assertEquals(
[
'same' => 'null,20',
'unbounded' => 'null,20',
2024-07-29 18:29:02 +00:00
'intersecting open start left' => 'null,19',
2024-07-29 17:06:00 +00:00
'intersecting open start right' => 'null,20',
2024-07-29 18:29:02 +00:00
'intersecting bounded start left' => '9,19',
'intersecting bounded start right' => '11,20',
2024-07-29 17:06:00 +00:00
'intersecting bounded start center' => '10,20',
'intersecting open end same' => '20,20',
2024-07-29 18:29:02 +00:00
'intersecting open end left' => '19,20',
2024-07-29 17:06:00 +00:00
'intersecting bounded end same' => '20,20',
2024-07-29 18:29:02 +00:00
'intersecting bounded end left' => '19,20',
2024-07-29 17:06:00 +00:00
'adjacent open end' => null,
'adjacent bounded end' => null,
'disjoint open end' => null,
'disjoint bounded end' => null,
],
$this->scenarioResults(
new IntegerRange(null, 20),
'booleanAnd',
)
);
// open end
$this->assertEquals(
[
'same' => '10,null',
'unbounded' => '10,null',
'intersecting open end left' => '10,null',
2024-07-29 18:29:02 +00:00
'intersecting open end right' => '11,null',
'intersecting bounded end left' => '10,19',
'intersecting bounded end right' => '11,21',
2024-07-29 17:06:00 +00:00
'intersecting bounded end center' => '10,20',
'intersecting open start same' => '10,10',
2024-07-29 18:29:02 +00:00
'intersecting open start right' => '10,11',
2024-07-29 17:06:00 +00:00
'intersecting bounded start same' => '10,10',
2024-07-29 18:29:02 +00:00
'intersecting bounded start right' => '10,11',
2024-07-29 17:06:00 +00:00
'adjacent open start' => null,
'adjacent bounded start' => null,
'disjoint open start' => null,
'disjoint bounded start' => null,
],
$this->scenarioResults(
new IntegerRange(10, null),
'booleanAnd',
)
);
// fully unbounded
$this->assertEquals(
[
'same' => 'null,null',
'open start' => 'null,20',
'open end' => '10,null',
'bounded' => '10,20',
],
$this->scenarioResults(
new IntegerRange(null, null),
'booleanAnd',
)
);
}
2024-07-29 18:29:02 +00:00
public function testBooleanOr()
{
// fully bounded
$this->assertEquals(
[
'same' => '10,20',
'unbounded' => 'null,null',
'intersecting open end left' => '9,null',
'intersecting open end right' => '10,null',
'intersecting open end center' => '10,null',
'intersecting open start left' => 'null,20',
'intersecting open start right' => 'null,21',
'intersecting open start center' => 'null,20',
'disjoint open start' => 'null,8;10,20',
'disjoint open end' => '10,20;22,null',
'disjoint left' => '-2,8;10,20',
'disjoint right' => '10,20;22,32',
'adjacent open start' => 'null,20',
'adjacent open end' => '10,null',
'adjacent left' => '-1,20',
'adjacent right' => '10,31',
'contained' => '10,20',
'contained same start' => '10,20',
'contained same end' => '10,20',
'containing' => '9,21',
'containing unbounded start' => 'null,21',
'containing unbounded end' => '9,null',
'containing same start' => '10,21',
'containing same end' => '9,20',
'containing same start unbounded end' => '10,null',
'containing same end unbounded start' => 'null,20',
],
$this->scenarioResults(
new IntegerRange(10, 20),
'booleanOr',
)
);
// open start
$this->assertEquals(
[
'same' => 'null,20',
'unbounded' => 'null,null',
'intersecting open start left' => 'null,20',
'intersecting open start right' => 'null,21',
'intersecting bounded start left' => 'null,20',
'intersecting bounded start right' => 'null,21',
'intersecting bounded start center' => 'null,20',
'intersecting open end same' => 'null,null',
'intersecting open end left' => 'null,null',
'intersecting bounded end same' => 'null,30',
'intersecting bounded end left' => 'null,29',
'adjacent open end' => 'null,null',
'adjacent bounded end' => 'null,30',
'disjoint open end' => 'null,20;22,null',
'disjoint bounded end' => 'null,20;22,32',
],
$this->scenarioResults(
new IntegerRange(null, 20),
'booleanOr',
)
);
// open end
$this->assertEquals(
[
'same' => '10,null',
'unbounded' => 'null,null',
'intersecting open end left' => '9,null',
'intersecting open end right' => '10,null',
'intersecting bounded end left' => '9,null',
'intersecting bounded end right' => '10,null',
'intersecting bounded end center' => '10,null',
'intersecting open start same' => 'null,null',
'intersecting open start right' => 'null,null',
'intersecting bounded start same' => '0,null',
'intersecting bounded start right' => '1,null',
'adjacent open start' => 'null,null',
'adjacent bounded start' => '0,null',
'disjoint open start' => 'null,8;10,null',
'disjoint bounded start' => '-2,8;10,null',
],
$this->scenarioResults(
new IntegerRange(10, null),
'booleanOr',
)
);
// fully unbounded
$this->assertEquals(
[
'same' => 'null,null',
'open start' => 'null,null',
'open end' => 'null,null',
'bounded' => 'null,null',
],
$this->scenarioResults(
new IntegerRange(null, null),
'booleanOr',
)
);
}
public function testBooleanNot()
{
// fully bounded
$this->assertEquals(
[
'same' => '',
'unbounded' => '',
'intersecting open end left' => '',
'intersecting open end right' => '10,10',
'intersecting open end center' => '',
'intersecting open start left' => '20,20',
'intersecting open start right' => '',
'intersecting open start center' => '',
'disjoint open start' => '10,20',
'disjoint open end' => '10,20',
'disjoint left' => '10,20',
'disjoint right' => '10,20',
'adjacent open start' => '10,20',
'adjacent open end' => '10,20',
'adjacent left' => '10,20',
'adjacent right' => '10,20',
'contained' => '10,10;20,20',
'contained same start' => '20,20',
'contained same end' => '10,10',
'containing' => '',
'containing unbounded start' => '',
'containing unbounded end' => '',
'containing same start' => '',
'containing same end' => '',
'containing same start unbounded end' => '',
'containing same end unbounded start' => '',
],
$this->scenarioResults(
new IntegerRange(10, 20),
'booleanNot',
)
);
// open start
$this->assertEquals(
[
'same' => '',
'unbounded' => '',
'intersecting open start left' => '20,20',
'intersecting open start right' => '',
'intersecting bounded start left' => 'null,8;20,20',
'intersecting bounded start right' => 'null,10',
'intersecting bounded start center' => 'null,9',
'intersecting open end same' => 'null,19',
'intersecting open end left' => 'null,18',
'intersecting bounded end same' => 'null,19',
'intersecting bounded end left' => 'null,18',
'adjacent open end' => 'null,20',
'adjacent bounded end' => 'null,20',
'disjoint open end' => 'null,20',
'disjoint bounded end' => 'null,20',
],
$this->scenarioResults(
new IntegerRange(null, 20),
'booleanNot',
)
);
// open end
$this->assertEquals(
[
'same' => '',
'unbounded' => '',
'intersecting open end left' => '',
'intersecting open end right' => '10,10',
'intersecting bounded end left' => '20,null',
'intersecting bounded end right' => '10,10;22,null',
'intersecting bounded end center' => '21,null',
'intersecting open start same' => '11,null',
'intersecting open start right' => '12,null',
'intersecting bounded start same' => '11,null',
'intersecting bounded start right' => '12,null',
'adjacent open start' => '10,null',
'adjacent bounded start' => '10,null',
'disjoint open start' => '10,null',
'disjoint bounded start' => '10,null',
],
$this->scenarioResults(
new IntegerRange(10, null),
'booleanNot',
)
);
// fully unbounded
$this->assertEquals(
[
'same' => '',
'open start' => '21,null',
'open end' => 'null,9',
'bounded' => 'null,9;21,null',
],
$this->scenarioResults(
new IntegerRange(null, null),
'booleanNot',
)
);
}
2024-07-29 20:07:27 +00:00
public function testBooleanXor()
{
// fully bounded
$this->assertEquals(
[
'same' => '',
'unbounded' => 'null,9;21,null',
'intersecting open end left' => '9,9;21,null',
'intersecting open end right' => '10,10;21,null',
'intersecting open end center' => '21,null',
'intersecting open start left' => 'null,9;20,20',
'intersecting open start right' => 'null,9;21,21',
'intersecting open start center' => 'null,9',
'disjoint open start' => 'null,8;10,20',
'disjoint open end' => '10,20;22,null',
'disjoint left' => '-2,8;10,20',
'disjoint right' => '10,20;22,32',
'adjacent open start' => 'null,20',
'adjacent open end' => '10,null',
'adjacent left' => '-1,20',
'adjacent right' => '10,31',
'contained' => '10,10;20,20',
'contained same start' => '20,20',
'contained same end' => '10,10',
'containing' => '9,9;21,21',
'containing unbounded start' => 'null,9;21,21',
'containing unbounded end' => '9,9;21,null',
'containing same start' => '21,21',
'containing same end' => '9,9',
'containing same start unbounded end' => '21,null',
'containing same end unbounded start' => 'null,9',
],
$this->scenarioResults(
new IntegerRange(10, 20),
'booleanXor',
)
);
// open start
$this->assertEquals(
[
'same' => '',
'unbounded' => '21,null',
'intersecting open start left' => '20,20',
'intersecting open start right' => '21,21',
'intersecting bounded start left' => 'null,8;20,20',
'intersecting bounded start right' => 'null,10;21,21',
'intersecting bounded start center' => 'null,9',
'intersecting open end same' => 'null,19;21,null',
'intersecting open end left' => 'null,18;21,null',
'intersecting bounded end same' => 'null,19;21,30',
'intersecting bounded end left' => 'null,18;21,29',
'adjacent open end' => 'null,null',
'adjacent bounded end' => 'null,30',
'disjoint open end' => 'null,20;22,null',
'disjoint bounded end' => 'null,20;22,32',
],
$this->scenarioResults(
new IntegerRange(null, 20),
'booleanXor',
)
);
// open end
$this->assertEquals(
[
'same' => '',
'unbounded' => 'null,9',
'intersecting open end left' => '9,9',
'intersecting open end right' => '10,10',
'intersecting bounded end left' => '9,9;20,null',
'intersecting bounded end right' => '10,10;22,null',
'intersecting bounded end center' => '21,null',
'intersecting open start same' => 'null,9;11,null',
'intersecting open start right' => 'null,9;12,null',
'intersecting bounded start same' => '0,9;11,null',
'intersecting bounded start right' => '1,9;12,null',
'adjacent open start' => 'null,null',
'adjacent bounded start' => '0,null',
'disjoint open start' => 'null,8;10,null',
'disjoint bounded start' => '-2,8;10,null',
],
$this->scenarioResults(
new IntegerRange(10, null),
'booleanXor',
)
);
// fully unbounded
$this->assertEquals(
[
'same' => '',
'open start' => '21,null',
'open end' => 'null,9',
'bounded' => 'null,9;21,null',
],
$this->scenarioResults(
new IntegerRange(null, null),
'booleanXor',
)
);
}
public function testBooleanSlice()
{
// fully bounded
$this->assertEquals(
[
'same' => '10,20',
'unbounded' => 'null,9;10,20;21,null',
'intersecting open end left' => '9,9;10,20;21,null',
'intersecting open end right' => '10,10;11,20;21,null',
'intersecting open end center' => '10,20;21,null',
'intersecting open start left' => 'null,9;10,19;20,20',
'intersecting open start right' => 'null,9;10,20;21,21',
'intersecting open start center' => 'null,9;10,20',
'disjoint open start' => 'null,8;10,20',
'disjoint open end' => '10,20;22,null',
'disjoint left' => '-2,8;10,20',
'disjoint right' => '10,20;22,32',
'adjacent open start' => 'null,9;10,20',
'adjacent open end' => '10,20;21,null',
'adjacent left' => '-1,9;10,20',
'adjacent right' => '10,20;21,31',
'contained' => '10,10;11,19;20,20',
'contained same start' => '10,19;20,20',
'contained same end' => '10,10;11,20',
'containing' => '9,9;10,20;21,21',
'containing unbounded start' => 'null,9;10,20;21,21',
'containing unbounded end' => '9,9;10,20;21,null',
'containing same start' => '10,20;21,21',
'containing same end' => '9,9;10,20',
'containing same start unbounded end' => '10,20;21,null',
'containing same end unbounded start' => 'null,9;10,20',
],
$this->scenarioResults(
new IntegerRange(10, 20),
'booleanSlice',
)
);
// open start
$this->assertEquals(
[
'same' => 'null,20',
'unbounded' => 'null,20;21,null',
'intersecting open start left' => 'null,19;20,20',
'intersecting open start right' => 'null,20;21,21',
'intersecting bounded start left' => 'null,8;9,19;20,20',
'intersecting bounded start right' => 'null,10;11,20;21,21',
'intersecting bounded start center' => 'null,9;10,20',
'intersecting open end same' => 'null,19;20,20;21,null',
'intersecting open end left' => 'null,18;19,20;21,null',
'intersecting bounded end same' => 'null,19;20,20;21,30',
'intersecting bounded end left' => 'null,18;19,20;21,29',
'adjacent open end' => 'null,20;21,null',
'adjacent bounded end' => 'null,20;21,30',
'disjoint open end' => 'null,20;22,null',
'disjoint bounded end' => 'null,20;22,32',
],
$this->scenarioResults(
new IntegerRange(null, 20),
'booleanSlice',
)
);
// open end
$this->assertEquals(
[
'same' => '10,null',
'unbounded' => 'null,9;10,null',
'intersecting open end left' => '9,9;10,null',
'intersecting open end right' => '10,10;11,null',
'intersecting bounded end left' => '9,9;10,19;20,null',
'intersecting bounded end right' => '10,10;11,21;22,null',
'intersecting bounded end center' => '10,20;21,null',
'intersecting open start same' => 'null,9;10,10;11,null',
'intersecting open start right' => 'null,9;10,11;12,null',
'intersecting bounded start same' => '0,9;10,10;11,null',
'intersecting bounded start right' => '1,9;10,11;12,null',
'adjacent open start' => 'null,9;10,null',
'adjacent bounded start' => '0,9;10,null',
'disjoint open start' => 'null,8;10,null',
'disjoint bounded start' => '-2,8;10,null',
],
$this->scenarioResults(
new IntegerRange(10, null),
'booleanSlice',
)
);
// fully unbounded
$this->assertEquals(
[
'same' => 'null,null',
'open start' => 'null,20;21,null',
'open end' => 'null,9;10,null',
'bounded' => 'null,9;10,20;21,null',
],
$this->scenarioResults(
new IntegerRange(null, null),
'booleanSlice',
)
);
}
2024-07-26 20:02:22 +00:00
protected function createScenarios(IntegerRange $range): array
{
if (is_null($range->start()) && is_null($range->end())) {
// scenarios for fully open range
return [
'same' => new IntegerRange(null, null),
2024-07-29 17:06:00 +00:00
'open start' => new IntegerRange(null, 20),
2024-07-26 20:02:22 +00:00
'open end' => new IntegerRange(10, null),
'bounded' => new IntegerRange(10, 20),
];
} elseif (is_null($range->start())) {
// scenarios for unbounded start
return [
'same' => new IntegerRange(null, $range->end()),
'unbounded' => new IntegerRange(null, null),
2024-07-29 18:29:02 +00:00
'intersecting open start left' => new IntegerRange(null, $range->end() - 1),
'intersecting open start right' => new IntegerRange(null, $range->end() + 1),
'intersecting bounded start left' => new IntegerRange($range->end() - 11, $range->end() - 1),
'intersecting bounded start right' => new IntegerRange($range->end() - 9, $range->end() + 1),
2024-07-26 20:02:22 +00:00
'intersecting bounded start center' => new IntegerRange($range->end() - 10, $range->end()),
'intersecting open end same' => new IntegerRange($range->end(), null),
2024-07-29 18:29:02 +00:00
'intersecting open end left' => new IntegerRange($range->end() - 1, null),
2024-07-26 20:02:22 +00:00
'intersecting bounded end same' => new IntegerRange($range->end(), $range->end() + 10),
2024-07-29 18:29:02 +00:00
'intersecting bounded end left' => new IntegerRange($range->end() - 1, $range->end() + 9),
2024-07-26 20:02:22 +00:00
'adjacent open end' => new IntegerRange($range->end() + 1, null),
'adjacent bounded end' => new IntegerRange($range->end() + 1, $range->end() + 10),
'disjoint open end' => new IntegerRange($range->end() + 2, null),
'disjoint bounded end' => new IntegerRange($range->end() + 2, $range->end() + 12),
];
} elseif (is_null($range->end())) {
// scenarios for unbounded end
return [
'same' => new IntegerRange($range->start(), null),
'unbounded' => new IntegerRange(null, null),
2024-07-29 18:29:02 +00:00
'intersecting open end left' => new IntegerRange($range->start() - 1, null),
'intersecting open end right' => new IntegerRange($range->start() + 1, null),
'intersecting bounded end left' => new IntegerRange($range->start() - 1, $range->start() + 9),
'intersecting bounded end right' => new IntegerRange($range->start() + 1, $range->start() + 11),
2024-07-26 20:02:22 +00:00
'intersecting bounded end center' => new IntegerRange($range->start(), $range->start() + 10),
'intersecting open start same' => new IntegerRange(null, $range->start()),
2024-07-29 18:29:02 +00:00
'intersecting open start right' => new IntegerRange(null, $range->start() + 1),
2024-07-26 20:02:22 +00:00
'intersecting bounded start same' => new IntegerRange($range->start() - 10, $range->start()),
2024-07-29 18:29:02 +00:00
'intersecting bounded start right' => new IntegerRange($range->start() - 9, $range->start() + 1),
2024-07-26 20:02:22 +00:00
'adjacent open start' => new IntegerRange(null, $range->start() - 1),
'adjacent bounded start' => new IntegerRange($range->start() - 10, $range->start() - 1),
'disjoint open start' => new IntegerRange(null, $range->start() - 2),
'disjoint bounded start' => new IntegerRange($range->start() - 12, $range->start() - 2),
];
} else {
// scenarios for fully bounded range
return [
'same' => new IntegerRange($range->start(), $range->end()),
'unbounded' => new IntegerRange(null, null),
2024-07-29 18:29:02 +00:00
'intersecting open end left' => new IntegerRange($range->start() - 1, null),
'intersecting open end right' => new IntegerRange($range->start() + 1, null),
2024-07-29 17:06:00 +00:00
'intersecting open end center' => new IntegerRange($range->start(), null),
2024-07-29 18:29:02 +00:00
'intersecting open start left' => new IntegerRange(null, $range->end() - 1),
'intersecting open start right' => new IntegerRange(null, $range->end() + 1),
2024-07-29 17:06:00 +00:00
'intersecting open start center' => new IntegerRange(null, $range->end()),
2024-07-26 20:02:22 +00:00
'disjoint open start' => new IntegerRange(null, $range->start() - 2),
'disjoint open end' => new IntegerRange($range->end() + 2, null),
2024-07-29 17:06:00 +00:00
'disjoint left' => new IntegerRange($range->start() - 12, $range->start() - 2),
'disjoint right' => new IntegerRange($range->end() + 2, $range->end() + 12),
2024-07-26 20:02:22 +00:00
'adjacent open start' => new IntegerRange(null, $range->start() - 1),
'adjacent open end' => new IntegerRange($range->end() + 1, null),
2024-07-29 17:06:00 +00:00
'adjacent left' => new IntegerRange($range->start() - 11, $range->start() - 1),
'adjacent right' => new IntegerRange($range->end() + 1, $range->end() + 11),
2024-07-26 20:02:22 +00:00
'contained' => new IntegerRange($range->start() + 1, $range->end() - 1),
'contained same start' => new IntegerRange($range->start(), $range->end() - 1),
'contained same end' => new IntegerRange($range->start() + 1, $range->end()),
2024-07-29 17:06:00 +00:00
'containing' => new IntegerRange($range->start() - 1, $range->end() + 1),
'containing unbounded start' => new IntegerRange(null, $range->end() + 1),
'containing unbounded end' => new IntegerRange($range->start() - 1, null),
'containing same start' => new IntegerRange($range->start(), $range->end() + 1),
'containing same end' => new IntegerRange($range->start() - 1, $range->end()),
2024-07-26 20:02:22 +00:00
'containing same start unbounded end' => new IntegerRange($range->start(), null),
'containing same end unbounded start' => new IntegerRange(null, $range->end()),
];
}
}
protected function scenarioResults(IntegerRange $range, string $method): array
2024-07-26 20:02:22 +00:00
{
return array_map(
function ($s) use ($range, $method) {
2024-07-29 17:06:00 +00:00
$result = $range->$method($s);
if ($result instanceof IntegerRange) {
$result = ($result->start() ?? 'null') . ',' . ($result->end() ?? 'null');
}
2024-07-29 18:29:02 +00:00
if (is_array($result)) {
$result = implode(';', array_map(
function ($r) {
return ($r->start() ?? 'null') . ',' . ($r->end() ?? 'null');
},
$result
));
}
2024-07-29 17:06:00 +00:00
return $result;
},
$this->createScenarios($range)
);
2024-07-26 20:02:22 +00:00
}
}