From aa0d37540f5bc4bb6327b43c7213b46d0e261cf0 Mon Sep 17 00:00:00 2001 From: justanothercatgirl Date: Fri, 4 Oct 2024 17:37:14 +0300 Subject: Пока не закончил логику выбора программы, но готовы все алгоритмы MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + Makefile | 37 +++++------ README.md | 44 ++++++++----- compile_flags.txt | 2 + hof_example.c | 33 ++++++++++ include/integral.h | 12 ++++ include/roots.h | 15 +++++ main_1.c | 89 -------------------------- main_23.c | 182 ----------------------------------------------------- src/integral.c | 38 +++++++++++ src/main.c | 99 +++++++++++++++++++++++++++++ src/roots.c | 94 +++++++++++++++++++++++++++ test_f.c | 5 -- 13 files changed, 339 insertions(+), 314 deletions(-) create mode 100644 .gitignore create mode 100644 compile_flags.txt create mode 100644 hof_example.c create mode 100644 include/integral.h create mode 100644 include/roots.h delete mode 100644 main_1.c delete mode 100644 main_23.c create mode 100644 src/integral.c create mode 100644 src/main.c create mode 100644 src/roots.c delete mode 100644 test_f.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae535a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +compile_flags.txt +build/ +*.o diff --git a/Makefile b/Makefile index 508c7de..b3e5061 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,24 @@ -all: t1_a t1_b t23 test.so +CFLAGS = -O2 -Wall -Wextra -Werror -Iinclude -T2FLAGS := -Wall +all: task4 -ifneq ($(OS),Windows_NT) - T2FLAGS += -DDYNAMIC_LOAD - T2LINKS = -lm -ldl - ifneq (, $(shell which gnuplot)) - T2FLAGS += -DPIPE_GNUPLOT - ifeq (, ${DISPLAY}) - T2FLAGS += -DTERMINAL_PLOT - endif - endif -endif +build: + mkdir -p build -t1_a: main_1.c - $(CC) -DPROG_SINUS_SUM $^ -o $@ +build/roots.o: src/roots.c build + $(CC) $(CFLAGS) -c -o $@ $< -t1_b: main_1.c - $(CC) -DPROG_MONTE_CARLO $^ -o $@ +build/integral.o: src/integral.c build + $(CC) $(CFLAGS) -c -o $@ $< -t23: main_23.c - $(CC) $(T2FLAGS) $^ -o $@ $(T2LINKS) +task4: build/roots.o build/integral.o src/main.c + $(CC) $(CFLAGS) -o $@ $^ -lm -ldl -test.so: test_f.c - $(CC) -fPIC -fPIE -shared $^ -o $@ +.PHONY: run clean -.PHONY: clean +run: task4 + ./task4 clean: - rm -rf t1_a t1_b t23 test.so - + rm -f build/*.o task4 diff --git a/README.md b/README.md index b39a043..b3b4bae 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,29 @@ -# сборка -1. `make` -2. готово. -# запуск -## задание 1: -`./t1_a` и `./t1_b`. Это старые задания с рядом тейлора синуса и с методом монте-карло. -Аргументы командной строки не требуются -## задание 2: -`./t23 -h` выведет помощь. необходимые флаги: `-o`, `-f`. -Если на компе есть программа gnuplot (в переменной PATH), программа построит в ней график. -Если gnuplot нет на компе, то в Makegfile найдите флаг `-DPIPE_GNUPLOT` и удалите его. -## задание 3: -`./t23 -h` выведет помощь. флаг `-d` (differentiate) активирует 3 задание. необходимые -для 3 задания флаги: `-o`, `-f`, `-x`. -про gnuplot то же самое, что и для 2 задания +# ВАЖНО +## Запуск +Файл `main.c` РАБОТАЕТ ТОЛЬКО НА ЛИНУКСЕ. Все алгоритмы работают на любой платформе, но в функции main есть вещи (например, ввод функции на языке С), не осуществимые на винде. +Поэтому, если вы хотите ЗАПУСТИТЬ программу целиком - либо берите линукс, либо пишите свою функцию main, которая будет использовать алгоритмы из файлов `roots.c` или `integrals.c` +## А что такое `func_t f` в аргументах? +Я передаю интегрируемую функцию как аргумент в свою функцию. Да, вы можете передавать функции в функции. Это называется функции высшего порядка, пример использования есть в `hof_example.c`. +файл `hof_example.c` запустится на любой ОС без изменений +# Задание 4 +## Описание программы +* Вы вводите с клавиатуры функцию от переменной x, используя синтаксис Си +* Потом вы выбираете, какое задание выполнить: решение уравнения (приравнять функцию к нулю) или выполнить численное интегрирование. +* После этого вы выбираете способ проведения операции и вводите нужные для способа числа (интервал, начальное число и т.п.) +* Ответ выводится на экран. +## Файлы +Директория `include/`: Заголовочные файлы +Директория `src/`: Файлы имплементации (с кодом) +`Makefile`: система сборки +`src/main.c`: логика по вводу функции и по взаимодействию с пользователем +`src/roots.c`: Все алгоритмы поиска корней +`src/integral.c`: Все алгоритмы вычисления интеграла +## Как пользоваться? +ВСЕ ИНСТРУКЦИИ ДАЮТСЯ ПРИ ЗАПУСКЕ ПРОГРАММЫ +Флагов командной строки нет +1. вводите функцию +2. вводите, что с ней делать (искать корни? интегрировать?) +3. вводите способ (трапеции, симпсон и т.п...) +4. вводите параметры (интервал или начальное приближение, допустимые погрешности...) +5. получаете результат + diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..1919e8f --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,2 @@ + -Iinclude + diff --git a/hof_example.c b/hof_example.c new file mode 100644 index 0000000..d8fee90 --- /dev/null +++ b/hof_example.c @@ -0,0 +1,33 @@ +// Это пример использования функций высшего порядка +#include +#include + +// Синтаксис непонятный, но так я создаю "синоним" +// к указателю на функцию, принимающую 2 double и возвращающую double +typedef double(*func_t)(double, double); + + +// Эта функция "передаёт" аргументы в функцию и выводит результат +void calculate_and_print(func_t function, double arg1, double arg2) { + printf("F(%lf, %lf) = %lf\n", arg1, arg2, function(arg1, arg2)); +} + +// считает сумму x и y +double sum(double x, double y) { + return x + y; +} + +// считает произведение x и y +double prod(double x, double y) { + return x * y; +} + +int main(void) { + // передаёшь функцию в функцию по имени, + // как самый обычный аргумент + calculate_and_print(sum, 420, 69); + calculate_and_print(prod, 420, 69); + // fmax - функция стандартной библиотеки + // возвращает максимальное из двух чисел + calculate_and_print(fmax, 420, 69); +} diff --git a/include/integral.h b/include/integral.h new file mode 100644 index 0000000..e3583b8 --- /dev/null +++ b/include/integral.h @@ -0,0 +1,12 @@ +#ifndef JUSTANOTHERCATGIRL_TASK4_INTEGRAL +#define JUSTANOTHERCATGIRL_TASK4_INTEGRAL + +typedef double(*f_t)(double); + +extern double accrc; + +double int_rect(f_t f, double a, double b); +double int_trap(f_t f, double a, double b); +double int_simp(f_t f, double a, double b); + +#endif //JUSTANOTHERCATGIRL_TASK4_INTEGRAL diff --git a/include/roots.h b/include/roots.h new file mode 100644 index 0000000..5307991 --- /dev/null +++ b/include/roots.h @@ -0,0 +1,15 @@ +#ifndef JUSTANOTHERCATGIRL_TASK4_ROOTS +#define JUSTANOTHERCATGIRL_TASK4_ROOTS + +typedef double(*f_t)(double); + +extern char root_ok; + +char is_root_ok(void); +double sol_binsr(f_t f, double a, double b, double ex, double ey); +double sol_chord(f_t f, double a, double b, double ex, double ey); +double sol_newtn(f_t f, double x0, double ex, double ey); +double sol_itern(f_t f, double x0, double ex, double ey); + + +#endif // JUSTANOTHERCATGIRL_TASK4_ROOTS diff --git a/main_1.c b/main_1.c deleted file mode 100644 index cd84b67..0000000 --- a/main_1.c +++ /dev/null @@ -1,89 +0,0 @@ -#include - -#if defined(PROG_SINUS_SUM) - -#define EPSILON 0.001*0.001 -#define MAX_ITERATIONS 100 - -int main(void) { - puts("defined macro PROG_SINUS_SUM"); - double x; - double result; - double last_elem; - int i; - - printf("Input a number\n"); - scanf("%lf", &x); - last_elem = result = x; - - for (i = 1; i < MAX_ITERATIONS; ++i) { - last_elem = last_elem * x * x / (2*i * (2*i+1)); - if (last_elem*last_elem <= EPSILON) break; - result += last_elem * ( (i%2 * -2) + 1 ); - } - printf("result = %lf; iterations = %i; last_elem = %lf\n", result, i, last_elem); - return 0; -} - -#elif defined(PROG_MONTE_CARLO) - -#include -#include - -#define _USE_MATH_DEFINES -#include - -struct point { - float x, y; -}; - -struct circle { - // center not needed for this task - /*struct point center;*/ - float rad; -}; - -char in_circle(const struct circle *c, const struct point p) { - /*p.x -= c->x;*/ - /*p.y -= c->y;*/ - return p.x * p.x + p.y * p.y < c->rad*c->rad; -} - -float calculate_pi(int iterations) { - int in = 0, out = 0; - int *arr[2] = {&out, &in}; - const struct circle c = {.rad = 1.0f}; - for (int i = 0; i < iterations; ++i) { - struct point p = { - .x = rand() % 2000000u / 1e6f - 1, - .y = rand() % 2000000u / 1e6f - 1 - }; - ++*arr[in_circle(&c, p)]; - } - return (float)in/iterations * 4; -} - -int main(int argc, char *argv[]) { - puts("defined macro PROG_MONTE_CARLO"); - srand(time(NULL)); - int iters; - if (argc == 2) { - iters = atoi(argv[1]); - } else { - fputs("Iterations: ", stdout); - if (!scanf("%i", &iters)) { - fputs("Input a valid number\n", stderr); - return EXIT_FAILURE; - } - } - float pi = calculate_pi(iters); - float err = fabs(M_PI-pi)/M_PI; - printf( "calculated pi: %.4f, actual pi: %.4f\n" - "error: %.2f%%\n", - pi, M_PI, err * 100); - return EXIT_SUCCESS; -} - -#else - #error "define macro PROG_SINUS_SUM or PROG_MONTE_CARLO in compilation flags" -#endif diff --git a/main_23.c b/main_23.c deleted file mode 100644 index 8460415..0000000 --- a/main_23.c +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include -#include -#include - -struct dval { double x, y; }; -typedef double (*func_t)(double); - -#ifdef PIPE_GNUPLOT - #include - #include - - void print_table(FILE*, const struct dval *, long int); - /// This function is creating a 'gnuplot' pipe - /// and writing data to it to be plotted - void pipe_gnuplot(struct dval *table, long len) { - FILE* gp = popen("gnuplot", "w"); - if (!gp) return; -#ifdef TERMINAL_PLOT - fputs("set terminal block\n", gp); -#endif // TERMINAL_PLOT - fputs("plot '-' with lines\n", gp); - print_table(gp, table, len); - fputs("e\n", gp); - fflush(gp); - - getchar(); - pclose(gp); - } -#endif // PIPE_GNUPLOT - -#ifdef DYNAMIC_LOAD - #include - void* handle = NULL; -#endif // DYNAMIC_LOAD - -double sin2(double x) { - double y = sin(x); - return y*y; -} -double cos2(double x) { - double y = cos(x); - return y*y; -} -double __sincos(double x) { - // sin(x)cos(x) = sin(2x)/2 - return sin(2*x)/2; -} -double sin2x(double x) { - return sin(2*x); -} - -/// generates the table as asked in the task. -/// The core function for task 2 -struct dval *get_table(func_t f, double start, double end, double step, long *length) { - long tlength = lround((end-start)/step); - struct dval *vals = calloc(tlength, sizeof(struct dval)); - for (long i = 0; i < tlength; ++i) { - double x = start + step * i; - // Not accumulative because if step is too small, - // start will never increment - vals[i] = (struct dval){.x = x, .y = f(x)}; - } - *length = tlength; - return vals; -} - -/// generates the table for derivative of function f. -/// The core function for task 3 -struct dval *differentiate(func_t f, double start, double end, double step, long *length) { - long tlength = lround((end-start)/step); - struct dval *vals = calloc(tlength, sizeof(struct dval)); - for (long i = 0; i < tlength; ++i) { - double x = start + step * i; - // central difference - double y = (f(x+step) - f(x-step))/ (2*step); - vals[i] = (struct dval){.x = x, .y = y}; - } - *length = tlength; - return vals; -} - -void print_table(FILE* stream, const struct dval *table, long size) { - for (long i = 0; i < size; ++i) { - fprintf(stream, "%lg %lg\n", table[i].x, table[i].y); - } -} - - -void print_help(int status) { - const char *help = - "This program is Licensed under 'the unlicense license'\n\n" - "Usage: ./t23 -f func_name -o output_file [-x number -h -d]\n" - "\t-h: this help\n" - "\t-d: differentiate function (do task 3)\n" - "\t-f: function: either 'sin2', 'cos2', 'sincos', 'tg' or 'sin2x' .\n" - "\t\tif compiled with -DDYNAMIC_LOAD, file name with dynamic function.\n" - "\t-o: output file.\n" - "\t-x: only needed with -d flag - differentiation step (the dx)\n\n" - "NOTE: the function in dynamic library must be named 'f'.\n" - "NOTE 2: with flag -DPIPE_GNUPLOT output will be piped in gnuplot\n"; - puts(help); - exit(status); -} - -/// Get the function based on the command line flags - either use -/// existing or load one from dynamic library -func_t get_function(const char* name) { - if (!strcmp(name, "cos2")) - return &cos2; - if (!strcmp(name, "sin2")) - return &sin2; - if (!strcmp(name, "sincos")) - return &__sincos; - if (!strcmp(name, "tg")) - return &tan; - if (!strcmp(name, "sin2x")) - return &sin2x; -#ifndef DYNAMIC_LOAD - fputs("ERROR: unknown function. To use dynamic loading, compile with `-DDYNAMIC_LOAD`.\n", stderr); - exit(EXIT_FAILURE); -#else // DYNAMIC_LOAD - handle = dlopen(name, RTLD_NOW); - if (!handle) goto fail; - func_t f = dlsym(handle, "f"); - if (!f) goto fail; - return f; -fail: - fputs(dlerror(), stderr); - exit(EXIT_FAILURE); -#endif // DYNAMIC_LOAD -} - -int main(int argc, char *argv[]) { - func_t function = NULL; - const char *out = NULL; - char task2 = 1; - float dx = 0.0; - // casual argument loop - for (int i = 1; i < argc; ++i) { - if (!strcmp(argv[i], "-h")) - print_help(EXIT_SUCCESS); - else if (!strcmp(argv[i], "-f")) - function = get_function(argv[++i]); - else if (!strcmp(argv[i], "-o")) - out = argv[++i]; - else if (!strcmp(argv[i], "-d")) - task2 = 0; - else if (!strcmp(argv[i], "-x")) - dx = atof(argv[++i]); - else print_help(EXIT_FAILURE); - } - // if function or output file is not set, or if dx is not set for task3, - // then exit with failure - if ((!function || !out) || (!task2 && dx == 0.0)) - print_help(EXIT_FAILURE); - - long table_len; - struct dval *table = NULL; - if (task2) - table = get_table(function, 0, 3, 0.01, &table_len); - else // task 3 - table = differentiate(function, -3, 3, dx, &table_len); - - // print data to stdout and to file - print_table(stdout, table, table_len); - FILE *f = fopen(out, "w+"); - if (f) print_table(f, table, table_len); - else perror(out); - -#ifdef PIPE_GNUPLOT - pipe_gnuplot(table, table_len); -#endif // PIPE_GNUPLOT - - fclose(f); - free(table); -#ifdef DYNAMIC_LOAD - if (handle) dlclose(handle); -#endif // DYNAMIC_LOAD - exit(EXIT_SUCCESS); -} - diff --git a/src/integral.c b/src/integral.c new file mode 100644 index 0000000..d2ae11f --- /dev/null +++ b/src/integral.c @@ -0,0 +1,38 @@ +#include "integral.h" + +// эта переменная позволяет контролировать точность интегрирования +// и может быть установлена в файле main +double accrc = 10000.0; + +// интеграл прямоугольниками +double int_rect(f_t f, double a, double b) { + // f(x + dx/2) * dx + const double dx = (b-a) / accrc; + const double dx_2 = dx / 2.0; + double sum = 0.0; + for (double i = a + dx; i < b; i += dx) + sum += f(i - dx_2) * dx; + return sum; +} + +// интеграл трапециями +double int_trap(f_t f, double a, double b) { + // ( f(x) + f(x+dx) )/2 * dx + const double dx = (b-a) / accrc; + double sum = 0.0; + for (double i = a; i < b; i += dx) + sum += dx * (f(i) + f(i+dx))/2 ; + return sum; +} + +// интегрирование параболами +double int_simp(f_t f, double a, double b) { + const double dx = (b-a) / accrc; + const double dx_2 = dx / 2; + const double dx_6 = dx / 6; + double sum = 0.0; + for(double i = a; i < b; i += dx) { + sum += dx_6 * (f(i) + 4 * f(i + dx_2) + f(i + dx)); + } + return sum; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..c3844f0 --- /dev/null +++ b/src/main.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "integral.h" +#include "roots.h" + +#define BLINK_TERM "\x1b[5m" +#define RESET_TERM "\x1b[0m" +#define FIFO_NAME "/tmp/compiler.fifo" +#define SYMNAME "f" +#define RDCHUNK 1024 + +void *sohandle; + +// This will be the type of the function +typedef double(*f_t)(double); + +enum optype { + // решение уравнений + SOL_BINSR, // дихтомии + SOL_HORDE, // хорды + SOL_NEWTN, // касательные + SOL_ITERN, // итерации + // численное интегрирование + INT_RECT, // прямоугольники + INT_TRAP, // трапеции + INT_SIMP, // симпсон +}; + +// Function that reads Your function, compiles and loads it. +f_t input_and_compile(void) { + puts("Напечатайте функцию от x используя синтаксис С и функции библиотеки cmath: \n" + "(например, 'atan(x) / (1-exp(2*x))').\n" + "Закончите ввод, нажав enter." BLINK_TERM); + + // input the function + size_t blen = 1024; + char *buffer = malloc(sizeof (char) * blen); + blen = read(STDIN_FILENO, buffer, 1024); + buffer[--blen] = '\0'; + if (blen <= 0) return NULL; + + // format the source + const char *fmt = + "#include \n" + "double " SYMNAME "(double x) { return %s; }\n"; + size_t sourcelen = strlen(fmt) - strlen("%s") + blen + 1; + char *source = malloc(sizeof (char) * sourcelen); + snprintf(source, sourcelen, fmt, buffer); + + // spawn the compiler process + FILE *cc = popen("cc -shared -fPIE -fPIC -o " FIFO_NAME " -xc - -lm", "w"); + fwrite(source, sizeof (char), sourcelen - 1, cc); + fflush(cc); + if (ferror(cc)) return NULL; + if (pclose(cc) != 0) return NULL; + + // load dynamic library + sohandle = dlopen(FIFO_NAME, RTLD_NOW); + if (!sohandle) return NULL; + f_t ret = dlsym(sohandle, SYMNAME); + + // free resources and return + free(buffer); + free(source); + return ret; +} + +// Prompts user to choose input method +enum optype get_optype(void) { + return INT_SIMP; +} + +int main(int argc, char *argv[]) { + (void)argc; (void)argv; + f_t f = input_and_compile(); + if (!f) goto exit; + double sln = sol_binsr(f, 0, 10, 0.001, 0.001); + printf("%lf\n", sln); + sln = sol_binsr(f, 0, 10, 0.001, 0.001); + printf("%lf\n", sln); + sln = sol_newtn(f, 5, 0.001, 0.001); + printf("%lf\n", sln); + sln = sol_itern(f, 0, 0.001, 0.001); + printf("%lf\n", sln); + +exit: + dlclose(sohandle); + return 0; +} diff --git a/src/roots.c b/src/roots.c new file mode 100644 index 0000000..0aef762 --- /dev/null +++ b/src/roots.c @@ -0,0 +1,94 @@ +#include "roots.h" +#include +#include + +char root_ok = 1; + +// функция для проверки того, успешно ли выполнилась последняя функция +char is_root_ok(void) { + return root_ok; +} + +// метод дихтомии +double sol_binsr(f_t f, double a, double b, double ex, double ey) { + if (copysign(1.0, f(a)) == copysign(1.0, f(b))) { + root_ok = 0; + return NAN; + } + + double mid; + do { + mid = (a + b) / 2; + if (copysign(1.0, f(a)) != copysign(1.0, f(mid))) b = mid; + else a = mid; + } while (f(mid) > ey || (b-a) > ex); + root_ok = 1; + return mid; +} + +// метод хорд +double sol_chord(f_t f, double a, double b, double ex, double ey) { + if (copysign(1.0, f(a)) == copysign(1.0, f(b))) { + root_ok = 0; + return NAN; + } + double mid = a + fabs( f(a) / (f(b)-f(a)) ) * (b-a); + double diff = mid; + while (f(mid) > ey || fabs(diff - mid) > ex) { + diff = mid; + mid = a + fabs( f(a) / (f(b)-f(a)) ) * (b-a); + if (copysign(1.0, f(a)) != copysign(1.0, f(mid))) b = mid; + else a = mid; + } + root_ok = 1; + return mid; + +} + +// метод касательных (Ньютона) +double sol_newtn(f_t f, double x0, double ex, double ey) { + const double dx = 1e-6; + double diff = x0+2*ex; + + // check for divergence + double d2x0 = (f(x0 + 2*dx) - 2*f(x0 + dx) + f(x0))/(dx*dx); + if (copysign(1.0, d2x0) != copysign(1.0, f(x0))) { + root_ok = 0; + return NAN; + } + while ((fabs(diff - x0) > ex || f(x0) > ey)) { + double fx0 = f(x0); + double dx0 = (f(x0+dx) - fx0) / dx; + diff = x0; + x0 -= fx0/dx0; + if (isinf(x0) || isnan(x0)) { + root_ok = 0; + return NAN; + } + } + root_ok = 1; + return x0; +} + +// метод итераций +double sol_itern(f_t f, double x0, double ex, double ey) { + const double dx = 1e-6; + double diff = x0 + 2*ex; + + do { + double dx0 = (f(x0+dx) - f(x0))/dx; + if (fabs(dx0) > 1.0) { + root_ok = 0; + return NAN; + } + diff = x0; + x0 = f(x0); + printf("x0=%lf, f(x0)=%lf\n", x0, f(x0)); + getchar(); + } while (fabs(diff - x0) > ex || fabs(f(x0) - x0) > ey); + root_ok = 1; + return x0; +} + + + diff --git a/test_f.c b/test_f.c deleted file mode 100644 index 8811f1e..0000000 --- a/test_f.c +++ /dev/null @@ -1,5 +0,0 @@ -double f(double x) { - static int counter = 0; - ++counter; - return counter * x; -} -- cgit v1.2.3-70-g09d2