Skip to main content

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