aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2023-09-04 13:01:40 +0100
committerLouis Pilfold <louis@lpil.uk>2023-09-04 13:38:50 +0100
commit4aca74566e861cef0f902602501bf274ba5c43f8 (patch)
tree921a3b7ffcc5cfa33b4ee52bbe726d8a0ca598e3
parent979f0d5d5bc9b149978ab88a6df64019b302d0e3 (diff)
downloadgleam_stdlib-4aca74566e861cef0f902602501bf274ba5c43f8.tar.gz
gleam_stdlib-4aca74566e861cef0f902602501bf274ba5c43f8.zip
Bitwise int functions
-rw-r--r--src/gleam/int.gleam54
-rw-r--r--src/gleam_stdlib.mjs29
-rw-r--r--test/gleam/int_test.gleam66
3 files changed, 149 insertions, 0 deletions
diff --git a/src/gleam/int.gleam b/src/gleam/int.gleam
index c0e650e..2324bb0 100644
--- a/src/gleam/int.gleam
+++ b/src/gleam/int.gleam
@@ -800,3 +800,57 @@ pub fn multiply(a: Int, b: Int) -> Int {
pub fn subtract(a: Int, b: Int) -> Int {
a - b
}
+
+/// Calculates the bitwise AND of its arguments.
+pub fn bitwise_and(x: Int, y: Int) -> Int {
+ do_and(x, y)
+}
+
+@external(erlang, "erlang", "band")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_and")
+fn do_and(a: Int, b: Int) -> Int
+
+/// Calculates the bitwise NOT of its argument.
+pub fn bitwise_not(x: Int) -> Int {
+ do_not(x)
+}
+
+@external(erlang, "erlang", "bnot")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_not")
+fn do_not(a: Int) -> Int
+
+/// Calculates the bitwise OR of its arguments.
+pub fn bitwise_or(x: Int, y: Int) -> Int {
+ do_or(x, y)
+}
+
+@external(erlang, "erlang", "bor")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_or")
+fn do_or(a: Int, b: Int) -> Int
+
+/// Calculates the bitwise XOR of its arguments.
+pub fn bitwise_exclusive_or(x: Int, y: Int) -> Int {
+ do_exclusive_or(x, y)
+}
+
+@external(erlang, "erlang", "bxor")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_exclusive_or")
+fn do_exclusive_or(a: Int, b: Int) -> Int
+
+/// Calculates the result of an arithmetic left bitshift.
+pub fn bitwise_shift_left(x: Int, y: Int) -> Int {
+ do_shift_left(x, y)
+}
+
+@external(erlang, "erlang", "bsl")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_shift_left")
+fn do_shift_left(a: Int, b: Int) -> Int
+
+/// Calculates the result of an arithmetic right bitshift.
+pub fn bitwise_shift_right(x: Int, y: Int) -> Int {
+ do_shift_right(x, y)
+}
+
+@external(erlang, "erlang", "bsr")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_shift_right")
+fn do_shift_right(a: Int, b: Int) -> Int
diff --git a/src/gleam_stdlib.mjs b/src/gleam_stdlib.mjs
index a2fbaa6..fb255d9 100644
--- a/src/gleam_stdlib.mjs
+++ b/src/gleam_stdlib.mjs
@@ -752,3 +752,32 @@ function try_get_field(value, field, or_else) {
export function byte_size(string) {
return new TextEncoder().encode(string).length;
}
+
+// In Javascript bitwise operations convert numbers to a sequence of 32 bits
+// while Erlang uses arbitrary precision.
+// To get around this problem and get consistent results use BigInt and then
+// downcast the value back to a Number value.
+
+export function bitwise_and(x, y) {
+ return Number(BigInt(x) & BigInt(y));
+}
+
+export function bitwise_not(x) {
+ return Number(~BigInt(x));
+}
+
+export function bitwise_or(x, y) {
+ return Number(BigInt(x) | BigInt(y));
+}
+
+export function bitwise_exclusive_or(x, y) {
+ return Number(BigInt(x) ^ BigInt(y));
+}
+
+export function bitwise_shift_left(x, y) {
+ return Number(BigInt(x) << BigInt(y));
+}
+
+export function bitwise_shift_right(x, y) {
+ return Number(BigInt(x) >> BigInt(y));
+}
diff --git a/test/gleam/int_test.gleam b/test/gleam/int_test.gleam
index fd1cb89..b9aac9d 100644
--- a/test/gleam/int_test.gleam
+++ b/test/gleam/int_test.gleam
@@ -556,3 +556,69 @@ pub fn subtract_test() {
|> int.subtract(2, _)
|> should.equal(-1)
}
+
+pub fn and_test() {
+ int.bitwise_and(9, 3)
+ |> should.equal(1)
+
+ // To check compatibility with JavaScript, try a 32 bit unsigned integer
+ // (signed integers are in the range -2147483648 to +2147483647, while
+ // 32 bit unsigned integers are in the range 0 to +4294967295).
+ int.bitwise_and(2_147_483_648, 2_147_483_648)
+ |> should.equal(2_147_483_648)
+}
+
+pub fn not_test() {
+ int.bitwise_not(2)
+ |> should.equal(-3)
+
+ // To check compatibility with JavaScript, try a 32 bit unsigned integer.
+ int.bitwise_not(2_147_483_648)
+ |> should.equal(-2_147_483_649)
+}
+
+pub fn or_test() {
+ int.bitwise_or(9, 3)
+ |> should.equal(11)
+
+ // To check compatibility with JavaScript, try a 32 bit unsigned integer.
+ int.bitwise_or(1, 2_147_483_648)
+ |> should.equal(2_147_483_649)
+}
+
+pub fn exclusive_or_test() {
+ int.bitwise_exclusive_or(9, 3)
+ |> should.equal(10)
+
+ // To check compatibility with JavaScript, try a 32 bit unsigned integer.
+ int.bitwise_exclusive_or(0, 2_147_483_648)
+ |> should.equal(2_147_483_648)
+}
+
+pub fn shift_left_test() {
+ int.bitwise_shift_left(1, 2)
+ |> should.equal(4)
+
+ int.bitwise_shift_left(1, -2)
+ |> should.equal(0)
+
+ int.bitwise_shift_left(-1, 2)
+ |> should.equal(-4)
+
+ int.bitwise_shift_left(-1, -2)
+ |> should.equal(-1)
+}
+
+pub fn shift_right_test() {
+ int.bitwise_shift_right(1, 2)
+ |> should.equal(0)
+
+ int.bitwise_shift_right(1, -2)
+ |> should.equal(4)
+
+ int.bitwise_shift_right(-1, 2)
+ |> should.equal(-1)
+
+ int.bitwise_shift_right(-1, -2)
+ |> should.equal(-4)
+}