# PrimePrinter

In questo esempio vedremo l'applicazione del principio SRP (Single Responsibility Principle) per la creazione di un sistema che si occupa di generare e stampare, sotto forma di righe-colonne, i primi 1000 numeri primi.

### **PrimeGenerator**

```
<?php

/**
 * Questa classe genera dei numeri primi fino a un massimo specificato dall'utente.
 * L'algoritmo usato è il Setaccio di Eratostene.
 * Dato un array di interi a partire da 2:
 * - trova il primo intero non eliminato ed elimina tutti i suoi multipli
 * - ripeti finché non vi sono più multipli nell'array
 */
class PrimeGenerator
{
    private static $crossedOut = [];
    private static $result = [];

    public static function generate(int $maxValue): array
    {
        if ($maxValue < 2) {
            return [];
        }

        self::uncrossIntegersUpTo($maxValue);
        self::crossOutMultiples();
        self::putUncrossedIntegersIntoResult();

        return self::$result;
    }

    private static function uncrossIntegersUpTo(int $maxValue): void
    {
        for ($i = 0; $i < $maxValue + 1; $i++) {
            self::$crossedOut[$i] = false;
        }
    }

    private static function crossOutMultiples(): void
    {
        $limit = self::determineIterationLimit();
        for ($i = 2; $i <= $limit; $i++) {
            if (self::notCrossed($i)) {
                self::crossOutMultiplesOf($i);
            }
        }
    }

    /**
     * Ogni multiplo nell'array ha un fattore primo che è minore o uguale alla radice
     * delle dimensioni dell'array, così che possiamo evitare di eliminare i multipli
     * dei numeri maggiori di tale radice
     * @return int
     */
    private static function determineIterationLimit(): int
    {
        return (int)sqrt(count(self::$crossedOut));
    }

    private static function notCrossed(int $i): bool
    {
        return self::$crossedOut[$i] === false;
    }

    private static function crossOutMultiplesOf(int $i): void
    {
        for ($multiple = 2 * $i; $multiple < count(self::$crossedOut); $multiple += $i) {
            self::$crossedOut[$multiple] = true;
        }
    }

    private static function putUncrossedIntegersIntoResult()
    {
        for ($j = 0, $i = 2; $i < count(self::$crossedOut); $i++) {
            if (self::notCrossed($i)) {
                self::$result[$j++] = $i;
            }
        }
    }
}
```

### **RowColumnPagePrinter**

```
<?php

class RowColumnPagePrinter
{
    private $rowsPerPage;
    private $columnsPerPage;
    private $numbersPerPage;
    private $pageHeader;
    private $data;

    public function __construct(
        int $rowsPerPage,
        int $columnsPerPage,
        string $pageHeader,
        array $data
    ) {
        $this->rowsPerPage = $rowsPerPage;
        $this->columnsPerPage = $columnsPerPage;
        $this->numbersPerPage = $rowsPerPage * $columnsPerPage;
        $this->pageHeader = $pageHeader;
        $this->data = $data;
    }

    public function print(): void
    {
        $pageNumber = 1;
        for ($firstIndexOnPage = 0; $firstIndexOnPage < count($this->data); $firstIndexOnPage += $this->numbersPerPage) {
            $lastIndexOnPage = min(($firstIndexOnPage + $this->numbersPerPage) - 1, count($this->data) - 1);
            $this->printPageHeader($pageNumber);
            $this->printPage($firstIndexOnPage, $lastIndexOnPage);
            echo nl2br(PHP_EOL);
            $pageNumber++;
        }
    }

    private function printPageHeader(int $pageNumber): void
    {
        echo nl2br($this->pageHeader . ' --- Page ' . $pageNumber . PHP_EOL);
        echo nl2br(PHP_EOL);
    }

    private function printPage(int $firstIndexOnPage, int $lastIndexOnPage): void
    {
        $firstIndexOfLastRowOnPage = ($firstIndexOnPage + $this->rowsPerPage) - 1;
        for ($firstIndexInRow = $firstIndexOnPage; $firstIndexInRow <= $firstIndexOfLastRowOnPage; $firstIndexInRow++) {
            $this->printRow($firstIndexInRow, $lastIndexOnPage);
            echo nl2br(PHP_EOL);
        }
    }

    private function printRow(int $firstIndexInRow, int $lastIndexOnPage): void
    {
        for ($column = 0; $column < $this->columnsPerPage; $column++) {
            $index = $firstIndexInRow + ($column * $this->rowsPerPage);
            if ($index <= $lastIndexOnPage) {
                echo sprintf('%10d', $this->data[$index]);
            }
        }
    }
}
```

### **PrimePrinter**

```
<?php
require_once('./PrimeGenerator.php');
require_once('./RowColumnPagePrinter.php');

class PrimePrinter
{
    private const ROWS_PER_PAGE = 10;
    private const COLUMNS_PER_PAGE = 4;

    private $rowColumnPagePrinter;

    public function __construct(int $numberOfPrimes)
    {
        $primes = PrimeGenerator::generate($numberOfPrimes);
        $this->rowColumnPagePrinter = new RowColumnPagePrinter(
            self::ROWS_PER_PAGE,
            self::COLUMNS_PER_PAGE,
            'The First ' . $numberOfPrimes . ' Prime Numbers',
            $primes
        );
    }

    public function __invoke(): void
    {
        $this->rowColumnPagePrinter->print();
    }
}
```

### **Index**

```
<?php
require_once('./PrimePrinter.php');

$numberOfPrimes = 1000;
$primePrinter = new PrimePrinter($numberOfPrimes);
$primePrinter();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mirkorap16.gitbook.io/clean-code/esempi/primeprinter.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
