Skip to content

Instantly share code, notes, and snippets.

@RanolP
Last active June 8, 2022 02:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RanolP/21574164cc799b729116f8a342aa1740 to your computer and use it in GitHub Desktop.
Save RanolP/21574164cc799b729116f8a342aa1740 to your computer and use it in GitHub Desktop.
놀라운 그래프 C 단일 파일
/*
* y = f(x) 꼴의 그래프를 그리는 프로그램입니다.
* 이 파일을 빌드하기 위해서는 gdi32가 필요합니다.
* Dev C++ 기준
* 1. 파일 > 새로 만들기 > 프로젝트 에서 Console Application을 만듭니다.
* 2. 해당 프로젝트가 열린 상태에서, 프로젝트 > 프로젝트 옵션을 열고, 매개변수들 탭에서 Linker 칸에 "-lgdi32" (따옴표 안의 것)을 넣습니다.
*
* 사용 방법
* 1. y = f(x)의 그래프에서 f(x)에 해당하는 부분을 입력합니다.
* 이 때, 연산자 우선순위는 적용되지 않으며 왼쪽부터 결합됩니다
* 지원하는 함수는 sin, gamma가 있습니다.
* 2. 함수에 대입할 x 값의 최소, 최대 범위를 각각 입력합니다.
* 3. GUI 창이 함수의 그래프 개형을 보여줍니다.
*
* 예시 입력:
* sin(x)
* -3.141592
* 3.141592
*/
#include <windows.h>
#include <math.h>
#include <tgmath.h>
#include <stdio.h>
#include <stdlib.h>
#define DEBUG printf
#define DEBUG DEBUG_RELEASE
void DEBUG_RELEASE() {
}
// Expression 관련
// 수식을 컴퓨터가 인식할 수 있게 해석하는 데에 필요합니다.
typedef struct Expression {
int id;
void* handle;
int skip;
} Expression;
typedef struct Literal {
double value;
} Literal;
typedef struct BinaryOp {
char id;
Expression* lhs;
Expression* rhs;
} BinaryOp;
static struct {
} VariableX;
typedef struct UnaryOp {
char id;
Expression* handle;
} UnaryOp;
typedef struct FuncCall {
char* name;
Expression* argument;
} FuncCall;
// 수식에 x를 대입해 계산합니다.
double evaluate(Expression* expr, double x) {
switch (expr->id) {
case 0: {
Literal* handle = (Literal*) expr->handle;
return handle->value;
}
case 1: {
BinaryOp* handle = (BinaryOp*) expr->handle;
double lhs = evaluate(handle->lhs, x);
double rhs = evaluate(handle->rhs, x);
// 중위 연산자 목록입니다.
switch (handle->id) {
case '+': {
return lhs + rhs;
}
case '-': {
return lhs - rhs;
}
case '*': {
return lhs * rhs;
}
case '^': {
return pow(lhs, rhs);
}
case '/': {
return lhs / rhs;
}
default: {
return (double) NAN;
}
}
}
case 2: {
return x;
}
case 3: {
UnaryOp* handle = (UnaryOp*) expr->handle;
double value = evaluate(handle->handle, x);
// 전위 연산자 목록입니다.
switch (handle->id) {
case '-': {
return -value;
}
default: {
return (double) NAN;
}
}
}
case 4: {
FuncCall* handle = (FuncCall*) expr->handle;
DEBUG("eval funccall %s\n", handle->name);
double value = evaluate(handle->argument, x);
// 함수 목록입니다.
if (strcmp(handle->name, "sin") == 0) {
return sin(value);
} else if (strcmp(handle->name, "gamma") == 0) {
DEBUG("gamma!!");
return tgamma(value);
} else {
return (double) NAN;
}
}
default: {
return (double) NAN;
}
}
}
// 수식을 파싱하는 부분입니다.
// 손으로 짠 재귀 하향 파서입니다.
Expression* parse(char* src);
Expression* parse_parenthesis(char* src) {
int skip = 0;
while (*src == ' ') {
skip += 1;
src++;
}
if (*src == '(') {
DEBUG("Brace Open\n");
src++;
skip++;
Expression* result = parse(src);
if (result == NULL) {
return NULL;
}
src += result->skip;
skip += result->skip;
while (*src == ' ') {
skip += 1;
src++;
}
DEBUG("Brace Close: %c\n", *src);
if (*src == ')') {
result->skip = skip + 1;
return result;
} else {
return NULL;
}
} else {
return NULL;
}
}
Expression* parse_func(char* src) {
int skip = 0;
while (*src == ' ') {
skip += 1;
src++;
}
if (*src == 'x') {
return NULL;
}
char* name = (char*) malloc(sizeof(char) * 20);
int len = 0;
while ('a' <= *src && *src <= 'z') {
DEBUG("FuncFrag %c\n", *src);
name[len++] = *src++;
skip++;
}
if (len == 0) {
return NULL;
}
name[len] = '\0';
DEBUG("FUNCCALL %s()\n", name);
FuncCall* call = (FuncCall*) malloc(sizeof(FuncCall));
call->name = name;
call->argument = parse_parenthesis(src);
Expression* result = malloc(sizeof(Expression));
result->id = 4;
result->skip = skip + call->argument->skip;
result->handle = call;
return result;
}
Expression* parse_simple(char* src) {
Expression* paren = parse_parenthesis(src);
if (paren != NULL) {
return paren;
}
Expression* func = parse_func(src);
if (func != NULL) {
return func;
}
int skip = 0;
while (*src == ' ') {
skip += 1;
src++;
}
if (*src == 'x') {
Expression* result = (Expression*) malloc(sizeof(Expression));
result->id = 2;
result->handle = &VariableX;
result->skip = skip + 1;
return result;
}
char current;
double literal = NAN;
double dotScale = 1;
while ((current = *src++) != '\0') {
skip += 1;
if ('0' <= current && current <= '9') {
if (isnan(literal)) {
literal = 0;
}
if (dotScale < 1) {
literal += dotScale * (current - '0');
dotScale *= 0.1;
} else {
literal = literal * 10 + (double) (current - '0');
}
} else if (current == '.') {
if (isnan(literal)) {
return NULL;
}
dotScale = 0.1;
} else {
if (current != '\0') {
skip -= 1;
}
break;
}
}
DEBUG("value=%lf, skips=%d\n", literal, skip);
if (isnan(literal)) {
return NULL;
}
Literal* lit = (Literal*) malloc(sizeof(Literal));
lit->value = literal;
Expression* result = (Expression*) malloc(sizeof(Expression));
result->id = 0;
result->handle = lit;
result->skip = skip;
return result;
}
Expression* parse(char* src) {
int skip = 0;
Expression* result = NULL;
while (*src != '\0') {
DEBUG("%c, parse_simple\n", *src);
Expression* expr = parse_simple(src);
if (expr != NULL) {
if (result != NULL) {
return NULL;
}
result = expr;
src += expr->skip;
skip += expr->skip;
} else {
while (*src == ' ') {
src++;
skip++;
}
char current = *src++;
DEBUG("current=%c\n", current);
if (current == ')') {
break;
} else {
skip++;
}
if (result == NULL) {
UnaryOp* op = (UnaryOp*) malloc(sizeof(UnaryOp));
op->id = current;
op->handle = parse_simple(src);
if (op->handle == NULL) {
return NULL;
}
result = (Expression*) malloc(sizeof(Expression));
result->id = 3;
result->handle = op;
src += op->handle->skip;
skip += op->handle->skip;
} else {
BinaryOp* op = (BinaryOp*) malloc(sizeof(BinaryOp));
op->id = current;
op->lhs = result;
op->rhs = parse_simple(src);
if (op->rhs == NULL) {
return NULL;
}
result = (Expression*) malloc(sizeof(Expression));
result->id = 1;
result->handle = op;
src += op->rhs->skip;
skip += op->rhs->skip;
}
}
}
if (result != NULL) {
result->skip = skip;
}
return result;
}
// 본격적인 GUI 앱의 시작입니다.
LPCTSTR className = TEXT("Granola");
LPCTSTR title = TEXT("Granola Graphs");
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
Expression* expr;
double domain_min;
double domain_max;
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR lpszCmdParam, int nCmdShow) {
char* str = malloc(sizeof(char) * 1000);
printf("y = ");
scanf("%1000[^\n]", str);
expr = parse(str);
if (expr == NULL) {
printf("Syntax Error");
}
printf("min value of x = ");
scanf("%lf", &domain_min);
printf("max value of x = ");
scanf("%lf", &domain_max);
printf("%lf to %lf", domain_min, domain_max);
WNDCLASS windowClass;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
windowClass.hInstance = instance;
windowClass.lpfnWndProc = WndProc;
windowClass.lpszClassName = className;
windowClass.lpszMenuName = NULL;
windowClass.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&windowClass);
HWND window = CreateWindow(
className,
title,
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
(HMENU) NULL,
instance,
NULL
);
ShowWindow(window, nCmdShow);
MSG message;
while (GetMessage(&message, NULL, 0, 0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
return (int) message.wParam;
}
LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_PAINT: {
RECT rect;
GetClientRect(window, &rect);
PAINTSTRUCT paint;
HDC hdc = BeginPaint(window, &paint);
// x축, y축을 나타내는 점 목록입니다.
POINT axis[4] = {
(POINT) {
rect.left,
(rect.bottom + rect.top) / 2
},
(POINT) {
rect.right,
(rect.bottom + rect.top) / 2
},
(POINT) {
(rect.left + rect.right) / 2,
rect.top
},
(POINT) {
(rect.left + rect.right) / 2,
rect.bottom
}
};
DWORD size[2] = { 2, 2 };
PolyPolyline(hdc, axis, size, 2);
double STEP = 0.1;
double STRETCH_VERTICAL = 100;
long long int SIZE = roundl((domain_max - domain_min) / STEP);
POINT* samples = (POINT*) malloc(sizeof(POINT) * SIZE);
long long int i;
double x = domain_min;
// -domain_min부터 domain_max까지 STEP 간격으로 대입해 점 목록을 만듭니다.
for (i = 0; i < SIZE; i++) {
x += STEP;
samples[i] = (POINT) {
rect.left + (800 - 500) / 2 + 500 * i / SIZE,
(rect.top + rect.bottom) / 2 - evaluate(expr, x) * STRETCH_VERTICAL
};
}
// 만든 점 목록을 직선으로 이어 그립니다.
Polyline(hdc, samples, SIZE);
EndPaint(window, &paint);
return 0;
}
default: {
return DefWindowProc(window, message, wParam, lParam);
}
}
}
@RanolP
Copy link
Author

RanolP commented Jun 8, 2022

For example: the Taylor series of sin function

x - (x^3 / (gamma(3))) + (x^5 / gamma(5)) - (x^7 / gamma(7)) + (x^9 / gamma(9)) - (x^11 / gamma(11))
-3.141592
3.141592

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment