<?php
header("Content-Type: text/plain");
mb_internal_encoding('utf-8');

//  ООП. Вектор и антикризичные меры

// Класс сотрудника
class Employee
{
    private $rank = 1;      // ранг
    private $chief = 0;     // статус руководителя
    private $profession;    // профессия

    // Коофициенты зарплаты для ранга
    private $rankSalary = array(1 => 1,
        2 => 1.25,
        3 => 1.5);
    // Коофициенты зарплаты для начальников
    private $chiefSalary = array(0 => 1,
        1 => 1.5);
    // Коофициенты потребления кофе для начальников
    private $chiefCoffee = array(0 => 1,
        1 => 2);
    // Коофициенты продуктивности для начальников
    private $chiefProduct = array(0 => 1,
        1 => 0);

    public function __construct($profession, $rank, $chief)
        {
        // Присвоение профессии сотруднику
        $this->profession = $profession;
        // Присвоение ранга сотруднику
        $this->rank = $rank;
        // Присвоение статуса начальник
        $this->chief = $chief;
    }

    // Присвоение профессии сотруднику
    public function addProfession(Profession $profession)
    {
        $this->profession = $profession;
    }

    // Присвоение ранга сотруднику
    public function getRank()
    {
        return $this->rank;
    }

    // Присвоение статуса начальник

    public function setRank($rank)
    {
        $this->rank = $rank;
    }

    // Изменение базовой ставки

    public function setСhief($chief)
    {
        $this->chief = $chief;
    }

    // Изменение кол-во выпиваемого кофе

    public function setSalaryRate($salary)
    {
        $this->profession->setSalaryRate($salary);
    }

    // Возвращаем ранг сотрудника

    public function setCoffeeRate($coffee)
    {
        $this->profession->setCoffeeRate($coffee);
    }

    // Возвращаем название профессии сотрудника

    public function getProfession()
    {
        return $this->profession;
    }

    // Возвращаем статус шафа сотрудника
    public function getChief()
    {
        return $this->chief;
    }

    // Расчет зарплаты
    public function getSalary()
    {
        // Профессия
        $profession = $this->profession;
        // Ставка по профессии
        $salaryRate = $profession->getSalaryRate();
        // Узнаем коэффициент согласно рангу сотрудника
        $tmpxRank = $this->rankSalary[$this->rank];
        // Узнаем коэффициент для начальника
        $tmpchiefSalary = $this->chiefSalary[$this->chief];
        // Расчет зарплаты
        $salary = $salaryRate * $tmpxRank * $tmpchiefSalary;

        return $salary;
    }

    // Расчет потребления кофе
    public function getCoffee()
    {
        // Профессия
        $profession = $this->profession;
        // Потребление кофе по профессии
        $coffeeRate = $profession->getCoffeeRate();
        // Вычисляем коэффициент для начальника
        $tmpchiefCoffee = $this->chiefCoffee[$this->chief];
        // Расчет потребления кофе
        $coffee = $coffeeRate * $tmpchiefCoffee;

        return $coffee;
    }

    // Расчет продуктивности
    public function getProduct()
    {
        // Профессия
        $profession = $this->profession;
        // Продуктивность по профессии
        $productRate = $profession->getProduct();
        // Вычисляем коэффициент для начальника
        $tmpchiefProduct = $this->chiefProduct[$this->chief];
        // Расчет продуктивности
        $product = $productRate * $tmpchiefProduct;

        return $product;
    }
}

// Абстрактный класс профессии
abstract class Profession
{
    protected $salaryRate; // базовая ставка
    protected $coffeeRate; // кол-во выпиваемого кофе

    // Изменение базовой ставки

    public function getSalaryRate()
    {
        return $this->salaryRate;
    }

    // Изменение кол-во выпиваемого кофе

    public function setSalaryRate($salary)
    {
        $this->salaryRate = $salary;
    }

    public function getCoffeeRate()
    {
        return $this->coffeeRate;
    }

    public function setCoffeeRate($coffee)
    {
        $this->coffeeRate = $coffee;
    }

    // Возвращает имя

