// Tests for pushdown.h - pushdown automaton instruction execution #include 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 #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; }