Add files via upload

This commit is contained in:
StarFinger
2026-02-22 16:50:46 +03:00
committed by GitHub
commit 9f9f22f22c
8 changed files with 1349 additions and 0 deletions

21
Makefile Normal file
View File

@@ -0,0 +1,21 @@
app: main.o dynamic_array.o types.o
gcc -g main.o dynamic_array.o types.o -o app
test: tests.o dynamic_array.o types.o
gcc -g tests.o dynamic_array.o types.o -o test
main.o: main.c
gcc main.c -c
tests.o : tests.c
gcc tests.c -c
dynamic_array.o: dynamic_array.c dynamic_array.h
gcc dynamic_array.c -c
types.o: types.h types.c type_info.h
gcc types.c -c
clean:
rm -f *.o

236
dynamic_array.c Normal file
View File

@@ -0,0 +1,236 @@
#include "dynamic_array.h"
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#define DA_DEFAULT_CAPACITY 4
#define DA_GROWTH_FACTOR 2
#define ELEM_PTR(arr, i) \
((arr)->data + (i) * (arr)->type->element_size)
static bool da_ensure_capacity(DynamicArray *arr, const size_t needed) {
if (needed <= arr->capacity) return true;
size_t new_cap = arr->capacity;
while (new_cap < needed)
new_cap *= DA_GROWTH_FACTOR;
char *new_data = realloc(arr->data, new_cap * arr->type->element_size);
if (!new_data) {
fprintf(stderr, "da_ensure_capacity: realloc failed\n");
return false;
}
arr->data = new_data;
arr->capacity = new_cap;
return true;
}
DynamicArray *da_create(const TypeInfo *type, size_t initial_capacity) {
if (!type) {
fprintf(stderr, "da_create: type is NULL\n");
return NULL;
}
if (initial_capacity <= 0)
initial_capacity = DA_DEFAULT_CAPACITY;
DynamicArray *arr = (DynamicArray *) malloc(sizeof(DynamicArray));
if (!arr) {
fprintf(stderr, "da_create: malloc failed\n");
return NULL;
}
arr->data = (char *) malloc(initial_capacity * type->element_size);
if (!arr->data) {
fprintf(stderr, "da_create: malloc for data failed\n");
free(arr);
return NULL;
}
arr->size = 0;
arr->capacity = initial_capacity;
arr->type = type;
return arr;
}
void da_destroy(DynamicArray *arr) {
if (!arr) return;
if (arr->type->destroy) {
for (size_t i = 0; i < arr->size; i++)
arr->type->destroy(ELEM_PTR(arr, i));
}
free(arr->data);
free(arr);
}
void *da_get(const DynamicArray *arr, const size_t index) {
if (!arr || index >= arr->size) {
fprintf(stderr, "da_get: index %zu out of bounds (size=%zu)\n",
index, arr ? arr->size : 0);
return NULL;
}
return ELEM_PTR(arr, index);
}
bool da_set(DynamicArray *arr, const size_t index, const void *value) {
if (!arr || !value || index >= arr->size) {
fprintf(stderr, "da_set: incorrect arguments\n");
return false;
}
void *dest = ELEM_PTR(arr, index);
if (arr->type->destroy)
arr->type->destroy(dest);
arr->type->copy(dest, value);
return true;
}
size_t da_size(const DynamicArray *arr) {
return arr ? arr->size : 0;
}
bool da_push_back(DynamicArray *arr, const void *value) {
if (!arr || !value) return false;
if (!da_ensure_capacity(arr, arr->size+1)) return false;
arr->type->copy(ELEM_PTR(arr, arr->size), value);
arr->size++;
return true;
}
bool da_insert(DynamicArray *arr, const size_t index, const void *value) {
if (!arr || !value || index > arr->size) {
fprintf(stderr, "da_insert: invalid arguments\n");
return false;
}
if (!da_ensure_capacity(arr, arr->size + 1)) return false;
const size_t es = arr->type->element_size;
memmove(ELEM_PTR(arr, index + 1), ELEM_PTR(arr, index), (arr->size - index) * es); //
arr->type->copy(ELEM_PTR(arr, index), value);
arr->size++;
return true;
}
bool da_remove(DynamicArray *arr, const size_t index) {
if (!arr || index >= arr->size) {
fprintf(stderr, "da_remove: index %zu out of bounds\n", index);
return false;
}
if (arr->type->destroy)
arr->type->destroy(ELEM_PTR(arr, index));
const size_t es = arr->type->element_size;
memmove(ELEM_PTR(arr, index), ELEM_PTR(arr, index + 1), (arr->size - index - 1) * es);
arr->size--;
return true;
}
// Вспомогательные функции сортировки
static void swap(void *a, void *b, const TypeInfo *type) {
void *temp = malloc(type->element_size);
type->copy(temp, a);
type->copy(a, b);
type->copy(b, temp);
if (type->destroy) type->destroy(temp);
free(temp);
}
void da_sort(DynamicArray *arr) {
for (size_t i = 0; i < arr->size; i++) {
for (size_t j = i + 1; j < arr->size; j++) {
if (arr->type->compare(ELEM_PTR(arr, i), ELEM_PTR(arr, j)) > 0)
swap(ELEM_PTR(arr, i), ELEM_PTR(arr, j), arr->type);
}
}
}
DynamicArray* da_map(const DynamicArray *arr, MapFunc f) {
if (!arr || !f) return NULL;
DynamicArray *result = da_create(arr->type, arr->size);
if (!result) return NULL;
void *temp = malloc(arr->type->element_size); // Буффер для хранения результатов map
if (!temp) {
da_destroy(result);
return NULL;
}
for (size_t i = 0; i < arr->size; i++) {
memset(temp, 0, arr->type->element_size);
f(temp, ELEM_PTR(arr, i));
da_push_back(result, temp);
if (arr->type->destroy)
arr->type->destroy(temp);
}
free(temp);
return result;
}
DynamicArray* da_where(const DynamicArray *arr, Predicate pred) {
if (!arr || !pred) return NULL;
DynamicArray *result = da_create(arr->type, arr->size);
if (!result) return NULL;
for (size_t i = 0; i < arr->size; i++) {
void *elem = ELEM_PTR(arr, i);
if (pred(elem))
da_push_back(result, elem);
}
return result;
}
void da_reduce(const DynamicArray *arr, ReduceFunc f, void *initial, void *result) {
if (!arr || !f || !result) return;
size_t es = arr->type->element_size;
memcpy(result, initial, es);
for (size_t i = 0; i < arr->size; i++) {
f(result, ELEM_PTR(arr, i));
}
}
DynamicArray* da_concat(const DynamicArray *a, const DynamicArray *b) {
if (!a || !b) return NULL;
if (!types_equal(a->type, b->type)) {
fprintf(stderr, "da_concat: type missmatch (%s vs %s)\n",
a->type->type_name, b->type->type_name);
return NULL;
}
DynamicArray *result = da_create(a->type, a->size + b->size);
if (!result) return NULL;
for (size_t i = 0; i < a->size; i++)
da_push_back(result, ELEM_PTR(a, i));
for (size_t i = 0; i < b->size; i++)
da_push_back(result, ELEM_PTR(b, i));
return result;
}
void da_print(const DynamicArray *arr) {
if (!arr) {
printf("(null)\n");
return;
}
printf("[%s] size=%zu, capacity=%zu\n",
arr->type->type_name, arr->size, arr->capacity);
printf("[ ");
for (size_t i = 0; i < arr->size; i++) {
if (i > 0) printf(", ");
arr->type->print(ELEM_PTR(arr, i));
}
printf(" ]\n");
}

