commit 9f9f22f22cf3adafe05bd2f20fbafa9ba36cf991 Author: StarFinger <52500158+AndrewPROn1@users.noreply.github.com> Date: Sun Feb 22 16:50:46 2026 +0300 Add files via upload diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c232803 --- /dev/null +++ b/Makefile @@ -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 + diff --git a/dynamic_array.c b/dynamic_array.c new file mode 100644 index 0000000..8910166 --- /dev/null +++ b/dynamic_array.c @@ -0,0 +1,236 @@ +#include "dynamic_array.h" +#include +#include +#include + +#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"); +} \ No newline at end of file diff --git a/dynamic_array.h b/dynamic_array.h new file mode 100644 index 0000000..c274b25 --- /dev/null +++ b/dynamic_array.h @@ -0,0 +1,51 @@ +#ifndef ARRAY_H +#define ARRAY_H + +#include +#include +#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 diff --git a/main.c b/main.c new file mode 100644 index 0000000..21cef87 --- /dev/null +++ b/main.c @@ -0,0 +1,427 @@ +#include +#include +#include +#include +#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; +} diff --git a/tests.c b/tests.c new file mode 100644 index 0000000..bc3495d --- /dev/null +++ b/tests.c @@ -0,0 +1,438 @@ +#include "dynamic_array.h" +#include "types.h" +#include +#include + +// Подсчёт тестов + +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; +} diff --git a/type_info.h b/type_info.h new file mode 100644 index 0000000..e65c378 --- /dev/null +++ b/type_info.h @@ -0,0 +1,22 @@ +#ifndef TYPE_INFO_H +#define TYPE_INFO_H + +#include +#include + +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 diff --git a/types.c b/types.c new file mode 100644 index 0000000..864ea10 --- /dev/null +++ b/types.c @@ -0,0 +1,137 @@ +#include "types.h" +#include +#include +#include + +// ----------------- 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; +} diff --git a/types.h b/types.h new file mode 100644 index 0000000..fb5c1eb --- /dev/null +++ b/types.h @@ -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