*/ public function scheduledDates( int $assignmentCount, DateTimeImmutable $startDate, DateTimeImmutable $targetDate, WorkloadPlacement $workloadPlacement, ): array { $differenceInDays = $startDate->diff($targetDate)->days; $dayCount = $differenceInDays + 1; return $this->distributedDates( assignmentCount: $assignmentCount, dayCount: $dayCount, startDate: $startDate, workloadPlacement: $workloadPlacement, ); } /** * @return list */ private function distributedDates( int $assignmentCount, int $dayCount, DateTimeImmutable $startDate, WorkloadPlacement $workloadPlacement, ): array { $assignmentsPerDay = intdiv($assignmentCount, $dayCount); $remainderDayCount = $assignmentCount % $dayCount; $remainderDayIndexes = $this->remainderDayIndexes( assignmentsPerDay: $assignmentsPerDay, dayCount: $dayCount, remainderDayCount: $remainderDayCount, workloadPlacement: $workloadPlacement, ); $remainderDays = array_fill_keys($remainderDayIndexes, true); $dates = []; for ($dayIndex = 0; $dayIndex < $dayCount; $dayIndex++) { $assignmentCountForDay = $assignmentsPerDay; if (isset($remainderDays[$dayIndex])) { $assignmentCountForDay++; } $date = $startDate->modify("+{$dayIndex} days"); for ($index = 0; $index < $assignmentCountForDay; $index++) { $dates[] = $date; } } return $dates; } /** * @return list */ private function remainderDayIndexes( int $assignmentsPerDay, int $dayCount, int $remainderDayCount, WorkloadPlacement $workloadPlacement, ): array { if ($remainderDayCount === 0) { return []; } if ($workloadPlacement === WorkloadPlacement::Spread) { return $this->spreadRemainderDayIndexes( assignmentsPerDay: $assignmentsPerDay, dayCount: $dayCount, remainderDayCount: $remainderDayCount, ); } $blockStart = $this->remainderBlockStart( dayCount: $dayCount, remainderDayCount: $remainderDayCount, workloadPlacement: $workloadPlacement, ); return range($blockStart, $blockStart + $remainderDayCount - 1); } /** * @return list */ private function spreadRemainderDayIndexes( int $assignmentsPerDay, int $dayCount, int $remainderDayCount, ): array { if ($remainderDayCount === 1) { if ($assignmentsPerDay === 0) { return [0]; } return [intdiv($dayCount - 1, 2)]; } $dayIndexes = []; for ($remainderIndex = 0; $remainderIndex < $remainderDayCount; $remainderIndex++ ) { $scaledIndex = $remainderIndex * ($dayCount - 1) / ($remainderDayCount - 1); $dayIndexes[] = (int) floor($scaledIndex + 0.5); } return $dayIndexes; } private function remainderBlockStart( int $dayCount, int $remainderDayCount, WorkloadPlacement $workloadPlacement, ): int { if ($workloadPlacement === WorkloadPlacement::Start) { return 0; } if ($workloadPlacement === WorkloadPlacement::End) { return $dayCount - $remainderDayCount; } return intdiv($dayCount - $remainderDayCount, 2); } }