// pushdown.h — Stack-based pushdown automata with 65+ math/stack operations // // Thread safety: Not thread-safe. External synchronization required. // Behavior: Instructions execute sequentially until END. Invalid instruction // values cause immediate termination (pushdown_run) or validation failure // (pushdown_validate). Arithmetic on invalid input (div/0, sqrt negative, // log <= 0, etc.) returns 0 rather than signaling error. #ifndef PUSHDOWN_H #define PUSHDOWN_H /* USER-DEFINED (define before including pushdown.h): * typedef stack_value_t; (required by stack.h) * typedef pushdown_value_t; (required by pushdown.h) */ #include "stack.h" typedef enum { NOOP, PUSH, POP, SKIP, COPY, ADD, SUB, MUL, DIV, RECIP, INC, DEC, SIGN, NEG, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, SINH, COSH, TANH, ASINH, ACOSH, ATANH, EXP, POW, LOG, SQRT, ABS, CEIL, FLOOR, ERF, GAMMA, MIN, MAX, CLAMP, ROUNDD, FRACT, MOD, LOG10, LOG2, SIGNUM, SWAP, ROT3, DEPTH, OVER, PICK, DROP2, NIP, TUCK, EQUAL, LESS, GREATER, COMPARE, PI_CONST, E_CONST, RAND, SQUARE, CUBE, RSQRT, SORT2, SORT3, MINMAX, END } pushdown_instruction_t; void pushdown_run(stack_t *, stack_t *, pushdown_instruction_t *); u32 pushdown_instruction_count(pushdown_instruction_t *); int pushdown_validate(pushdown_instruction_t *); void pushdown_print(pushdown_instruction_t *); #endif #ifdef PUSHDOWN_IMPLEMENTATION #ifndef PUSHDOWN_IMPLEMENTED #define PUSHDOWN_IMPLEMENTED #include static void _noop(stack_t *output, stack_t *input) { (void)output; (void)input; } static void _push(stack_t *output, stack_t *input) { if (input->size < 1) return; pushdown_value_t x = stack_peek(input); if (!stack_push(output, x)) return; stack_pop(input); } static void _pop(stack_t *output, stack_t *input) { (void)input; stack_pop(output); } static void _skip(stack_t *output, stack_t *input) { (void)output; stack_pop(input); } static void _copy(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_peek(output); if (!stack_push(output, x)) return; } static void _add(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = x + y; } static void _sub(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = x - y; } static void _mul(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = x * y; } static void _div(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (y == 0) ? 0 : x / y; } static void _recip(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x == 0) ? 0 : 1.0 / x; } static void _inc(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = x + 1; } static void _dec(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = x - 1; } static void _sign(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x > 0) ? 1 : ((x < 0) ? -1 : 0); } static void _neg(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = -x; } static void _sin(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = sin(x); } static void _cos(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = cos(x); } static void _tan(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = tan(x); } static void _asin(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x < -1 || x > 1) ? 0 : asin(x); } static void _acos(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x < -1 || x > 1) ? 0 : acos(x); } static void _atan(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = atan(x); } static void _sinh(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = sinh(x); } static void _cosh(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = cosh(x); } static void _tanh(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = tanh(x); } static void _asinh(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = asinh(x); } static void _acosh(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x < 1) ? 0 : acosh(x); } static void _atanh(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x <= -1 || x >= 1) ? 0 : atanh(x); } static void _atan2(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = atan2(x, y); } static void _exp(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = exp(x); } static void _pow(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (x < 0 && fmod(y, 1.0) != 0.0) ? 0 : pow(x, y); } static void _log(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x <= 0) ? 0 : log(x); } static void _sqrt(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x < 0) ? 0 : sqrt(x); } static void _abs(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = fabs(x); } static void _ceil(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = ceil(x); } static void _floor(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = floor(x); } static void _erf(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = erf(x); } static void _min(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (x < y) ? x : y; } static void _max(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (x > y) ? x : y; } static void _clamp(stack_t *output, stack_t *input) { (void)input; if (output->size < 3) return; pushdown_value_t hi = stack_pop(output); pushdown_value_t lo = stack_pop(output); pushdown_value_t v = stack_pop(output); output->data[output->size++] = (v < lo) ? lo : ((v > hi) ? hi : v); } static void _roundd(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = round(x); } static void _fract(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = x - floor(x); } static void _mod(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (y == 0) ? 0 : fmod(x, y); } static void _log10_fn(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x <= 0) ? 0 : log10(x); } static void _log2_fn(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x <= 0) ? 0 : log2(x); } static void _signum(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x > 0) ? 1 : ((x < 0) ? -1 : 0); } static void _swap(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = x; output->data[output->size++] = y; } static void _rot3(stack_t *output, stack_t *input) { (void)input; if (output->size < 3) return; pushdown_value_t t1 = stack_pop(output); pushdown_value_t t2 = stack_pop(output); pushdown_value_t t3 = stack_pop(output); output->data[output->size++] = t2; output->data[output->size++] = t3; output->data[output->size++] = t1; } static void _depth(stack_t *output, stack_t *input) { (void)input; pushdown_value_t d = (pushdown_value_t)output->size; output->data[output->size++] = d; } static void _over(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = output->data[output->size - 2]; if (!stack_push(output, x)) return; } static void _pick(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t n = stack_pop(output); u32 idx = (u32)n; if (idx >= output->size) { output->data[output->size++] = n; return; } pushdown_value_t x = output->data[output->size - 1 - idx]; if (!stack_push(output, x)) return; } static void _drop2(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; output->size -= 2; } static void _nip(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t top = stack_pop(output); stack_pop(output); output->data[output->size++] = top; } static void _tuck(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t top = stack_pop(output); pushdown_value_t second = output->data[output->size - 1]; if (!stack_push(output, top)) return; output->data[output->size++] = second; output->data[output->size++] = top; } static void _equal(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (x == y) ? 1 : 0; } static void _less(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (y < x) ? 1 : 0; } static void _greater(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (y > x) ? 1 : 0; } static void _compare(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (y > x) ? -1 : ((y < x) ? 1 : 0); } static void _pi_const(stack_t *output, stack_t *input) { (void)input; if (!stack_push(output, 3.14159265358979323846)) return; } static void _e_const(stack_t *output, stack_t *input) { (void)input; if (!stack_push(output, 2.71828182845904523536)) return; } static void _rand(stack_t *output, stack_t *input) { (void)input; if (!stack_push(output, (pushdown_value_t)((f64)rand() / RAND_MAX))) return; } static void _square(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = x * x; } static void _cube(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = x * x * x; } static void _rsqrt(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = (x == 0) ? 0 : 1.0 / sqrt(x < 0 ? -x : x); } static void _sort2(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); if (x < y) { output->data[output->size++] = x; output->data[output->size++] = y; } else { output->data[output->size++] = y; output->data[output->size++] = x; } } static void _sort3(stack_t *output, stack_t *input) { (void)input; if (output->size < 3) return; pushdown_value_t a = stack_pop(output); pushdown_value_t b = stack_pop(output); pushdown_value_t c = stack_pop(output); pushdown_value_t s[3] = {a, b, c}; for (u32 i = 0; i < 3; i++) for (u32 j = i + 1; j < 3; j++) if (s[j] < s[i]) { pushdown_value_t tmp = s[i]; s[i] = s[j]; s[j] = tmp; } output->data[output->size++] = s[0]; output->data[output->size++] = s[1]; output->data[output->size++] = s[2]; } static void _minmax(stack_t *output, stack_t *input) { (void)input; if (output->size < 2) return; pushdown_value_t x = stack_pop(output); pushdown_value_t y = stack_pop(output); output->data[output->size++] = (x < y) ? x : y; output->data[output->size++] = (x > y) ? x : y; } static void _gamma(stack_t *output, stack_t *input) { (void)input; if (output->size < 1) return; pushdown_value_t x = stack_pop(output); output->data[output->size++] = tgamma(x); } static void (*OPERATIONS[])(stack_t *output, stack_t *input) = { [NOOP] = _noop, [PUSH] = _push, [POP] = _pop, [SKIP] = _skip, [COPY] = _copy, [ADD] = _add, [SUB] = _sub, [MUL] = _mul, [DIV] = _div, [RECIP] = _recip, [INC] = _inc, [DEC] = _dec, [SIGN] = _sign, [NEG] = _neg, [SIN] = _sin, [COS] = _cos, [TAN] = _tan, [ASIN] = _asin, [ACOS] = _acos, [ATAN] = _atan, [ATAN2] = _atan2, [SINH] = _sinh, [COSH] = _cosh, [TANH] = _tanh, [ASINH] = _asinh, [ACOSH] = _acosh, [ATANH] = _atanh, [EXP] = _exp, [POW] = _pow, [LOG] = _log, [SQRT] = _sqrt, [ABS] = _abs, [CEIL] = _ceil, [FLOOR] = _floor, [ERF] = _erf, [GAMMA] = _gamma, [MIN] = _min, [MAX] = _max, [CLAMP] = _clamp, [ROUNDD] = _roundd, [FRACT] = _fract, [MOD] = _mod, [LOG10] = _log10_fn, [LOG2] = _log2_fn, [SIGNUM] = _signum, [SWAP] = _swap, [ROT3] = _rot3, [DEPTH] = _depth, [OVER] = _over, [PICK] = _pick, [DROP2] = _drop2, [NIP] = _nip, [TUCK] = _tuck, [EQUAL] = _equal, [LESS] = _less, [GREATER] = _greater, [COMPARE] = _compare, [PI_CONST] = _pi_const, [E_CONST] = _e_const, [RAND] = _rand, [SQUARE] = _square, [CUBE] = _cube, [RSQRT] = _rsqrt, [SORT2] = _sort2, [SORT3] = _sort3, [MINMAX] = _minmax, }; // pushdown_run() // // Execute a sequence of instructions until END. Instructions operate on // the output stack, pulling values from the input stack via PUSH. void pushdown_run(stack_t *output, stack_t *input, pushdown_instruction_t *instructions) { if (!output || !input || !instructions) return; pushdown_instruction_t *op = instructions; while (*op != END) { if (*op > MINMAX || *op < NOOP) break; OPERATIONS[*op](output, input); op++; } } // pushdown_instruction_count() // // Count the number of instructions before END marker. // // return - Instruction count, or 0 if NULL or invalid u32 pushdown_instruction_count(pushdown_instruction_t *instructions) { if (!instructions) return 0; u32 count = 0; pushdown_instruction_t *op = instructions; while (*op != END) { if (*op > MINMAX || *op < NOOP) return count; count++; op++; } return count; } // pushdown_validate() // // Check that all instruction values in the sequence are valid enum members. // // return - 1 if valid, 0 if invalid int pushdown_validate(pushdown_instruction_t *instructions) { if (!instructions) return 0; pushdown_instruction_t *op = instructions; while (*op != END) { if (*op < NOOP || *op > MINMAX) return 0; op++; } return 1; } // pushdown_print() // // Print the instruction sequence to stdout in readable format void pushdown_print(pushdown_instruction_t *instructions) { if (!instructions) return; pushdown_instruction_t *op = instructions; printf("["); int first = 1; while (*op != END) { if (!first) printf(", "); switch (*op) { case NOOP: printf("NOOP"); break; case PUSH: printf("PUSH"); break; case POP: printf("POP"); break; case SKIP: printf("SKIP"); break; case COPY: printf("COPY"); break; case ADD: printf("ADD"); break; case SUB: printf("SUB"); break; case MUL: printf("MUL"); break; case DIV: printf("DIV"); break; case RECIP: printf("RECIP"); break; case INC: printf("INC"); break; case DEC: printf("DEC"); break; case SIGN: printf("SIGN"); break; case NEG: printf("NEG"); break; case SIN: printf("SIN"); break; case COS: printf("COS"); break; case TAN: printf("TAN"); break; case ASIN: printf("ASIN"); break; case ACOS: printf("ACOS"); break; case ATAN: printf("ATAN"); break; case ATAN2: printf("ATAN2"); break; case SINH: printf("SINH"); break; case COSH: printf("COSH"); break; case TANH: printf("TANH"); break; case ASINH: printf("ASINH"); break; case ACOSH: printf("ACOSH"); break; case ATANH: printf("ATANH"); break; case EXP: printf("EXP"); break; case POW: printf("POW"); break; case LOG: printf("LOG"); break; case SQRT: printf("SQRT"); break; case ABS: printf("ABS"); break; case CEIL: printf("CEIL"); break; case FLOOR: printf("FLOOR"); break; case ERF: printf("ERF"); break; case GAMMA: printf("GAMMA"); break; case MIN: printf("MIN"); break; case MAX: printf("MAX"); break; case CLAMP: printf("CLAMP"); break; case ROUNDD: printf("ROUNDD"); break; case FRACT: printf("FRACT"); break; case MOD: printf("MOD"); break; case LOG10: printf("LOG10"); break; case LOG2: printf("LOG2"); break; case SIGNUM: printf("SIGNUM"); break; case SWAP: printf("SWAP"); break; case ROT3: printf("ROT3"); break; case DEPTH: printf("DEPTH"); break; case OVER: printf("OVER"); break; case PICK: printf("PICK"); break; case DROP2: printf("DROP2"); break; case NIP: printf("NIP"); break; case TUCK: printf("TUCK"); break; case EQUAL: printf("EQUAL"); break; case LESS: printf("LESS"); break; case GREATER: printf("GREATER"); break; case COMPARE: printf("COMPARE"); break; case PI_CONST: printf("PI"); break; case E_CONST: printf("E"); break; case RAND: printf("RAND"); break; case SQUARE: printf("SQUARE"); break; case CUBE: printf("CUBE"); break; case RSQRT: printf("RSQRT"); break; case SORT2: printf("SORT2"); break; case SORT3: printf("SORT3"); break; case MINMAX: printf("MINMAX"); break; default: printf("?"); break; } first = 0; op++; } printf(", END]\n"); } #endif /* PUSHDOWN_IMPLEMENTED */ #endif /* PUSHDOWN_IMPLEMENTATION */