/*==============================================================================
  Лабораторна робота №6. Завдання 1. Варіант 19.
  Тема: одновимірні масиви, базові операції обробки.

  Умова (таблиця 6.1, завдання 19.1): створити одновимірний масив, кількість
  елементів якого ввести з клавіатури. Передбачити меню вибору способу
  створення масиву: введення з клавіатури або генерація псевдовипадкових
  чисел. Поміняти місцями елементи, що мають парні індекси, з елементами,
  які мають непарні індекси. Надрукувати отриманий масив.

  ОБМЕЖЕННЯ УМОВИ: забороняється використовувати бібліотеку стандартних
  шаблонів STL і шаблон вектору std::vector. Застосування покажчиків,
  динамічних масивів, операторів new, delete в лабораторній роботі №6
  не схвалюється — тому використано масив сталого розміру.

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

  Компілятор: gcc -std=c17
==============================================================================*/

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>

/* Найбільша припустима кількість елементів масиву. */
const int MAX_SIZE = 1000;

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

  Параметри:
      prompt [вхідний]  — текст запрошення;
      value  [вихідний] — адреса змінної для введеного числа;
      low, high [вхідні] — межі припустимого діапазону.
  Повертає : true — число прочитано; false — вхідні дані вичерпано.
------------------------------------------------------------------------------*/
bool readInt(const char *prompt, int *value, int low, int high)
{
    for (;;) {
        printf("%s", prompt);
        const int scanned = scanf("%d", value);

        if (scanned == EOF)
            return false;
        if (scanned == 1 && *value >= low && *value <= high)
            return true;

        if (scanned != 1) {
            int c;
            /* очищення буфера */
            while ((c = getchar()) != '\n' && c != EOF) {
            }
        }
        printf("Помилка: потрібне ціле число від %d до %d.\n", low, high);
    }
}

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

  Параметри:
      a [вихідний] — масив, що заповнюється;
      n [вхідний]  — кількість елементів.
  Повертає : true — масив заповнено; false — вхідні дані вичерпано.
------------------------------------------------------------------------------*/
bool fillFromKeyboard(int a[], int n)
{
    printf("Уведіть цілі числа (усього %d):\n", n);

    for (int i = 0; i < n; ++i) {
        char prompt[32];
        snprintf(prompt, sizeof prompt, "  a[%d] = ", i);
        if (!readInt(prompt, &a[i], -1000000, 1000000))
            return false;
    }
    return true;
}

/*------------------------------------------------------------------------------
  fillRandom — заповнити масив псевдовипадковими числами із заданого діапазону.

  Параметри:
      a [вихідний] — масив, що заповнюється;
      n [вхідний]  — кількість елементів;
      low, high [вхідні] — межі діапазону значень.

  Локальні змінні:
      range — кількість різних значень у діапазоні.
------------------------------------------------------------------------------*/
void fillRandom(int a[], int n, int low, int high)
{
    const int range = high - low + 1;

    /* Масштабування rand() на діапазон замість rand() % range: стандарт гарантує
       лише RAND_MAX >= 32767, і для ширшого діапазону остача не охопила б
       усіх значень. */
    for (int i = 0; i < n; ++i)
        a[i] = low + (int)((double)rand() / ((double)RAND_MAX + 1.0) * range);
}

/*------------------------------------------------------------------------------
  printArray — вивести масив на екран у табличній формі.

  Параметри:
      title [вхідний] — заголовок, що передує масиву;
      a     [вхідний] — масив;
      n     [вхідний] — кількість елементів.

  Індекси виводяться над значеннями, щоб було видно, які саме позиції
  обмінялися місцями.
------------------------------------------------------------------------------*/
void printArray(const char *title, const int a[], int n)
{
    printf("\n%s (елементів: %d):\n", title, n);

    printf("  індекс:  "); /* ширина збігається з написом «значення:» */
    for (int i = 0; i < n; ++i)
        printf("%8d", i);
    printf("\n");

    printf("  значення:");
    for (int i = 0; i < n; ++i)
        printf("%8d", a[i]);
    printf("\n");
}

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

  Обмінюються сусідні пари: a[0] з a[1], a[2] з a[3], a[4] з a[5] і так далі.
  Якщо кількість елементів непарна, останній елемент пари не має і лишається
  на своєму місці.

  Параметри:
      a [вхідний/вихідний] — масив, елементи якого переставляються;
      n [вхідний]          — кількість елементів.
  Повертає : кількість виконаних обмінів.

  Локальні змінні:
      swaps — лічильник виконаних обмінів;
      temp  — проміжна змінна для обміну значень.
------------------------------------------------------------------------------*/
int swapEvenOdd(int a[], int n)
{
    int swaps = 0;

    for (int i = 0; i + 1 < n; i += 2) {
        const int temp = a[i];
        a[i] = a[i + 1];
        a[i + 1] = temp;
        ++swaps;
    }

    return swaps;
}

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

  Локальні змінні:
      a      — оброблюваний масив;
      n      — кількість елементів масиву;
      choice — обраний спосіб створення масиву;
      low, high — межі діапазону для генерації псевдовипадкових чисел;
      swaps  — кількість виконаних обмінів.
------------------------------------------------------------------------------*/
int main()
{
    printf("Лабораторна робота №6, завдання 1 (варіант 19)\n");
    printf("Виконав: студент групи ІПЗ-11 Одарчук Олексій\n");
    printf("Обмін елементів з парними індексами з елементами з непарними\n\n");

    int n = 0;
    if (!readInt("Уведіть кількість елементів масиву (1..1000): ", &n, 1, MAX_SIZE))
        return 1;

    int a[MAX_SIZE];

    printf("\nСпосіб створення масиву:\n"
           "  1 - введення з клавіатури\n"
           "  2 - генерація псевдовипадкових чисел\n");

    int choice = 0;
    if (!readInt("Оберіть спосіб (1..2): ", &choice, 1, 2))
        return 1;

    if (choice == 1) {
        if (!fillFromKeyboard(a, n))
            return 1;
    } else {
        int low = 0, high = 0;
        if (!readInt("Уведіть нижню межу діапазону: ", &low, -1000000, 1000000))
            return 1;
        if (!readInt("Уведіть верхню межу діапазону: ", &high, low, 1000000))
            return 1;

        srand((unsigned)time(NULL));
        fillRandom(a, n, low, high);
    }

    printArray("Вхідний масив", a, n);

    const int swaps = swapEvenOdd(a, n);

    printArray("Масив після перестановки", a, n);

    printf("\nВиконано обмінів: %d\n", swaps);
    if (n % 2 != 0)
        printf("Кількість елементів непарна, тому останній елемент a[%d] "
               "пари не має і лишився на місці.\n",
               n - 1);

    return 0;
}