51
dynamic_array.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef ARRAY_H
#define ARRAY_H
#include <stdlib.h>
#include <stdbool.h>
#include "type_info.h"
typedef struct DynamicArray {
char *data;
const TypeInfo *type;
size_t capacity; // ёмкость
size_t size; // текущий размер
} DynamicArray;
// Создание/уничтожение
DynamicArray* da_create(const TypeInfo *type, size_t initial_capacity);
void da_destroy(DynamicArray *arr);
// Доступ к элементам
void* da_get(const DynamicArray *arr, const size_t index);
bool da_set(DynamicArray *arr, const size_t index, const void *value);
size_t da_size(const DynamicArray *arr);
// Модификация
bool da_push_back(DynamicArray *arr, const void *value);
bool da_insert(DynamicArray *arr, const size_t index, const void *value);
bool da_remove(DynamicArray *arr, const size_t index);
// Операции
// Сортировка (используется compare из TypeInfo)
void da_sort(DynamicArray *arr);
// map: применить f к каждому элементу и вернуть новый массив
typedef void (*MapFunc)(void *dest, const void *src);
DynamicArray* da_map(const DynamicArray *arr, MapFunc f);
// where: фильтрация по предикату
typedef bool (*Predicate)(const void *elem);
DynamicArray* da_where(const DynamicArray *arr, Predicate pred);
// reduce: свёртка массива в один элемент
typedef void (*ReduceFunc)(void *accumulator, const void *elem);
void da_reduce(const DynamicArray *arr, ReduceFunc f, void *initial, void *result);
// Конкатенация: возвращает новый массив
DynamicArray* da_concat(const DynamicArray *a, const DynamicArray *b);
// Вспомогательные функции
void da_print(const DynamicArray *arr);
#endif // ARRAY_H

427
main.c Normal file
View File

