spec: bitwise operations (bitwise-and/or/xor/not, arithmetic-shift, bit-count, integer-length)

OCaml: land/lor/lxor/lnot/lsl/asr in sx_primitives.ml
JS: & | ^ ~ << >> with Kernighan popcount and Math.clz32 for integer-length
spec/primitives.sx: stdlib.bitwise module with 7 entries
26 tests, 158 assertions, all pass OCaml+JS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 19:06:09 +00:00
parent 1ad9d63f1b
commit a8a79dc902
5 changed files with 289 additions and 2 deletions

View File

@@ -805,3 +805,47 @@
:doc "Create a new empty mutable string buffer for O(1) amortised append.")
(define-module :stdlib.coroutines)
(define-module :stdlib.bitwise)
(define-primitive
"bitwise-and"
:params (((a :as number) (b :as number)))
:returns "number"
:doc "Bitwise AND of two integers.")
(define-primitive
"bitwise-or"
:params (((a :as number) (b :as number)))
:returns "number"
:doc "Bitwise OR of two integers.")
(define-primitive
"bitwise-xor"
:params (((a :as number) (b :as number)))
:returns "number"
:doc "Bitwise XOR of two integers.")
(define-primitive
"bitwise-not"
:params ((a :as number))
:returns "number"
:doc "Bitwise NOT (one's complement) of an integer.")
(define-primitive
"arithmetic-shift"
:params (((a :as number) (count :as number)))
:returns "number"
:doc "Arithmetic shift: left if count > 0, right if count < 0.")
(define-primitive
"bit-count"
:params ((a :as number))
:returns "number"
:doc "Count set bits (popcount) in a non-negative integer.")
(define-primitive
"integer-length"
:params ((a :as number))
:returns "number"
:doc "Number of bits needed to represent integer a (excluding sign).")

157
spec/tests/test-bitwise.sx Normal file
View File

@@ -0,0 +1,157 @@
(defsuite
"bitwise-operations"
(deftest
"bitwise-and basic"
(do
(assert= 0 (bitwise-and 0 0))
(assert= 1 (bitwise-and 3 1))
(assert= 0 (bitwise-and 5 2))
(assert= 4 (bitwise-and 12 6))))
(deftest
"bitwise-and identity and zero"
(do
(assert= 255 (bitwise-and 255 255))
(assert= 0 (bitwise-and 255 0))))
(deftest
"bitwise-or basic"
(do
(assert= 0 (bitwise-or 0 0))
(assert= 3 (bitwise-or 1 2))
(assert= 7 (bitwise-or 5 3))
(assert= 15 (bitwise-or 9 6))))
(deftest
"bitwise-or identity"
(do
(assert= 255 (bitwise-or 255 0))
(assert= 255 (bitwise-or 0 255))))
(deftest
"bitwise-xor basic"
(do
(assert= 0 (bitwise-xor 0 0))
(assert= 3 (bitwise-xor 1 2))
(assert= 6 (bitwise-xor 3 5))
(assert= 0 (bitwise-xor 255 255))))
(deftest
"bitwise-xor toggle bits"
(do
(assert= 14 (bitwise-xor 10 4))
(assert= 10 (bitwise-xor 14 4))))
(deftest
"bitwise-not zero"
(do (assert= -1 (bitwise-not 0))))
(deftest
"bitwise-not positive"
(do
(assert= -2 (bitwise-not 1))
(assert= -5 (bitwise-not 4))
(assert= -256 (bitwise-not 255))))
(deftest
"bitwise-not negative"
(do
(assert= 0 (bitwise-not -1))
(assert= 1 (bitwise-not -2))
(assert= 4 (bitwise-not -5))))
(deftest
"bitwise-not double negation"
(do
(assert= 42 (bitwise-not (bitwise-not 42)))
(assert= 0 (bitwise-not (bitwise-not 0)))))
(deftest
"arithmetic-shift left"
(do
(assert= 2 (arithmetic-shift 1 1))
(assert= 4 (arithmetic-shift 1 2))
(assert= 16 (arithmetic-shift 1 4))
(assert= 8 (arithmetic-shift 2 2))))
(deftest
"arithmetic-shift right"
(do
(assert= 1 (arithmetic-shift 2 -1))
(assert= 1 (arithmetic-shift 4 -2))
(assert= 5 (arithmetic-shift 10 -1))
(assert= 2 (arithmetic-shift 16 -3))))
(deftest
"arithmetic-shift by zero"
(do
(assert= 42 (arithmetic-shift 42 0))
(assert= 0 (arithmetic-shift 0 5))))
(deftest
"arithmetic-shift negative value right preserves sign"
(do
(assert= -1 (arithmetic-shift -1 -1))
(assert= -2 (arithmetic-shift -4 -1))))
(deftest
"bit-count zero"
(do (assert= 0 (bit-count 0))))
(deftest
"bit-count powers of two"
(do
(assert= 1 (bit-count 1))
(assert= 1 (bit-count 2))
(assert= 1 (bit-count 4))
(assert= 1 (bit-count 128))))
(deftest
"bit-count all-ones values"
(do
(assert= 8 (bit-count 255))
(assert= 4 (bit-count 15))
(assert= 2 (bit-count 3))))
(deftest
"bit-count mixed"
(do
(assert= 3 (bit-count 7))
(assert= 2 (bit-count 5))
(assert= 3 (bit-count 11))
(assert= 4 (bit-count 30))))
(deftest
"integer-length zero"
(do (assert= 0 (integer-length 0))))
(deftest
"integer-length powers of two"
(do
(assert= 1 (integer-length 1))
(assert= 2 (integer-length 2))
(assert= 3 (integer-length 4))
(assert= 4 (integer-length 8))
(assert= 8 (integer-length 128))))
(deftest
"integer-length non-powers"
(do
(assert= 2 (integer-length 3))
(assert= 3 (integer-length 5))
(assert= 3 (integer-length 7))
(assert= 8 (integer-length 255))
(assert= 9 (integer-length 256))))
(deftest
"bitwise ops compose"
(do
(assert=
5
(bitwise-and
(bitwise-or 5 3)
(bitwise-xor 7 2)))
(assert= 0 (bitwise-and 170 85))))
(deftest
"arithmetic-shift round-trip"
(do
(assert=
10
(arithmetic-shift (arithmetic-shift 10 3) -3))))
(deftest
"extract bits with mask"
(do
(let
((x 52))
(assert=
5
(bitwise-and (arithmetic-shift x -2) 7)))))
(deftest
"clear low bits with bitwise-not mask"
(do
(assert= 252 (bitwise-and 255 (bitwise-not 3)))))
(deftest
"integer-length after shift"
(do
(assert=
4
(integer-length (arithmetic-shift 1 3))))))