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

  Умова (таблиця 7.1, завдання 19.1): створити матрицю, вимірність m > 3 якої
  ввести з клавіатури. Передбачити меню вибору способу створення матриці:
  введення з клавіатури або генерація псевдовипадкових додатних та від'ємних
  чисел. Визначити найбільший за модулем елемент та його індекси серед
  елементів, що розташовані в заштрихованій частині малюнку. Підрахувати суму
  елементів по межі заштрихованої частини матриці. Вивести на екран вхідну
  матрицю, значення шуканого елемента та його індекси, шукану суму елементів.
  Матриці виводити у табличному вигляді.

  ОПИС ЗАШТРИХОВАНОЇ ЧАСТИНИ (рисунок до варіанта 19).
      На рисунку заштриховано трикутник, вершина якого збігається з центром
      квадрата, а основою є права сторона. Сторонами трикутника є відрізки
      обох діагоналей квадрата. У термінах індексів квадратної матриці m x m
      це «східний» сектор, утворений двома діагоналями:

              j >= i        (на головній діагоналі або праворуч від неї)
              j >= m-1-i    (на побічній діагоналі або праворуч від неї)

      Приклад для m = 5 (позначено * — елементи заштрихованої частини):

              . . . . *
              . . . * *
              . . * * *
              . . . * *
              . . . . *

  МЕЖА ЗАШТРИХОВАНОЇ ЧАСТИНИ — це її контур: відрізки обох діагоналей
  (i == j та i + j == m-1) і права сторона квадрата (j == m-1).

  ОБМЕЖЕННЯ УМОВИ: забороняється використовувати STL, std::vector, ітератори,
  контейнери, бібліотеки для роботи з матрицями.

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

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

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

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

/*------------------------------------------------------------------------------
  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);
    }
}

/*------------------------------------------------------------------------------
  inShadedPart — чи належить елемент заштрихованій частині матриці.

  Параметри:
      i, j [вхідні] — індекси рядка та стовпця елемента;
      m    [вхідний] — вимірність квадратної матриці.
  Повертає : true — елемент належить «східному» сектору між діагоналями.
------------------------------------------------------------------------------*/
bool inShadedPart(int i, int j, int m)
{
    return j >= i && j >= m - 1 - i;
}

/*------------------------------------------------------------------------------
  onShadedBorder — чи лежить елемент на межі заштрихованої частини.

  Межу утворюють відрізки головної діагоналі (i == j), побічної діагоналі
  (i + j == m-1) та права сторона матриці (j == m-1) — але лише ті їх точки,
  що належать самому сектору.

  Параметри:
      i, j [вхідні] — індекси елемента;
      m    [вхідний] — вимірність матриці.
  Повертає : true — елемент лежить на межі заштрихованої частини.
------------------------------------------------------------------------------*/
bool onShadedBorder(int i, int j, int m)
{
    if (!inShadedPart(i, j, m))
        return false;

    return i == j || i + j == m - 1 || j == m - 1;
}

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

  Параметри:
      title [вхідний] — заголовок таблиці;
      a     [вхідний] — матриця;
      m     [вхідний] — вимірність матриці;
      mark  [вхідний] — якщо true, елементи заштрихованої частини позначаються
                        символом '*', а елементи її межі — символом '#'.

  ЗАУВАЖЕННЯ ЩОДО ПАРАМЕТРА-МАТРИЦІ. Параметр оголошено без модифікатора
  const, хоча функція матрицю не змінює. Причина суто мовна: у стандарті C
  до редакції C23 масив виду int a[N][M] НЕ перетворюється неявно на
  const int (*)[M] — на відміну від C++, де таке перетворення дозволене.
  Додавання const тут спричинило б попередження -Wpedantic.
------------------------------------------------------------------------------*/
void printMatrix(const char *title, int a[][MAX_SIZE], int m, bool mark)
{
    printf("\n%s (%d x %d):\n", title, m, m);

    printf("      ");
    for (int j = 0; j < m; ++j)
        printf("%8d", j);
    printf("\n");

    printf("      ");
    for (int j = 0; j < m; ++j)
        printf("--------");
    printf("\n");

    for (int i = 0; i < m; ++i) {
        printf("%4d |", i);
        for (int j = 0; j < m; ++j) {
            if (mark) {
                char flag = ' ';
                if (onShadedBorder(i, j, m))
                    flag = '#';
                else if (inShadedPart(i, j, m))
                    flag = '*';
                printf("%7d%c", a[i][j], flag);
            } else {
                printf("%8d", a[i][j]);
            }
        }
        printf("\n");
    }

    if (mark)
        printf("      * — заштрихована частина,  # — її межа\n");
}

