Occurrences of each key inside the window. */ private array $count = []; private int $enter = 0; private int $leave = 0; private int $size; /** @param array $events [day, key], ordered by day */ public function __construct(private array $events) { $this->size = count($events); } /** * Move the window to [$start, $end] (inclusive; null start = no lower * bound) and return what entered and left since the previous call. * * Both bounds may only move forward. * * @return array{added: array, removed: array} */ public function slideTo(?string $start, string $end): array { $before = []; while ($this->enter < $this->size && $this->events[$this->enter][0] <= $end) { $key = $this->events[$this->enter++][1]; $before[$key] ??= ($this->count[$key] ?? 0) > 0; $this->count[$key] = ($this->count[$key] ?? 0) + 1; } if ($start !== null) { while ($this->leave < $this->enter && $this->events[$this->leave][0] < $start) { $key = $this->events[$this->leave++][1]; $before[$key] ??= ($this->count[$key] ?? 0) > 0; if (--$this->count[$key] === 0) { unset($this->count[$key]); } } } $added = []; $removed = []; foreach ($before as $key => $was) { $now = isset($this->count[$key]); if ($now && !$was) { $added[] = $key; } elseif (!$now && $was) { $removed[] = $key; } } return ['added' => $added, 'removed' => $removed]; } }