    public function getName()
    {
        return get_class($this);
    }

    abstract public function getProduct();
}

// Класс менджер
class Manager extends Profession
{
    protected $salaryRate = 500; // базовая ставка
    protected $coffeeRate = 20; // кол-во выпиваемого кофе

    public function getProduct()
    {
        return 200;
    }
}

// Класс маркетолог
class Marketer extends Profession
{
    protected $salaryRate = 400; // базовая ставка
    protected $coffeeRate = 15; // кол-во выпиваемого кофе

    public function getProduct()
    {
        return 150;
    }
}

// Класс инженер
class Engineer extends Profession
{
    protected $salaryRate = 200; // базовая ставка
    protected $coffeeRate = 5; // кол-во выпиваемого кофе

    public function getProduct()
    {
        return 50;
    }
}

// Класс аналитик
class Analyst extends Profession
{
    protected $salaryRate = 800; // базовая ставка
    protected $coffeeRate = 50; // кол-во выпиваемого кофе

    public function getProduct()
    {
        return 5;
    }
}

// Класс департамента
class Departament
{
    public $name = "";            // название
    private $employees = array(); // сотрудники

    public function __construct($name)
    {
        $this->name = $name;
    }

    // Клонирование департамента
    public function __clone()
    {
        $employeesClones = array(); // клоны работников

        foreach ($this->employees as $employee) {
            $employee = clone $employee;
            $departamentsClones[] = $employee;
        }
        // Заменяем массив работников массивом клонов
        $this->employees = $departamentsClones;
    }

    // Прием сотрудника на работу
    public function addEmployee(Employee $employee)
    {
        $this->employees[] = $employee;
    }

    // Расчет количества сотрудников по профессии, не указанный параметр возвращает общее кол-во
    public function countEmployees($profession = null)
    {
        $employeesNumber = 0; // число сотрудников

        // Если профессия не задана
        if (!$profession) {
            return count($this->employees);
        }

        // Если считаем сотрудников определенной профессии
        foreach ($this->employees as $employee) {

            // Если сотрудник не соответствует профессии
            if ($employee->getProfession()->getName() != $profession) {
                continue;
            }
            // Счетчик
            $employeesNumber++;
        }
        return $employeesNumber;
    }

    // Возвращаем сотрудников массивом
    public function getEmployees()
    {
        return $this->employees;
    }
    // Поиск сотрудника по профессии, рангу, статусу шефа
    // параметр может принимать значение 'all', параметр будет игнорироваться
    public function findEmployee($profession, $rank, $chief)
    {
        $employee = null; // искомый Сотрудник

        // Перебираем всех сотрудников
        foreach ($this->employees as $employee) {

            // Если сотрудник не соответствует профессии
            if ($employee->getProfession()->getName() != $profession &&
                $profession != 'all'
            ) {
                continue;
            }
            // Если сотрудник не соответствует рангу
            if ($employee->getRank() != $rank &&
                $rank != 'all'
            ) {
                continue;
            }
            // Если сотрудник не является начальником
            if ($employee->getChief() != $chief &&
                $chief != 'all'
            ) {
                continue;
            }
            // Возвращаем Сотрудника
            return $employee;
        }
    }

    // Возвращает сотрудников заданной профессии массивом
    public function findEmployees($profession)
    {
        $employees = array(); // массив Сотрудников

        // Перебираем всех сотрудников
        foreach ($this->employees as $employee) {
            // Если сотрудник соответствует профессии
            if ($employee->getProfession()->getName() == $profession) {
                $employees[] = $employee;
            }
        }
        // Возвращаем Сотрудников
        return $employees;
    }

    // Увольнение сотрудника
    public function dismissEmployee($employee)
    {
        $employeeKey = array_search($employee, $this->employees, TRUE);
        unset ($this->employees[$employeeKey]);
    }

    // Расходы на зарплату

    public function getTotalCoffee()
    {
        $totalCoffee = 0;     // сумарное потребление кофе
        // Для каждого сотрудника
        foreach ($this->employees as $employee) {
            // Запрашиваем потребление кофе у сотрудника  и суммируем результат
            $totalCoffee += $employee->getCoffee();
        }
        return $totalCoffee;
    }