@@ -0,0 +1,427 @@
#include <stdio.h>
#include <stddef.h>
#include <time.h>
#include <string.h>
#include "dynamic_array.h"
#include "types.h"
#include "type_info.h"
static void clear_stdin(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
static int read_int(const char *prompt) {
int val;
printf("%s", prompt);
while (scanf("%d", &val) != 1) {
printf("Ошибка ввода. Повторите: ");
clear_stdin();
}
clear_stdin();
return val;
}
static void read_line(const char *prompt, char *buf, size_t size) {
printf("%s", prompt);
if (!fgets(buf, size, stdin))
buf[0] = '\0';
// Убираем перевод строки
size_t len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n')
buf[len - 1] = '\0';
}
static const TypeInfo *choose_type(void) {
printf("\nВыберите тип элементов:\n");
printf(" 1. Вещественные числа (double)\n");
printf(" 2. Комплексные числа\n");
int choice = read_int("Ваш выбор: ");
switch (choice) {
case 1: return get_double_type();
case 2: return get_complex_type();
default:
printf("Неизвестный тип данных. Используется double.\n");
return get_double_type();
}
}
static bool input_element(const TypeInfo *type, void *dest) {
char buf[512];
if (type == get_double_type()) {
read_line("Введите вещественное число: ", buf, sizeof(buf));
} else if (type == get_complex_type()) {
read_line("Введите комплексное число: ", buf, sizeof(buf));
} else {
printf("Неизвестный тип\n");
return false;
}
return type->parse(dest, buf);
}
// --------------- MAP-функции (для демонстрации) ---------------
static void double_double(void *dest, const void *src) {
*(double *) dest = *(const double *) src * 2;
}
static void double_square(void *dest, const void *src) {
const double v = *(const double *) src;
*(double *) dest = v * v;
}
static void double_negate(void *dest, const void *src) {
*(double *) dest = -(*(const double *) src);
}
static void complex_double(void *dest, const void *src) {
Complex *c_dest = (Complex *) dest;
const Complex *c_src = (const Complex *) src;
c_dest->re = c_src->re * 2;
c_dest->im = c_src->im * 2;
}
static void complex_square(void *dest, const void *src) {
Complex *c_dest = (Complex *) dest;
const Complex *c_src = (const Complex *) src;
c_dest->re = (c_src->re * c_src->re) - (c_src->im * c_src->im);
c_dest->im = 2.0 * (c_src->re * c_src->im);
}
static void complex_negate(void *dest, const void *src) {
Complex *c_dest = (Complex *) dest;
const Complex *c_src = (const Complex *) src;
c_dest->re = -c_src->re;
c_dest->im = -c_src->im;
}
static MapFunc choose_map_func(const TypeInfo *type) {
printf("Выберите функцию для map:\n");
printf(" 1. Удвоить (x * 2)\n");
printf(" 2. Квадрат (x ^ 2)\n");
printf(" 3. Отрицание (-x)\n");
int choice = read_int("Ваш выбор: ");
if (type == get_double_type()) {
switch (choice) {
case 1: return double_double;
case 2: return double_square;
case 3: return double_negate;
default:
printf("Неизвестный тип. Используется 1.\n");
return double_double;
}
} else if (type == get_complex_type()) {
switch (choice) {
case 1: return complex_double;
case 2: return complex_square;
case 3: return complex_negate;
default:
printf("Неизвестный тип. Используется 1.\n");
return complex_double;
}
}
printf("Map не реализован для типа: %s\n", type->type_name);
return NULL;
}
// --------------- WHERE-предикаты (для демонстрации) ---------------
static bool double_is_positive(const void *e) { return *(const double *) e > 0; }
static bool complex_is_real(const void *e) {
const Complex *ce = (Complex *) e;
return ce->im == 0;
}
static Predicate choose_predicate(const TypeInfo *type) {
if (type == get_double_type()) {
printf("Фильтр: положительные числа (x > 0)\n");
return double_is_positive;
} else if (type == get_complex_type()) {
printf("Фильтр: действительные числа.\n");
return complex_is_real;
}
printf("Where не реализован для типа: %s\n", type->type_name);
return NULL;
}
// --------------- REDUCE-функции (для демонстрации) ---------------
static void double_sum(void *acc, const void *elem) {
*(double *) acc += *(const double *) elem;
}
static void complex_sum(void *acc, const void *elem) {
Complex *c_acc = (Complex *) acc;
const Complex *c_elem = (const Complex *) acc;
c_acc->re += c_elem->re;
c_acc->im += c_elem->im;
}
static ReduceFunc choose_reduce(const TypeInfo *type) {
if (type == get_double_type()) {
printf("Функция: сумма вещественных чисел\n");
return double_sum;
} else if (type == get_complex_type()) {
printf("Функция: сумма комплексных чисел.\n");
return complex_sum;
}
printf("Reduce не реализован для типа: %s\n", type->type_name);
return NULL;
}
static void auto_fill(DynamicArray *arr) {
int count = read_int("Количество элементов: ");
if (count <= 0 || count > 10000) {
printf("Некорректное количество.\n");
return;
}
const TypeInfo *type = arr->type; // random -> type
if (type == get_double_type()) {
for (int i = 0; i < count; i++) {
double v = (rand() % 2001 - 1000) / 10.0; // [-100, 100]
da_push_back(arr, &v);
}
} else if (type == get_complex_type()) {
for (int i = 0; i < count; i++) {
Complex c = {
.re = (rand() % 201 - 100) / 10.0,
.im = (rand() % 201 - 100) / 10.0
};
da_push_back(arr, &c);
}
} else {
printf("Автозаполнение не поддерживается для типа: %s\n", type->type_name);
return;
}
printf("Добавлено %d элементов.\n", count);
}
static void print_menu(void) {
printf("\n╔═══════════════════════════════════════╗\n");
printf("║ Полиморфный динамический массив ║\n");
printf("╠═══════════════════════════════════════╣\n");
printf("║ 1. Показать массив ║\n");
printf("║ 2. Добавить элемент (в конец) ║\n");
printf("║ 3. Вставить элемент (по индексу) ║\n");
printf("║ 4. Удалить элемент (по индексу) ║\n");
printf("║ 5. Изменить элемент (по индексу) ║\n");
printf("║ 6. Сортировка ║\n");
printf("║ 7. map (преобразование) ║\n");
printf("║ 8. where (фильтрация) ║\n");
printf("║ 9. reduce (свёртка) ║\n");
printf("║ 10. Конкатенация (создать второй) ║\n");
printf("║ 11. Автозаполнение случайными ║\n");
printf("║ 0. Выход ║\n");
printf("╚═══════════════════════════════════════╝\n");
}
int main(void) {
srand(time(NULL));
// Выбор типа
const TypeInfo *type = choose_type();
DynamicArray *arr = da_create(type, 0);
if (!arr) {
fprintf(stderr, "Не удалось создать массив.\n");
return 1;
}
printf("Создан массив типа: %s\n", type->type_name);
void *elem;
int index;
int choice = -1;
MapFunc map_f;
Predicate pred;
ReduceFunc reduce_f;
// Основной цикл
bool running = true;
while (running) {
print_menu();
choice = read_int(">>> ");
switch (choice) {
case 0:
running = false;
break;
case 1: // Показать
da_print(arr);
break;
case 2: // Добавить в конец
elem = malloc(type->element_size);
memset(elem, 0, type->element_size);
if (input_element(type, elem)) {
if (da_push_back(arr, elem))
printf("Элемент добавлен. Размер: %zu\n", da_size(arr));
else
printf("Ошибка добавления элемента\n");
} else {
printf("Ошибка разбора значения.\n");
}
if (type->destroy)
type->destroy(elem);
free(elem);
break;
case 3: // Вставить
index = read_int("Индекс: ");
elem = malloc(type->element_size);
memset(elem, 0, type->element_size);
if (input_element(type, elem)) {
if (da_insert(arr, index, elem))
printf("Элемент вставлен.\n");
else
printf("Ошибка вставки.\n");
} else {
printf("Ошибка разбора значения.\n");
}
if (type->destroy)
type->destroy(elem);
free(elem);
break;
case 4: // Удалить
index = read_int("Индекс: ");
if (da_remove(arr, index))
printf("Элемент удалён. Размер: %zu\n", da_size(arr));
else
printf("Ошибка удаления.\n");
break;
case 5: // Изменить
index = read_int("Индекс: ");
elem = malloc(type->element_size);
memset(elem, 0, type->element_size);
if (input_element(type, elem)) {
if (da_set(arr, index, elem))
printf("Элемент изменён.\n");
else
printf("Ошибка изменения.\n");
} else {
printf("Ошибка разбора значения.\n");
}
if (type->destroy)
type->destroy(elem);
free(elem);
break;
case 6: // Сортировка
da_sort(arr);
printf("Массив отсортирован.\n");
da_print(arr);
break;
case 7: // map
map_f = choose_map_func(type);
if (!map_f) break;
DynamicArray *mapped = da_map(arr, map_f);
if (!mapped) break;
printf("Результат map:\n");
da_print(mapped);
da_destroy(mapped);
break;
case 8: // where
pred = choose_predicate(type);
if (!pred) break;
DynamicArray *filtered = da_where(arr, pred);
if (!filtered) break;
printf("Результат where:\n");
da_print(filtered);
da_destroy(filtered);
break;
case 9: // reduce
reduce_f = choose_reduce(type);
if (!reduce_f) break;
if (type == get_double_type()) {
double init = 0.0, result = 0.0;
da_reduce(arr, reduce_f, &init, &result);
printf("Результат reduce: ");
type->print(&result);
printf("\n");
} else if (type == get_complex_type()) {
Complex init = {
.re = 0.0,
.im = 0.0
};
Complex result = {
.re = 0.0,
.im = 0.0
};
da_reduce(arr, reduce_f, &init, &result);
printf("Результат reduce: ");
type->print(&result);
printf("\n");
}
break;
case 10: // Конкатенация
printf("Создание второго массива с типом: %s\n", type->type_name);
DynamicArray *b = da_create(type, 0);
int n = read_int("Сколько элементов добавить во второй массив? ");
for (int i = 0; i < n; i++) {
elem = malloc(type->element_size);
memset(elem, 0, type->element_size);
printf("Элемент %d: ", i + 1);
if (input_element(type, elem))
da_push_back(b, elem);
if (type->destroy)
type->destroy(elem);
free(elem);
}
printf("Второй массив:\n");
da_print(b);
DynamicArray *c = da_concat(arr, b);
if (c) {
printf("Результат конкатенации:\n");
da_print(c);
da_destroy(c);
}
da_destroy(b);
break;
case 11: // Автозаполнение
auto_fill(arr);
da_print(arr);
break;
default:
printf("Неизвестная команда.\n");
}
}
da_destroy(arr);
printf("До свидания!\n");
return 0;
}

438
tests.c Normal file
View File

@@ -0,0 +1,438 @@
#include "dynamic_array.h"
#include "types.h"
#include <stdio.h>
#include <stdlib.h>
// Подсчёт тестов
static int tests_passed = 0;
static int tests_failed = 0;
#define TEST(name) static bool name(void)
#define RUN(name) do { \
printf(" %-50s", #name); \
if (name()) { \
tests_passed++; \
printf(" [OK]\n"); \
} \
} while (0)
#define ASSERT(cond) do { \
if (!(cond)) { \
printf(" [FAIL] %s:%d: %s\n", __FILE__, __LINE__, #cond); \
tests_failed++; \
return false; \
} \
} while (0)
// --------------- ТЕСТЫ ---------------
// Создание и базовые операции
TEST(test_create_destroy) {
DynamicArray *arr = da_create(get_double_type(), 0);
ASSERT(arr != NULL);
ASSERT(da_size(arr) == 0);
da_destroy(arr);
return true;
}
TEST(test_push_back_double) {
DynamicArray *arr = da_create(get_double_type(), 0);
const double vals[] = {1.1, 2.2, 3.3};
for (size_t i = 0; i < 3; i++) {
da_push_back(arr, &vals[i]);
}
ASSERT(da_size(arr) == 3);
ASSERT(*(double *)da_get(arr, 0) == 1.1);
ASSERT(*(double *)da_get(arr, 1) == 2.2);
ASSERT(*(double *)da_get(arr, 2) == 3.3);
da_destroy(arr);
return true;
}
TEST(test_push_back_complex) {
DynamicArray *arr = da_create(get_complex_type(), 0);
const Complex vals[] = {
{1.1, 1}, {2.2, 2}, {3.3, 3}
};
for (size_t i = 0; i < 3; i++) {
da_push_back(arr, &vals[i]);
}
Complex *c_get;
ASSERT(da_size(arr) == 3);
c_get = (Complex *) da_get(arr, 0);
ASSERT(c_get->re == 1.1 && c_get->im == 1);
c_get = (Complex *) da_get(arr, 1);
ASSERT(c_get->re == 2.2 && c_get->im == 2);
c_get = (Complex *) da_get(arr, 2);
ASSERT(c_get->re == 3.3 && c_get->im == 3);
da_destroy(arr);
return true;
}
TEST(test_set_get) {
DynamicArray *arr = da_create(get_double_type(), 0);
const double v1 = 42, v2 = 99;
da_push_back(arr, &v1);
ASSERT(*(double *)da_get(arr, 0) == 42);
da_set(arr, 0, &v2);
ASSERT(*(double *)da_get(arr, 0) == 99);
da_destroy(arr);
return true;
}
TEST(test_insert) {
DynamicArray *arr = da_create(get_double_type(), 0);
const double a = 1, b = 2, c = 3, ins = 99;
da_push_back(arr, &a);
da_push_back(arr, &b);
da_push_back(arr, &c);
da_insert(arr, 1, &ins);
ASSERT(da_size(arr) == 4);
ASSERT(*(double *)da_get(arr, 0) == 1);
ASSERT(*(double *)da_get(arr, 1) == 99);
ASSERT(*(double *)da_get(arr, 2) == 2);
ASSERT(*(double *)da_get(arr, 3) == 3);
da_destroy(arr);
return true;
}
TEST(test_remove) {
DynamicArray *arr = da_create(get_double_type(), 0);
const double vals[] = {10, 20, 30};
for (int i = 0; i < 3; i++) da_push_back(arr, &vals[i]);
da_remove(arr, 1);
ASSERT(da_size(arr) == 2);
ASSERT(*(double *)da_get(arr, 0) == 10);
ASSERT(*(double *)da_get(arr, 1) == 30);
da_destroy(arr);
return true;
}
// Граничные случаи
TEST(test_get_out_of_bounds) {
DynamicArray *arr = da_create(get_double_type(), 0);
ASSERT(da_get(arr, 0) == NULL);
const double v = 1;
da_push_back(arr, &v);
ASSERT(da_get(arr, 1) == NULL);
ASSERT(da_get(arr, 100) == NULL);
da_destroy(arr);
return true;
}
TEST(test_remove_out_of_bounds) {
DynamicArray *arr = da_create(get_double_type(), 0);
ASSERT(da_remove(arr, 0) == false);
da_destroy(arr);
return true;
}
TEST(test_empty_array_operations) {
DynamicArray *arr = da_create(get_double_type(), 0);
da_sort(arr); // Не должен ломаться
ASSERT(da_size(arr) == 0);
da_destroy(arr);
return true;
}
TEST(test_create_null_type) {
DynamicArray *arr = da_create(NULL, 0);
ASSERT(arr == NULL);
return true;
}
TEST(test_capacity_growth) {
DynamicArray *arr = da_create(get_int_type(), 2);
for (int i = 0; i < 100; i++)
ASSERT(da_push_back(arr, &i));
ASSERT(da_size(arr) >= 100);
for (int i = 0; i < 100; i++)
ASSERT(*(int *)da_get(arr, i) == i);
da_destroy(arr);
return true;
}
// Сортировка
TEST(test_sort_double) {
DynamicArray *arr = da_create(get_double_type(), 0);
const double vals[] = {3.14, 1.0, 2.71, 0.5};
for (int i = 0; i < 4; i++) da_push_back(arr, &vals[i]);
da_sort(arr);
for (size_t i = 1; i < da_size(arr); i++)
ASSERT(*(double *)da_get(arr, i - 1) <= *(double *)da_get(arr, i));
da_destroy(arr);
return true;
}
TEST(test_sort_complex) {
DynamicArray *arr = da_create(get_complex_type(), 0);
const Complex vals[] = {
{3, 4}, {1, 0}, {0, 2}
}; // |5|, |1|, |2|
for (int i = 0; i < 3; i++) da_push_back(arr, &vals[i]);
da_sort(arr);
// Порядок по модулю: (1,0), (0,2), (3,4)
const Complex *c0 = da_get(arr, 0);
const Complex *c1 = da_get(arr, 1);
const Complex *c2 = da_get(arr, 2);
ASSERT(c0->re == 1.0 && c0->im == 0.0);
ASSERT(c1->re == 0.0 && c1->im == 2.0);
ASSERT(c2->re == 3.0 && c2->im == 4.0);
da_destroy(arr);
return true;
}
// map
static void double_value(void *dest, const void *src) {
*(double *)dest = *(const double *)src * 2;
}
TEST(test_map_double) {
DynamicArray *arr = da_create(get_double_type(), 0);
const double vals[] = {1.0, 2.0, 3.0};
for (int i = 0; i < 3; i++) da_push_back(arr, &vals[i]);
DynamicArray *mapped = da_map(arr, double_value);
ASSERT(mapped != NULL);
ASSERT(da_size(mapped) == 3);
ASSERT(*(double *)da_get(mapped, 0) == 2.0);
ASSERT(*(double *)da_get(mapped, 1) == 4.0);
ASSERT(*(double *)da_get(mapped, 2) == 6.0);
da_destroy(arr);
da_destroy(mapped);
return true;
}
TEST(test_map_empty) {
DynamicArray *arr = da_create(get_double_type(), 0);
DynamicArray *mapped = da_map(arr, double_value);
ASSERT(mapped != NULL);
ASSERT(da_size(mapped) == 0);
da_destroy(arr);
da_destroy(mapped);
return true;
}
// where
static bool is_positive(const void *elem) {
return *(const double *)elem > 0;
}
static bool is_real(const void *elem) {
const Complex *c_elem = (Complex *)elem;
return c_elem->im == 0;
}
TEST(test_where_double) {
DynamicArray *arr = da_create(get_double_type(), 0);
const double vals[] = {-1.0, 2.5, -3.0, 4.0};
for (int i = 0; i < 4; i++)
da_push_back(arr, &vals[i]);
DynamicArray *filtered = da_where(arr, is_positive);
ASSERT(da_size(filtered) == 2);
ASSERT(*(double *)da_get(filtered, 0) == 2.5);
ASSERT(*(double *)da_get(filtered, 1) == 4.0);
da_destroy(arr);
da_destroy(filtered);
return true;
}
TEST(test_where_complex) {
DynamicArray *arr = da_create(get_complex_type(), 0);
const Complex vals[] = {
{1.0, 2.0}, {4.0, 0.0}, {-1.0, 1.0}
};
for (int i = 0; i < 3; i++)
da_push_back(arr, &vals[i]);
DynamicArray *filtered = da_where(arr, is_real);
ASSERT(da_size(filtered) == 1);
const Complex *num = da_get(filtered, 0);
ASSERT(num->re == 4.0 && num->im == 0.0);
da_destroy(arr);
da_destroy(filtered);
return true;
}
TEST(test_where_none_match) {
DynamicArray *arr = da_create(get_double_type(), 0);
const double vals[] = {-1.0, -3.0, -5.0};
for (int i = 0; i < 3; i++) da_push_back(arr, &vals[i]);
DynamicArray *filtered = da_where(arr, is_positive);
ASSERT(da_size(filtered) == 0);
da_destroy(arr);
da_destroy(filtered);
return true;
}
// reduce
static void sum_reduce_double(void *acc, const void *elem) {
*(double *)acc += *(const double *)elem;
}
static void sum_reduce_complex(void *acc, const void *elem) {
Complex *c_acc = (Complex *)acc;
const Complex *c_elem = (const Complex *)elem;
c_acc->re += c_elem->re;
c_acc->im += c_elem->im;
}
TEST(test_reduce_sum_double) {
DynamicArray *arr = da_create(get_double_type(), 0);
const double vals[] = {1.0, 2.0, 3.0, 4.0, 5.0};
for (int i = 0; i < 5; i++) da_push_back(arr, &vals[i]);
double initial = 0.0, result = 0.0;
da_reduce(arr, sum_reduce_double, &initial, &result);
ASSERT(result == 15.0);
da_destroy(arr);
return true;
}
TEST(test_reduce_sum_complex) {
DynamicArray *arr = da_create(get_complex_type(), 0);
const Complex vals[] = {
{1.0, 0.0}, {2.0, 1.0}, {3.0, 2.0}
};
for (int i = 0; i < 3; i++) da_push_back(arr, &vals[i]);
Complex initial = {0.0, 0.0}, result = {0.0, 0.0};
da_reduce(arr, sum_reduce_complex, &initial, &result);
ASSERT(result.re == 6.0 && result.im == 3.0);
da_destroy(arr);
return true;
}
TEST(test_reduce_empty) {
DynamicArray *arr = da_create(get_double_type(), 0);
double initial = 42.0, result = 0.0;
da_reduce(arr, sum_reduce_double, &initial, &result);
ASSERT(result == 42);
da_destroy(arr);
return true;
}
// Конкатенация
TEST(test_concat_double) {
DynamicArray *a = da_create(get_double_type(), 0);
DynamicArray *b = da_create(get_double_type(), 0);
const double v1 = 1.0, v2 = 2.0, v3 = 3.0, v4 = 4.0;
da_push_back(a, &v1);
da_push_back(a, &v2);
da_push_back(b, &v3);
da_push_back(b, &v4);
DynamicArray *c = da_concat(a, b);
ASSERT(c != NULL);
ASSERT(da_size(c) == 4);
ASSERT(*(double *)da_get(c, 0) == 1.0);
ASSERT(*(double *)da_get(c, 1) == 2.0);
ASSERT(*(double *)da_get(c, 2) == 3.0);
ASSERT(*(double *)da_get(c, 3) == 4.0);
da_destroy(a);
da_destroy(b);
da_destroy(c);
return true;
}
TEST(test_concat_type_mismatch) {
DynamicArray *a = da_create(get_double_type(), 0);
DynamicArray *b = da_create(get_complex_type(), 0);
DynamicArray *c = da_concat(a, b);
ASSERT(c == NULL); // Несовпадение типов
da_destroy(a);
da_destroy(b);
return true;
}
TEST(test_concat_with_empty) {
DynamicArray *a = da_create(get_double_type(), 0);
DynamicArray *b = da_create(get_double_type(), 0);
const double v = 42.0;
da_push_back(a, &v);
DynamicArray *c = da_concat(a, b);
ASSERT(da_size(c) == 1);
ASSERT(*(double *)da_get(c, 0) == 42.0);
da_destroy(a);
da_destroy(b);
da_destroy(c);
return true;
}
int main() {
printf(" === Модульные тесты ===\n");
printf("--- Создание и базовые операции ---\n");
RUN(test_create_destroy);
RUN(test_push_back_double);
RUN(test_push_back_complex);
RUN(test_set_get);
RUN(test_insert);
RUN(test_remove);
printf("--- Граничные случаи ---\n");
RUN(test_get_out_of_bounds);
RUN(test_remove_out_of_bounds);
RUN(test_empty_array_operations);
RUN(test_create_null_type);
RUN(test_capacity_growth);
printf("--- Сортировка ---\n");
RUN(test_sort_double);
RUN(test_sort_complex);
printf("--- map ---\n");
RUN(test_map_double);
RUN(test_map_empty);
printf("--- where ---\n");
RUN(test_where_double);
RUN(test_where_complex);
RUN(test_where_none_match);
printf("--- reduce ---\n");
RUN(test_reduce_sum_double);
RUN(test_reduce_sum_complex);
RUN(test_reduce_empty);
printf("--- Конкатенация ---\n");
RUN(test_concat_double);
RUN(test_concat_type_mismatch);
RUN(test_concat_with_empty);
printf("\n======================\n");
printf("Итого: %d пройдено, %d провалено\n", tests_passed, tests_failed);
return tests_failed > 0 ? 1 : 0;
}

22
type_info.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef TYPE_INFO_H
#define TYPE_INFO_H
#include <stdbool.h>
#include <stddef.h>
typedef struct TypeInfo {
size_t element_size;
void (*copy)(void *dest, const void *src);
void (*destroy)(void *elem);
int (*compare)(const void *a, const void *b); // <0, 0, >0
void (*print)(const void *elem);
bool (*parse)(void *dest, const char *str); // для ввода
const char *type_name;
} TypeInfo;
// static inline используется для создания копии функции в каждом файле
static inline bool types_equal(const TypeInfo *a, const TypeInfo *b) {
return a == b;
}
#endif // TYPE_INFO_H

137
types.c Normal file
View File

@@ -0,0 +1,137 @@
#include "types.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// ----------------- INT -----------------
static void int_copy(void *dest, const void *src) {
*(int *) dest = *(const int *) src;
}
static int int_compare(const void *a, const void *b) {
const int va = *(const int *) a;
const int vb = *(const int *) b;
return (va > vb) - (va < vb);
}
static void int_print(const void *elem) {
printf("%d", *(const int *) elem);
}
static bool int_parse(void *dest, const char *str) {
char *end;
const long val = strtol(str, &end, 10);
if (end == str || *end != '\0') return false;
*(int *) dest = (int) val;
return true;
}
static TypeInfo INT_TYPE_INFO = {
.element_size = sizeof(int),
.copy = int_copy,
.destroy = NULL,
.compare = int_compare,
.print = int_print,
.parse = int_parse,
.type_name = "int"
};
const TypeInfo *get_int_type(void) {
return &INT_TYPE_INFO;
}
// ----------------- DOUBLE -----------------
static void double_copy(void *dest, const void *src) {
*(double *) dest = *(const double *) src;
}
static int double_compare(const void *a, const void *b) {
const double va = *(const double *) a;
const double vb = *(const double *) b;
const double error = 1e-12;
if (fabs(va - vb) < error) return 0;
return (va > vb) ? 1 : -1;
}
static void double_print(const void *elem) {
printf("%.4f", *(const double *) elem);
}
static bool double_parse(void *dest, const char *str) {
char *end;
const double val = strtod(str, &end);
if (end == str || *end != '\0') return false;
*(double *) dest = val;
return true;
}
static TypeInfo DOUBLE_TYPE_INFO = {
.element_size = sizeof(double),
.copy = double_copy,
.destroy = NULL,
.compare = double_compare,
.print = double_print,
.parse = double_parse,
.type_name = "double"
};
const TypeInfo *get_double_type(void) {
return &DOUBLE_TYPE_INFO;
}
// ----------------- COMPLEX -----------------
static void complex_copy(void *dest, const void *src) {
*(Complex *) dest = *(const Complex *) src;
}
static int complex_compare(const void *a, const void *b) {
const Complex *ca = (const Complex *) a;
const Complex *cb = (const Complex *) b;
const double error = 1e-12;
const double ma = ca->re * ca->re + ca->im * ca->im;
const double mb = cb->re * cb->re + cb->im * cb->im;
if (fabs(ma - mb) < error) return 0;
return (ma > mb) ? 1 : -1;
}
static void complex_print(const void *elem) {
const Complex *c = (Complex*)elem;
if (c->im > 0)
printf("%.4f+%.4fi", c->re, c->im);
else if (c->im == 0)
printf("%.4f", c->re);
else
printf("%.4f%.4fi", c->re, c->im);
}
static bool complex_parse(void *dest, const char *str) {
Complex *c = (Complex *)dest;
// Формат: "re im"
if (sscanf(str, "%lf %lf", &c->re, &c->im) == 2)
return true;
// Попробуем только re
c->im = 0;
if (sscanf(str, "%lf", &c->re) == 1)
return true;
return false;
}
static TypeInfo COMPLEX_TYPE_INFO = {
.element_size = sizeof(Complex),
.copy = complex_copy,
.destroy = NULL,
.compare = complex_compare,
.print = complex_print,
.parse = complex_parse,
.type_name = "complex"
};
const TypeInfo *get_complex_type(void) {
return &COMPLEX_TYPE_INFO;
}

17
types.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef TYPES_H
#define TYPES_H
#include "type_info.h"
const TypeInfo* get_int_type(void);
const TypeInfo* get_double_type(void);
typedef struct {
double re;
double im;
} Complex;
const TypeInfo* get_complex_type(void);
#endif //TYPES_H