summaryrefslogtreecommitdiffstats
path: root/tests/test_pushdown.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_pushdown.c')
-rw-r--r--tests/test_pushdown.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/tests/test_pushdown.c b/tests/test_pushdown.c
new file mode 100644
index 0000000..7e2e18a
--- /dev/null
+++ b/tests/test_pushdown.c
@@ -0,0 +1,442 @@
+// Tests for pushdown.h - pushdown automaton instruction execution
+#include <stdio.h>
+typedef double stack_value_t;
+#define STACK_IMPLEMENTATION
+#include "../include/stack.h"
+
+typedef double pushdown_value_t;
+#define PUSHDOWN_IMPLEMENTATION
+#include "../include/pushdown.h"
+
+#include <math.h>
+
+#define EPS 1e-9
+
+#define ASSERT(cond, msg) \
+ do { \
+ if (!(cond)) { \
+ fprintf(stderr, "FAIL: %s (test_pushdown.c:%d)\n", msg, __LINE__); \
+ failures++; \
+ } \
+ } while (0)
+
+#define ASSERT_FLOAT_EQ(a, b, msg) \
+ do { \
+ if (fabs((a) - (b)) > EPS) { \
+ fprintf(stderr, "FAIL: %s (got %f, expected %f) (test_pushdown.c:%d)\n", \
+ msg, (double)(a), (double)(b), __LINE__); \
+ failures++; \
+ } \
+ } while (0)
+
+static int failures = 0;
+
+void setup_stacks(stack_t *input, stack_t *output) {
+ *input = (stack_t){.data = NULL, .size = 0, .capacity = 16};
+ *output = (stack_t){.data = NULL, .size = 0, .capacity = 16};
+ stack_init(input);
+ stack_init(output);
+}
+
+void teardown_stacks(stack_t *input, stack_t *output) {
+ stack_free(input);
+ stack_free(output);
+}
+
+void test_push() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 5);
+ pushdown_instruction_t ops[] = {PUSH, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "push size");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 5, "push value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_pop() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&output, 5);
+ pushdown_instruction_t ops[] = {POP, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 0, "pop size");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_skip() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 5);
+ stack_push(&input, 10);
+ pushdown_instruction_t ops[] = {SKIP, PUSH, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "skip+push size");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 5, "skip top(10), push next(5)");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_add() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 3);
+ stack_push(&input, 4);
+ pushdown_instruction_t ops[] = {PUSH, PUSH, ADD, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "add size");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 7, "add value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_sub() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 10);
+ stack_push(&input, 3);
+ pushdown_instruction_t ops[] = {PUSH, PUSH, SUB, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "sub size");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 7, "sub value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_mul() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 3);
+ stack_push(&input, 4);
+ pushdown_instruction_t ops[] = {PUSH, PUSH, MUL, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "mul size");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 12, "mul value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_div() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 10);
+ stack_push(&input, 2);
+ pushdown_instruction_t ops[] = {PUSH, PUSH, DIV, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "div size");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 5, "div value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_div_zero() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 0);
+ stack_push(&input, 10);
+ pushdown_instruction_t ops[] = {PUSH, PUSH, DIV, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "div zero size");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 0, "div zero result is 0");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_recip() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 4);
+ pushdown_instruction_t ops[] = {PUSH, RECIP, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "recip size");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 0.25, "recip value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_inc() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 5);
+ pushdown_instruction_t ops[] = {PUSH, INC, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT_FLOAT_EQ(stack_peek(&output), 6, "inc value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_dec() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 5);
+ pushdown_instruction_t ops[] = {PUSH, DEC, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT_FLOAT_EQ(stack_peek(&output), 4, "dec value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_sign() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, -5);
+ pushdown_instruction_t ops[] = {PUSH, SIGN, END};
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), -1, "sign negative");
+ teardown_stacks(&input, &output);
+
+ setup_stacks(&input, &output);
+ stack_push(&input, 5);
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), 1, "sign positive");
+ teardown_stacks(&input, &output);
+
+ setup_stacks(&input, &output);
+ stack_push(&input, 0);
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), 0, "sign zero");
+ teardown_stacks(&input, &output);
+}
+
+void test_neg() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 5);
+ pushdown_instruction_t ops[] = {PUSH, NEG, END};
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), -5, "neg value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_sin() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 0);
+ pushdown_instruction_t ops[] = {PUSH, SIN, END};
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), 0, "sin(0)");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_sqrt() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 9);
+ pushdown_instruction_t ops[] = {PUSH, SQRT, END};
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), 3, "sqrt(9)");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_abs() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, -5);
+ pushdown_instruction_t ops[] = {PUSH, ABS, END};
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), 5, "abs(-5)");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_floor() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 3.7);
+ pushdown_instruction_t ops[] = {PUSH, FLOOR, END};
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), 3, "floor(3.7)");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_ceil() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 3.2);
+ pushdown_instruction_t ops[] = {PUSH, CEIL, END};
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), 4, "ceil(3.2)");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_copy() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 42);
+ pushdown_instruction_t ops[] = {PUSH, COPY, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 2, "copy size");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_noop() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 5);
+ pushdown_instruction_t ops[] = {NOOP, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 0, "noop no change");
+ ASSERT(input.size == 1, "noop input unchanged");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_sqrt_negative() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, -1);
+ pushdown_instruction_t ops[] = {PUSH, SQRT, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "sqrt negative pushes 0");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 0, "sqrt negative result is 0");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_pow() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 2);
+ stack_push(&input, 3);
+ pushdown_instruction_t ops[] = {PUSH, PUSH, POW, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 1, "pow size");
+ ASSERT_FLOAT_EQ(stack_peek(&output), 8, "pow value 2^3");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_log() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 1);
+ pushdown_instruction_t ops[] = {PUSH, LOG, END};
+ pushdown_run(&output, &input, ops);
+ ASSERT_FLOAT_EQ(stack_peek(&output), 0, "log(1)");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_complex_program() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 3);
+ stack_push(&input, 4);
+ pushdown_instruction_t ops[] = {PUSH, PUSH, MUL, INC, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT_FLOAT_EQ(stack_peek(&output), 13, "3*4+1");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_minmax() {
+ stack_t input, output;
+ setup_stacks(&input, &output);
+
+ stack_push(&input, 3);
+ stack_push(&input, 7);
+ pushdown_instruction_t ops[] = {PUSH, PUSH, MINMAX, END};
+ pushdown_run(&output, &input, ops);
+
+ ASSERT(output.size == 2, "minmax size is 2");
+ ASSERT_FLOAT_EQ(output.data[0], 3, "minmax min value");
+ ASSERT_FLOAT_EQ(output.data[1], 7, "minmax max value");
+
+ teardown_stacks(&input, &output);
+}
+
+void test_validate_minmax() {
+ pushdown_instruction_t valid[] = {PUSH, MINMAX, END};
+ ASSERT(pushdown_validate(valid), "validate accepts MINMAX");
+
+ pushdown_instruction_t invalid[] = {PUSH, (pushdown_instruction_t)999, END};
+ ASSERT(!pushdown_validate(invalid), "validate rejects out-of-range");
+}
+
+void test_instruction_count_bounds() {
+ pushdown_instruction_t ops[] = {PUSH, ADD, (pushdown_instruction_t)999, END};
+ ASSERT(pushdown_instruction_count(ops) == 2, "stops at invalid instruction");
+
+ ASSERT(pushdown_instruction_count(NULL) == 0, "NULL returns 0");
+}
+
+int main() {
+ test_push();
+ test_pop();
+ test_skip();
+ test_add();
+ test_sub();
+ test_mul();
+ test_div();
+ test_div_zero();
+ test_recip();
+ test_inc();
+ test_dec();
+ test_sign();
+ test_neg();
+ test_sin();
+ test_sqrt();
+ test_abs();
+ test_floor();
+ test_ceil();
+ test_copy();
+ test_noop();
+ test_sqrt_negative();
+ test_pow();
+ test_log();
+ test_complex_program();
+ test_minmax();
+ test_validate_minmax();
+ test_instruction_count_bounds();
+
+ printf("test_pushdown: %d failures\n", failures);
+ return failures;
+}