/*==============================================================================
  Лабораторна робота №2. Завдання 2. Варіант 19.
  Тема: розгалужені обчислювальні процеси.

  Умова: увести з клавіатури дійсні числа x, y. Визначити, чи належить точка
         з координатами (x, y) зафарбованій частині площини
         (таблиця 2.2, варіант 19).

  Опис фігур із рисунка варіанта.
      Фігура 1 — кільце: зафарбовано частину площини між двома концентричними
      колами з центром у початку координат, внутрішній радіус 0,5, зовнішній 1.
      Межі кіл на рисунку суцільні, тобто належать зафарбованій частині:
                        0,5 <= sqrt(x^2 + y^2) <= 1.

      Фігура 2 — ромб із вершинами (-0,5; 0), (0; 1), (0,5; 0), (0; -1).
      Канонічне рівняння ромба з півдіагоналями a = 0,5 (по осі Ox)
      та b = 1 (по осі Oy):
                        |x| / 0,5 + |y| / 1 <= 1,   тобто  2*|x| + |y| <= 1.

  Оскільки на рисунку варіанта подано дві фігури в окремих системах координат,
  програма визначає належність точки кожній фігурі окремо та їх об'єднанню.
  Режим перевірки обирає користувач у меню (оператор switch).

  Виконав: Одарчук Олексій, КНУ імені Тараса Шевченка, ФІТ, група ІПЗ-11.

  Компілятор: g++ -std=c++17
==============================================================================*/

#include <iostream>
#include <cmath>

/* Півдіагоналі ромба та радіуси кільця — константи умови варіанта. */
const double R_INNER = 0.5; /* внутрішній радіус кільця */
const double R_OUTER = 1.0; /* зовнішній радіус кільця  */
const double RHOMB_A = 0.5; /* півдіагональ ромба по осі Ox */
const double RHOMB_B = 1.0; /* півдіагональ ромба по осі Oy */

/* Допуск для порівняння дійсних чисел: точка на самій межі фігури має
   зараховуватись як така, що належить фігурі, попри похибку округлення. */
const double EPS = 1e-9;

/*------------------------------------------------------------------------------
  inRing — чи належить точка кільцю 0,5 <= r <= 1.

  Параметри: x, y [вхідні] — координати точки.
  Повертає : true — точка належить кільцю (разом з межами).

  Локальні змінні:
      r — відстань від початку координат до точки.
------------------------------------------------------------------------------*/
bool inRing(double x, double y)
{
    const double r = std::sqrt(x * x + y * y);
    return r >= R_INNER - EPS && r <= R_OUTER + EPS;
}

/*------------------------------------------------------------------------------
  inRhombus — чи належить точка ромбу |x|/a + |y|/b <= 1.

  Параметри: x, y [вхідні] — координати точки.
  Повертає : true — точка належить ромбу (разом з межами).
------------------------------------------------------------------------------*/
bool inRhombus(double x, double y)
{
    return std::fabs(x) / RHOMB_A + std::fabs(y) / RHOMB_B <= 1.0 + EPS;
}

/*------------------------------------------------------------------------------
  readChoice — прочитати номер пункту меню (1..3) з контролем введення.

  Параметри: choice [вихідний] — адреса змінної для номера пункту.
  Повертає : true — номер прочитано; false — вхідні дані вичерпано.
------------------------------------------------------------------------------*/
bool readChoice(int *choice)
{
    std::cout << "Оберіть фігуру (1..3): ";

    while (!(std::cin >> *choice) || *choice < 1 || *choice > 3) {
        if (std::cin.eof())
            return false;

        std::cin.clear();            /* зняти прапорець помилки */
        std::cin.ignore(1024, '\n'); /* очистити буфер клавіатури */
        std::cout << "Помилка: потрібне число 1, 2 або 3. Повторіть: ";
    }
    return true;
}