    // Рассход кофе

    public function getAverageCostPerProduct()
    {
        // Средняя стоимость 1 страницы = суммарная зарплата / сумарное количество страниц
        $avrCostPerProduct = $this->getTotalSalary() / $this->getTotalProduct();

        return $avrCostPerProduct;
    }

    // Суммарная производительность

    public function getTotalSalary()
    {
        $totalSalary = 0;     // сумарная зарплата
        // Для каждого сотрудника
        foreach ($this->employees as $employee) {
            // Запрашиваем зп у сотрудника и суммируем результат
            $totalSalary += $employee->getSalary();
        }
        return $totalSalary;
    }

    // Средний расход тугриков на одну страницу

    public function getTotalProduct()
    {
        $totalProduct = 0;     // сумарная продуктивность
        // Для каждого сотрудника
        foreach ($this->employees as $employee) {
            // Запрашиваем продуктивность у сотрудника и суммируем результат
            $totalProduct += $employee->getProduct();
        }
        return $totalProduct;
    }
}

// Класс компании
class Company
{
    public $name = ""; // название
    private $departaments = array(); // департаменты

    public function __construct($name)
    {
        $this->name = $name;
    }

    // Клонирование компании
    public function __clone()
    {
        $departamentsClones = array(); // клоны департаментов

        foreach ($this->departaments as $departament) {
            $departament = clone $departament;
            $departamentsClones[] = $departament;
        }
        // Заменяем массив департаментов массивом клонов
        $this->departaments = $departamentsClones;
    }

    // Создание департамента
    public function createDepartament($name)
    {
        $this->departaments[$name] = new Departament ($name);
    }

    // Поиск департамента по названию,
    // если название не определено, возращает все департааменты
    public function findDepartamentByName($name = null)
    {
        $result; // ссылка на департамент или массив департаментов
        // Ищем департамент в массиве по названию
        if ($name) {
            $result = $this->departaments[$name];
        } else {
        // Возвращаем все департаменты массивом
            foreach ($this->departaments as $name => $departament) {
                $result[$name] = $departament;
            }
        }
        // Если ничего не нашли
        if ($result == null) {
            // Ничего не возвращам, выкидываем исключение
            throw new Exception ( "Департамент не найден" ) ;
        }
        return $result;
    }

    // Сумарное количество работников

    public function getAverageEmployees()
    {
        // Среднее количество работников = суммарное кол-во работников / кол-во департаментов
        $avrEmployees = $this->getTotalEmployees() / count($this->departaments);

        return $avrEmployees;
    }

    // Расходы на зарплату в сумме

    public function getTotalEmployees()
    {
        $totalEmployees = 0;
        // Для каждого департамента
        foreach ($this->departaments as $departament) {
            // Сумируем работников всех департаментов
            $totalEmployees += $departament->countEmployees();
        }
        return $totalEmployees;
    }

    // Рассход кофе в сумме

    public function getAverageSalary()
    {
        // Средняя зарплата = суммарная зарплата / кол-во департаментов
        $avrSalary = $this->getTotalSalary() / count($this->departaments);

        return $avrSalary;
    }

    // Суммарная производительность

    public function getTotalSalary()
    {
        $salary = 0;
        // Для каждого департамента
        foreach ($this->departaments as $departament) {
            // Сумируем зп всех департаментов
            $salary += $departament->getTotalSalary();
        }
        return $salary;
    }

    // Среднее количество работников по департаментам

    public function getAverageCoffee()
    {
        // Среднее потребление кофе = суммарная потребление кофе / кол-во департаментов
        $avrCoffee = $this->getTotalCoffee() / count($this->departaments);

        return $avrCoffee;
    }

    // Средняя зарплата по департаментам

    public function getTotalCoffee()
    {
        $coffee = 0;
        // Для каждого департамента
        foreach ($this->departaments as $departament) {
            // Суммируем расход кофе у департаментов
            $coffee += $departament->getTotalCoffee();
        }
        return $coffee;
    }

