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