diff options
author | Louis Pilfold <louis@lpil.uk> | 2023-09-04 13:01:40 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2023-09-04 13:38:50 +0100 |
commit | 4aca74566e861cef0f902602501bf274ba5c43f8 (patch) | |
tree | 921a3b7ffcc5cfa33b4ee52bbe726d8a0ca598e3 | |
parent | 979f0d5d5bc9b149978ab88a6df64019b302d0e3 (diff) | |
download | gleam_stdlib-4aca74566e861cef0f902602501bf274ba5c43f8.tar.gz gleam_stdlib-4aca74566e861cef0f902602501bf274ba5c43f8.zip |
Bitwise int functions
-rw-r--r-- | src/gleam/int.gleam | 54 | ||||
-rw-r--r-- | src/gleam_stdlib.mjs | 29 | ||||
-rw-r--r-- | test/gleam/int_test.gleam | 66 |
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) +} |