summaryrefslogtreecommitdiff
path: root/lab04/ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'lab04/ui.c')
-rw-r--r--lab04/ui.c1543
1 files changed, 1543 insertions, 0 deletions
diff --git a/lab04/ui.c b/lab04/ui.c
new file mode 100644
index 0000000..a6f6b1d
--- /dev/null
+++ b/lab04/ui.c
@@ -0,0 +1,1543 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+#include "ui.h"
+
+#include "polynomial.h"
+#include "expression.h"
+#include "tui.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#define STYLE_RESET "\033[0m"
+#define STYLE_BOLD "\033[1m"
+#define STYLE_DIM "\033[2m"
+#define STYLE_SELECTED "\033[7;1m"
+#define COLOR_CYAN "\033[1;36m"
+#define COLOR_GREEN "\033[1;32m"
+#define COLOR_RED "\033[1;31m"
+#define COLOR_YELLOW "\033[1;33m"
+
+enum ui_vector_result_kind {
+ UI_VECTOR_RESULT_NONE,
+ UI_VECTOR_RESULT_VALUE,
+ UI_VECTOR_RESULT_COSINE,
+};
+
+struct ui_state {
+ struct vec linear;
+
+ struct vec vector_a;
+ struct vec vector_b;
+ struct vec vector_result;
+ enum ui_vector_result_kind vector_result_kind;
+ double vector_cosine;
+
+ struct poly_vec poly_vec_a;
+ struct poly_vec poly_vec_b;
+ struct poly_vec poly_vec_result;
+ bool poly_vec_result_valid;
+
+ struct poly_list poly_list_a;
+ struct poly_list poly_list_b;
+ struct poly_list poly_list_result;
+ bool poly_list_result_valid;
+
+ struct expression_context expression;
+ char expression_text[256];
+ double expression_result;
+ bool expression_result_valid;
+
+ char status[256];
+};
+
+static void ui_set_status(struct ui_state *state, const char *format, ...)
+{
+ va_list arguments;
+
+ va_start(arguments, format);
+ vsnprintf(state->status, sizeof(state->status), format, arguments);
+ va_end(arguments);
+}
+
+static void ui_clear_status(struct ui_state *state)
+{
+ state->status[0] = '\0';
+}
+
+/* 计算 UTF-8 字符串在终端中的实际显示列宽(中文字符占 2 列) */
+static size_t ui_str_display_width(const char *str)
+{
+ size_t width = 0;
+ while (*str) {
+ unsigned char c = (unsigned char)*str;
+ if (c < 0x80) {
+ width += 1;
+ str += 1;
+ } else if ((c & 0xE0) == 0xC0) {
+ width += 1;
+ str += 2;
+ } else if ((c & 0xF0) == 0xE0) {
+ width += 2;
+ str += 3;
+ } else if ((c & 0xF8) == 0xF0) {
+ width += 2;
+ str += 4;
+ } else {
+ str += 1;
+ }
+ }
+ return width;
+}
+
+static int ui_terminal_width(void)
+{
+ struct winsize size;
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_col > 0) {
+ return size.ws_col;
+ }
+ return 50;
+}
+
+static void ui_print_rule(char character)
+{
+ int width = ui_terminal_width();
+ for (int i = 0; i < width; ++i) {
+ putchar(character);
+ }
+ putchar('\n');
+}
+
+static void ui_print_centered(const char *text, int width)
+{
+ size_t disp_w = ui_str_display_width(text);
+ int pad = (width > (int)disp_w) ? (width - (int)disp_w) / 2 : 0;
+ for (int i = 0; i < pad; ++i) {
+ putchar(' ');
+ }
+ printf("%s\n", text);
+}
+
+static void ui_print_header(const char *title)
+{
+ int width = ui_terminal_width();
+ printf(COLOR_CYAN);
+ ui_print_rule('=');
+ printf(STYLE_BOLD);
+ ui_print_centered(title, width);
+ printf(STYLE_RESET);
+ printf(COLOR_CYAN);
+ ui_print_rule('=');
+ printf(STYLE_RESET "\n");
+}
+
+static void ui_print_status(const struct ui_state *state)
+{
+ if (state->status[0] != '\0') {
+ printf("\n" STYLE_DIM);
+ ui_print_rule('-');
+ printf(STYLE_RESET);
+ if (strncmp(state->status, "错误", 6) == 0) {
+ printf(COLOR_RED " [!] %s" STYLE_RESET "\n", state->status);
+ } else if (strncmp(state->status, "已取消", 9) == 0) {
+ printf(COLOR_YELLOW " [*] %s" STYLE_RESET "\n", state->status);
+ } else {
+ printf(COLOR_GREEN " [√] %s" STYLE_RESET "\n", state->status);
+ }
+ }
+}
+
+static void ui_print_footer(const char *text)
+{
+ printf("\n" STYLE_DIM);
+ ui_print_rule('-');
+ printf(STYLE_RESET);
+ printf(STYLE_DIM " %s" STYLE_RESET "\n", text);
+}
+
+static void ui_print_menu(const char *const *items,
+ size_t count,
+ size_t selected,
+ const char *zero_item)
+{
+ for (size_t i = 0; i < count; ++i) {
+ if (i == selected) {
+ printf(STYLE_SELECTED " > %zu. %s " STYLE_RESET "\n", i + 1, items[i]);
+ } else {
+ printf(" %zu. %s\n", i + 1, items[i]);
+ }
+ }
+ if (selected == count) {
+ printf(STYLE_SELECTED " > 0. %s " STYLE_RESET "\n", zero_item);
+ } else {
+ printf(STYLE_DIM " 0. %s" STYLE_RESET "\n", zero_item);
+ }
+}
+
+static int ui_menu_input(size_t *selected, size_t count)
+{
+ size_t total = count + 1;
+ struct tui_event event = tui_read_key();
+
+ if (event.kind == TUI_KEY_UP) {
+ *selected = *selected == 0 ? total - 1 : *selected - 1;
+ return -2;
+ }
+ if (event.kind == TUI_KEY_DOWN) {
+ *selected = (*selected + 1) % total;
+ return -2;
+ }
+ if (event.kind == TUI_KEY_CHARACTER &&
+ (event.character == 'k' || event.character == 'K')) {
+ *selected = *selected == 0 ? total - 1 : *selected - 1;
+ return -2;
+ }
+ if (event.kind == TUI_KEY_CHARACTER &&
+ (event.character == 'j' || event.character == 'J')) {
+ *selected = (*selected + 1) % total;
+ return -2;
+ }
+ if (event.kind == TUI_KEY_ENTER) {
+ if (*selected == count) {
+ return -1;
+ }
+ return (int)*selected;
+ }
+ if (event.kind == TUI_KEY_ESCAPE ||
+ (event.kind == TUI_KEY_CHARACTER &&
+ (event.character == 'q' || event.character == 'Q' ||
+ event.character == '0'))) {
+ return -1;
+ }
+ if (event.kind == TUI_KEY_CHARACTER &&
+ event.character >= '1' &&
+ event.character <= '0' + (int)count) {
+ return event.character - '1';
+ }
+ return -2;
+}
+
+static void ui_print_vec_int(const struct vec *vector)
+{
+ printf("[");
+ for (size_t i = 0; i < vector->size; ++i) {
+ if (i != 0) {
+ printf(", ");
+ }
+ printf("%d", *(const int *)vec_at_const(vector, i));
+ }
+ printf("]");
+}
+
+static void ui_print_vec_double(const struct vec *vector)
+{
+ printf("[");
+ for (size_t i = 0; i < vector->size; ++i) {
+ if (i != 0) {
+ printf(", ");
+ }
+ printf("%g", *(const double *)vec_at_const(vector, i));
+ }
+ printf("]");
+}
+
+static void ui_print_poly_term(double coefficient,
+ unsigned int exponent,
+ bool *first)
+{
+ double magnitude = fabs(coefficient);
+
+ if (*first) {
+ if (coefficient < 0.0) {
+ printf("-");
+ }
+ *first = false;
+ } else {
+ printf(coefficient < 0.0 ? " - " : " + ");
+ }
+
+ if (exponent == 0) {
+ printf("%g", magnitude);
+ return;
+ }
+ if (magnitude != 1.0) {
+ printf("%g", magnitude);
+ }
+ printf("x");
+ if (exponent != 1) {
+ printf("^%u", exponent);
+ }
+}
+
+static void ui_print_poly_vec(const struct poly_vec *poly)
+{
+ bool first = true;
+
+ for (size_t i = 0; i < poly->terms.size; ++i) {
+ const struct poly_term *term = vec_at_const(&poly->terms, i);
+ ui_print_poly_term(term->coef, term->exp, &first);
+ }
+ if (first) {
+ printf("0");
+ }
+}
+
+static void ui_print_poly_list(const struct poly_list *poly)
+{
+ bool first = true;
+ const struct list_head *position = poly->head.next;
+
+ while (position != &poly->head) {
+ const struct poly_node *node =
+ list_entry(position, const struct poly_node, link);
+ ui_print_poly_term(node->term.coef, node->term.exp, &first);
+ position = position->next;
+ }
+ if (first) {
+ printf("0");
+ }
+}
+
+static const char *ui_skip_space(const char *text)
+{
+ while (isspace((unsigned char)*text)) {
+ ++text;
+ }
+ return text;
+}
+
+static bool ui_parse_int(const char *text, int *value)
+{
+ char *end;
+ const char *start = ui_skip_space(text);
+
+ errno = 0;
+ long parsed = strtol(start, &end, 10);
+ if (start == end || errno == ERANGE || parsed < INT_MIN ||
+ parsed > INT_MAX || *ui_skip_space(end) != '\0') {
+ return false;
+ }
+ *value = (int)parsed;
+ return true;
+}
+
+static bool ui_parse_size(const char *text, size_t *value)
+{
+ char *end;
+ const char *start = ui_skip_space(text);
+
+ if (*start == '-') {
+ return false;
+ }
+ errno = 0;
+ uintmax_t parsed = strtoumax(start, &end, 10);
+ if (start == end || errno == ERANGE || parsed > SIZE_MAX ||
+ *ui_skip_space(end) != '\0') {
+ return false;
+ }
+ *value = (size_t)parsed;
+ return true;
+}
+
+static bool ui_parse_unsigned(const char *text, unsigned int *value)
+{
+ char *end;
+ const char *start = ui_skip_space(text);
+
+ if (*start == '-') {
+ return false;
+ }
+ errno = 0;
+ uintmax_t parsed = strtoumax(start, &end, 10);
+ if (start == end || errno == ERANGE || parsed > UINT_MAX ||
+ *ui_skip_space(end) != '\0') {
+ return false;
+ }
+ *value = (unsigned int)parsed;
+ return true;
+}
+
+static bool ui_parse_vector(const char *text, struct vec *destination)
+{
+ struct vec temporary;
+ vec_init(&temporary, double);
+ const char *position = text;
+
+ for (;;) {
+ char *end;
+ position = ui_skip_space(position);
+ if (*position == '\0') {
+ break;
+ }
+
+ errno = 0;
+ double value = strtod(position, &end);
+ if (position == end || errno == ERANGE ||
+ !vec_push_back(&temporary, &value)) {
+ vec_destroy(&temporary);
+ return false;
+ }
+ position = end;
+ }
+
+ if (temporary.size == 0) {
+ vec_destroy(&temporary);
+ return false;
+ }
+ vec_swap(destination, &temporary);
+ vec_destroy(&temporary);
+ return true;
+}
+
+static bool ui_parse_poly_vec(const char *text, struct poly_vec *destination)
+{
+ struct poly_vec temporary;
+ poly_vec_init(&temporary);
+ const char *position = text;
+ size_t term_count = 0;
+
+ for (;;) {
+ char *end;
+ position = ui_skip_space(position);
+ if (*position == '\0') {
+ break;
+ }
+
+ errno = 0;
+ double coefficient = strtod(position, &end);
+ if (position == end || errno == ERANGE) {
+ poly_vec_destroy(&temporary);
+ return false;
+ }
+ position = ui_skip_space(end);
+
+ if (*position == '-') {
+ poly_vec_destroy(&temporary);
+ return false;
+ }
+ errno = 0;
+ uintmax_t exponent = strtoumax(position, &end, 10);
+ if (position == end || errno == ERANGE || exponent > UINT_MAX) {
+ poly_vec_destroy(&temporary);
+ return false;
+ }
+ if (!poly_vec_add_term(&temporary, coefficient,
+ (unsigned int)exponent)) {
+ poly_vec_destroy(&temporary);
+ return false;
+ }
+ ++term_count;
+ position = end;
+ }
+
+ if (term_count == 0) {
+ poly_vec_destroy(&temporary);
+ return false;
+ }
+ vec_swap(&destination->terms, &temporary.terms);
+ poly_vec_destroy(&temporary);
+ return true;
+}
+
+static bool ui_parse_poly_list(const char *text, struct poly_list *destination)
+{
+ struct poly_list temporary;
+ poly_list_init(&temporary);
+ const char *position = text;
+ size_t term_count = 0;
+
+ for (;;) {
+ char *end;
+ position = ui_skip_space(position);
+ if (*position == '\0') {
+ break;
+ }
+
+ errno = 0;
+ double coefficient = strtod(position, &end);
+ if (position == end || errno == ERANGE) {
+ poly_list_destroy(&temporary);
+ return false;
+ }
+ position = ui_skip_space(end);
+
+ if (*position == '-') {
+ poly_list_destroy(&temporary);
+ return false;
+ }
+ errno = 0;
+ uintmax_t exponent = strtoumax(position, &end, 10);
+ if (position == end || errno == ERANGE || exponent > UINT_MAX) {
+ poly_list_destroy(&temporary);
+ return false;
+ }
+ if (!poly_list_add_term(&temporary, coefficient,
+ (unsigned int)exponent)) {
+ poly_list_destroy(&temporary);
+ return false;
+ }
+ ++term_count;
+ position = end;
+ }
+
+ if (term_count == 0) {
+ poly_list_destroy(&temporary);
+ return false;
+ }
+ __poly_list_replace(destination, &temporary);
+ return true;
+}
+
+/* 原位输入框:保留界面上下文数据 */
+static bool ui_prompt_inline(const char *title,
+ const char *description,
+ char *buffer,
+ size_t capacity)
+{
+ printf("\n" COLOR_YELLOW "┌── %s" STYLE_RESET "\n", title);
+ if (description != NULL && *description != '\0') {
+ printf(COLOR_YELLOW "│ " STYLE_RESET "%s\n", description);
+ }
+ printf(COLOR_YELLOW "└─> " STYLE_RESET);
+ fflush(stdout);
+ return tui_read_line("", buffer, capacity);
+}
+
+static bool ui_confirm(const char *title, const char *question)
+{
+ char input[16];
+
+ if (!ui_prompt_inline(title, question, input, sizeof(input))) {
+ return false;
+ }
+ return input[0] == 'y' || input[0] == 'Y';
+}
+
+static bool ui_state_init(struct ui_state *state)
+{
+ memset(state, 0, sizeof(*state));
+ vec_init(&state->linear, int);
+ vec_init(&state->vector_a, double);
+ vec_init(&state->vector_b, double);
+ vec_init(&state->vector_result, double);
+ poly_vec_init(&state->poly_vec_a);
+ poly_vec_init(&state->poly_vec_b);
+ poly_vec_init(&state->poly_vec_result);
+ poly_list_init(&state->poly_list_a);
+ poly_list_init(&state->poly_list_b);
+ poly_list_init(&state->poly_list_result);
+ expression_init(&state->expression);
+
+ int linear_values[] = { 10, 20, 30, 40 };
+ double vector_a_values[] = { 1.0, 2.0, 3.0 };
+ double vector_b_values[] = { 4.0, 5.0, 6.0 };
+ for (size_t i = 0; i < sizeof(linear_values) / sizeof(linear_values[0]);
+ ++i) {
+ if (!vec_push_back(&state->linear, &linear_values[i])) {
+ return false;
+ }
+ }
+ for (size_t i = 0; i < sizeof(vector_a_values) / sizeof(vector_a_values[0]);
+ ++i) {
+ if (!vec_push_back(&state->vector_a, &vector_a_values[i]) ||
+ !vec_push_back(&state->vector_b, &vector_b_values[i])) {
+ return false;
+ }
+ }
+
+ if (!poly_vec_add_term(&state->poly_vec_a, 5.0, 4) ||
+ !poly_vec_add_term(&state->poly_vec_a, 3.0, 2) ||
+ !poly_vec_add_term(&state->poly_vec_a, 1.0, 0) ||
+ !poly_vec_add_term(&state->poly_vec_b, 2.0, 3) ||
+ !poly_vec_add_term(&state->poly_vec_b, -3.0, 2) ||
+ !poly_vec_add_term(&state->poly_vec_b, 7.0, 0) ||
+ !poly_list_add_term(&state->poly_list_a, 5.0, 4) ||
+ !poly_list_add_term(&state->poly_list_a, 3.0, 2) ||
+ !poly_list_add_term(&state->poly_list_a, 1.0, 0) ||
+ !poly_list_add_term(&state->poly_list_b, 2.0, 3) ||
+ !poly_list_add_term(&state->poly_list_b, -3.0, 2) ||
+ !poly_list_add_term(&state->poly_list_b, 7.0, 0)) {
+ return false;
+ }
+ return true;
+}
+
+static void ui_state_destroy(struct ui_state *state)
+{
+ vec_destroy(&state->linear);
+ vec_destroy(&state->vector_a);
+ vec_destroy(&state->vector_b);
+ vec_destroy(&state->vector_result);
+ poly_vec_destroy(&state->poly_vec_a);
+ poly_vec_destroy(&state->poly_vec_b);
+ poly_vec_destroy(&state->poly_vec_result);
+ poly_list_destroy(&state->poly_list_a);
+ poly_list_destroy(&state->poly_list_b);
+ poly_list_destroy(&state->poly_list_result);
+ expression_destroy(&state->expression);
+}
+
+static void ui_draw_linear(const struct ui_state *state, size_t selected)
+{
+ static const char *const items[] = {
+ "尾部添加",
+ "指定位置插入",
+ "删除元素",
+ "修改元素",
+ "查找元素",
+ "遍历线性表",
+ "清空线性表",
+ "查看内部状态",
+ };
+
+ tui_clear();
+ ui_print_header("线性表操作");
+ printf(COLOR_CYAN "当前线性表: " STYLE_BOLD);
+ ui_print_vec_int(&state->linear);
+ printf(STYLE_RESET "\n" STYLE_DIM "size = %zu capacity = %zu" STYLE_RESET "\n\n",
+ state->linear.size, state->linear.capacity);
+
+ ui_print_menu(items, sizeof(items) / sizeof(items[0]), selected, "返回");
+ ui_print_status(state);
+ ui_print_footer("↑/↓ 或 j/k 选择 Enter 确认 q/Esc 返回");
+ fflush(stdout);
+}
+
+static bool ui_read_int_value(const char *title,
+ const char *description,
+ int *value,
+ struct ui_state *state)
+{
+ char input[128];
+
+ if (!ui_prompt_inline(title, description, input, sizeof(input))) {
+ ui_set_status(state, "已取消");
+ return false;
+ }
+ if (!ui_parse_int(input, value)) {
+ ui_set_status(state, "错误:输入格式无效");
+ return false;
+ }
+ return true;
+}
+
+static bool ui_read_size_value(const char *title,
+ const char *description,
+ size_t *value,
+ struct ui_state *state)
+{
+ char input[128];
+
+ if (!ui_prompt_inline(title, description, input, sizeof(input))) {
+ ui_set_status(state, "已取消");
+ return false;
+ }
+ if (!ui_parse_size(input, value)) {
+ ui_set_status(state, "错误:输入格式无效");
+ return false;
+ }
+ return true;
+}
+
+static void ui_linear_append(struct ui_state *state)
+{
+ int value;
+
+ if (!ui_read_int_value("尾部添加", "元素值:", &value, state)) {
+ return;
+ }
+ if (!vec_push_back(&state->linear, &value)) {
+ ui_set_status(state, "错误:内存分配失败");
+ return;
+ }
+ ui_set_status(state, "已添加元素 %d", value);
+}
+
+static void ui_linear_insert(struct ui_state *state)
+{
+ size_t index;
+ int value;
+
+ if (!ui_read_size_value("指定位置插入", "插入位置:", &index, state) ||
+ !ui_read_int_value("指定位置插入", "元素值:", &value, state)) {
+ return;
+ }
+ if (index > state->linear.size) {
+ ui_set_status(state, "错误:插入位置超出范围");
+ return;
+ }
+ if (!vec_insert(&state->linear, index, &value)) {
+ ui_set_status(state, "错误:内存分配失败");
+ return;
+ }
+ ui_set_status(state, "已在位置 %zu 插入 %d", index, value);
+}
+
+static void ui_linear_erase(struct ui_state *state)
+{
+ size_t index;
+
+ if (!ui_read_size_value("删除元素", "删除位置:", &index, state)) {
+ return;
+ }
+ if (index >= state->linear.size) {
+ ui_set_status(state, "错误:删除位置超出范围");
+ return;
+ }
+ vec_erase(&state->linear, index);
+ ui_set_status(state, "已删除位置 %zu 的元素", index);
+}
+
+static void ui_linear_modify(struct ui_state *state)
+{
+ size_t index;
+ int value;
+
+ if (!ui_read_size_value("修改元素", "修改位置:", &index, state) ||
+ !ui_read_int_value("修改元素", "新元素值:", &value, state)) {
+ return;
+ }
+ int *element = vec_at_checked(&state->linear, index);
+ if (element == NULL) {
+ ui_set_status(state, "错误:修改位置超出范围");
+ return;
+ }
+ *element = value;
+ ui_set_status(state, "已修改位置 %zu", index);
+}
+
+static void ui_linear_find(struct ui_state *state)
+{
+ int value;
+
+ if (!ui_read_int_value("查找元素", "查找值:", &value, state)) {
+ return;
+ }
+ for (size_t i = 0; i < state->linear.size; ++i) {
+ if (*(const int *)vec_at_const(&state->linear, i) == value) {
+ ui_set_status(state, "元素 %d 位于位置 %zu", value, i);
+ return;
+ }
+ }
+ ui_set_status(state, "未找到元素 %d", value);
+}
+
+static void ui_linear_traverse(const struct ui_state *state)
+{
+ tui_clear();
+ ui_print_header("遍历线性表");
+ printf("当前线性表:");
+ ui_print_vec_int(&state->linear);
+ printf("\n");
+ tui_wait();
+}
+
+static void ui_linear_clear(struct ui_state *state)
+{
+ if (!ui_confirm("清空线性表", "确定清空当前线性表? [y/N]")) {
+ ui_set_status(state, "已取消");
+ return;
+ }
+ vec_clear(&state->linear);
+ ui_set_status(state, "已清空线性表");
+}
+
+static void ui_linear_state(const struct ui_state *state)
+{
+ tui_clear();
+ ui_print_header("线性表内部状态");
+ printf("size = %zu\n", state->linear.size);
+ printf("capacity = %zu\n", state->linear.capacity);
+ printf("elem_size = %zu\n\n", state->linear.elem_size);
+ printf("下标 元素\n");
+ printf("────────────────────────\n");
+ for (size_t i = 0; i < state->linear.size; ++i) {
+ printf("%-11zu %d\n", i,
+ *(const int *)vec_at_const(&state->linear, i));
+ }
+ tui_wait();
+}
+
+static void ui_linear_action(struct ui_state *state, int choice)
+{
+ ui_clear_status(state);
+ switch (choice) {
+ case 0:
+ ui_linear_append(state);
+ break;
+ case 1:
+ ui_linear_insert(state);
+ break;
+ case 2:
+ ui_linear_erase(state);
+ break;
+ case 3:
+ ui_linear_modify(state);
+ break;
+ case 4:
+ ui_linear_find(state);
+ break;
+ case 5:
+ ui_linear_traverse(state);
+ break;
+ case 6:
+ ui_linear_clear(state);
+ break;
+ case 7:
+ ui_linear_state(state);
+ break;
+ default:
+ break;
+ }
+}
+
+static void ui_linear_page(struct ui_state *state)
+{
+ size_t selected = 0;
+
+ for (;;) {
+ ui_draw_linear(state, selected);
+ int choice = ui_menu_input(&selected, 8);
+ if (choice == -1) {
+ return;
+ }
+ if (choice >= 0) {
+ ui_linear_action(state, choice);
+ }
+ }
+}
+
+static void ui_draw_vector(const struct ui_state *state, size_t selected)
+{
+ static const char *const items[] = {
+ "输入向量 A",
+ "输入向量 B",
+ "A + B",
+ "A - B",
+ "计算夹角余弦",
+ };
+
+ tui_clear();
+ ui_print_header("向量运算");
+ printf(COLOR_CYAN "A = " STYLE_BOLD);
+ ui_print_vec_double(&state->vector_a);
+ printf(STYLE_RESET "\n" COLOR_CYAN "B = " STYLE_BOLD);
+ ui_print_vec_double(&state->vector_b);
+ printf(STYLE_RESET "\n\n");
+
+ ui_print_menu(items, sizeof(items) / sizeof(items[0]), selected, "返回");
+
+ if (state->vector_result_kind == UI_VECTOR_RESULT_VALUE) {
+ printf("\n" COLOR_GREEN "结果:" STYLE_BOLD);
+ ui_print_vec_double(&state->vector_result);
+ printf(STYLE_RESET "\n");
+ } else if (state->vector_result_kind == UI_VECTOR_RESULT_COSINE) {
+ printf("\n" COLOR_GREEN "cos(A, B) = %.6f" STYLE_RESET "\n", state->vector_cosine);
+ }
+
+ ui_print_status(state);
+ ui_print_footer("↑/↓ 或 j/k 选择 Enter 确认 q/Esc 返回");
+ fflush(stdout);
+}
+
+static void ui_vector_input(struct vec *destination,
+ const char *name,
+ struct ui_state *state)
+{
+ char input[512];
+ char description[128];
+
+ snprintf(description, sizeof(description),
+ "请输入各分量,以空格分隔(向量 %s):", name);
+ if (!ui_prompt_inline(name, description, input, sizeof(input))) {
+ ui_set_status(state, "已取消");
+ return;
+ }
+ if (!ui_parse_vector(input, destination)) {
+ ui_set_status(state, "错误:向量输入格式无效");
+ return;
+ }
+ state->vector_result_kind = UI_VECTOR_RESULT_NONE;
+ ui_set_status(state, "已更新向量 %s", name);
+}
+
+static void ui_vector_binary(struct ui_state *state, bool subtract)
+{
+ if (state->vector_a.size != state->vector_b.size) {
+ ui_set_status(state, "错误:两个向量维度不同");
+ return;
+ }
+
+ struct vec temporary;
+ vec_init(&temporary, double);
+ for (size_t i = 0; i < state->vector_a.size; ++i) {
+ double left = *(const double *)vec_at_const(&state->vector_a, i);
+ double right = *(const double *)vec_at_const(&state->vector_b, i);
+ double value = subtract ? left - right : left + right;
+ if (!vec_push_back(&temporary, &value)) {
+ vec_destroy(&temporary);
+ ui_set_status(state, "错误:内存分配失败");
+ return;
+ }
+ }
+ vec_swap(&state->vector_result, &temporary);
+ vec_destroy(&temporary);
+ state->vector_result_kind = UI_VECTOR_RESULT_VALUE;
+ ui_set_status(state, "已计算 A %c B", subtract ? '-' : '+');
+}
+
+static void ui_vector_cosine(struct ui_state *state)
+{
+ if (state->vector_a.size != state->vector_b.size) {
+ ui_set_status(state, "错误:两个向量维度不同");
+ return;
+ }
+
+ double dot = 0.0;
+ double norm_a = 0.0;
+ double norm_b = 0.0;
+ for (size_t i = 0; i < state->vector_a.size; ++i) {
+ double left = *(const double *)vec_at_const(&state->vector_a, i);
+ double right = *(const double *)vec_at_const(&state->vector_b, i);
+ dot += left * right;
+ norm_a += left * left;
+ norm_b += right * right;
+ }
+ if (norm_a == 0.0 || norm_b == 0.0) {
+ ui_set_status(state, "错误:零向量无法计算夹角余弦");
+ return;
+ }
+ state->vector_cosine = dot / (sqrt(norm_a) * sqrt(norm_b));
+ state->vector_result_kind = UI_VECTOR_RESULT_COSINE;
+ ui_set_status(state, "已计算夹角余弦");
+}
+
+static void ui_vector_action(struct ui_state *state, int choice)
+{
+ ui_clear_status(state);
+ switch (choice) {
+ case 0:
+ ui_vector_input(&state->vector_a, "输入向量 A", state);
+ break;
+ case 1:
+ ui_vector_input(&state->vector_b, "输入向量 B", state);
+ break;
+ case 2:
+ ui_vector_binary(state, false);
+ break;
+ case 3:
+ ui_vector_binary(state, true);
+ break;
+ case 4:
+ ui_vector_cosine(state);
+ break;
+ default:
+ break;
+ }
+}
+
+static void ui_vector_page(struct ui_state *state)
+{
+ size_t selected = 0;
+
+ for (;;) {
+ ui_draw_vector(state, selected);
+ int choice = ui_menu_input(&selected, 5);
+ if (choice == -1) {
+ return;
+ }
+ if (choice >= 0) {
+ ui_vector_action(state, choice);
+ }
+ }
+}
+
+static void ui_draw_poly_vec(const struct ui_state *state, size_t selected)
+{
+ static const char *const items[] = {
+ "输入多项式 A",
+ "输入多项式 B",
+ "A + B",
+ "A - B",
+ "A × B",
+ "多项式求导",
+ "查看存储结构",
+ };
+
+ tui_clear();
+ ui_print_header("顺序表多项式");
+ printf(COLOR_CYAN "A(x) = " STYLE_BOLD);
+ ui_print_poly_vec(&state->poly_vec_a);
+ printf(STYLE_RESET "\n" COLOR_CYAN "B(x) = " STYLE_BOLD);
+ ui_print_poly_vec(&state->poly_vec_b);
+ printf(STYLE_RESET "\n\n");
+
+ ui_print_menu(items, sizeof(items) / sizeof(items[0]), selected, "返回");
+
+ if (state->poly_vec_result_valid) {
+ printf("\n" COLOR_GREEN "结果:" STYLE_BOLD);
+ ui_print_poly_vec(&state->poly_vec_result);
+ printf(STYLE_RESET "\n");
+ }
+
+ ui_print_status(state);
+ ui_print_footer("↑/↓ 或 j/k 选择 Enter 确认 q/Esc 返回");
+ fflush(stdout);
+}
+
+static void ui_poly_vec_input(struct poly_vec *destination,
+ const char *name,
+ struct ui_state *state)
+{
+ char input[512];
+ char description[256];
+
+ snprintf(description, sizeof(description),
+ "输入格式:系数 指数;多个项用空格分隔\n例如:5 4 3 2 1 0\n输入多项式 %s:",
+ name);
+ if (!ui_prompt_inline("输入多项式", description, input, sizeof(input))) {
+ ui_set_status(state, "已取消");
+ return;
+ }
+ if (!ui_parse_poly_vec(input, destination)) {
+ ui_set_status(state, "错误:输入格式无效,应输入“系数 指数”对");
+ return;
+ }
+ state->poly_vec_result_valid = false;
+ ui_set_status(state, "已更新多项式 %s", name);
+}
+
+static void ui_poly_vec_binary(struct ui_state *state, char operation)
+{
+ bool success;
+
+ switch (operation) {
+ case '+':
+ success = poly_vec_add(&state->poly_vec_result,
+ &state->poly_vec_a, &state->poly_vec_b);
+ break;
+ case '-':
+ success = poly_vec_sub(&state->poly_vec_result,
+ &state->poly_vec_a, &state->poly_vec_b);
+ break;
+ default:
+ success = poly_vec_mul(&state->poly_vec_result,
+ &state->poly_vec_a, &state->poly_vec_b);
+ break;
+ }
+ if (!success) {
+ ui_set_status(state, "错误:多项式运算失败");
+ return;
+ }
+ state->poly_vec_result_valid = true;
+ ui_set_status(state, "已计算 A %c B", operation);
+}
+
+static void ui_poly_vec_derivative(struct ui_state *state)
+{
+ char input[128];
+ unsigned int which;
+ unsigned int order;
+ const struct poly_vec *source;
+
+ if (!ui_prompt_inline("多项式求导", "选择多项式(1=A,2=B):",
+ input, sizeof(input))) {
+ ui_set_status(state, "已取消");
+ return;
+ }
+ if (!ui_parse_unsigned(input, &which) || (which != 1 && which != 2)) {
+ ui_set_status(state, "错误:多项式选择无效");
+ return;
+ }
+ if (!ui_prompt_inline("多项式求导", "输入求导阶数:",
+ input, sizeof(input))) {
+ ui_set_status(state, "已取消");
+ return;
+ }
+ if (!ui_parse_unsigned(input, &order)) {
+ ui_set_status(state, "错误:求导阶数无效");
+ return;
+ }
+ source = which == 1 ? &state->poly_vec_a : &state->poly_vec_b;
+ if (!poly_vec_derivative(&state->poly_vec_result, source, order)) {
+ ui_set_status(state, "错误:多项式求导失败");
+ return;
+ }
+ state->poly_vec_result_valid = true;
+ ui_set_status(state, "已完成多项式求导");
+}
+
+static void ui_poly_vec_storage(const struct ui_state *state)
+{
+ tui_clear();
+ ui_print_header("顺序表存储结构");
+ printf("A(x) = ");
+ ui_print_poly_vec(&state->poly_vec_a);
+ printf("\nsize = %zu capacity = %zu\n",
+ state->poly_vec_a.terms.size,
+ state->poly_vec_a.terms.capacity);
+ printf("下标 系数 指数\n");
+ printf("─────────────────────────────\n");
+ for (size_t i = 0; i < state->poly_vec_a.terms.size; ++i) {
+ const struct poly_term *term =
+ vec_at_const(&state->poly_vec_a.terms, i);
+ printf("%-12zu %-12.6g %u\n", i, term->coef, term->exp);
+ }
+ printf("\nB(x) = ");
+ ui_print_poly_vec(&state->poly_vec_b);
+ printf("\nsize = %zu capacity = %zu\n",
+ state->poly_vec_b.terms.size,
+ state->poly_vec_b.terms.capacity);
+ tui_wait();
+}
+
+static void ui_poly_vec_action(struct ui_state *state, int choice)
+{
+ ui_clear_status(state);
+ switch (choice) {
+ case 0:
+ ui_poly_vec_input(&state->poly_vec_a, "A", state);
+ break;
+ case 1:
+ ui_poly_vec_input(&state->poly_vec_b, "B", state);
+ break;
+ case 2:
+ ui_poly_vec_binary(state, '+');
+ break;
+ case 3:
+ ui_poly_vec_binary(state, '-');
+ break;
+ case 4:
+ ui_poly_vec_binary(state, '*');
+ break;
+ case 5:
+ ui_poly_vec_derivative(state);
+ break;
+ case 6:
+ ui_poly_vec_storage(state);
+ break;
+ default:
+ break;
+ }
+}
+
+static void ui_poly_vec_page(struct ui_state *state)
+{
+ size_t selected = 0;
+
+ for (;;) {
+ ui_draw_poly_vec(state, selected);
+ int choice = ui_menu_input(&selected, 7);
+ if (choice == -1) {
+ return;
+ }
+ if (choice >= 0) {
+ ui_poly_vec_action(state, choice);
+ }
+ }
+}
+
+static void ui_draw_poly_list(const struct ui_state *state, size_t selected)
+{
+ static const char *const items[] = {
+ "输入多项式 A",
+ "输入多项式 B",
+ "A + B",
+ "A - B",
+ "A × B",
+ "多项式求导",
+ "查看链表结构",
+ };
+
+ tui_clear();
+ ui_print_header("链表多项式");
+ printf(COLOR_CYAN "A(x) = " STYLE_BOLD);
+ ui_print_poly_list(&state->poly_list_a);
+ printf(STYLE_RESET "\n" COLOR_CYAN "B(x) = " STYLE_BOLD);
+ ui_print_poly_list(&state->poly_list_b);
+ printf(STYLE_RESET "\n\n");
+
+ ui_print_menu(items, sizeof(items) / sizeof(items[0]), selected, "返回");
+
+ if (state->poly_list_result_valid) {
+ printf("\n" COLOR_GREEN "结果:" STYLE_BOLD);
+ ui_print_poly_list(&state->poly_list_result);
+ printf(STYLE_RESET "\n");
+ }
+
+ ui_print_status(state);
+ ui_print_footer("↑/↓ 或 j/k 选择 Enter 确认 q/Esc 返回");
+ fflush(stdout);
+}
+
+static void ui_poly_list_input(struct poly_list *destination,
+ const char *name,
+ struct ui_state *state)
+{
+ char input[512];
+ char description[256];
+
+ snprintf(description, sizeof(description),
+ "输入格式:系数 指数;多个项用空格分隔\n例如:5 4 3 2 1 0\n输入多项式 %s:",
+ name);
+ if (!ui_prompt_inline("输入多项式", description, input, sizeof(input))) {
+ ui_set_status(state, "已取消");
+ return;
+ }
+ if (!ui_parse_poly_list(input, destination)) {
+ ui_set_status(state, "错误:输入格式无效,应输入“系数 指数”对");
+ return;
+ }
+ state->poly_list_result_valid = false;
+ ui_set_status(state, "已更新多项式 %s", name);
+}
+
+static void ui_poly_list_binary(struct ui_state *state, char operation)
+{
+ bool success;
+
+ switch (operation) {
+ case '+':
+ success = poly_list_add(&state->poly_list_result,
+ &state->poly_list_a, &state->poly_list_b);
+ break;
+ case '-':
+ success = poly_list_sub(&state->poly_list_result,
+ &state->poly_list_a, &state->poly_list_b);
+ break;
+ default:
+ success = poly_list_mul(&state->poly_list_result,
+ &state->poly_list_a, &state->poly_list_b);
+ break;
+ }
+ if (!success) {
+ ui_set_status(state, "错误:多项式运算失败");
+ return;
+ }
+ state->poly_list_result_valid = true;
+ ui_set_status(state, "已计算 A %c B", operation);
+}
+
+static void ui_poly_list_derivative(struct ui_state *state)
+{
+ char input[128];
+ unsigned int which;
+ unsigned int order;
+ const struct poly_list *source;
+
+ if (!ui_prompt_inline("多项式求导", "选择多项式(1=A,2=B):",
+ input, sizeof(input))) {
+ ui_set_status(state, "已取消");
+ return;
+ }
+ if (!ui_parse_unsigned(input, &which) || (which != 1 && which != 2)) {
+ ui_set_status(state, "错误:多项式选择无效");
+ return;
+ }
+ if (!ui_prompt_inline("多项式求导", "输入求导阶数:",
+ input, sizeof(input))) {
+ ui_set_status(state, "已取消");
+ return;
+ }
+ if (!ui_parse_unsigned(input, &order)) {
+ ui_set_status(state, "错误:求导阶数无效");
+ return;
+ }
+ source = which == 1 ? &state->poly_list_a : &state->poly_list_b;
+ if (!poly_list_derivative(&state->poly_list_result, source, order)) {
+ ui_set_status(state, "错误:多项式求导失败");
+ return;
+ }
+ state->poly_list_result_valid = true;
+ ui_set_status(state, "已完成多项式求导");
+}
+
+static void ui_poly_list_storage(const struct ui_state *state)
+{
+ tui_clear();
+ ui_print_header("链表存储结构");
+ printf("A(x) = ");
+ ui_print_poly_list(&state->poly_list_a);
+ printf("\n节点数:%zu\n\n", state->poly_list_a.size);
+ printf("HEAD");
+ const struct list_head *position = state->poly_list_a.head.next;
+ while (position != &state->poly_list_a.head) {
+ const struct poly_node *node =
+ list_entry(position, const struct poly_node, link);
+ printf(" <-> [ %g, %u ]", node->term.coef, node->term.exp);
+ position = position->next;
+ }
+ printf(" <-> HEAD\n");
+ printf("\n每个节点:[ coef | exp | link ]\n");
+ printf("\nB(x) = ");
+ ui_print_poly_list(&state->poly_list_b);
+ printf("\n节点数:%zu\n", state->poly_list_b.size);
+ tui_wait();
+}
+
+static void ui_poly_list_action(struct ui_state *state, int choice)
+{
+ ui_clear_status(state);
+ switch (choice) {
+ case 0:
+ ui_poly_list_input(&state->poly_list_a, "A", state);
+ break;
+ case 1:
+ ui_poly_list_input(&state->poly_list_b, "B", state);
+ break;
+ case 2:
+ ui_poly_list_binary(state, '+');
+ break;
+ case 3:
+ ui_poly_list_binary(state, '-');
+ break;
+ case 4:
+ ui_poly_list_binary(state, '*');
+ break;
+ case 5:
+ ui_poly_list_derivative(state);
+ break;
+ case 6:
+ ui_poly_list_storage(state);
+ break;
+ default:
+ break;
+ }
+}
+
+static void ui_poly_list_page(struct ui_state *state)
+{
+ size_t selected = 0;
+
+ for (;;) {
+ ui_draw_poly_list(state, selected);
+ int choice = ui_menu_input(&selected, 7);
+ if (choice == -1) {
+ return;
+ }
+ if (choice >= 0) {
+ ui_poly_list_action(state, choice);
+ }
+ }
+}
+
+static const char *ui_expression_error_text(enum expression_error_code code)
+{
+ switch (code) {
+ case EXPRESSION_SYNTAX_ERROR: return "表达式语法错误";
+ case EXPRESSION_PAREN_ERROR: return "括号不匹配";
+ case EXPRESSION_UNKNOWN_VARIABLE: return "变量不存在";
+ case EXPRESSION_UNSET_VARIABLE: return "变量尚未赋值";
+ case EXPRESSION_DIVISION_BY_ZERO: return "除数不能为零";
+ default: return "表达式错误";
+ }
+}
+
+static void ui_draw_expression(const struct ui_state *state, size_t selected)
+{
+ static const char *const items[] = {
+ "输入新表达式",
+ "修改变量值",
+ "计算表达式",
+ };
+
+ tui_clear();
+ ui_print_header("表达式计算");
+ printf(COLOR_CYAN "当前表达式:" STYLE_RESET "\n %s\n\n",
+ state->expression_text[0] ? state->expression_text : "(未输入)");
+ printf(COLOR_CYAN "变量表:" STYLE_RESET "\n");
+ if (state->expression.variables.size == 0) {
+ printf(" (无变量)\n");
+ }
+ for (size_t i = 0; i < state->expression.variables.size; ++i) {
+ const struct expression_variable *v =
+ vec_at_const(&state->expression.variables, i);
+ if (v->assigned) printf(" %s = %g\n", v->name, v->value);
+ else printf(" %s = (未赋值)\n", v->name);
+ }
+ printf("\n" COLOR_CYAN "计算结果:" STYLE_RESET "\n");
+ if (state->expression_result_valid) printf(" %g\n", state->expression_result);
+ else printf(" (未计算)\n");
+ ui_print_menu(items, 3, selected, "返回");
+ ui_print_status(state);
+ ui_print_footer("↑/↓ 或 j/k 选择 Enter 确认 q/Esc 返回");
+ fflush(stdout);
+}
+
+static void ui_expression_input(struct ui_state *state)
+{
+ char input[sizeof(state->expression_text)];
+ if (!ui_prompt_inline("输入新表达式", "例如:3+4*X2-2*(20-5*2)",
+ input, sizeof(input))) return;
+ struct expression_error error;
+ if (!expression_collect_variables(&state->expression, input, &error)) {
+ ui_set_status(state, "错误:%s(位置 %zu)",
+ ui_expression_error_text(error.code), error.position);
+ return;
+ }
+ strcpy(state->expression_text, input);
+ state->expression_result_valid = false;
+ ui_set_status(state, "已更新表达式");
+}
+
+static void ui_expression_set_variable(struct ui_state *state)
+{
+ char name[EXPRESSION_NAME_MAX];
+ char input[64];
+ if (state->expression.variables.size == 0) {
+ ui_set_status(state, "当前表达式没有变量");
+ return;
+ }
+ if (!ui_prompt_inline("修改变量值", "输入变量名:", name, sizeof(name))) return;
+ struct expression_variable *variable = expression_variable(&state->expression, name);
+ if (!variable) {
+ ui_set_status(state, "错误:变量不存在");
+ return;
+ }
+ if (!ui_prompt_inline("修改变量值", "输入新值:", input, sizeof(input))) return;
+ char *end;
+ errno = 0;
+ double value = strtod(input, &end);
+ if (end == input || errno == ERANGE || *ui_skip_space(end) != '\0') {
+ ui_set_status(state, "错误:数值格式无效");
+ return;
+ }
+ variable->value = value;
+ variable->assigned = true;
+ state->expression_result_valid = false;
+ ui_set_status(state, "已更新变量 %s", name);
+}
+
+static void ui_expression_calculate(struct ui_state *state)
+{
+ if (state->expression_text[0] == '\0') {
+ ui_set_status(state, "错误:请先输入表达式");
+ return;
+ }
+ struct expression_error error;
+ if (!expression_eval(&state->expression, state->expression_text,
+ &state->expression_result, &error)) {
+ state->expression_result_valid = false;
+ ui_set_status(state, "错误:%s(位置 %zu)",
+ ui_expression_error_text(error.code), error.position);
+ return;
+ }
+ state->expression_result_valid = true;
+ ui_set_status(state, "计算完成");
+}
+
+static void ui_expression_action(struct ui_state *state, int choice)
+{
+ switch (choice) {
+ case 0:
+ ui_expression_input(state);
+ break;
+ case 1:
+ ui_expression_set_variable(state);
+ break;
+ case 2:
+ ui_expression_calculate(state);
+ break;
+ default:
+ break;
+ }
+}
+
+static void ui_expression_page(struct ui_state *state)
+{
+ size_t selected = 0;
+ for (;;) {
+ ui_draw_expression(state, selected);
+ int choice = ui_menu_input(&selected, 3);
+ if (choice == -1) {
+ return;
+ }
+ if (choice >= 0) {
+ ui_expression_action(state, choice);
+ }
+ }
+}
+
+static void ui_draw_main(const struct ui_state *state, size_t selected)
+{
+ static const char *const items[] = {
+ "线性表操作",
+ "向量运算",
+ "顺序表多项式",
+ "链表多项式",
+ "表达式计算",
+ };
+
+ (void)state;
+ tui_clear();
+ printf(COLOR_CYAN);
+ ui_print_rule('=');
+ printf(STYLE_BOLD);
+ ui_print_centered("简单计算器", ui_terminal_width());
+ printf(STYLE_RESET);
+ printf(COLOR_CYAN);
+ ui_print_rule('=');
+ printf(STYLE_RESET "\n");
+ ui_print_menu(items, sizeof(items) / sizeof(items[0]), selected, "退出");
+ ui_print_footer("↑/↓ 或 j/k 选择 Enter 确认 q 退出");
+ fflush(stdout);
+}
+
+int ui_run(void)
+{
+ struct ui_state state;
+
+ if (!ui_state_init(&state)) {
+ ui_state_destroy(&state);
+ fputs("初始化数据失败\n", stderr);
+ return 1;
+ }
+ if (!tui_init()) {
+ ui_state_destroy(&state);
+ fputs("无法初始化终端交互\n", stderr);
+ return 1;
+ }
+
+ size_t selected = 0;
+ for (;;) {
+ ui_draw_main(&state, selected);
+ int choice = ui_menu_input(&selected, 5);
+ if (choice == -1) {
+ break;
+ }
+ switch (choice) {
+ case 0:
+ ui_linear_page(&state);
+ break;
+ case 1:
+ ui_vector_page(&state);
+ break;
+ case 2:
+ ui_poly_vec_page(&state);
+ break;
+ case 3:
+ ui_poly_list_page(&state);
+ break;
+ case 4:
+ ui_expression_page(&state);
+ break;
+ default:
+ break;
+ }
+ }
+
+ tui_shutdown();
+ ui_state_destroy(&state);
+ return 0;
+}