    // Среднее потребление кофе по департаментам

    public function getAverageProduct()
    {
        // Средняя производительность = суммарная производительность / кол-во департаментов
        $avrProduct = $this->getTotalProduct() / count($this->departaments);

        return $avrProduct;
    }

    // Средняя производительность по департаментам

    public function getTotalProduct()
    {
        $product = 0;
        // Для каждого департамента
        foreach ($this->departaments as $departament) {
            // Суммируем производительность каждого департамента
            $product += $departament->getTotalProduct();;
        }
        return $product;
    }

    // Средний расход тугриков на одну страницу по департаментам

    public function getAverageCostPerProduct()
    {
        $avrCostPerProduct = 0;
        // Для каждого департамента
        foreach ($this->departaments as $departament) {
            // Сумируем средний расход
            $avrCostPerProduct += $departament->getAverageCostPerProduct();
        }
        // Средняя стоимость 1 страницы = cредняя стоимость всех департ  / кол-во департаментов
        $avrCostPerProduct = $avrCostPerProduct / count($this->departaments);

        return $avrCostPerProduct;
    }
}

// Антикризисный коммитет
class AnticrisisCommittee
{
    // Сожержит клон компании, к которому применяются АМ
    private $companyClone;

    // Клонирует компанию
    public function cloneCompany($company)
    {
        $this->companyClone = clone $company;
    }

    /*
    *  Сократить в каждом департаменте 40% (округляя в большую сторону) инженеров,
    *  преимущественно самого низкого ранга. Если инженер является боссом,
    *  вместо него надо уволить другого инженера, не босса.
    *  */
    public function dismissEngineers()
    {
        // Запрашивавем все департаменты в компании
        $departaments = $this->companyClone->findDepartamentByName();

        // Перебираем все департаменты в компании
        foreach ($departaments as $departamentName => $departament) {

            // Берем всех работников определенной профессии          
            $engineers = $departament->findEmployees("Engineer");
            // Если таковых нет, переходим к следующему департаменту  
            if (!$engineers) continue;

            // Исключаем из списка босса
            foreach ($engineers as $key => $engineer) {
                if ($engineer->getChief() == 1) {
                    unset($engineers[$key]);
                }
            }
            sort($engineers);

            // Сортируем массив работников по рангу по возрастанию
            // Внешний цикл повторяется до тех пор, 
            // пока во внутреннем цикле не будет перестановок
            $isSort = false; // массив не отсортирован
            while (!$isSort) {
                $isSort = true; // Считаем, что массив отсортирован, и перестановок не будет
                // Перебираем массив старым добрым for
                for ($i = 0; $i < (count($engineers) - 1); $i++) {
                    // Ранги первого и следующего в массиве работника
                    $rank1 = $engineers[$i]->getRank();
                    $rank2 = $engineers[$i++]->getRank();
                    // Сравниваем ранги работников попарно.
                    // Если ранг первого работника больше ранга следующего работника
                    // их нужно поменять местами
                    if ($rank1 > $rank2) {
                        // Меняем местами работников в массиве
                        $tmp = $engineers[$i++];
                        $engineers[$i++] = $engineers[$i];
                        $engineers[$i] = $tmp;
                        // Массив не отсортирован, т.к. была перестановка
                        $isSort = false;
                    }
                }
            }

            // Берем первые 40% через array_slice
            $dismissingEngineers = ceil(count($engineers) * 0.4);
            $engineers = array_slice($engineers, $dismissingEngineers);

            // Увольняем сотрудников
            foreach ($engineers as $engineer) {
                $departament->dismissEmployee($engineer);
            }
        }
    }

