#include <stdlib.h>
#include <iostream.h>


const int n  = 10;
const int n5 =  6;
const int n6 =  3;
const int nT = 50;


int f1() {
  return random(4) + 2;
}
int f2() {
  return random(10);
}

template <class T>
class TQueue {

public:
  TQueue()
  {
    front = back = 0;
  }
  ~TQueue();

  T get();
  void put(const T&);

  int is_empty()
  {
    return((!front) ? 1 : 0);
  }

private:

  class TQueueItem {
  public:
    TQueueItem(T value): item(value), next(0)
    {
    }

    T item;
    TQueueItem *next;
  };

  TQueueItem *front, *back;
};

template <class T>
TQueue<T> :: ~TQueue()
{
  while(!is_empty()) (void)get();
}

template <class T>
void TQueue<T> :: put(const T &value)
{
  TQueueItem *pt = new TQueueItem(value);
  if(is_empty()) {
    front = back = pt;
  }
  else {
    back -> next = pt;
    back = pt;
  }
}

template <class T>
T TQueue<T> :: get()
{
  if(is_empty()) {
    cout << "Trying to Get from the empty queue !\n";

    exit(-1);
  }

  TQueueItem *pt = front;
  front = front -> next;

  T value = pt -> item;
  delete pt;

  return value;
}


class TClient {

  int ID;

public:
  int get_id() const
  {
    return ID;
  }
  TClient(int value): ID(value)
  {
  }

};



enum status {
  // состояние "занят: работаю с клиентом"
  _busy = 0,
  // состояние: "свободен - жду очередного клиента"
  _waiting,
  // состояние: "отдыхаю"
  _vacation
};

class TCasher {

  status state;
  const int work_time, ID;
  int working_time, this_client;

public:
  TCasher(int _id, int _work):
    ID(_id), work_time(_work), state(_waiting)
    {
      working_time = work_time;
    }

  // Функция Handle вызывается ежеминутно из "менеджера", и возвращает текущее
  // состояние кассира (как результат) и через параметр changed - признак того, что состояние изменилось
  status Handle(int &changed) {

    changed = 0;

    // Если кассир работал с клиентом, то уменьшаем оставшееся время работы с ним,
    // и одновременно уменьшаем счетчик времени до очередного отдыха кассира
    if(state == _busy) {
      this_client -= 1;
      working_time -= 1;

      // Если this_client = 0, это значит, что с этим клиентом мы закончили работать, статус изменился
      // как минимум на "ожидание", кроме этого - устанавливаем признак того, что состояние изменилось
      if(!this_client) state = _waiting, changed = 1;
    }

    // Если кассир отдыхал - то уменьшаем время отдыха (когда кассир начинает отдыхать, в переменную
    // working_time записывается _отрицательное_ значение, и постепенно увеличивается, чтобы не вводить
    // специальную переменную)
    if(state == _vacation) {

      working_time += 1;
      // Если working_time = 0, то отдых закончился, и переходим в "ожидание", с установкой признака изменения
      // состояния, и кроме этого, устанавливаем положительное время работы ДО следующего отдыха
      if(working_time > 0)
        state = _waiting, working_time = work_time, changed = 1;

    }

    // Если после всех предыдущих проверок состояние = "ожидание" ...
    if(state == _waiting) {

      // ... если не было изменения состояния - уменьшаем время до очередного отдыха
      // (если это условие не поставить, то время может уменьшится дважды - один раз при
      // переходе от _busy к _waiting, и второй раз - здесь, а это делать нежелательно)
      if(!changed) working_time -= 1;

      // ... и, наконец, проверяем не пришло ли время отдохнуть (это уже проверяется независимо
      // от того, было ли изменение состояния, т.е. если кассир на этой минуте закончил работать с
      // клиентом, и ему уже пора отдыхать, он должен начать отдыхать...), и если пришло, то ставим
      // отрицательное время работы равное константе N6, и сигнализируем об изменении состояния
      if(working_time <= 0)
        state = _vacation, working_time = -n6, changed = 1;
    }

    return state;
  }

