/* ============================================================
Подключаем стандартные типы:
size_t — беззнаковый тип для индексов и размеров
bool — логический тип true / false
============================================================ */
#include <stddef.h>
#include <stdbool.h>
/* ============================================================
СТРУКТУРА MATRIX
============================================================
Описывает матрицу объектов произвольного типа.
data — трёхуровневый указатель:
data[i][j] — указатель на объект в ячейке
rows — количество строк матрицы (m)
cols — количество столбцов матрицы (n)
============================================================ */
typedef struct {
void*** data; /* Двумерный массив указателей на объекты */
size_t rows; /* Число строк */
size_t cols; /* Число столбцов */
} Matrix;
/* ============================================================
ТИП ФУНКЦИИ СРАВНЕНИЯ
============================================================
Так как элементы имеют тип void*,
мы не знаем их реальный тип.
Поэтому пользователь ОБЯЗАН передать
функцию сравнения элементов.
Функция должна вернуть:
true — если элементы равны
false — если элементы различны
============================================================ */
typedef bool (*CompareFunc)(const void* a, const void* b);
/* ============================================================
ПРОВЕРКА ТЕПЛИЦЕВОЙ МАТРИЦЫ
============================================================
Матрица теплицева, если:
все элементы на диагоналях,
идущих из левого верхнего угла
в правый нижний, одинаковы.
Формально:
matrix[i][j] == matrix[i-1][j-1]
============================================================ */
bool is_toeplitz(const Matrix* matrix, CompareFunc cmp) {
/* Начинаем со второй строки,
потому что первой не с чем сравнивать */
for (size_t i = 1; i < matrix->rows; ++i) {
/* Начинаем со второго столбца
по той же причине */
for (size_t j = 1; j < matrix->cols; ++j) {
/* Сравниваем текущий элемент
с элементом на диагонали выше */
if (!cmp(
matrix->data[i][j], /* текущий элемент */
matrix->data[i - 1][j - 1] /* диагональный элемент */
)) {
/* Если хотя бы одна диагональ
нарушена — матрица НЕ теплицева */
return false;
}
}
}
/* Если все диагонали корректны */
return true;
}
/* ============================================================
ПРОВЕРКА ЦИКЛИЧЕСКОГО СДВИГА СТРОК
============================================================
Проверяет, что строка current_row
получена циклическим сдвигом строки
previous_row вправо на 1 позицию.
Пример:
[1 2 3 4]
[4 1 2 3]
Формула:
current[j] == previous[(j - 1 + cols) % cols]
============================================================ */
bool is_cyclic_shift_row(
const Matrix* matrix, /* указатель на матрицу */
size_t previous_row, /* индекс предыдущей строки */
size_t current_row, /* индекс текущей строки */
CompareFunc cmp /* функция сравнения */
) {
/* Запоминаем количество столбцов */
size_t cols = matrix->cols;
/* Проходим по всем столбцам строки */
for (size_t j = 0; j < cols; ++j) {
/* Вычисляем индекс элемента
в предыдущей строке с учётом
циклического перехода */
size_t prev_index = (j + cols - 1) % cols;
/* Сравниваем элементы */
if (!cmp(
matrix->data[current_row][j], /* текущий элемент */
matrix->data[previous_row][prev_index]/* ожидаемый элемент */
)) {
/* Если хотя бы один элемент
не совпал — сдвиг неверен */
return false;
}
}
/* Все элементы совпали */
return true;
}
/* ============================================================
ПРОВЕРКА ЦИКЛИЧЕСКОГО СДВИГА СТОЛБЦОВ
============================================================
Проверяет, что столбец current_col
получен циклическим сдвигом столбца
previous_col вниз на 1 позицию.
Формула:
current[i] == previous[(i - 1 + rows) % rows]
============================================================ */
bool is_cyclic_shift_col(
const Matrix* matrix, /* указатель на матрицу */
size_t previous_col, /* индекс предыдущего столбца */
size_t current_col, /* индекс текущего столбца */
CompareFunc cmp /* функция сравнения */
) {
/* Количество строк */
size_t rows = matrix->rows;
/* Проходим по всем строкам */
for (size_t i = 0; i < rows; ++i) {
/* Индекс элемента с циклическим сдвигом */
size_t prev_index = (i + rows - 1) % rows;
/* Сравнение элементов */
if (!cmp(
matrix->data[i][current_col], /* текущий элемент */
matrix->data[prev_index][previous_col]/* ожидаемый элемент */
)) {
return false;
}
}
return true;
}
/* ============================================================
ГЛАВНАЯ ПРОВЕРКА ЦИРКУЛЯНТНОЙ МАТРИЦЫ
============================================================
Матрица считается циркулянтной, если:
1. Она теплицева
2. Каждая строка — циклический сдвиг предыдущей
3. Каждый столбец — циклический сдвиг предыдущего
============================================================ */
bool is_circulant(const Matrix* matrix, CompareFunc cmp) {
/* Матрица 1×N или M×1 считается циркулянтной */
if (matrix->rows < 2 && matrix->cols < 2) {
return true;
}
/* Проверяем теплицевость */
if (!is_toeplitz(matrix, cmp)) {
return false;
}
/* Проверяем циклический сдвиг строк */
for (size_t i = 1; i < matrix->rows; ++i) {
if (!is_cyclic_shift_row(matrix, i - 1, i, cmp)) {
return false;
}
}
/* Проверяем циклический сдвиг столбцов */
for (size_t j = 1; j < matrix->cols; ++j) {
if (!is_cyclic_shift_col(matrix, j - 1, j, cmp)) {
return false;
}
}
/* Все условия выполнены */
return true;
}
/* ============================================================
ТОЧКА ВХОДА В ПРОГРАММУ
============================================================
Нужна только для корректной линковки.
В учебной задаче логика находится выше.
============================================================ */
int main(void) {
return 0;
}