1*37748cd8SNickeau<?php 2*37748cd8SNickeau 3*37748cd8SNickeaudeclare(strict_types=1); 4*37748cd8SNickeau 5*37748cd8SNickeaunamespace Antlr\Antlr4\Runtime; 6*37748cd8SNickeau 7*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Comparison\Equality; 8*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Comparison\Equatable; 9*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\StringUtils; 10*37748cd8SNickeau 11*37748cd8SNickeau/** 12*37748cd8SNickeau * This class implements the {@see IntSet} backed by a sorted array of 13*37748cd8SNickeau * non-overlapping intervals. It is particularly efficient for representing 14*37748cd8SNickeau * large collections of numbers, where the majority of elements appear as part 15*37748cd8SNickeau * of a sequential range of numbers that are all part of the set. For example, 16*37748cd8SNickeau * the set { 1, 2, 3, 4, 7, 8 } may be represented as { [1, 4], [7, 8] }. 17*37748cd8SNickeau * 18*37748cd8SNickeau * This class is able to represent sets containing any combination of values in 19*37748cd8SNickeau * the range {@see Integer::MIN_VALUE} to {@see Integer::MAX_VALUE} 20*37748cd8SNickeau * (inclusive). 21*37748cd8SNickeau */ 22*37748cd8SNickeaufinal class IntervalSet implements Equatable 23*37748cd8SNickeau{ 24*37748cd8SNickeau /** @var array<Interval> */ 25*37748cd8SNickeau protected $intervals = []; 26*37748cd8SNickeau 27*37748cd8SNickeau /** @var bool */ 28*37748cd8SNickeau protected $readOnly = false; 29*37748cd8SNickeau 30*37748cd8SNickeau /** 31*37748cd8SNickeau * Create a set with a single element, el. 32*37748cd8SNickeau */ 33*37748cd8SNickeau public static function fromInt(int $number) : self 34*37748cd8SNickeau { 35*37748cd8SNickeau $set = new self(); 36*37748cd8SNickeau 37*37748cd8SNickeau $set->addOne($number); 38*37748cd8SNickeau 39*37748cd8SNickeau return $set; 40*37748cd8SNickeau } 41*37748cd8SNickeau 42*37748cd8SNickeau /** 43*37748cd8SNickeau * Create a set with all ints within range [start..end] (inclusive). 44*37748cd8SNickeau */ 45*37748cd8SNickeau public static function fromRange(int $start, int $end) : self 46*37748cd8SNickeau { 47*37748cd8SNickeau $set = new self(); 48*37748cd8SNickeau 49*37748cd8SNickeau $set->addRange($start, $end); 50*37748cd8SNickeau 51*37748cd8SNickeau return $set; 52*37748cd8SNickeau } 53*37748cd8SNickeau 54*37748cd8SNickeau public function complement(IntervalSet $set) : ?self 55*37748cd8SNickeau { 56*37748cd8SNickeau if ($set->isNull()) { 57*37748cd8SNickeau return null; 58*37748cd8SNickeau } 59*37748cd8SNickeau 60*37748cd8SNickeau return $set->subtract($this); 61*37748cd8SNickeau } 62*37748cd8SNickeau 63*37748cd8SNickeau public function subtract(IntervalSet $set) : self 64*37748cd8SNickeau { 65*37748cd8SNickeau if ($set->isNull()) { 66*37748cd8SNickeau return $this; 67*37748cd8SNickeau } 68*37748cd8SNickeau 69*37748cd8SNickeau return self::subtractSets($this, $set); 70*37748cd8SNickeau } 71*37748cd8SNickeau 72*37748cd8SNickeau public function orSet(IntervalSet $other) : self 73*37748cd8SNickeau { 74*37748cd8SNickeau $result = new self(); 75*37748cd8SNickeau 76*37748cd8SNickeau $result->addSet($this); 77*37748cd8SNickeau $result->addSet($other); 78*37748cd8SNickeau 79*37748cd8SNickeau return $result; 80*37748cd8SNickeau } 81*37748cd8SNickeau 82*37748cd8SNickeau /** 83*37748cd8SNickeau * Compute the set difference between two interval sets. The specific 84*37748cd8SNickeau * operation is `left - right`. If either of the input sets is `null`, 85*37748cd8SNickeau * it is treated as though it was an empty set. 86*37748cd8SNickeau */ 87*37748cd8SNickeau public static function subtractSets(IntervalSet $left, IntervalSet $right) : self 88*37748cd8SNickeau { 89*37748cd8SNickeau if ($left->isNull()) { 90*37748cd8SNickeau return new self(); 91*37748cd8SNickeau } 92*37748cd8SNickeau 93*37748cd8SNickeau if ($right->isNull()) { 94*37748cd8SNickeau // right set has no elements; just return the copy of the current set 95*37748cd8SNickeau return $left; 96*37748cd8SNickeau } 97*37748cd8SNickeau 98*37748cd8SNickeau $result = $left; 99*37748cd8SNickeau $resultI = 0; 100*37748cd8SNickeau $rightI = 0; 101*37748cd8SNickeau while ($resultI < \count($result->intervals) && $rightI < \count($right->intervals)) { 102*37748cd8SNickeau $resultInterval = $result->intervals[$resultI]; 103*37748cd8SNickeau $rightInterval = $right->intervals[$rightI]; 104*37748cd8SNickeau 105*37748cd8SNickeau // operation: (resultInterval - rightInterval) and update indexes 106*37748cd8SNickeau 107*37748cd8SNickeau if ($rightInterval->stop < $resultInterval->start) { 108*37748cd8SNickeau $rightI++; 109*37748cd8SNickeau 110*37748cd8SNickeau continue; 111*37748cd8SNickeau } 112*37748cd8SNickeau 113*37748cd8SNickeau if ($rightInterval->start > $resultInterval->stop) { 114*37748cd8SNickeau $resultI++; 115*37748cd8SNickeau 116*37748cd8SNickeau continue; 117*37748cd8SNickeau } 118*37748cd8SNickeau 119*37748cd8SNickeau $beforeCurrent = null; 120*37748cd8SNickeau $afterCurrent = null; 121*37748cd8SNickeau if ($rightInterval->start > $resultInterval->start) { 122*37748cd8SNickeau $beforeCurrent = new Interval($resultInterval->start, $rightInterval->start - 1); 123*37748cd8SNickeau } 124*37748cd8SNickeau 125*37748cd8SNickeau if ($rightInterval->stop < $resultInterval->stop) { 126*37748cd8SNickeau $afterCurrent = new Interval($rightInterval->stop + 1, $resultInterval->stop); 127*37748cd8SNickeau } 128*37748cd8SNickeau 129*37748cd8SNickeau if ($beforeCurrent !== null) { 130*37748cd8SNickeau if ($afterCurrent !== null) { 131*37748cd8SNickeau // split the current interval into two 132*37748cd8SNickeau $result->intervals[$resultI] = $beforeCurrent; 133*37748cd8SNickeau $result->intervals[$resultI + 1] = $afterCurrent; 134*37748cd8SNickeau $resultI++; 135*37748cd8SNickeau $rightI++; 136*37748cd8SNickeau 137*37748cd8SNickeau continue; 138*37748cd8SNickeau } 139*37748cd8SNickeau 140*37748cd8SNickeau // replace the current interval 141*37748cd8SNickeau $result->intervals[$resultI] = $beforeCurrent; 142*37748cd8SNickeau $resultI++; 143*37748cd8SNickeau continue; 144*37748cd8SNickeau } 145*37748cd8SNickeau 146*37748cd8SNickeau if ($afterCurrent !== null) { 147*37748cd8SNickeau // replace the current interval 148*37748cd8SNickeau $result->intervals[$resultI] = $afterCurrent; 149*37748cd8SNickeau $rightI++; 150*37748cd8SNickeau 151*37748cd8SNickeau continue; 152*37748cd8SNickeau } 153*37748cd8SNickeau 154*37748cd8SNickeau // remove the current interval (thus no need to increment resultI) 155*37748cd8SNickeau \array_splice($result->intervals, $resultI, 1); 156*37748cd8SNickeau 157*37748cd8SNickeau continue; 158*37748cd8SNickeau } 159*37748cd8SNickeau 160*37748cd8SNickeau // If rightI reached right.intervals.size(), no more intervals to subtract from result. 161*37748cd8SNickeau // If resultI reached result.intervals.size(), we would be subtracting from an empty set. 162*37748cd8SNickeau // Either way, we are done. 163*37748cd8SNickeau return $result; 164*37748cd8SNickeau } 165*37748cd8SNickeau 166*37748cd8SNickeau public function isReadOnly() : bool 167*37748cd8SNickeau { 168*37748cd8SNickeau return $this->readOnly; 169*37748cd8SNickeau } 170*37748cd8SNickeau 171*37748cd8SNickeau public function setReadOnly(bool $readOnly) : void 172*37748cd8SNickeau { 173*37748cd8SNickeau $this->readOnly = $readOnly; 174*37748cd8SNickeau } 175*37748cd8SNickeau 176*37748cd8SNickeau public function first() : int 177*37748cd8SNickeau { 178*37748cd8SNickeau if ($this->intervals === null || \count($this->intervals) === 0) { 179*37748cd8SNickeau return Token::INVALID_TYPE; 180*37748cd8SNickeau } 181*37748cd8SNickeau 182*37748cd8SNickeau return $this->intervals[0]->start; 183*37748cd8SNickeau } 184*37748cd8SNickeau 185*37748cd8SNickeau public function addOne(int $value) : void 186*37748cd8SNickeau { 187*37748cd8SNickeau $this->addInterval(new Interval($value, $value)); 188*37748cd8SNickeau } 189*37748cd8SNickeau 190*37748cd8SNickeau public function addRange(int $left, int $right) : void 191*37748cd8SNickeau { 192*37748cd8SNickeau $this->addInterval(new Interval($left, $right)); 193*37748cd8SNickeau } 194*37748cd8SNickeau 195*37748cd8SNickeau protected function addInterval(Interval $addition) : void 196*37748cd8SNickeau { 197*37748cd8SNickeau if ($this->readOnly) { 198*37748cd8SNickeau throw new \InvalidArgumentException('Can\'t alter readonly IntervalSet.'); 199*37748cd8SNickeau } 200*37748cd8SNickeau 201*37748cd8SNickeau if ($addition->stop < $addition->start) { 202*37748cd8SNickeau return; 203*37748cd8SNickeau } 204*37748cd8SNickeau 205*37748cd8SNickeau // find position in list 206*37748cd8SNickeau // Use iterators as we modify list in place 207*37748cd8SNickeau for ($i = 0, $count = \count($this->intervals); $i < $count; $i++) { 208*37748cd8SNickeau /** @var Interval $resilt */ 209*37748cd8SNickeau $resilt = $this->intervals[$i]; 210*37748cd8SNickeau 211*37748cd8SNickeau if ($addition->equals($resilt)) { 212*37748cd8SNickeau return; 213*37748cd8SNickeau } 214*37748cd8SNickeau 215*37748cd8SNickeau if ($addition->adjacent($resilt) || !$addition->disjoint($resilt)) { 216*37748cd8SNickeau // next to each other, make a single larger interval 217*37748cd8SNickeau $bigger = $addition->union($resilt); 218*37748cd8SNickeau $this->intervals[$i] = $bigger; 219*37748cd8SNickeau 220*37748cd8SNickeau // make sure we didn't just create an interval that 221*37748cd8SNickeau // should be merged with next interval in list 222*37748cd8SNickeau $i++; 223*37748cd8SNickeau while ($i < \count($this->intervals)) { 224*37748cd8SNickeau $next = $this->intervals[$i]; 225*37748cd8SNickeau 226*37748cd8SNickeau if (!$bigger->adjacent($next) && $bigger->disjoint($next)) { 227*37748cd8SNickeau break; 228*37748cd8SNickeau } 229*37748cd8SNickeau 230*37748cd8SNickeau // if we bump up against or overlap next, merge 231*37748cd8SNickeau \array_splice($this->intervals, $i, 1); // remove this one 232*37748cd8SNickeau 233*37748cd8SNickeau $i--; // move backwards to what we just set 234*37748cd8SNickeau 235*37748cd8SNickeau $this->intervals[$i] = $bigger->union($next); // set to 3 merged ones 236*37748cd8SNickeau 237*37748cd8SNickeau $i++; // first call to next after previous duplicates the result 238*37748cd8SNickeau } 239*37748cd8SNickeau 240*37748cd8SNickeau return; 241*37748cd8SNickeau } 242*37748cd8SNickeau 243*37748cd8SNickeau if ($addition->startsBeforeDisjoint($resilt)) { 244*37748cd8SNickeau // insert before r 245*37748cd8SNickeau \array_splice($this->intervals, $i, 0, [$addition]); 246*37748cd8SNickeau 247*37748cd8SNickeau return; 248*37748cd8SNickeau } 249*37748cd8SNickeau 250*37748cd8SNickeau // if disjoint and after r, a future iteration will handle it 251*37748cd8SNickeau } 252*37748cd8SNickeau 253*37748cd8SNickeau // ok, must be after last interval (and disjoint from last interval) just add it 254*37748cd8SNickeau $this->intervals[] = $addition; 255*37748cd8SNickeau } 256*37748cd8SNickeau 257*37748cd8SNickeau public function addSet(IntervalSet $other) : self 258*37748cd8SNickeau { 259*37748cd8SNickeau if ($other->intervals !== null) { 260*37748cd8SNickeau foreach ($other->intervals as $i) { 261*37748cd8SNickeau $this->addInterval(new Interval($i->start, $i->stop)); 262*37748cd8SNickeau } 263*37748cd8SNickeau } 264*37748cd8SNickeau 265*37748cd8SNickeau return $this; 266*37748cd8SNickeau } 267*37748cd8SNickeau 268*37748cd8SNickeau public function contains(int $item) : bool 269*37748cd8SNickeau { 270*37748cd8SNickeau $count = \count($this->intervals); 271*37748cd8SNickeau $left = 0; 272*37748cd8SNickeau $right = $count - 1; 273*37748cd8SNickeau // Binary search for the element in the (sorted, disjoint) array of intervals. 274*37748cd8SNickeau 275*37748cd8SNickeau while ($left <= $right) { 276*37748cd8SNickeau $m = \intval($left + $right, 2); 277*37748cd8SNickeau 278*37748cd8SNickeau $interval = $this->intervals[$m]; 279*37748cd8SNickeau $start = $interval->start; 280*37748cd8SNickeau $stop = $interval->stop; 281*37748cd8SNickeau 282*37748cd8SNickeau if ($stop < $item) { 283*37748cd8SNickeau $left = $m + 1; 284*37748cd8SNickeau } elseif ($start > $item) { 285*37748cd8SNickeau $right = $m - 1; 286*37748cd8SNickeau } else { // item >= start && item <= stop 287*37748cd8SNickeau return true; 288*37748cd8SNickeau } 289*37748cd8SNickeau } 290*37748cd8SNickeau 291*37748cd8SNickeau return false; 292*37748cd8SNickeau } 293*37748cd8SNickeau 294*37748cd8SNickeau public function length() : int 295*37748cd8SNickeau { 296*37748cd8SNickeau $length = 0; 297*37748cd8SNickeau 298*37748cd8SNickeau foreach ($this->intervals as $i) { 299*37748cd8SNickeau $length += $i->getLength(); 300*37748cd8SNickeau } 301*37748cd8SNickeau 302*37748cd8SNickeau return $length; 303*37748cd8SNickeau } 304*37748cd8SNickeau 305*37748cd8SNickeau public function removeOne(int $v) : void 306*37748cd8SNickeau { 307*37748cd8SNickeau foreach ($this->intervals as $k => $i) { 308*37748cd8SNickeau // intervals is ordered 309*37748cd8SNickeau if ($v < $i->start) { 310*37748cd8SNickeau return; 311*37748cd8SNickeau } 312*37748cd8SNickeau 313*37748cd8SNickeau // check for single value range 314*37748cd8SNickeau if ($v === $i->start && $v === $i->stop) { 315*37748cd8SNickeau \array_splice($this->intervals, $k, 1); 316*37748cd8SNickeau 317*37748cd8SNickeau return; 318*37748cd8SNickeau } 319*37748cd8SNickeau 320*37748cd8SNickeau // check for lower boundary 321*37748cd8SNickeau if ($v === $i->start) { 322*37748cd8SNickeau $this->intervals[$k] = new Interval($i->start + 1, $i->stop); 323*37748cd8SNickeau 324*37748cd8SNickeau return; 325*37748cd8SNickeau } 326*37748cd8SNickeau 327*37748cd8SNickeau // check for upper boundary 328*37748cd8SNickeau if ($v === $i->stop - 1) { 329*37748cd8SNickeau $this->intervals[$k] = new Interval($i->start, $i->stop - 1); 330*37748cd8SNickeau 331*37748cd8SNickeau return; 332*37748cd8SNickeau } 333*37748cd8SNickeau 334*37748cd8SNickeau // split existing range 335*37748cd8SNickeau if ($v < $i->stop - 1) { 336*37748cd8SNickeau $x = new Interval($i->start, $v); 337*37748cd8SNickeau 338*37748cd8SNickeau $i->start = $v + 1; 339*37748cd8SNickeau 340*37748cd8SNickeau \array_splice($this->intervals, $k, 0, [$x]); 341*37748cd8SNickeau 342*37748cd8SNickeau return; 343*37748cd8SNickeau } 344*37748cd8SNickeau } 345*37748cd8SNickeau } 346*37748cd8SNickeau 347*37748cd8SNickeau public function isNull() : bool 348*37748cd8SNickeau { 349*37748cd8SNickeau return \count($this->intervals) === 0; 350*37748cd8SNickeau } 351*37748cd8SNickeau 352*37748cd8SNickeau /** 353*37748cd8SNickeau * Returns the maximum value contained in the set if not isNull(). 354*37748cd8SNickeau * 355*37748cd8SNickeau * @return int The maximum value contained in the set. 356*37748cd8SNickeau * 357*37748cd8SNickeau * @throws \RuntimeException If set is empty. 358*37748cd8SNickeau */ 359*37748cd8SNickeau public function getMaxElement() : int 360*37748cd8SNickeau { 361*37748cd8SNickeau if ($this->isNull()) { 362*37748cd8SNickeau throw new \RuntimeException('The set is empty.'); 363*37748cd8SNickeau } 364*37748cd8SNickeau 365*37748cd8SNickeau return $this->intervals[\count($this->intervals)-1]->stop; 366*37748cd8SNickeau } 367*37748cd8SNickeau 368*37748cd8SNickeau /** 369*37748cd8SNickeau * Returns the minimum value contained in the set if not isNull(). 370*37748cd8SNickeau * 371*37748cd8SNickeau * @return int The minimum value contained in the set. 372*37748cd8SNickeau * 373*37748cd8SNickeau * @throws \RuntimeException If set is empty. 374*37748cd8SNickeau */ 375*37748cd8SNickeau public function getMinElement() : int 376*37748cd8SNickeau { 377*37748cd8SNickeau if ($this->isNull()) { 378*37748cd8SNickeau throw new \RuntimeException('The set is empty.'); 379*37748cd8SNickeau } 380*37748cd8SNickeau 381*37748cd8SNickeau return $this->intervals[0]->start; 382*37748cd8SNickeau } 383*37748cd8SNickeau 384*37748cd8SNickeau public function toStringChars(bool $elemAreChar) : string 385*37748cd8SNickeau { 386*37748cd8SNickeau if (!$this->intervals) { 387*37748cd8SNickeau return '{}'; 388*37748cd8SNickeau } 389*37748cd8SNickeau 390*37748cd8SNickeau $buf = ''; 391*37748cd8SNickeau 392*37748cd8SNickeau if ($this->length() > 1) { 393*37748cd8SNickeau $buf .= '{'; 394*37748cd8SNickeau } 395*37748cd8SNickeau 396*37748cd8SNickeau $iter = new \ArrayIterator($this->intervals); 397*37748cd8SNickeau 398*37748cd8SNickeau while ($iter->valid()) { 399*37748cd8SNickeau $interval = $iter->current(); 400*37748cd8SNickeau $iter->next(); 401*37748cd8SNickeau $start = $interval->start; 402*37748cd8SNickeau $stop = $interval->stop; 403*37748cd8SNickeau 404*37748cd8SNickeau if ($start === $stop) { 405*37748cd8SNickeau if ($start === Token::EOF) { 406*37748cd8SNickeau $buf .= '<EOF>'; 407*37748cd8SNickeau } elseif ($elemAreChar) { 408*37748cd8SNickeau $buf .= '\'' . StringUtils::char($start) . '\''; 409*37748cd8SNickeau } else { 410*37748cd8SNickeau $buf .= $start; 411*37748cd8SNickeau } 412*37748cd8SNickeau } else { 413*37748cd8SNickeau if ($elemAreChar) { 414*37748cd8SNickeau $buf .= \sprintf( 415*37748cd8SNickeau '\'%s\'..\'%s\'', 416*37748cd8SNickeau StringUtils::char($start), 417*37748cd8SNickeau StringUtils::char($stop) 418*37748cd8SNickeau ); 419*37748cd8SNickeau } else { 420*37748cd8SNickeau $buf .= \sprintf('%s..%s', $start, $stop); 421*37748cd8SNickeau } 422*37748cd8SNickeau } 423*37748cd8SNickeau 424*37748cd8SNickeau if ($iter->valid()) { 425*37748cd8SNickeau $buf .= ', '; 426*37748cd8SNickeau } 427*37748cd8SNickeau } 428*37748cd8SNickeau 429*37748cd8SNickeau if ($this->length() > 1) { 430*37748cd8SNickeau $buf .= '}'; 431*37748cd8SNickeau } 432*37748cd8SNickeau 433*37748cd8SNickeau return $buf; 434*37748cd8SNickeau } 435*37748cd8SNickeau 436*37748cd8SNickeau public function toStringVocabulary(Vocabulary $vocabulary) : string 437*37748cd8SNickeau { 438*37748cd8SNickeau if (!$this->intervals) { 439*37748cd8SNickeau return '{}'; 440*37748cd8SNickeau } 441*37748cd8SNickeau 442*37748cd8SNickeau $buf = ''; 443*37748cd8SNickeau if ($this->length() > 1) { 444*37748cd8SNickeau $buf .= '{'; 445*37748cd8SNickeau } 446*37748cd8SNickeau 447*37748cd8SNickeau $iterator = new \ArrayIterator($this->intervals); 448*37748cd8SNickeau 449*37748cd8SNickeau while ($iterator->valid()) { 450*37748cd8SNickeau $interval = $iterator->current(); 451*37748cd8SNickeau $iterator->next(); 452*37748cd8SNickeau $start = $interval->start; 453*37748cd8SNickeau $stop = $interval->stop; 454*37748cd8SNickeau 455*37748cd8SNickeau if ($start === $stop) { 456*37748cd8SNickeau $buf .= $this->elementName($vocabulary, $start); 457*37748cd8SNickeau } else { 458*37748cd8SNickeau for ($i = $start; $i <= $stop; $i++) { 459*37748cd8SNickeau if ($i > $start) { 460*37748cd8SNickeau $buf .= ', '; 461*37748cd8SNickeau } 462*37748cd8SNickeau 463*37748cd8SNickeau $buf .= $this->elementName($vocabulary, $i); 464*37748cd8SNickeau } 465*37748cd8SNickeau } 466*37748cd8SNickeau 467*37748cd8SNickeau if ($iterator->valid()) { 468*37748cd8SNickeau $buf .= ', '; 469*37748cd8SNickeau } 470*37748cd8SNickeau } 471*37748cd8SNickeau 472*37748cd8SNickeau if ($this->length() > 1) { 473*37748cd8SNickeau $buf .= '}'; 474*37748cd8SNickeau } 475*37748cd8SNickeau 476*37748cd8SNickeau return $buf; 477*37748cd8SNickeau } 478*37748cd8SNickeau 479*37748cd8SNickeau protected function elementName(Vocabulary $vocabulary, int $a) : string 480*37748cd8SNickeau { 481*37748cd8SNickeau if ($a === Token::EOF) { 482*37748cd8SNickeau return '<EOF>'; 483*37748cd8SNickeau } 484*37748cd8SNickeau 485*37748cd8SNickeau if ($a === Token::EPSILON) { 486*37748cd8SNickeau return '<EPSILON>'; 487*37748cd8SNickeau } 488*37748cd8SNickeau 489*37748cd8SNickeau return $vocabulary->getDisplayName($a); 490*37748cd8SNickeau } 491*37748cd8SNickeau 492*37748cd8SNickeau public function equals(object $other) : bool 493*37748cd8SNickeau { 494*37748cd8SNickeau if ($this === $other) { 495*37748cd8SNickeau return true; 496*37748cd8SNickeau } 497*37748cd8SNickeau 498*37748cd8SNickeau if (!$other instanceof self) { 499*37748cd8SNickeau return false; 500*37748cd8SNickeau } 501*37748cd8SNickeau 502*37748cd8SNickeau return $this->readOnly === $other->readOnly 503*37748cd8SNickeau && Equality::equals($this->intervals, $other->intervals); 504*37748cd8SNickeau } 505*37748cd8SNickeau 506*37748cd8SNickeau /** 507*37748cd8SNickeau * @return array<int> 508*37748cd8SNickeau */ 509*37748cd8SNickeau public function toArray() : array 510*37748cd8SNickeau { 511*37748cd8SNickeau $values = []; 512*37748cd8SNickeau foreach ($this->intervals as $interval) { 513*37748cd8SNickeau $start = $interval->start; 514*37748cd8SNickeau $stop = $interval->stop; 515*37748cd8SNickeau 516*37748cd8SNickeau for ($value = $start; $value <= $stop; $value++) { 517*37748cd8SNickeau $values[] = $value; 518*37748cd8SNickeau } 519*37748cd8SNickeau } 520*37748cd8SNickeau 521*37748cd8SNickeau return $values; 522*37748cd8SNickeau } 523*37748cd8SNickeau 524*37748cd8SNickeau public function __toString() : string 525*37748cd8SNickeau { 526*37748cd8SNickeau return $this->toStringChars(false); 527*37748cd8SNickeau } 528*37748cd8SNickeau} 529