seq_runtime/
arithmetic.rs

1//! Arithmetic operations for Seq
2//!
3//! These functions are exported with C ABI for LLVM codegen to call.
4//!
5//! # Safety Contract
6//!
7//! **IMPORTANT:** These functions are designed to be called ONLY by compiler-generated code,
8//! not by end users or arbitrary C code. The compiler's type checker is responsible for:
9//!
10//! - Ensuring stack has correct number of values
11//! - Ensuring values are the correct types (Int for arithmetic, Int for comparisons)
12//! - Preventing division by zero at compile time when possible
13//!
14//! # Overflow Behavior
15//!
16//! All arithmetic operations use **wrapping semantics** for predictable, defined behavior:
17//! - `add`: i64::MAX + 1 wraps to i64::MIN
18//! - `subtract`: i64::MIN - 1 wraps to i64::MAX
19//! - `multiply`: overflow wraps around
20//! - `divide`: i64::MIN / -1 wraps to i64::MIN (special case)
21//!
22//! This matches the behavior of Forth and Factor, providing consistency for low-level code.
23
24use crate::stack::DISC_INT;
25use crate::stack::{Stack, peek_sv, pop, pop_two, push};
26use crate::value::Value;
27
28/// Push an integer literal onto the stack (for compiler-generated code)
29///
30/// Stack effect: ( -- n )
31///
32/// # Safety
33/// Always safe to call
34#[unsafe(no_mangle)]
35pub unsafe extern "C" fn patch_seq_push_int(stack: Stack, value: i64) -> Stack {
36    unsafe { push(stack, Value::Int(value)) }
37}
38
39/// Push a boolean literal onto the stack (for compiler-generated code)
40///
41/// Stack effect: ( -- bool )
42///
43/// # Safety
44/// Always safe to call
45#[unsafe(no_mangle)]
46pub unsafe extern "C" fn patch_seq_push_bool(stack: Stack, value: bool) -> Stack {
47    unsafe { push(stack, Value::Bool(value)) }
48}
49
50/// Add two integers
51///
52/// Stack effect: ( a b -- a+b )
53///
54/// # Safety
55/// Stack must have two Int values on top
56#[unsafe(no_mangle)]
57pub unsafe extern "C" fn patch_seq_add(stack: Stack) -> Stack {
58    let (rest, a, b) = unsafe { pop_two(stack, "add") };
59    match (a, b) {
60        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
61            push(rest, Value::Int(a_val.wrapping_add(b_val)))
62        },
63        _ => panic!("add: expected two integers on stack"),
64    }
65}
66
67/// Subtract two integers (a - b)
68///
69/// Stack effect: ( a b -- a-b )
70///
71/// # Safety
72/// Stack must have two Int values on top
73#[unsafe(no_mangle)]
74pub unsafe extern "C" fn patch_seq_subtract(stack: Stack) -> Stack {
75    let (rest, a, b) = unsafe { pop_two(stack, "subtract") };
76    match (a, b) {
77        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
78            push(rest, Value::Int(a_val.wrapping_sub(b_val)))
79        },
80        _ => panic!("subtract: expected two integers on stack"),
81    }
82}
83
84/// Multiply two integers
85///
86/// Stack effect: ( a b -- a*b )
87///
88/// # Safety
89/// Stack must have two Int values on top
90#[unsafe(no_mangle)]
91pub unsafe extern "C" fn patch_seq_multiply(stack: Stack) -> Stack {
92    let (rest, a, b) = unsafe { pop_two(stack, "multiply") };
93    match (a, b) {
94        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
95            push(rest, Value::Int(a_val.wrapping_mul(b_val)))
96        },
97        _ => panic!("multiply: expected two integers on stack"),
98    }
99}
100
101/// Divide two integers (a / b)
102///
103/// Stack effect: ( a b -- result success )
104///
105/// Returns the quotient and a Bool success flag.
106/// On division by zero, returns (0, false).
107/// On success, returns (a/b, true).
108///
109/// # Safety
110/// Stack must have at least two values on top
111#[unsafe(no_mangle)]
112pub unsafe extern "C" fn patch_seq_divide(stack: Stack) -> Stack {
113    let (rest, a, b) = unsafe { pop_two(stack, "divide") };
114    match (a, b) {
115        (Value::Int(_a_val), Value::Int(0)) => {
116            // Division by zero - return 0 and false
117            let stack = unsafe { push(rest, Value::Int(0)) };
118            unsafe { push(stack, Value::Bool(false)) }
119        }
120        (Value::Int(a_val), Value::Int(b_val)) => {
121            // Use wrapping_div to handle i64::MIN / -1 overflow edge case
122            let stack = unsafe { push(rest, Value::Int(a_val.wrapping_div(b_val))) };
123            unsafe { push(stack, Value::Bool(true)) }
124        }
125        _ => {
126            // Type error - should not happen with type-checked code
127            panic!("divide: expected two integers on stack");
128        }
129    }
130}
131
132/// Modulo (remainder) of two integers (a % b)
133///
134/// Stack effect: ( a b -- result success )
135///
136/// Returns the remainder and a Bool success flag.
137/// On division by zero, returns (0, false).
138/// On success, returns (a%b, true).
139///
140/// # Safety
141/// Stack must have at least two values on top
142#[unsafe(no_mangle)]
143pub unsafe extern "C" fn patch_seq_modulo(stack: Stack) -> Stack {
144    let (rest, a, b) = unsafe { pop_two(stack, "modulo") };
145    match (a, b) {
146        (Value::Int(_a_val), Value::Int(0)) => {
147            // Division by zero - return 0 and false
148            let stack = unsafe { push(rest, Value::Int(0)) };
149            unsafe { push(stack, Value::Bool(false)) }
150        }
151        (Value::Int(a_val), Value::Int(b_val)) => {
152            // Use wrapping_rem to handle i64::MIN % -1 overflow edge case
153            let stack = unsafe { push(rest, Value::Int(a_val.wrapping_rem(b_val))) };
154            unsafe { push(stack, Value::Bool(true)) }
155        }
156        _ => {
157            // Type error - should not happen with type-checked code
158            panic!("modulo: expected two integers on stack");
159        }
160    }
161}
162
163/// Integer equality: =
164///
165/// Returns 1 if equal, 0 if not (Forth-style boolean)
166/// Stack effect: ( a b -- flag )
167///
168/// # Safety
169/// Stack must have two Int values on top
170#[unsafe(no_mangle)]
171pub unsafe extern "C" fn patch_seq_eq(stack: Stack) -> Stack {
172    let (rest, a, b) = unsafe { pop_two(stack, "=") };
173    match (a, b) {
174        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
175            push(rest, Value::Bool(a_val == b_val))
176        },
177        _ => panic!("=: expected two integers on stack"),
178    }
179}
180
181/// Less than: <
182///
183/// Returns 1 if a < b, 0 otherwise (Forth-style boolean)
184/// Stack effect: ( a b -- flag )
185///
186/// # Safety
187/// Stack must have two Int values on top
188#[unsafe(no_mangle)]
189pub unsafe extern "C" fn patch_seq_lt(stack: Stack) -> Stack {
190    let (rest, a, b) = unsafe { pop_two(stack, "<") };
191    match (a, b) {
192        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Bool(a_val < b_val)) },
193        _ => panic!("<: expected two integers on stack"),
194    }
195}
196
197/// Greater than: >
198///
199/// Returns 1 if a > b, 0 otherwise (Forth-style boolean)
200/// Stack effect: ( a b -- flag )
201///
202/// # Safety
203/// Stack must have two Int values on top
204#[unsafe(no_mangle)]
205pub unsafe extern "C" fn patch_seq_gt(stack: Stack) -> Stack {
206    let (rest, a, b) = unsafe { pop_two(stack, ">") };
207    match (a, b) {
208        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Bool(a_val > b_val)) },
209        _ => panic!(">: expected two integers on stack"),
210    }
211}
212
213/// Less than or equal: <=
214///
215/// Returns 1 if a <= b, 0 otherwise (Forth-style boolean)
216/// Stack effect: ( a b -- flag )
217///
218/// # Safety
219/// Stack must have two Int values on top
220#[unsafe(no_mangle)]
221pub unsafe extern "C" fn patch_seq_lte(stack: Stack) -> Stack {
222    let (rest, a, b) = unsafe { pop_two(stack, "<=") };
223    match (a, b) {
224        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
225            push(rest, Value::Bool(a_val <= b_val))
226        },
227        _ => panic!("<=: expected two integers on stack"),
228    }
229}
230
231/// Greater than or equal: >=
232///
233/// Returns 1 if a >= b, 0 otherwise (Forth-style boolean)
234/// Stack effect: ( a b -- flag )
235///
236/// # Safety
237/// Stack must have two Int values on top
238#[unsafe(no_mangle)]
239pub unsafe extern "C" fn patch_seq_gte(stack: Stack) -> Stack {
240    let (rest, a, b) = unsafe { pop_two(stack, ">=") };
241    match (a, b) {
242        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
243            push(rest, Value::Bool(a_val >= b_val))
244        },
245        _ => panic!(">=: expected two integers on stack"),
246    }
247}
248
249/// Not equal: <>
250///
251/// Returns 1 if a != b, 0 otherwise (Forth-style boolean)
252/// Stack effect: ( a b -- flag )
253///
254/// # Safety
255/// Stack must have two Int values on top
256#[unsafe(no_mangle)]
257pub unsafe extern "C" fn patch_seq_neq(stack: Stack) -> Stack {
258    let (rest, a, b) = unsafe { pop_two(stack, "<>") };
259    match (a, b) {
260        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
261            push(rest, Value::Bool(a_val != b_val))
262        },
263        _ => panic!("<>: expected two integers on stack"),
264    }
265}
266
267/// Logical AND operation (Forth-style: multiply for boolean values)
268///
269/// Stack effect: ( a b -- result )
270/// where 0 is false, non-zero is true
271/// Returns 1 if both are true (non-zero), 0 otherwise
272///
273/// # Safety
274/// Stack must have at least two Int values
275#[unsafe(no_mangle)]
276pub unsafe extern "C" fn patch_seq_and(stack: Stack) -> Stack {
277    let (rest, a, b) = unsafe { pop_two(stack, "and") };
278    match (a, b) {
279        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
280            push(
281                rest,
282                Value::Int(if a_val != 0 && b_val != 0 { 1 } else { 0 }),
283            )
284        },
285        _ => panic!("and: expected two integers on stack"),
286    }
287}
288
289/// Logical OR operation (Forth-style)
290///
291/// Stack effect: ( a b -- result )
292/// where 0 is false, non-zero is true
293/// Returns 1 if either is true (non-zero), 0 otherwise
294///
295/// # Safety
296/// Stack must have at least two Int values
297#[unsafe(no_mangle)]
298pub unsafe extern "C" fn patch_seq_or(stack: Stack) -> Stack {
299    let (rest, a, b) = unsafe { pop_two(stack, "or") };
300    match (a, b) {
301        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
302            push(
303                rest,
304                Value::Int(if a_val != 0 || b_val != 0 { 1 } else { 0 }),
305            )
306        },
307        _ => panic!("or: expected two integers on stack"),
308    }
309}
310
311/// Logical NOT operation
312///
313/// Stack effect: ( a -- result )
314/// where 0 is false, non-zero is true
315/// Returns 1 if false (0), 0 otherwise
316///
317/// # Safety
318/// Stack must have at least one Int value
319#[unsafe(no_mangle)]
320pub unsafe extern "C" fn patch_seq_not(stack: Stack) -> Stack {
321    assert!(!stack.is_null(), "not: stack is empty");
322    let (rest, a) = unsafe { pop(stack) };
323
324    match a {
325        Value::Int(a_val) => unsafe { push(rest, Value::Int(if a_val == 0 { 1 } else { 0 })) },
326        _ => panic!("not: expected integer on stack"),
327    }
328}
329
330// ============================================================================
331// Bitwise Operations
332// ============================================================================
333
334/// Bitwise AND
335///
336/// Stack effect: ( a b -- a&b )
337///
338/// # Safety
339/// Stack must have two Int values on top
340#[unsafe(no_mangle)]
341pub unsafe extern "C" fn patch_seq_band(stack: Stack) -> Stack {
342    let (rest, a, b) = unsafe { pop_two(stack, "band") };
343    match (a, b) {
344        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Int(a_val & b_val)) },
345        _ => panic!("band: expected two integers on stack"),
346    }
347}
348
349/// Bitwise OR
350///
351/// Stack effect: ( a b -- a|b )
352///
353/// # Safety
354/// Stack must have two Int values on top
355#[unsafe(no_mangle)]
356pub unsafe extern "C" fn patch_seq_bor(stack: Stack) -> Stack {
357    let (rest, a, b) = unsafe { pop_two(stack, "bor") };
358    match (a, b) {
359        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Int(a_val | b_val)) },
360        _ => panic!("bor: expected two integers on stack"),
361    }
362}
363
364/// Bitwise XOR
365///
366/// Stack effect: ( a b -- a^b )
367///
368/// # Safety
369/// Stack must have two Int values on top
370#[unsafe(no_mangle)]
371pub unsafe extern "C" fn patch_seq_bxor(stack: Stack) -> Stack {
372    let (rest, a, b) = unsafe { pop_two(stack, "bxor") };
373    match (a, b) {
374        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Int(a_val ^ b_val)) },
375        _ => panic!("bxor: expected two integers on stack"),
376    }
377}
378
379/// Bitwise NOT (one's complement)
380///
381/// Stack effect: ( a -- !a )
382///
383/// # Safety
384/// Stack must have one Int value on top
385#[unsafe(no_mangle)]
386pub unsafe extern "C" fn patch_seq_bnot(stack: Stack) -> Stack {
387    assert!(!stack.is_null(), "bnot: stack is empty");
388    let (rest, a) = unsafe { pop(stack) };
389    match a {
390        Value::Int(a_val) => unsafe { push(rest, Value::Int(!a_val)) },
391        _ => panic!("bnot: expected integer on stack"),
392    }
393}
394
395/// Shift left
396///
397/// Stack effect: ( value count -- result )
398/// Shifts value left by count bits. Negative count or count >= 64 returns 0.
399///
400/// # Safety
401/// Stack must have two Int values on top
402#[unsafe(no_mangle)]
403pub unsafe extern "C" fn patch_seq_shl(stack: Stack) -> Stack {
404    let (rest, value, count) = unsafe { pop_two(stack, "shl") };
405    match (value, count) {
406        (Value::Int(v), Value::Int(c)) => {
407            // Use checked_shl to avoid undefined behavior for out-of-range shifts
408            // Negative counts become large u32 values, which correctly return None
409            let result = if c < 0 {
410                0
411            } else {
412                v.checked_shl(c as u32).unwrap_or(0)
413            };
414            unsafe { push(rest, Value::Int(result)) }
415        }
416        _ => panic!("shl: expected two integers on stack"),
417    }
418}
419
420/// Logical shift right (zero-fill)
421///
422/// Stack effect: ( value count -- result )
423/// Shifts value right by count bits, filling with zeros.
424/// Negative count or count >= 64 returns 0.
425///
426/// # Safety
427/// Stack must have two Int values on top
428#[unsafe(no_mangle)]
429pub unsafe extern "C" fn patch_seq_shr(stack: Stack) -> Stack {
430    let (rest, value, count) = unsafe { pop_two(stack, "shr") };
431    match (value, count) {
432        (Value::Int(v), Value::Int(c)) => {
433            // Use checked_shr to avoid undefined behavior for out-of-range shifts
434            // Cast to u64 for logical (zero-fill) shift behavior
435            let result = if c < 0 {
436                0
437            } else {
438                (v as u64).checked_shr(c as u32).unwrap_or(0) as i64
439            };
440            unsafe { push(rest, Value::Int(result)) }
441        }
442        _ => panic!("shr: expected two integers on stack"),
443    }
444}
445
446/// Population count (count number of 1 bits)
447///
448/// Stack effect: ( n -- count )
449///
450/// # Safety
451/// Stack must have one Int value on top
452#[unsafe(no_mangle)]
453pub unsafe extern "C" fn patch_seq_popcount(stack: Stack) -> Stack {
454    assert!(!stack.is_null(), "popcount: stack is empty");
455    let (rest, a) = unsafe { pop(stack) };
456    match a {
457        Value::Int(v) => unsafe { push(rest, Value::Int(v.count_ones() as i64)) },
458        _ => panic!("popcount: expected integer on stack"),
459    }
460}
461
462/// Count leading zeros
463///
464/// Stack effect: ( n -- count )
465///
466/// # Safety
467/// Stack must have one Int value on top
468#[unsafe(no_mangle)]
469pub unsafe extern "C" fn patch_seq_clz(stack: Stack) -> Stack {
470    assert!(!stack.is_null(), "clz: stack is empty");
471    let (rest, a) = unsafe { pop(stack) };
472    match a {
473        Value::Int(v) => unsafe { push(rest, Value::Int(v.leading_zeros() as i64)) },
474        _ => panic!("clz: expected integer on stack"),
475    }
476}
477
478/// Count trailing zeros
479///
480/// Stack effect: ( n -- count )
481///
482/// # Safety
483/// Stack must have one Int value on top
484#[unsafe(no_mangle)]
485pub unsafe extern "C" fn patch_seq_ctz(stack: Stack) -> Stack {
486    assert!(!stack.is_null(), "ctz: stack is empty");
487    let (rest, a) = unsafe { pop(stack) };
488    match a {
489        Value::Int(v) => unsafe { push(rest, Value::Int(v.trailing_zeros() as i64)) },
490        _ => panic!("ctz: expected integer on stack"),
491    }
492}
493
494/// Push the bit width of Int (64)
495///
496/// Stack effect: ( -- 64 )
497///
498/// # Safety
499/// Always safe to call
500#[unsafe(no_mangle)]
501pub unsafe extern "C" fn patch_seq_int_bits(stack: Stack) -> Stack {
502    unsafe { push(stack, Value::Int(64)) }
503}
504
505/// Helper for peeking at the top integer value without popping
506///
507/// Returns the integer value on top of the stack without modifying the stack.
508/// Used in conjunction with pop_stack for conditional branching.
509///
510/// **Why separate peek and pop?**
511/// In LLVM IR for conditionals, we need to:
512/// 1. Extract the integer value to test it (peek_int_value)
513/// 2. Branch based on that value (icmp + br)
514/// 3. Free the stack node in both branches (pop_stack)
515///
516/// A combined pop_int_value would leak memory since we'd need the value
517/// before branching but couldn't return the updated stack pointer through
518/// both branches. Separating these operations prevents memory leaks.
519///
520/// Stack effect: ( n -- n ) returns n
521///
522/// # Safety
523/// Stack must have an Int value on top
524#[unsafe(no_mangle)]
525pub unsafe extern "C" fn patch_seq_peek_int_value(stack: Stack) -> i64 {
526    assert!(!stack.is_null(), "peek_int_value: stack is empty");
527
528    // Stack points to next push location, so top is at stack - 1
529    let sv = unsafe { peek_sv(stack) };
530    if sv.slot0 == DISC_INT {
531        sv.slot1 as i64
532    } else {
533        panic!(
534            "peek_int_value: expected Int on stack, got discriminant {}",
535            sv.slot0
536        )
537    }
538}
539
540/// Peek at a bool value on top of stack without popping (for pattern matching)
541///
542/// # Safety
543/// Stack must have a Bool value on top
544#[unsafe(no_mangle)]
545pub unsafe extern "C" fn patch_seq_peek_bool_value(stack: Stack) -> bool {
546    use crate::stack::DISC_BOOL;
547    assert!(!stack.is_null(), "peek_bool_value: stack is empty");
548
549    // Stack points to next push location, so top is at stack - 1
550    let sv = unsafe { peek_sv(stack) };
551    if sv.slot0 == DISC_BOOL {
552        sv.slot1 != 0
553    } else {
554        panic!(
555            "peek_bool_value: expected Bool on stack, got discriminant {}",
556            sv.slot0
557        )
558    }
559}
560
561/// Helper for popping without extracting the value (for conditionals)
562///
563/// Pops the top stack node and returns the updated stack pointer.
564/// Used after peek_int_value to free the condition value's stack node.
565///
566/// Stack effect: ( n -- )
567///
568/// # Safety
569/// Stack must not be empty
570#[unsafe(no_mangle)]
571pub unsafe extern "C" fn patch_seq_pop_stack(stack: Stack) -> Stack {
572    assert!(!stack.is_null(), "pop_stack: stack is empty");
573    let (rest, _value) = unsafe { pop(stack) };
574    rest
575}
576
577// Public re-exports with short names for internal use
578pub use patch_seq_add as add;
579pub use patch_seq_and as and;
580pub use patch_seq_band as band;
581pub use patch_seq_bnot as bnot;
582pub use patch_seq_bor as bor;
583pub use patch_seq_bxor as bxor;
584pub use patch_seq_clz as clz;
585pub use patch_seq_ctz as ctz;
586pub use patch_seq_divide as divide;
587pub use patch_seq_eq as eq;
588pub use patch_seq_gt as gt;
589pub use patch_seq_gte as gte;
590pub use patch_seq_int_bits as int_bits;
591pub use patch_seq_lt as lt;
592pub use patch_seq_lte as lte;
593pub use patch_seq_multiply as multiply;
594pub use patch_seq_neq as neq;
595pub use patch_seq_not as not;
596pub use patch_seq_or as or;
597pub use patch_seq_popcount as popcount;
598pub use patch_seq_push_bool as push_bool;
599pub use patch_seq_push_int as push_int;
600pub use patch_seq_shl as shl;
601pub use patch_seq_shr as shr;
602pub use patch_seq_subtract as subtract;
603
604#[cfg(test)]
605mod tests {
606    use super::*;
607
608    #[test]
609    fn test_add() {
610        unsafe {
611            let stack = crate::stack::alloc_test_stack();
612            let stack = push_int(stack, 5);
613            let stack = push_int(stack, 3);
614            let stack = add(stack);
615
616            let (_stack, result) = pop(stack);
617            assert_eq!(result, Value::Int(8));
618        }
619    }
620
621    #[test]
622    fn test_subtract() {
623        unsafe {
624            let stack = crate::stack::alloc_test_stack();
625            let stack = push_int(stack, 10);
626            let stack = push_int(stack, 3);
627            let stack = subtract(stack);
628
629            let (_stack, result) = pop(stack);
630            assert_eq!(result, Value::Int(7));
631        }
632    }
633
634    #[test]
635    fn test_multiply() {
636        unsafe {
637            let stack = crate::stack::alloc_test_stack();
638            let stack = push_int(stack, 4);
639            let stack = push_int(stack, 5);
640            let stack = multiply(stack);
641
642            let (_stack, result) = pop(stack);
643            assert_eq!(result, Value::Int(20));
644        }
645    }
646
647    #[test]
648    fn test_divide() {
649        unsafe {
650            let stack = crate::stack::alloc_test_stack();
651            let stack = push_int(stack, 20);
652            let stack = push_int(stack, 4);
653            let stack = divide(stack);
654
655            // Division now returns (result, success_bool)
656            let (stack, success) = pop(stack);
657            assert_eq!(success, Value::Bool(true));
658            let (_stack, result) = pop(stack);
659            assert_eq!(result, Value::Int(5));
660        }
661    }
662
663    #[test]
664    fn test_comparisons() {
665        unsafe {
666            // Test eq (returns true/false Bool)
667            let stack = crate::stack::alloc_test_stack();
668            let stack = push_int(stack, 5);
669            let stack = push_int(stack, 5);
670            let stack = eq(stack);
671            let (_stack, result) = pop(stack);
672            assert_eq!(result, Value::Bool(true));
673
674            // Test lt
675            let stack = push_int(stack, 3);
676            let stack = push_int(stack, 5);
677            let stack = lt(stack);
678            let (_stack, result) = pop(stack);
679            assert_eq!(result, Value::Bool(true));
680
681            // Test gt
682            let stack = push_int(stack, 7);
683            let stack = push_int(stack, 5);
684            let stack = gt(stack);
685            let (_stack, result) = pop(stack);
686            assert_eq!(result, Value::Bool(true));
687        }
688    }
689
690    // This test uses i64::MAX and i64::MIN which are outside the 44-bit NaN-boxing range
691    #[test]
692    fn test_overflow_wrapping() {
693        // Test that arithmetic uses wrapping semantics (defined overflow behavior)
694        unsafe {
695            // Test add overflow: i64::MAX + 1 should wrap
696            let stack = crate::stack::alloc_test_stack();
697            let stack = push_int(stack, i64::MAX);
698            let stack = push_int(stack, 1);
699            let stack = add(stack);
700            let (_stack, result) = pop(stack);
701            assert_eq!(result, Value::Int(i64::MIN)); // Wraps to minimum
702
703            // Test multiply overflow
704            let stack = push_int(stack, i64::MAX);
705            let stack = push_int(stack, 2);
706            let stack = multiply(stack);
707            let (_stack, result) = pop(stack);
708            // Result is well-defined (wrapping)
709            assert!(matches!(result, Value::Int(_)));
710
711            // Test subtract underflow
712            let stack = push_int(stack, i64::MIN);
713            let stack = push_int(stack, 1);
714            let stack = subtract(stack);
715            let (_stack, result) = pop(stack);
716            assert_eq!(result, Value::Int(i64::MAX)); // Wraps to maximum
717        }
718    }
719
720    #[test]
721    fn test_negative_division() {
722        unsafe {
723            // Test negative dividend
724            let stack = crate::stack::alloc_test_stack();
725            let stack = push_int(stack, -10);
726            let stack = push_int(stack, 3);
727            let stack = divide(stack);
728            let (stack, success) = pop(stack);
729            assert_eq!(success, Value::Bool(true));
730            let (_stack, result) = pop(stack);
731            assert_eq!(result, Value::Int(-3)); // Truncates toward zero
732
733            // Test negative divisor
734            let stack = push_int(stack, 10);
735            let stack = push_int(stack, -3);
736            let stack = divide(stack);
737            let (stack, success) = pop(stack);
738            assert_eq!(success, Value::Bool(true));
739            let (_stack, result) = pop(stack);
740            assert_eq!(result, Value::Int(-3));
741
742            // Test both negative
743            let stack = push_int(stack, -10);
744            let stack = push_int(stack, -3);
745            let stack = divide(stack);
746            let (stack, success) = pop(stack);
747            assert_eq!(success, Value::Bool(true));
748            let (_stack, result) = pop(stack);
749            assert_eq!(result, Value::Int(3));
750        }
751    }
752
753    // This test uses i64::MIN which is outside the 44-bit NaN-boxing range
754    #[test]
755    fn test_division_overflow_edge_case() {
756        // Critical edge case: i64::MIN / -1 would overflow
757        // Regular division panics, but wrapping_div wraps to i64::MIN
758        unsafe {
759            let stack = crate::stack::alloc_test_stack();
760            let stack = push_int(stack, i64::MIN);
761            let stack = push_int(stack, -1);
762            let stack = divide(stack);
763            let (stack, success) = pop(stack);
764            assert_eq!(success, Value::Bool(true));
765            let (_stack, result) = pop(stack);
766            // i64::MIN / -1 would be i64::MAX + 1, which wraps to i64::MIN
767            assert_eq!(result, Value::Int(i64::MIN));
768        }
769    }
770
771    #[test]
772    fn test_and_true_true() {
773        unsafe {
774            let stack = crate::stack::alloc_test_stack();
775            let stack = push_int(stack, 1);
776            let stack = push_int(stack, 1);
777            let stack = and(stack);
778            let (_stack, result) = pop(stack);
779            assert_eq!(result, Value::Int(1));
780        }
781    }
782
783    #[test]
784    fn test_and_true_false() {
785        unsafe {
786            let stack = crate::stack::alloc_test_stack();
787            let stack = push_int(stack, 1);
788            let stack = push_int(stack, 0);
789            let stack = and(stack);
790            let (_stack, result) = pop(stack);
791            assert_eq!(result, Value::Int(0));
792        }
793    }
794
795    #[test]
796    fn test_and_false_false() {
797        unsafe {
798            let stack = crate::stack::alloc_test_stack();
799            let stack = push_int(stack, 0);
800            let stack = push_int(stack, 0);
801            let stack = and(stack);
802            let (_stack, result) = pop(stack);
803            assert_eq!(result, Value::Int(0));
804        }
805    }
806
807    #[test]
808    fn test_or_true_true() {
809        unsafe {
810            let stack = crate::stack::alloc_test_stack();
811            let stack = push_int(stack, 1);
812            let stack = push_int(stack, 1);
813            let stack = or(stack);
814            let (_stack, result) = pop(stack);
815            assert_eq!(result, Value::Int(1));
816        }
817    }
818
819    #[test]
820    fn test_or_true_false() {
821        unsafe {
822            let stack = crate::stack::alloc_test_stack();
823            let stack = push_int(stack, 1);
824            let stack = push_int(stack, 0);
825            let stack = or(stack);
826            let (_stack, result) = pop(stack);
827            assert_eq!(result, Value::Int(1));
828        }
829    }
830
831    #[test]
832    fn test_or_false_false() {
833        unsafe {
834            let stack = crate::stack::alloc_test_stack();
835            let stack = push_int(stack, 0);
836            let stack = push_int(stack, 0);
837            let stack = or(stack);
838            let (_stack, result) = pop(stack);
839            assert_eq!(result, Value::Int(0));
840        }
841    }
842
843    #[test]
844    fn test_not_true() {
845        unsafe {
846            let stack = crate::stack::alloc_test_stack();
847            let stack = push_int(stack, 1);
848            let stack = not(stack);
849            let (_stack, result) = pop(stack);
850            assert_eq!(result, Value::Int(0));
851        }
852    }
853
854    #[test]
855    fn test_not_false() {
856        unsafe {
857            let stack = crate::stack::alloc_test_stack();
858            let stack = push_int(stack, 0);
859            let stack = not(stack);
860            let (_stack, result) = pop(stack);
861            assert_eq!(result, Value::Int(1));
862        }
863    }
864
865    #[test]
866    fn test_and_nonzero_values() {
867        // Forth-style: any non-zero is true
868        unsafe {
869            let stack = crate::stack::alloc_test_stack();
870            let stack = push_int(stack, 42);
871            let stack = push_int(stack, -5);
872            let stack = and(stack);
873            let (_stack, result) = pop(stack);
874            assert_eq!(result, Value::Int(1));
875        }
876    }
877
878    #[test]
879    fn test_divide_by_zero_returns_false() {
880        unsafe {
881            let stack = crate::stack::alloc_test_stack();
882            let stack = push_int(stack, 42);
883            let stack = push_int(stack, 0);
884            let stack = divide(stack);
885
886            // Division by zero now returns (0, false) instead of setting runtime error
887            let (stack, success) = pop(stack);
888            assert_eq!(success, Value::Bool(false));
889
890            // Should have pushed 0 as result
891            let (_stack, result) = pop(stack);
892            assert_eq!(result, Value::Int(0));
893        }
894    }
895}