    /*
     *  Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков,
     *  а количество выпиваемого им кофе с 50 до 75 литров.
     *  */
    public function increaseSalaryRate()
    {
        // Запрашивавем все департаменты в компании
        $departaments = $this->companyClone->findDepartamentByName();

        // Перебираем все департаменты в компании
        foreach ($departaments as $departamentName => $departament) {

            // Запрашиваем работников
            $analysts = $departament->findEmployees("Analyst");
            // Если таковых нет, переходим к следующему департаменту  
            if (!$analysts) continue;

            // Перебираем $analysts
            foreach ($analysts as $analyst) {

                // Повышаем ставку
                $analyst->setSalaryRate(1100);
                // Повышаем количество выпиваемого им кофе
                $analyst->setCoffeeRate(75);
            }
        }
    }

    /*
     *  В тех департаментах, где руководитель не является аналитиком,
     *  заменить его на аналитика самого высшего ранга из этого департамента 
     *  (а бывшего руководителя вернуть к обычной работе)
     *  */
    public function setAnalystAsChief()
    {
        // Запрашивавем все департаменты в компании
        $departaments = $this->companyClone->findDepartamentByName();

        // Перебираем все департаменты в компании
        foreach ($departaments as $departamentName => $departament) {

            // Находим шефа в компании
            $chiefEmployee = $departament->findEmployee("all", "all", 1);
            // Узнаем профессию
            $professionName = $chiefEmployee->getProfession()->getName();
            // Если профессия не Аналитик, начинаем переназначение
            if ($professionName != "Analyst") {

                // Находим аналитика самого высокого ранга
                // Повторяем для всех рангов, начиная с самого высокого
                for ($rank = 3; $rank > 0; $rank--) {

                    // Запрашиваем Ааналитика из Департамента текущего ранга
                    // Т.к. мы начинаем с самого высокого ранга, то
                    // первый Аналитик, которого мы найдем,
                    // будет иметь самый высокий ранг в департаменте
                    $analyst = $departament->findEmployee("Analyst", $rank, 0);

                    // Проверяем, вернул ли Департамент Аналитика
                    if ($analyst != null) {

                        // Назначаем Аналитика шефом
                        $analyst->setСhief(1);

                        // Снимаем с должности текущего шефа
                        $chiefEmployee->setСhief(0);
                        break; // танцуем
                    }
                }
            }
        }
    }

    /*  
     *  В каждом департаменте повысить 50% (округляя в большую сторону)
     *  менеджеров 1-го и 2-го ранга на один ранг с целью расширить их полномочия.
     *  */
    public function liftManager()
    {
        // Запрашивавем все департаменты в компании
        $departaments = $this->companyClone->findDepartamentByName();

        // Перебираем все департаменты в компании
        foreach ($departaments as $departamentName => $departament) {

            // Берем всех работников определенной профессии          
            $employees = $departament->findEmployees("Manager");
            // Если таковых нет, переходим к следующему департаменту  
            if (!$employees) continue;

            // Исключаем из списка менеджеров вышые 2ого ранга
            foreach ($employees as $key => $employee) {
                if ($employee->getRank() > 2) {
                    unset($employees[$key]);
                }
            }
            sort($employees);

            // Берем 50% через array_slice
            $enhancingEmployees = ceil(count($employees) * 0.5);
            $employees = array_slice($employees, $enhancingEmployees);

            // Повышаем сотрудников
            foreach ($employees as $employee) {
                $currentRank = $employee->getRank();
                $employee->setRank($currentRank + 1);
            }
        }
    }

    // Выводим статистику на печать
    public function printStat($message)
    {
        printStatistics($this->companyClone, $message);
    }
}