/*------------------------------------------------------------------------------
  findMaxByModulus — знайти найбільший за модулем елемент заштрихованої частини.

  Параметри:
      a   [вхідний]  — матриця;
      m   [вхідний]  — вимірність матриці;
      row [вихідний] — адреса змінної для індексу рядка знайденого елемента;
      col [вихідний] — адреса змінної для індексу стовпця;
      count [вихідний] — адреса лічильника елементів сектора.
  Повертає : значення елемента з найбільшим модулем.

  За наявності кількох елементів з однаковим модулем запам'ятовується перший
  знайдений при обході рядками: порівняння виконується строгою нерівністю.

  Локальні змінні:
      maxAbs   — найбільше знайдене абсолютне значення;
      maxValue — сам елемент з найбільшим модулем.
------------------------------------------------------------------------------*/
int findMaxByModulus(int a[][MAX_SIZE], int m, int *row, int *col, int *count)
{
    long long maxAbs = -1;
    int maxValue = 0;

    *row = -1;
    *col = -1;
    *count = 0;

    for (int i = 0; i < m; ++i)
        for (int j = 0; j < m; ++j) {
            if (!inShadedPart(i, j, m))
                continue;

            ++(*count);
            const long long absValue = llabs((long long)a[i][j]);

            if (absValue > maxAbs) {
                maxAbs = absValue;
                maxValue = a[i][j];
                *row = i;
                *col = j;
            }
        }

    return maxValue;
}

/*------------------------------------------------------------------------------
  sumOnShadedBorder — сума елементів по межі заштрихованої частини матриці.

  Параметри:
      a     [вхідний]  — матриця;
      m     [вхідний]  — вимірність матриці;
      count [вихідний] — адреса лічильника елементів межі.
  Повертає : суму елементів межі.
------------------------------------------------------------------------------*/
long long sumOnShadedBorder(int a[][MAX_SIZE], int m, int *count)
{
    long long sum = 0;

    *count = 0;

    for (int i = 0; i < m; ++i)
        for (int j = 0; j < m; ++j)
            if (onShadedBorder(i, j, m)) {
                sum += a[i][j];
                ++(*count);
            }

    return sum;
}

/*------------------------------------------------------------------------------
  Головна функція. Створює матрицю, знаходить найбільший за модулем елемент
  заштрихованої частини та суму елементів по її межі.

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

    int m = 0;
    if (!readInt("Уведіть вимірність квадратної матриці m (4..20): ", &m, 4, MAX_SIZE))
        return 1;

    int a[MAX_SIZE][MAX_SIZE];

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

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

    if (choice == 1) {
        printf("Уведіть елементи матриці по рядках (усього %d):\n", m * m);
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < m; ++j) {
                char prompt[32];
                snprintf(prompt, sizeof prompt, "  a[%d][%d] = ", i, j);
                if (!readInt(prompt, &a[i][j], -1000000, 1000000))
                    return 1;
            }
    } else {
        int low = 0, high = 0;
        if (!readInt("Уведіть нижню межу діапазону (від'ємну): ", &low, -1000000, 0))
            return 1;
        if (!readInt("Уведіть верхню межу діапазону (додатну): ", &high, 1, 1000000))
            return 1;

        srand((unsigned)time(NULL));

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

    printMatrix("Вхідна матриця", a, m, false);
    printMatrix("Розмітка заштрихованої частини", a, m, true);

    int maxRow = -1, maxCol = -1, shadedCount = 0, borderCount = 0;
    const int maxValue = findMaxByModulus(a, m, &maxRow, &maxCol, &shadedCount);
    const long long borderSum = sumOnShadedBorder(a, m, &borderCount);

    printf("\nРезультати\n");
    printf("  Елементів у заштрихованій частині: %d\n", shadedCount);
    printf("  Найбільший за модулем елемент:     %d (модуль %lld)\n", maxValue,
           llabs((long long)maxValue));
    printf("  Його індекси:                      рядок %d, стовпець %d\n", maxRow,
           maxCol);
    printf("  Елементів на межі:                 %d\n", borderCount);
    printf("  Сума елементів по межі:            %lld\n", borderSum);

    return 0;
}