/*------------------------------------------------------------------------------
  readPoint — прочитати координати точки з контролем коректності введення.

  Параметри: x, y [вихідні] — адреси змінних для координат точки.
  Повертає : true — координати прочитано; false — вхідні дані вичерпано.
------------------------------------------------------------------------------*/
bool readPoint(double *x, double *y)
{
    std::cout << "Уведіть координати точки x та y: ";

    while (!(std::cin >> *x >> *y)) {
        if (std::cin.eof())
            return false;

        std::cin.clear();
        std::cin.ignore(1024, '\n');
        std::cout << "Помилка: очікуються два дійсних числа. Повторіть: ";
    }
    return true;
}

/*------------------------------------------------------------------------------
  yesNo — слово «так» або «ні» для логічного значення.

  Параметри: value [вхідний] — логічне значення.
  Повертає : рядок-константу «так» або «ні».
------------------------------------------------------------------------------*/
const char *yesNo(bool value)
{
    if (value)
        return "так";
    return "ні";
}

/*------------------------------------------------------------------------------
  Головна функція. Виводить меню вибору фігури, читає координати точки
  та повідомляє результат перевірки.

  Локальні змінні:
      choice  — номер пункту меню, обраний користувачем;
      x, y    — координати точки;
      ring    — ознака належності точки кільцю;
      rhomb   — ознака належності точки ромбу;
      belongs — ознака належності точки обраній фігурі;
      figure  — назва обраної фігури для повідомлення.
------------------------------------------------------------------------------*/
int main()
{
    std::cout << "Лабораторна робота №2, завдання 2 (варіант 19)\n"
                 "Виконав: студент групи ІПЗ-11 Одарчук Олексій\n"
                 "Належність точки зафарбованій частині площини\n\n"
                 "  1 - кільце 0,5 <= sqrt(x^2 + y^2) <= 1\n"
                 "  2 - ромб |x|/0,5 + |y|/1 <= 1\n"
                 "  3 - об'єднання обох фігур\n"
              << std::endl;

    int choice = 0;
    if (!readChoice(&choice)) {
        std::cout << "\nВхідні дані вичерпано." << std::endl;
        return 1;
    }

    double x = 0.0, y = 0.0;
    if (!readPoint(&x, &y)) {
        std::cout << "\nВхідні дані вичерпано." << std::endl;
        return 1;
    }

    const bool ring = inRing(x, y);
    const bool rhomb = inRhombus(x, y);

    /* Дійсні числа виводяться у фіксованому форматі з чотирма знаками. */
    std::cout.setf(std::ios::fixed);
    std::cout.precision(4);

    std::cout << "\nТочка: x = " << x << ", y = " << y << std::endl;
    std::cout << "  відстань від початку координат r = " << std::sqrt(x * x + y * y)
              << std::endl;
    std::cout << "  значення |x|/0,5 + |y|/1        = "
              << std::fabs(x) / RHOMB_A + std::fabs(y) / RHOMB_B << std::endl;

    bool belongs = false;
    const char *figure;

    /* Поліваріантний вибір: кожна гілка завершується оператором break,
       інакше керування «провалилося» б у наступну гілку. */
    switch (choice) {
    case 1:
        belongs = ring;
        figure = "кільцю";
        break;
    case 2:
        belongs = rhomb;
        figure = "ромбу";
        break;
    case 3:
        belongs = ring || rhomb;
        figure = "об'єднанню кільця та ромба";
        break;
    default:
        std::cout << "Неіснуючий пункт меню." << std::endl;
        return 2;
    }

    if (belongs)
        std::cout << "\nНалежність " << figure << ": ТАК — точка у зафарбованій частині"
                  << std::endl;
    else
        std::cout << "\nНалежність " << figure
                  << ": НІ — точка поза зафарбованою частиною" << std::endl;

    std::cout << "\nДовідково: кільце - " << yesNo(ring) << ", ромб - " << yesNo(rhomb)
              << std::endl;
    return 0;
}
