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