  // Эта функция просто напросто имитирует подход клиента к кассиру: переводит кассира в
  // состояние "занят" и устанавливает время обслуживания данного клиента...
  void next() {

    state = _busy;
    this_client = f2();

  }

};



// Основной управляющий класс - "менеджер"...
class TManager {

  // Включает в себя очередь "посетителей", массив "кассиров" ...
  TQueue<TClient> queue;
  TCasher *cash_workers[n];

  // ... текущее время (0 - при начале работы банка), и время после прихода последнего посетителя
  int current_time, after_prev_enter;

public:
  TManager();
  ~TManager();

  void run();
};

TManager :: TManager():
  current_time(0), after_prev_enter(0)
{
  // инициализируем "кассиров"
  for(int i = 0; i < n; i++)
    cash_workers[i] = new TCasher(i + 1, n5);
}

// в дестркуторе - "закрываем кассы"
TManager :: ~TManager() {
  for(int i = 0; i < n; i++)
    delete cash_workers[i];
}


// вот в этой функции как раз и происходит вся имитация:
void TManager :: run() {

  // эта переменная - для хранения количества пришедших посетителей
  // (используется для присвоения ID клиенту)
  static int client_entered = 0;

  // Все операции продолжаются до тех пор, пока не вышло время работы банка,
  // и потом, пока в очереди есть хотя бы один посетитель (не выгонять же, правда?)
  while((current_time <= nT) || (!queue.is_empty())) {

    // Перебинаем всех кассиров, и для каждого ...
    for(int i = 0; i < n; i++) {

      // предполагаем, что его статус НЕ изменился
      int status_changed = 0;
      // получаем статус кассира обращением к ЕГО методу
      status curr_status =
        cash_workers[i] -> Handle(status_changed);

      // если статус изменился - отобразить этот факт в статистике (какой кассир, как изменился статус,
      // когда изменился - в какое время)
      if(status_changed)
        // statistics
        switch(curr_status) {
          case _vacation:
            cout << "casher #" << i << " goes to rest at:: " << current_time << endl;
            break;
          case _waiting:
            cout << "casher #" << i << " awaiting at:: " << current_time << endl;
            break;
        }

        // если сейчас проверяемый кассир ожидает клиента, то ...
        if(curr_status == _waiting)
          if(!queue.is_empty()) {

            // ... вытянуть посетителя из очереди (если там кто-то есть, разумеется)
            TClient client = queue.get();
            // добавить в статистику, что такой-то посетитель направлен к такому-то кассиру в такое-то время
            cout << "client #" << client.get_id() << " sent to casher #" << i << " at:: " << current_time << endl;

            // и, собственно, направить клиента к кассиру
            cash_workers[i] -> next();

          }

    }


    // здесь (после каждого прохода по всем кассирам) проверяем, не закончилось ли время работы банка ...
    if(current_time <= nT) {

      // если еще НЕ закончилось - то увеличиваем время, прошедшее после прихода последнего посетителя
      after_prev_enter += 1;
      // и если это время больше того, которое должно быть по формуле (интервал между приходом посетителей)
      if(after_prev_enter > f1()) {

        // то имитируем приход очередного клиента: он только что пришел, т.е. время обнуляем ...
        after_prev_enter = 0;
        // вызываем конструктор TClient, перед этим увеличиваем общий счетчик посетителей, и передаем
        // его значение как ID в конструктор (чтобы ВСЕ посетители хоть чем-то различались)
        TClient client(++client_entered);
        // добавляем вновь прибывшего посетителя в очередь
        queue.put(client);

      }

      // если же время работы банка уже закончилось, то новые посетители НЕ будут добавляться в очередь,
      // а те, кто пришел раньше, и уже стоит в очереди, будут обслужены, и только тогда этот цикл закончится...

    }
    // прибавляем к "банковскому" времени минуту, и начинаем заново опрашивать кассиров
    current_time += 1;
  }

}


int main() {

  TManager bank;
  bank.run();

  return 0;
}