// Функция создания компании
function createCompany($name)
{
    // Создаем новый класс компании
    $vector = new Company('$name');

    // Создаем классы департаментов
    $vector->createDepartament('Департамент закупок');
    $vector->createDepartament('Департамент продаж');
    $vector->createDepartament('Департамент рекламы');
    $vector->createDepartament('Департамент логистики');

    // Создаем классы профессий
    $me = new Manager();
    $ma = new Marketer();
    $en = new Engineer();
    $an = new Analyst();

    // Добавляем профессии в масив для последующего создания сотрудников пачкой
    $professions['me'] = $me;
    $professions['ma'] = $ma;
    $professions['en'] = $en;
    $professions['an'] = $an;

    // Лист вакансий: департамент => группы вакансий: профессияРанг статус_начальника => кол-во
    $vacancies = array(
        'Департамент закупок' => array('me1' => '9', 'me2' => '3', 'me3' => '2', 'ma1' => '2', 'me2 1' => '1'),
        'Департамент продаж' => array('me1' => '12', 'ma1' => '6', 'an1' => '3', 'an2' => '2', 'ma2 1' => '1'),
        'Департамент рекламы' => array('ma1' => '15', 'ma2' => '10', 'me1' => '8', 'en1' => '2', 'ma3 1' => '1'),
        'Департамент логистики' => array('me1' => '13', 'me2' => '5', 'en1' => '5', 'me1 1' => '1')
    );

    // Для каждого департамента
    foreach ($vacancies as $depName => $vacancyGroup) {

        // Для каждой группы вакансий
        foreach ($vacancyGroup as $vacancyName => $vacancyQuantity) {

            // Достаем название профессии из вакансии
            $professionIndex = mb_substr($vacancyName, 0, 2);
            $profession = $professions[$professionIndex];
            // Достаем ранг и из вакансии
            preg_match ('/\d/', $vacancyName, $matches1);            
            $rank = $matches1[0];
            // Достаем статус начальника из вакансии
            if(preg_match ('/ \d/', $vacancyName, $matches2)) {
               $chief = trim($matches2[0]);
            }  else {
                $chief = 0;
            }

            // Создаем столько сотрудников, сколько вакансий в группе
            for ($id = 1; $id <= $vacancyQuantity; $id++) {
                // Создаем сотрудника
                $employee = new Employee($profession, $rank, $chief);

                // Запрашиваем в Компании нужный департамент
                $departament = $vector->findDepartamentByName($depName);
                // Добавляем сотрудника в этот Департамент
                $departament->addEmployee($employee);
            }
        }
    }
    // Возвращаем объект Компания
    return $vector;
}

// Класс строки таблицы, хранит не фарматированную строку
class TableRow
{
    private $columns = array();   // строка это массив из колонок

    // Конструктор принимает массив колонок
    public function __construct($columns)
    {
        $this->columns = $columns;
    }

    // Метод возвращает содержимое строки (все ее колоночки)
    public function getColumns()
    {
        return $this->columns;
    }
}

// Класс таблицы
class Table
{
    private $tableRows = array();  // массив объектов Строк
    private $length = 15;           // ширина колонок

    // Добавление строки в таблицу, принимает массив
    public function addRow($row)
    {
        // Создаем новую строку
        $this->tableRows[] = new TableRow($row);
    }

    // Печать таблицы
    public function printTable()
    {
        $caption = 1;   // первая строка - заголовок таблицы

        // Перебираем массив объектов Строк
        foreach ($this->tableRows as $row) {

            $stringRow = "";    // строка для печати
            $columnNumber = 0;  // счетчик колонок

            // Извлекаем колонки из строки
            $columns = $row->getColumns();

            // Форматируем каждую колонку в строке
            foreach ($columns as $column) {

                // Округляем числа до 2х знаков после запятой
                if (is_float($column)) {
                    $column = round($column, 2);
                }
                // Добавляем отступы справа для первой колонки
                if ($columnNumber == 0) {
                    $stringRow .= "\n" . $this->padRight($column, $this->length);
                    // Добавляем оступы слева для остальных колонок
                } else {
                    $stringRow .= $this->padLeft($column, $this->length);
                }
                $columnNumber++;
            }
            // Добавляем черту после заголовка
            if ($caption == 1) {
                $stringRow .= "\n" . $this->formatCaption($columnNumber, $this->length);
            }
            // Выводим строку на печать
            echo "{$stringRow}";

            $caption--; // останется только один заголовок
        }
    }

    // Добавление пробелов слева
    private function padRight($string, $length)
    {
        // Разница между переданной строкой и шириной колонки
        $lenghtDifference = $length - mb_strlen($string);
        // Проверка на отрицательное значение при вычитании
        if ($lenghtDifference >= 0) {
            // Заполняем разницу пробелами
            $string .= str_repeat(" ", $lenghtDifference);
        } else {
            // Если таки отрицательное, обрезаем край строки до ширины колонки
            $string = mb_substr($string, 0, $length);
        }
        return $string;
    }

