From becafe4001b63baa32fbc4101084b6076118a611 Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Fri, 10 Sep 2021 19:30:41 +0200 Subject: [PATCH] Add Bitwise.aes to stdlib --- CHANGELOG.md | 1 + docs/sophia_stdlib.md | 87 ++++++++++++++++++++++++- priv/stdlib/Bitwise.aes | 136 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 priv/stdlib/Bitwise.aes diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d97cd9..37a237f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- `Bitwise` stdlib - `Set` stdlib - `Option.force_msg` - Loading namespaces into the current scope (e.g. `using Pair`) diff --git a/docs/sophia_stdlib.md b/docs/sophia_stdlib.md index 04421d5..7920a6c 100644 --- a/docs/sophia_stdlib.md +++ b/docs/sophia_stdlib.md @@ -37,6 +37,7 @@ include "List.aes" - [Func](#func) - [Pair](#pair) - [Triple](#triple) +- [Bitwise](#bitwise) - [BLS12_381](bls12_381) - [Frac](#frac) - [Set](#set-stdlib) @@ -1294,7 +1295,7 @@ Escapes `option` wrapping by providing default value for `None`. Option.force(o : option('a)) : 'a ``` -Forcefully escapes the `option` wrapping assuming it is `Some`. +Forcefully escapes the `option` wrapping assuming it is `Some`. Aborts on `None`. @@ -1817,6 +1818,90 @@ Triple.rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) Cyclic rotation of the elements to the left. +### Bitwise + +Bitwise operations on arbitary precision integers. + +#### bsr +``` +Bitwise.bsr(n : int, x : int) : int +``` + +Logical bit shift `x` right `n` positions. + + +#### bsl +``` +Bitwise.bsl(n : int, x : int) : int +``` + +Logical bit shift `x` left `n` positions. + + +#### bsli +``` +Bitwise.bsli(n : int, x : int, lim : int) : int +``` + +Logical bit shift `x` left `n` positions, limit to `lim` bits. + + +#### band +``` +Bitwise.band(x : int, y : int) : int +``` + +Bitwise `and` of `x` and `y`. + + +#### bor +``` +Bitwise.bor(x : int, y : int) : int +``` + +Bitwise `or` of `x` and `y`. + + +#### bxor +``` +Bitwise.bxor(x : int, y : int) : int +``` + +Bitwise `xor` of `x` and `y`. + + +#### bnot +``` +Bitwise.bnot(x : int) : int +``` + +Bitwise `not` of `x`. Defined and implemented as `bnot(x) = bxor(x, -1)`. + + +#### uband +``` +Bitwise.uband(x : int, y : int) : int +``` + +Bitwise `and` of _non-negative_ numbers `x` and `y`. + + +#### ubor +``` +Bitwise.ubor(x : int, y : int) : int +``` + +Bitwise `or` of _non-negative_ `x` and `y`. + + +#### ubxor +``` +Bitwise.ubxor(x : int, y : int) : int +``` + +Bitwise `xor` of _non-negative_ `x` and `y`. + + ### BLS12\_381 #### Types diff --git a/priv/stdlib/Bitwise.aes b/priv/stdlib/Bitwise.aes new file mode 100644 index 0000000..8538074 --- /dev/null +++ b/priv/stdlib/Bitwise.aes @@ -0,0 +1,136 @@ +@compiler >= 4.3 + +namespace Bitwise = + + // bit shift 'x' right 'n' postions + function bsr(n : int, x : int) : int = + let step = 2^n + let res = x / step + if (x >= 0 || x mod step == 0) + res + else + res - 1 + + // bit shift 'x' left 'n' positions + function bsl(n : int, x : int) : int = + x * 2^n + + // bit shift 'x' left 'n' positions, limit at 'lim' bits + function bsli(n : int, x : int, lim : int) : int = + (x * 2^n) mod (2^lim) + + // bitwise 'and' for arbitrary precision integers + function band(a : int, b : int) : int = + if (a >= 0 && b >= 0) + uband_(a, b) + elif (b >= 0) + ubnand_(b, -1 - a) + elif (a >= 0) + ubnand_(a, -1 - b) + else + -1 - ubor_(-1 - a, -1 - b) + + // bitwise 'or' for arbitrary precision integers + function + bor : (int, int) => int + bor(0, b) = b + bor(a, 0) = a + bor(a : int, b : int) : int = + if (a >= 0 && b >= 0) + ubor_(a, b) + elif (b >= 0) + -1 - ubnand_(-1 - a, b) + elif (a >= 0) + -1 - ubnand_(-1 - b, a) + else + -1 - uband_(-1 - a, -1 - b) + + // bitwise 'xor' for arbitrary precision integers + function + bxor : (int, int) => int + bxor(0, b) = b + bxor(a, 0) = a + bxor(a, b) = + if (a >= 0 && b >= 0) + ubxor_(a, b) + elif (b >= 0) + -1 - ubxor_(-1 - a, b) + elif (a >= 0) + -1 - ubxor_(a, -1 - b) + else + ubxor_(-1 - a, -1 - b) + + // bitwise 'not' for arbitrary precision integers + function bnot(a : int) = bxor(a, -1) + + // Bitwise 'and' for positive integers + function uband(a : int, b : int) : int = + require(a >= 0 && b >= 0, "uband is only defined for non-negative integers") + switch((a, b)) + (0, _) => 0 + (_, 0) => 0 + _ => uband__(a, b, 1, 0) + + private function uband_(a, b) = uband__(a, b, 1, 0) + + private function + uband__(0, b, val, acc) = acc + uband__(a, 0, val, acc) = acc + uband__(a, b, val, acc) = + switch (a mod 2 + b mod 2) + 2 => uband__(a / 2, b / 2, val * 2, acc + val) + _ => uband__(a / 2, b / 2, val * 2, acc) + + // Bitwise or for positive integers + function ubor(a, b) = + require(a >= 0 && b >= 0, "ubor is only defined for non-negative integers") + switch((a, b)) + (0, _) => b + (_, 0) => a + _ => ubor__(a, b, 1, 0) + + private function ubor_(a, b) = ubor__(a, b, 1, 0) + + private function + ubor__(0, 0, val, acc) = acc + ubor__(a, b, val, acc) = + switch (a mod 2 + b mod 2) + 0 => ubor__(a / 2, b / 2, val * 2, acc) + _ => ubor__(a / 2, b / 2, val * 2, acc + val) + + //Bitwise xor for positive integers + function + ubxor : (int, int) => int + ubxor(0, b) = b + ubxor(a, 0) = a + ubxor(a, b) = + require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers") + ubxor__(a, b, 1, 0) + + private function ubxor_(a, b) = ubxor__(a, b, 1, 0) + + private function + ubxor__(0, 0, val, acc) = acc + ubxor__(a, b, val, acc) = + switch(a mod 2 + b mod 2) + 1 => ubxor__(a / 2, b / 2, val * 2, acc + val) + _ => ubxor__(a / 2, b / 2, val * 2, acc) + + // Bitwise combined 'and' and 'not' of second argument for positive integers + // x 'bnand' y = x 'band' ('bnot' y) + // The tricky bit is that after negation the second argument has an infinite number of 1's + // use as many as needed! + // + // NOTE: this function is not symmetric! + private function ubnand(a, b) = + require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers") + ubnand__(a, b, 1, 0) + + private function ubnand_(a, b) = ubnand__(a, b, 1, 0) + + private function + ubnand__(0, b, val, acc) = acc + ubnand__(a, b, val, acc) = + switch((a mod 2, b mod 2)) + (1, 0) => ubnand__(a / 2, b / 2, val * 2, acc + val) + _ => ubnand__(a / 2, b / 2, val * 2, acc)