diff options
author | justanothercatgirl <sotov@twistea.su> | 2024-12-06 15:08:31 +0300 |
---|---|---|
committer | justanothercatgirl <sotov@twistea.su> | 2024-12-06 15:08:31 +0300 |
commit | a470c304199866aa1f3d39ff22ec30734f03d617 (patch) | |
tree | b08858e91b39fc108f2ab9b83c03ef7f881711ba | |
parent | 24b8430fa7a9a81b88c5c172c99bbc9a520ff4ba (diff) |
сделал 8 задание))))task8
-rw-r--r-- | 7_2/Makefile | 16 | ||||
-rw-r--r-- | 7_2/data | 4 | ||||
-rw-r--r-- | 7_2/include/common.h | 32 | ||||
-rw-r--r-- | 7_2/include/drawable.h | 55 | ||||
-rw-r--r-- | 7_2/include/figure.h | 56 | ||||
-rw-r--r-- | 7_2/include/input.h | 24 | ||||
-rw-r--r-- | 7_2/main.c | 44 | ||||
-rw-r--r-- | 7_2/src/drawable.c | 113 | ||||
-rw-r--r-- | 7_2/src/figure.c | 139 | ||||
-rw-r--r-- | 7_2/src/input.c | 116 | ||||
-rw-r--r-- | Makefile | 37 | ||||
-rw-r--r-- | README.md | 50 | ||||
-rw-r--r-- | check.c | 36 | ||||
-rw-r--r-- | include/diffeq.h | 22 | ||||
-rw-r--r-- | include/equations.h | 10 | ||||
-rw-r--r-- | include/types.h | 29 | ||||
-rw-r--r-- | main.c | 89 | ||||
-rw-r--r-- | src/diffeq.c | 117 | ||||
-rw-r--r-- | src/equations.c | 8 |
19 files changed, 359 insertions, 638 deletions
diff --git a/7_2/Makefile b/7_2/Makefile deleted file mode 100644 index 7c29431..0000000 --- a/7_2/Makefile +++ /dev/null @@ -1,16 +0,0 @@ - -all: main - -build: - mkdir -p build - -build/%.o: src/%.c include/%.h build - $(CC) -c -g -ggdb -o $@ $< -Iinclude - -main: main.c build/drawable.o build/figure.o build/input.o - $(CC) -o $@ $^ -lm -lX11 -Iinclude - -.PHONY: clean - -clean: - $(RM) *.o main build/* diff --git a/7_2/data b/7_2/data deleted file mode 100644 index 9d831b7..0000000 --- a/7_2/data +++ /dev/null @@ -1,4 +0,0 @@ -l {0, 1} {1, 2}; -r {2, 2} {40, 18}; - -c {20, 20} 10; diff --git a/7_2/include/common.h b/7_2/include/common.h deleted file mode 100644 index 660532f..0000000 --- a/7_2/include/common.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef JAC_COMMON_H -#define JAC_COMMON_H - -/* Для чтающих: - * чтобы писать - * `vector2 point = { 10, 20 };` - * вместо - * `struct vector2 point = { 10, 20 };` - * (то есть чтобы писать struct перед vector2 было необязательно) - * надо объявлять структуру так: - * ``` - * typedef struct { - * long x, y; - * } vector2; - * ``` - * Таким образом мы объявляем структуру без официального названия, - * но присваиваем ей синоним. Чтобы пользоваться и тем, и тем вариантами, - * можно сделать так: - * ``` - * typedef struct vector2 { - * long x, y; - * } vector2; - * ``` - * Так мы и даём ей название, и даём ей синоним. - */ - -// координаты точки в 2-мерном пространстве -typedef struct vector2 { - long x, y; -} vector2; - -#endif // JAC_COMMON_H diff --git a/7_2/include/drawable.h b/7_2/include/drawable.h deleted file mode 100644 index 8986342..0000000 --- a/7_2/include/drawable.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef JCG_DRAWABLE_H -#define JCG_DRAWABLE_H - -#define FILL_CHR '#' -#define SPACE_CHR ' ' - -#include <X11/Xlib.h> -#include <X11/Xutil.h> -extern struct X11context { - enum {CTX_NO, CTX_INIT, CTX_FAIL} init; - Display *d; - Window r, w; - int s; - GC gctx; - XVisualInfo v; - XImage *i; - int* data; -} X11; - -// что угодно, на чём можно ''нарисовать'' -// должно иметь размер, размер пикселей, данные -// функция put записывает пиксель по координатам x, y на поле -// функция show выводит данные на экран/куда угодно ещё -struct drawable { - long x, y, pix_s; - void *data; - void (*put)(struct drawable *self, long x, long y); - void(*show)(const struct drawable *self); -}; - - -// конструктор -void drawable_init(struct drawable* self, - void(*put_f)(struct drawable *, long, long), - void(*show_f)(const struct drawable *), - long width, - long height, - long pix_s); -// деструктор -void drawable_destroy(struct drawable *self); -// конструктор для консольного поля -struct drawable drawable_plaintxt(long width, long height); -// конструктор для графического поля -struct drawable drawable_X11(long width, long height); - -// put для консольного drawable -void put_plaintxt(struct drawable *self, long x, long y); -// show для консольного drawable -void show_plaintxt(const struct drawable *self); -// put для графического drawable -void put_X11(struct drawable *self, long x, long y); -// show для графического drawable -void show_X11(const struct drawable *self); - -#endif // JCG_DRAWABLE_H diff --git a/7_2/include/figure.h b/7_2/include/figure.h deleted file mode 100644 index 393ca00..0000000 --- a/7_2/include/figure.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef JCG_FIGURE_H -#define JCG_FIGURE_H - -#include "drawable.h" -#include "common.h" - -// любая фигура -// создаётся функциями figure_point, figure_line и т.д. -// у каждой фигуры должен быть способ "нарисоваться" на -// любое "рисовальное" поле, используя функцию drawable.put -struct figure { - enum { - F_NONE, F_POINT, F_RECT, F_LINE, F_CIRCLE - } type; - union { - struct { - vector2 coords; - } point; - struct { - vector2 upleft, downright; - } rect; - struct { - vector2 start, end; - } line; - struct { - vector2 center; - long rad; - } circle; - }; - void (*draw)(const struct figure *self, struct drawable *d); -}; - -// массив функций по типам (lookup table) -extern void (*const draw_lookup[])(const struct figure *, struct drawable *); - -// конструктор 1 -struct figure figure_point(vector2 p); -// конструктор 2 -struct figure figure_rect(vector2 upleft, vector2 downright); -// конструктор 3 -struct figure figure_line(vector2 start, vector2 end); -// конструктор 4 -struct figure figure_circle(vector2 center, long radius); - -// draw для F_POINT -void draw_point(const struct figure *self, struct drawable *d); -// draw для F_RECT -void draw_rect(const struct figure *self, struct drawable *d); -// draw для F_LINE -void draw_line(const struct figure *self, struct drawable *d); -// draw для F_CIRCLE -void draw_circle(const struct figure *self, struct drawable *f); - -void print_figure(const struct figure *self); - -#endif // JCG_FIGURE_H diff --git a/7_2/include/input.h b/7_2/include/input.h deleted file mode 100644 index 24285b8..0000000 --- a/7_2/include/input.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef JAC_INPUT_H -#define JAC_INPUT_H - -#include "common.h" -#include "figure.h" - -extern void(*settings_handler)(const char *sett_string); - -// пропустить пробелы -void skipw(char const **s); -// проспустит цифры -void skipd(char const **s); -// проерить наличие символа -char chktok(char const **str, char tok); -// прочитать число -char parsel(char const **str, long *l); -// прочитать вектор из 2 чисел -char parsev(char const **str, vector2 *v); -// прочитать одну фигуру -struct figure read_figure(const char *str); -// прочитать целый файл -struct figure *read_file(const char *path); - -#endif // JAC_INPUT_H diff --git a/7_2/main.c b/7_2/main.c deleted file mode 100644 index edb4b76..0000000 --- a/7_2/main.c +++ /dev/null @@ -1,44 +0,0 @@ -#include <X11/Xlib.h> - -#include <assert.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "input.h" -#include "drawable.h" -#include "figure.h" - -long fatness = 1; // пока никак не используется - -int sx = 100, sy = 100; - -int main(int argc, char *argv[]) { - char file[256] = {}; - if (argc >= 2) { - size_t len = strlen(argv[1]); - memcpy(file, argv[1], len > 255 ? 255 : len); - } else { - printf("введите файл: "); - scanf("%255s", file); - } - char x11 = 0; - for (int i = 0; i < argc; ++i) { - if (!strcmp("-X", argv[i])) x11 = 1; - } - struct figure *fs = read_file(file); - struct drawable d = x11 ? drawable_X11(sx, sy) : drawable_plaintxt(sx, sy); - if (!fs) { - fprintf(stderr, "ERROR: errno %i: %s\n", errno, strerror(errno)); - return 1; - } - for (struct figure *i = fs; i->type != F_NONE; ++i) - i->draw(i, &d); - - d.show(&d); - free(fs); - drawable_destroy(&d); - return 0; -} - diff --git a/7_2/src/drawable.c b/7_2/src/drawable.c deleted file mode 100644 index ab4bf27..0000000 --- a/7_2/src/drawable.c +++ /dev/null @@ -1,113 +0,0 @@ -#include "drawable.h" - -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <assert.h> -#include <X11/Xlib.h> -#include <X11/Xutil.h> - -struct X11context X11 = { .init=CTX_NO }; - -struct drawable drawable_plaintxt(long width, long height) { - struct drawable ret; - // with, height +1 because inclusive - drawable_init(&ret, put_plaintxt, show_plaintxt, width + 1, height + 1, sizeof (char)); - memset(ret.data, 0, height*width*1); - return ret; -} -struct drawable drawable_X11(long width, long height) { - struct drawable ret; - drawable_init(&ret, put_X11, show_X11, width, height, sizeof (int)); - int* data = ret.data; - for (size_t i = 0; i < width*height; ++i) - data[i] = 0xFFFFFFFF; - return ret; -} - -void drawable_init(struct drawable* self, - void(*put_f)(struct drawable *, long, long), - void(*show_f)(const struct drawable *), - long width, - long height, - long pix_s) -{ - self->x = width; - self->y = height; - self->pix_s = pix_s; - self->data = malloc(height*width*pix_s); - self->put = put_f; - self->show = show_f; -} - -void drawable_destroy(struct drawable *self) { - free(self->data); -} - -/// plaintxt methods /// - -void put_plaintxt(struct drawable *self, long x, long y) { - if (x >= self->x || y >= self->y || x < 0 || y < 0) return; - char *data = self->data; - data[self->x * y + x] = 1; -} - -void show_plaintxt(const struct drawable *self) { - char map[2] = {SPACE_CHR, FILL_CHR}; - for (int i = 0; i < self->y; ++i) { - for (int j = 0; j < self->x; ++j) { - char *d = self->data; - putchar(map[d[i*self->x + j]]); - } - putchar('\n'); - } -} - - -/// X11 from now on /// - -void init_X11(const struct drawable *self) { - X11.d = XOpenDisplay(NULL); - if (X11.d == NULL) { - X11.init = CTX_FAIL; - printf("why tho???\n"); - return; - } - X11.s = DefaultScreen(X11.d); - X11.r = RootWindow(X11.d, X11.s); - X11.gctx = DefaultGC(X11.d, X11.s); - if (!XMatchVisualInfo(X11.d, X11.s, 24, TrueColor, &X11.v)) { - XDestroyWindow(X11.d, X11.w); - XCloseDisplay(X11.d); - X11.init = CTX_FAIL; - return; - }; - X11.w = XCreateSimpleWindow(X11.d, X11.r, 0, 0, self->x, self->y, 0, 0, 0x00FFFFFF); - XSelectInput(X11.d, X11.w, ExposureMask); - XMapWindow(X11.d, X11.w); - X11.data = self->data; - X11.i = XCreateImage(X11.d, X11.v.visual, X11.v.depth, ZPixmap, 0, (void*)X11.data, self->x, self->y, 32, 0); - X11.init = CTX_INIT; -} - -void put_X11(struct drawable *self, long x, long y) { - if (x >= self->x || y >= self->y || x < 0 || y < 0) return; - int *data = self->data; - data[self->x * y + x] = 0x00000000; -} - -void show_X11(const struct drawable *self) { -start: - switch (X11.init) { - case CTX_NO: init_X11(self); goto start; - case CTX_INIT: break; - case CTX_FAIL: - fprintf(stderr, "Error: Could not initialize Xlib\n"); - return; - } - XEvent e = {}; - while (!XNextEvent(X11.d, &e)) { - XPutImage(X11.d, X11.w, X11.gctx, X11.i, 0, 0, 0, 0, self->x, self->y); - } -} - diff --git a/7_2/src/figure.c b/7_2/src/figure.c deleted file mode 100644 index 290d4af..0000000 --- a/7_2/src/figure.c +++ /dev/null @@ -1,139 +0,0 @@ - -#include "figure.h" -#include <stdio.h> -#include <stdlib.h> -#include <math.h> - -void (*const draw_lookup[])(const struct figure *, struct drawable *) = { - [F_NONE] = NULL, - [F_POINT] = draw_point, - [F_RECT] = draw_rect, - [F_LINE] = draw_line, - [F_CIRCLE] = draw_circle -}; - -void swapl(long *x, long *y) { - long tmp = *x; - *x = *y; - *y = tmp; -} - -struct figure figure_point(vector2 p) { - return (struct figure) { - .type = F_POINT, - .point.coords = p, - .draw = draw_point - }; -} - -struct figure figure_rect(vector2 upleft, vector2 downright) { - return (struct figure) { - .type = F_RECT, - .rect = {upleft, downright}, - .draw = draw_rect - }; -} - -struct figure figure_line(vector2 start, vector2 end) { - return (struct figure) { - .type = F_LINE, - .line = {start, end}, - .draw = draw_line - }; -} - -struct figure figure_circle(vector2 center, long radius) { - return (struct figure) { - .type = F_CIRCLE, - .circle = {center, radius}, - .draw = draw_circle - }; -} - - -void draw_point(const struct figure *self, struct drawable *d) { - d->put(d, self->point.coords.x, self->point.coords.y); -} - -void draw_rect(const struct figure *self, struct drawable *d) { - vector2 upleft = self->rect.upleft; - vector2 downright = self->rect.downright; - for (long i = upleft.x; i < downright.x + 1; ++i) { - d->put(d, i, upleft.y); - d->put(d, i, downright.y); - } - for (long j = upleft.y + 1; j < downright.y; ++j) { - d->put(d, upleft.x, j); - d->put(d, downright.x, j); - } -} - -void draw_line(const struct figure *self, struct drawable *d) { - vector2 start = self->line.start; - vector2 end = self->line.end; - char gt45 = labs(end.y - start.y) > labs(end.x - start.x); - long x0 = start.x, y0 = start.y, - x1 = end.x, y1 = end.y; - if (gt45) { - swapl(&x0, &y0); - swapl(&x1, &y1); - } - if (x0 > x1) { - swapl(&x0, &x1); - swapl(&y0, &y1); - } - long dy = labs(y1-y0), - dx = x1 - x0, - err = dx >> 1, // *2 - yinc = (y1 > y0 ? 1 : -1); - - for (long x = x0, y = y0; x <= x1; ++x) { - if (gt45) d->put(d, y, x); - else d->put(d, x, y); - err -= dy; - if (err < 0) { - y += yinc; - err += dx; - } - } -} - -void draw_circle(const struct figure *self, struct drawable *f) { - const vector2 p = self->circle.center; - const long rad = self->circle.rad - 1; - const long pi4 = ceil(M_SQRT1_2 * rad); - long x0 = 0, y0 = rad; - do { - // выглядит коряво, зато в 8 раз меньше повторений цикла - f->put(f, p.x-x0, p.y-y0); // 3 четверть - f->put(f, p.x-x0, p.y+y0); // 2 чет. - f->put(f, p.x+x0, p.y-y0); // 4 ч. - f->put(f, p.x+x0, p.y+y0); // 1 ч - f->put(f, p.x-y0, p.y-x0); - f->put(f, p.x-y0, p.y+x0); - f->put(f, p.x+y0, p.y-x0); - f->put(f, p.x+y0, p.y+x0); - ++x0; - // вычисляем x^2 + (y+1/2)^2 - r^2 - // раскрыли скобки и умножили на 4, чтобы не иметь дело - // с дробными числами (они медленные) - long func = 4*x0*x0 + 4*y0*y0 - 4*y0 + 1 - 4*rad*rad; - y0 = (func >= 0 ? y0 - 1 : y0); - } while (x0 < pi4); -} - -void print_figure(const struct figure *self) { - printf("figure {\n\t.type = %i\n\t{%li, %li},\n\t", self->type, self->line.start.x, self->line.start.y); - switch (self->type) { - case F_LINE: - case F_RECT: - printf("{%li, %li}\n\t", self->line.end.x, self->line.end.y); - break; - case F_CIRCLE: - printf("%li\n\t", self->circle.rad); - // fallthrough - case F_NONE: - case F_POINT:; - } - printf(".draw = %p\n}\n", self->draw); -} diff --git a/7_2/src/input.c b/7_2/src/input.c deleted file mode 100644 index d2dca7c..0000000 --- a/7_2/src/input.c +++ /dev/null @@ -1,116 +0,0 @@ -#include "input.h" - -#include <ctype.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -const char *const unexpchtok = "Unexpected token: %c\n"; -const char *const invalidint = "Invalid integer: %s\n"; - -#define error(fmt, ...) do { \ - fprintf(stderr, fmt, __VA_ARGS__); \ - return 0; \ -} while(0) - -#define maybe_realloc(size, cap, pointer, type) do { \ - if (size >= cap) { \ - cap <<= 1; \ - pointer = realloc(pointer, cap * sizeof (type));\ - } \ -} while(0) - -void(*settings_handler)(const char*) = NULL; - -void skipw(char const **s) { while(isspace(**s)) ++*s; } -void skipd(char const **s) { while(isdigit(**s)) ++*s; } - -char chktok(char const **str, char tok) { - skipw(str); - long tmp = atol(*str); - if (**str != tok) error(unexpchtok, **str); - ++*str; - return 1; -} - -char parsel(char const **str, long *l) { - skipw(str); - long tmp = atoi(*str); - if (tmp == 0 && **str != '0') error(invalidint, *str); - *l = tmp; - skipd(str); - return 1; -} - -char parsev(char const **str, vector2 *v) { - long x, y; - // я ща кончу на этот код - char suc = chktok(str, '{') - && parsel(str, &x) - && chktok(str, ',') - && parsel(str, &y) - && chktok(str, '}'); - if (suc) { - v->x = x; - v->y = y; - return 1; - } else return 0; -} - -struct figure read_figure(const char *str) { - struct figure ret = {.type = F_NONE}; - const char *strp = str; - skipw(&strp); - ++strp; - switch(strp[-1]) { - case '.': { - if (settings_handler != NULL) settings_handler(strp); - } break; - case 'p': { - if (parsev(&strp, &ret.point.coords) - && chktok(&strp, ';')) ret.type = F_POINT; - } break; - case 'r': { - if (parsev(&strp, &ret.rect.upleft) - && parsev(&strp, &ret.rect.downright) - && chktok(&strp, ';')) ret.type = F_RECT; - } break; - case 'l': { - if (parsev(&strp, &ret.line.start) - && parsev(&strp, &ret.line.end) - && chktok(&strp, ';')) ret.type = F_LINE; - } break; - case 'c': { - if (parsev(&strp, &ret.circle.center) - && parsel(&strp, &ret.circle.rad) - && chktok(&strp, ';')) ret.type = F_CIRCLE; - } break; - default:; - } - ret.draw = draw_lookup[ret.type]; - return ret; -} - -struct figure *read_file(const char *path) { - FILE *f = fopen(path, "r"); - if (!f) return NULL; - size_t bs = 1024; - size_t fs = -1, fcap = 16; - char *buf = malloc(fcap * sizeof(struct figure)); - struct figure *figures = malloc(fcap * sizeof (struct figure)); - while (!feof(f)) { - if (getdelim(&buf, &bs, ';', f) < 0) break; - struct figure f = read_figure(buf); - if (f.type == F_NONE) continue; - ++fs; - maybe_realloc(fs, fcap, figures, struct figure); - memcpy(figures + fs, &f, sizeof (struct figure)); - } - ++fs; - maybe_realloc(fs, fcap, figures, struct figure); - struct figure END = {.type = F_NONE, 0}; - memcpy(figures + fs, &END, sizeof (struct figure)); - free(buf); - return figures; -} - diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..993582c --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ + +CFLAGS += -std=c89 -g -ggdb -Iinclude +TASK_ARGS += -t 0.1 + +.PHONY: clean all euler rk4 run tests + +all: + echo "either make euler or make rk4" + +euler: task8 + ./$< $(TASK_ARGS) -m euler + +rk4: task8 + ./$< $(TASK_ARGS) -m rk4 + +tests: task8 check + ./check f1.data f2.data f3.data f4.data out.data + echo "plot 'out.data' with lines" | gnuplot - -p + echo "plot 'f1.data','f2.data','f3.data','f4.data'" | gnuplot - -p + +run: task8 + ./$< $(TASK_ARGS) + +build: + mkdir -p build + +build/%.o: src/%.c include/%.h build + $(CC) -c -o $@ $(CFLAGS) $< -lm + +task8: main.c build/diffeq.o build/equations.o + $(CC) -o $@ $(CFLAGS) $^ -lm + +check: check.c + $(CC) -o check $(CFLAGS) check.c -lm + +clean: + $(RM) *.o task8 check build/* *.data @@ -1,42 +1,14 @@ -# Задания 7.1 и 7.2 +# Задание 8 + +Это финальное задание потрясающего курса информатики Андрея Александровича Ангелуца. + +Он придрался к тому, что у меня функции решения ДУ реализованы полноценно, а не как "сделать один шаг", но задание принял + +Сборка: +* `make euler`: решить 4 дифура эйлером +* `make rk4`: решить рунгекуттой +* `make tests`: построить графики уравнений и суммы уравнений +* `make task8 && ./task8 --help`: показать опции, которые принимает task8 -## 7.1: -пока не сделал -## 7.2: -### Запуск -кросс-платформенный, система сборки GNU make -### идея -### формат входного файлыа: -Каждая строка соответствует шаблону: -``` -ФИГУРА <параметры...>; -``` -Параметры разделяются пробелом. Возможные параметры: число и точка. Формат точки: `{A, B}`, где A, B - неотрицательные числа. -Файл не чувствителен к пробелам, переносам на другую строку и т.п. -Вместо фигуры могут указываться дополнительные параметры, например `.fld_size` (TODO: ЗАКОНЧИТЬ И ЗАДОКУМЕНТИРОВАТЬ) -#### Фигуры: -* p - точка -* l - линия -* r - прямоугольник -* c - круг -#### Дополнительные параметры: -* Пока ничего... -#### Пример файла: -``` -p { - 0, 1 -}; -l {0, 1} {1, 2}; -r {2, 2} {40, 18}; -c - {20, 20} - 1000; -``` -### файлы исходников -В файле [main.c](7_2/main.c) почти ничего нет. Как всегда, файлы .h и .c разбиты по папками [include](7_2/include) и [src](7_2/src) соответстенно. -* `common.h`: только определение структуры точки. Там есть один довольно важный комментарий! -* `drawable`: определяет функции поля (показать, нарисовать точку). -* `figure`: определяет структуру figure и алгоритмы, которые позволяют рисовать их на любом поле типа drawable. -* `input`: определяет функции для чтения входного файла в формате, описанном выше @@ -0,0 +1,36 @@ +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + FILE ** files; + FILE * output; + size_t i; + double t, sum, tmp1, tmp2; + + if (argc < 3) return 1; + files = calloc(argc - 2, sizeof(FILE*)); + for (i = 1; i < argc - 1; ++i) { + if (!(files[i-1] = fopen(argv[i], "r"))) { + perror("opening files"); + return 1; + } + } + + output = fopen(argv[argc-1], "w+"); + for(;;) { + t = 0; + sum = 0; + for (i = 0; i < argc - 2; ++i) { + if (2 != fscanf(files[i], "%lf %lf", &tmp1, &tmp2)) goto end; + t += tmp1; + sum += tmp2; + } + t /= (argc-2); + fprintf(output, "%f %f\n", t, sum); + } +end: + fclose(output); + for (i = 0; i < argc - 2; ++i) fclose(files[i]); + free(files); + return 0; +} diff --git a/include/diffeq.h b/include/diffeq.h new file mode 100644 index 0000000..51f85e6 --- /dev/null +++ b/include/diffeq.h @@ -0,0 +1,22 @@ +#ifndef T8_DIFFEQ_H +#define T8_DIFFEQ_H +#include <types.h> + +/* + * `eqfs` - Массив функций + * `init` - массив начальных условий f_n(a) = init[n] (значение функций в начале интервала!) + * `files` = массив файлов, в который записывать получившиеся приближения + * `n` - количество уравнений ( и начальных условий ) ( и переменных ) + * `a` - начало интервала решения ( по t? ) + * `b` - конец интервала решения ( по t? ) + * `dt` - шаг по времени + */ +void difeq_solve_euler(eqf_t *eqfs, f64 *init, const char **files, usz n, f64 a, f64 b, f64 dt); + +/* + * То же самое, что и прошлая функция, только использует не метод эйлера, а метод рунге-кутты. + * Аргументы все точно такие же + */ + void difeq_solve_RK(eqf_t *eqfs, f64 *init, const char **files, usz n, f64 a, f64 b, f64 dt); + +#endif /* T8_DIFFEQ_H */ diff --git a/include/equations.h b/include/equations.h new file mode 100644 index 0000000..48936fb --- /dev/null +++ b/include/equations.h @@ -0,0 +1,10 @@ +#ifndef T8_EQUATIONS_H +#define T8_EQUATIONS_H +#include <types.h> + +f64 f1(f64* argv, usz argc, f64 t); +f64 f2(f64* argv, usz argc, f64 t); +f64 f3(f64* argv, usz argc, f64 t); +f64 f4(f64* argv, usz argc, f64 t); + +#endif /* T8_EQUATIONS_H */ diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..c83c9bd --- /dev/null +++ b/include/types.h @@ -0,0 +1,29 @@ +#ifndef T8_TYPES_H +#define T8_TYPES_H + +#include <stdint.h> +#include <stddef.h> + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef float f32; +typedef double f64; + +typedef size_t usz; +typedef ptrdiff_t isz; + +#if __SIZEOF_LONG_DOUBLE__ == 16 +typedef long double f128; +#endif + +typedef f64(*eqf_t)(f64*, usz, f64); + +#endif @@ -0,0 +1,89 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <diffeq.h> +#include <equations.h> +#include <types.h> + +#define neqs 4 +eqf_t eqfs[] = {f1, f2, f3, f4}; + +void help(const char *program, int exit_code) { + printf("Usage:\n%s --method euler|rk4 [--equations <integer>] [--files <f1 f2 ... fn>] [--dt <number>] [--interval <start> <end>] [--init <num1 num2 ... num_n>]\n", program); + puts( "\t--method, -m:\twhich method to use, euler's method or 4th order Runge-Kutta method. Default: euler\n" + "\t--equations, -e:\tAmount of equations. Optional, default 4\n" + "\t--files, -f:\tSpecifies output files for approximations. Default: f1.data, f2.data, ...\n" + "\t--dt, -t:\tApproximation step. default 0.1\n" + "\t--range, -r:\tInterval of an approximation. defaults to '0 40'\n" + "\t--init, -i:\tInitial conditions for problem at the start of an interval. Default: '0.1 -0.1 0.1 -0.1' ...\n" + "\t--help, -h:\tThis message\n\n" + "Note: -e MUST come before -f and -i. If not done so, amount of files will be determined incorrectly" + ); + if (exit_code < 0) return; + else exit(exit_code); +} + +int main(int argc, char *argv[]) { + i32 i, j, numeq = 4; + f64 dt = 0.1, start = 0, end = 40; + f64 * init = NULL; + const char **files = NULL; + void (*solver)(eqf_t*, f64*, const char**, usz, f64, f64, f64) = difeq_solve_euler; + + for (i = 1; i < argc; ++i) { + if (!strcmp("-h", argv[i]) || !strcmp("--help", argv[i])) + help(*argv, 0); + if (!strcmp("-e", argv[i]) || !strcmp("--equations", argv[i])) + numeq = atoi(argv[++i]); + if (!strcmp("-t", argv[i]) || !strcmp("--dt", argv[i])) + dt = atof(argv[++i]); + if (!strcmp("-r", argv[i]) || !strcmp("--range", argv[i])) { + start = atof(argv[++i]); + end = atof(argv[++i]); + } + if (!strcmp("-i", argv[i]) || !strcmp("--init", argv[i])) { + init = calloc(numeq, sizeof(f64)); + for (j = 0; j < numeq; ++j) init[j] = atof(argv[++i]); + } + if (!strcmp("-f", argv[i]) || !strcmp("--files", argv[i])) { + files = calloc(numeq, sizeof(char*)); + for (j = 0; j < numeq; ++j) files[j] = argv[++i]; + } + if (!strcmp("-m", argv[i]) || !strcmp("--method", argv[i])) { + ++i; + if (!strcmp(argv[i], "euler")) { + solver = difeq_solve_euler; + continue; + } + if (!strcmp(argv[i], "rk4")) { + solver = difeq_solve_RK; + continue; + } + printf("\x1b[91mError:\x1b[0mNo such method \"%s\". Available methods: \"euler\"\"rk4\". see --help for details.\n", argv[i]); + return EXIT_FAILURE; + } + } + + if (numeq != neqs) { + printf("\x1b[91mError:\x1b[0mCan not solve more than 4 equations without changing source code of main."); + return EXIT_FAILURE; + } + if (files == NULL) { + files = calloc(4, sizeof(char*)); + files[0] = "f1.data", files[1] = "f2.data", + files[2] = "f3.data", files[3] = "f4.data"; + } + if (init == NULL) { + init = calloc(4, sizeof (double)); + init[0] = init[2] = 0.1, + init[1] = init[3] = -0.1; + } + + solver(eqfs, init, files, neqs, start, end, dt); + puts("Done"); + + free(init); + free(files); + return EXIT_SUCCESS; +} diff --git a/src/diffeq.c b/src/diffeq.c new file mode 100644 index 0000000..47b231c --- /dev/null +++ b/src/diffeq.c @@ -0,0 +1,117 @@ +#include "diffeq.h" + +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +/* Calling: + * if data is non-null, t is used. if data is null, t is not used. + * + * 1. Initializing files: files = [array of files], data = NULL, n = sizeof [array of files] + * 2. Updating files: files = anything non-NULL [unused], data = [array of data], n = sizeof [array of data]. + * n may be 0, then saved n from init is used + * 3. Closing files: files = NULL, data = NULL, n = anything + * + * returns: whether the operation was successful + */ +char flush_approx(const char ** files, f64 t, f64 * data, usz n) { + static FILE ** handlers; + static usz size; + usz i; + + if (data == NULL && files == NULL) { + for (i = 0; i < n; ++i) { + if (fclose(handlers[i]) != 0) + perror("closing files"); + } + free(handlers); + return 1; + } + if (data == NULL) { + size = n; + handlers = calloc(n, sizeof (FILE*)); + for (i = 0; i < n; ++i) { + handlers[i] = fopen(files[i], "w+"); + if (handlers[i] == NULL) { + perror(files[i]); + return 0; + } + } + return 1; + } + if (files == NULL) { + for (i = 0; i < (n == 0 ? size : n); ++i) + fprintf(handlers[i], "%f %f\n", t, data[i]); + return 1; + } + return 0; +} + +void difeq_solve_euler(eqf_t *eqfs, f64 *init, const char **files, usz n, f64 a, f64 b, f64 dt) { + f64 *guesses1 = calloc(n, sizeof(f64)); + f64 *guesses2 = calloc(n, sizeof(f64)); + f64 cur = a; + size_t i; + + flush_approx(files, 0, NULL, n); + + memcpy(guesses1, init, n * sizeof(f64)); + memcpy(guesses2, init, n * sizeof(f64)); + + for(cur = a; cur < b; cur += dt) { + for (i = 0; i < n; ++i) + guesses2[i] += dt * eqfs[i](guesses1, n, cur); + memcpy(guesses1, guesses2, n * sizeof(f64)); + flush_approx(NULL, cur, guesses1, n); + } + flush_approx(NULL, 0, NULL, 0); + free(guesses1); + free(guesses2); +} + +void difeq_solve_RK(eqf_t *eqfs, f64 *init, const char **files, usz n, f64 a, f64 b, f64 dt) { + f64 *guesses1 = calloc(n, sizeof(f64)); + f64 *guesses2 = calloc(n, sizeof(f64)); + f64 cur = a; + f64 *k1s = calloc(n, sizeof(f64)), + *k2s = calloc(n, sizeof(f64)), + *k3s = calloc(n, sizeof(f64)), + *k4s = calloc(n, sizeof(f64)); + size_t step, i; + + flush_approx(files, 0, NULL, n); + + memcpy(guesses2, init, n * sizeof(f64)); + memcpy(guesses1, init, n * sizeof(f64)); + + for(step = 0; step < (b-a)/dt; cur = a + ++step*dt) { + for (i = 0; i < n; ++i) + k1s[i] = eqfs[i](guesses1, n, cur); + for (i = 0; i < n; ++i) + guesses2[i] = guesses1[i] + dt/2 * k1s[i]; + for (i = 0; i < n; ++i) + k2s[i] = eqfs[i](guesses2, n, cur + dt/2); + for (i = 0; i < n; ++i) + guesses2[i] = guesses1[i] + dt/2 * k2s[i]; + for (i = 0; i < n; ++i) + k3s[i] = eqfs[i](guesses2, n, cur + dt/2); + for (i = 0; i < n; ++i) + guesses2[i] = guesses1[i] + dt * k3s[i]; + for (i = 0; i < n; ++i) + k4s[i] = eqfs[i](guesses2, n, cur + dt); + + for (i = 0; i < n; ++i) + guesses2[i] += dt/6 * (k1s[i] + 2*k2s[i] +2*k3s[i] + k4s[i]); + + memcpy(guesses1, guesses2, n * sizeof(f64)); + flush_approx(NULL, cur, guesses1, n); + } + flush_approx(NULL, 0, NULL, 0); + free(guesses1); + free(guesses2); + free(k1s); + free(k2s); + free(k3s); + free(k4s); +} diff --git a/src/equations.c b/src/equations.c new file mode 100644 index 0000000..efe176e --- /dev/null +++ b/src/equations.c @@ -0,0 +1,8 @@ +#include <equations.h> +#include <types.h> + +f64 f1(f64* argv, usz argc, f64 t) { return -argv[0] + argv[1] - argv[2] + argv[3]; } +f64 f2(f64* argv, usz argc, f64 t) { return -argv[0] + argv[2] - argv[3]; } +f64 f3(f64* argv, usz argc, f64 t) { return argv[0] - argv[1] + argv[3] + t/10; } +f64 f4(f64* argv, usz argc, f64 t) { return argv[0] - argv[3] - t/10; } + |