    // Добавление пробелов справа
    private function padLeft($string, $length)
    {
        // Разница между переданной строкой и шириной колонки
        $lenghtDifference = $length - mb_strlen($string);
        // Проверка на отрицательное значение при вычитании
        if ($lenghtDifference >= 0) {
            // Заполняем разницу пробелами
            $string = str_repeat(" ", $lenghtDifference) . $string;
        } else {
            // Если таки отрицательное, обрезаем край строки до ширины колонки
            $string = mb_substr($string, 0, $length);
        }
        return $string;
    }

    // Делаем горизонтальную черту
    private function formatCaption($columnNumber, $length)
    {
        $string = str_repeat("-", $length * $columnNumber);
        return $string;
    }
}

/*  Вывод таблицы результатов   */

// Функция вывода статистики по компании
function printStatistics($company, $caption)
{

    // Создаем таблицу
    $table = new Table();

    // Заголовок таблицы
    $table->addRow(array("Департамент",
        "сотр.",
        "тугр.",
        "кофе",
        "стр.",
        "тугр./стр.."));

    // Статистика по департаентам
    $departaments = $company->findDepartamentByName();
    // Каждый департамент строка
    foreach ($departaments as $departament) {
        unset($columns); // обнуляем массив
        // Заполняем массив строк
        $columns[] = mb_convert_case(mb_substr($departament->name, 12), MB_CASE_TITLE, "UTF-8");
        $columns[] = $departament->countEmployees();
        $columns[] = $departament->getTotalSalary();
        $columns[] = $departament->getTotalCoffee();
        $columns[] = $departament->getTotalProduct();
        $columns[] = $departament->getAverageCostPerProduct();
        // Добавляем строку в таблицу
        $table->addRow($columns);
    }

    // Пустая строка
    unset($columns); // обнуляем массив
    $columns[] = ' ';
    // Добавляем строку в таблицу
    $table->addRow($columns);

    // Среднее для всей компании
    unset($columns); // обнуляем массив
    // Заполняем массив строк
    $columns[] = 'Среднее';
    $columns[] = $company->getAverageEmployees();
    $columns[] = $company->getAverageSalary();
    $columns[] = $company->getAverageCoffee();
    $columns[] = $company->getAverageProduct();
    $columns[] = $company->getAverageCostPerProduct();
    // Добавляем строку в таблицу
    $table->addRow($columns);

    // Всего для всей компании
    unset($columns); // обнуляем массив
    // Заполняем массив строк
    $columns[] = 'Всего';
    $columns[] = $company->getTotalEmployees();
    $columns[] = $company->getTotalSalary();
    $columns[] = $company->getTotalCoffee();
    $columns[] = $company->getTotalProduct();
    // Добавляdем строку в таблицу
    $table->addRow($columns);

    // Выводим на печать заголовок
    echo "{$caption}\n";

    // Выводим таблицу на печать
    $table->printTable();
    echo "\n\n";
}

// Регистрируем Vector Inc на Сейшельских островах
$vector = createCompany('Vector');
printStatistics($vector, "Докризисная статистика");
// Создаем антикризисный коммитет
$anticrisisCommittee = new AnticrisisCommittee();
// Первый набор Антикризисных мер
$anticrisisCommittee->cloneCompany($vector);
$anticrisisCommittee->dismissEngineers();
$anticrisisCommittee->printStat("Антикризисные меры 1");
// Второй набор Антикризисных мер
$anticrisisCommittee->cloneCompany($vector);
$anticrisisCommittee->increaseSalaryRate();
$anticrisisCommittee->setAnalystAsChief();
$anticrisisCommittee->printStat("Антикризисные меры 2");
// Третий набор Антикризисных мер
$anticrisisCommittee->cloneCompany($vector);
$anticrisisCommittee->liftManager();
$anticrisisCommittee->printStat("Антикризисные меры 3");
?>