Number/Binary.bruijn
# MIT License, Copyright (c) 2023 Marvin Borner
# binary implementation of T.Æ. Mogensen (see refs in README)
# TODO: Use abstract representation for logic instead of listifying
:import std/Combinator .
:import std/List .
:import std/Logic .
# TODO: remove ternary conversion shortcuts
# TODO: then move functions to reflect order of std/Ternary
:import std/Number/Ternary T
# bit indicating a one, compatible with std/Logic
b¹ true ⧗ Bit
# bit indicating a zero, compatible with std/Logic
b⁰ false ⧗ Bit
# returns true if a bit is set
b¹? i ⧗ Bit → Boolean
:test (b¹? b¹) (true)
:test (b¹? b⁰) (false)
# returns true if a bit is unset
b⁰? not! ⧗ Bit → Boolean
:test (b⁰? b⁰) (true)
:test (b⁰? b¹) (false)
# shifts a one into a binary number
↑¹‣ [[[[1 (3 2 1 0)]]]] ⧗ Binary → Binary
:test (↑¹[[[0 2]]]) ([[[1 (0 2)]]])
# shifts a zero into a binary number
↑⁰‣ [[[[0 (3 2 1 0)]]]] ⧗ Binary → Binary
:test (↑⁰(+1b)) ((+2b))
# shifts a specified bit into a binary number
up [[[[[4 1 0 (3 2 1 0)]]]]] ⧗ Bit → Binary → Binary
:test (up b¹ [[[0 2]]]) ([[[1 (0 2)]]])
:test (up b⁰ (+1b)) ((+2b))
# returns true if a binary number is zero
zero? [0 true [false] i] ⧗ Binary → Boolean
=?‣ zero?
:test (=?(+0b)) (true)
:test (=?[[[0 (0 2)]]]) (true)
:test (=?(+1b)) (false)
# extracts least significant bit from a binary number
lsb [0 b⁰ [b¹] [b⁰]] ⧗ Binary → Bit
:test (lsb (+0b)) (b⁰)
:test (lsb (+1b)) (b¹)
:test (lsb (+42b)) (b⁰)
# returns true if the number is even (remainder mod 2 == 0)
even? ¬‣ ∘ lsb ⧗ Binary → Boolean
=²?‣ even?
:test (even? (+0b)) (true)
:test (even? (+1b)) (false)
:test (even? (+41b)) (false)
:test (even? (+42b)) (true)
# converts the normal binary representation into abstract
abstract! [0 z a¹ a⁰] ⧗ Binary → AbstractBinary
z (+0b)
a¹ [[[[1 3]]]]
a⁰ [[[[0 3]]]]
→^‣ abstract!
:test (→^(+2b)) ([[[0 [[[1 [[[2]]]]]]]]])
# converts the abstracted binary representation back to normal
normal! ω [[0 z a¹ a⁰]] ⧗ AbstractBinary → Binary
z (+0b)
a¹ [↑¹([3 3 0] 0)]
a⁰ [↑⁰([3 3 0] 0)]
→_‣ normal!
:test (→_[[[0 [[[1 [[[2]]]]]]]]]) ((+2b))
# returns true if two binary numbers are equal
# → ignores leading 0s!
# also: ⋀?‣ ∘∘ (ψ* zip-with xnor? list!)
eq? [[abs 1 →^0]] ⧗ Binary → Binary → Boolean
abs [0 z a¹ a⁰]
z [=?(→_0)]
a¹ [[0 false [2 0] [false]]]
a⁰ [[0 (1 0) [false] [2 0]]]
…=?… eq?
:test ((+0b) =? (+0b)) (true)
:test ([[[1 (0 2)]]] =? [[[1 (0 2)]]]) (true)
:test ([[[1 2]]] =? (+2b)) (false)
# rshifts least significant bit of a binary number
div² [~(0 z a¹ a⁰)] ⧗ Binary → Binary
z (+0b) : (+0b)
a¹ &[[↑¹1 : 1]]
a⁰ &[[↑⁰1 : 1]]
/²‣ div²
:test (/²(+6b) =? (+3b)) (true)
:test (/²(+5b) =? (+2b)) (true)
# subs 1 from a binary number (can introduce leading 0s)
dec [~(0 z a¹ a⁰)] ⧗ Binary → Binary
z (+0b) : (+0b)
a¹ &[[↑¹1 : ↑⁰1]]
a⁰ &[[↑⁰1 : ↑¹0]]
--‣ dec
:test (--(+0b)) ((+0b))
:test (--(+1b)) ([[[0 2]]])
:test (--(+3b)) ((+2b))
# TODO: Remove (duplicate of std/Conversion because of dep loop)
binary→ternary [y [[[rec]]] [0] 0 (+0t)] ⧗ Binary → Ternary
rec zero? 0 case-end case-rec
case-rec even? 0 (2 (1 ∘ (T.mul (+2t))) (div² 0)) (2 (1 ∘ T.inc) (dec 0))
case-end 1
# returns eq, gt, lt depending on comparison of two numbers
# TODO: remove ternary conversion
compare-case [[[(T.compare-case 2 1 0) ⋔ binary→ternary]]] ⧗ a → b → c → Binary → Binary → d
<?>‣ &compare-case
# returns true if number is greater than other number
# TODO: remove ternary conversion
gt? T.gt? ⋔ binary→ternary ⧗ Binary → Binary → Boolean
…>?… gt?
:test ((+1b) >? (+2b)) (false)
:test ((+2b) >? (+2b)) (false)
:test ((+3b) >? (+2b)) (true)
# ============================================================================ #
# most relevant functions are defined - we can now derive from Generic/Number! #
# ============================================================================ #
:input std/Generic/Number
# converts a binary number to a list of bits
list! [0 z a¹ a⁰] ⧗ Binary → (List Bit)
z empty
a¹ [b¹ : 0]
a⁰ [b⁰ : 0]
:test (list! (+0b)) (empty)
:test (list! (+6b)) (b⁰ : (b¹ : {}b¹))
# converts a list of bits to a binary number
binary! foldr up (+0b) ⧗ (List Bit) → Binary
:test (binary! (list! (+0b))) ((+0b))
:test (binary! (list! (+42b))) ((+42b))
# strips leading 0s from a binary number
strip [^(0 z a¹ a⁰)] ⧗ Binary → Binary
z (+0b) : true
a¹ &[[↑¹1 : false]]
a⁰ &[[(0 (+0b) ↑⁰1) : 0]]
%‣ strip
:test (%[[[0 2]]]) ((+0b))
:test (%[[[1 (0 (0 (0 (0 2))))]]]) ((+1b))
:test (%(+2b)) ((+2b))
# extracts most significant bit from a binary number
# not really useful for binary numbers, but part of interface
msb [=?0 b⁰ b¹] ⧗ Binary → Bit
:test (msb (+0b)) (b⁰)
:test (msb (+1b)) (b¹)
:test (msb (+42b)) (b¹)
# extracts nth bit from a binary number
nth …!!… ∘ list! ⧗ Binary → Number → Bit
# logical and on two binary numbers
and! binary! ∘∘ (ψ* zip-with …⋀?… list!) ⧗ Binary → Binary → Binary
…⋀!… and!
:test ((+1b) ⋀! (+0b)) ((+0b))
:test ((+5b) ⋀! (+4b)) ((+4b))
:test ((+10b) ⋀! (+12b)) ((+8b))
# logical or on two binary numbers
# TODO: Fix for numbers with different length (→ zero padding?)
or! binary! ∘∘ (ψ* zip-with …⋁?… list!) ⧗ Binary → Binary → Binary
…⋁!… or!
:test ((+10b) ⋁! (+12b)) ((+14b))
# :test (or! (+1b) (+0b)) ((+1b))
# :test (or! (+5b) (+3b)) ((+7b))
# logical or on two binary numbers
# TODO: Fix for numbers with different length (→ zero padding?)
xor! binary! ∘∘ (ψ* zip-with …^?… list!) ⧗ Binary → Binary → Binary
…^!… xor!
:test (((+10b) ^! (+12b)) =? (+6b)) (true)
# adds 1 to a binary number (can introduce leading 0s)
inc [~(0 z a¹ a⁰)] ⧗ Binary → Binary
z (+0b) : (+1b)
a¹ &[[↑¹1 : ↑⁰0]]
a⁰ &[[↑⁰1 : ↑¹1]]
++‣ inc
:test (++(+0b)) ((+1b))
:test (++(+2b)) ((+3b))
# flips the bits of a binary number (1's complement)
complement [[[[3 2 0 1]]]] ⧗ Binary → Binary
-*‣ complement
:test (-*(+0b) =? (+0b)) (true)
:test (-*(+1b) =? (+0b)) (true)
:test (-*(+42b)) ([[[1 (0 (1 (0 (1 (0 2)))))]]])
# inverts a binary number by complementing and incrementing (2's complement)
# don't forget to pad the number with 0s if needed
invert ++‣ ∘ -*‣ ⧗ Binary → Binary
-‣ invert
:test (-(+0b)) ((+1b))
:test (-(+1b)) ((+1b))
# pads a binary number with 0s until it's as long a another binary number
# TODO: this could be done without list magic (see Ternary)
pad [[binary! (pad-right ∀(list! %1) b⁰ (list! %0))]] ⧗ Binary → Binary → Binary
:test (pad (+5b) [[[1 2]]]) ([[[1 (0 (0 2))]]])
# adds two binary numbers (can introduce leading 0s)
# second argument gets abstracted (performance)
add [[abs 1 →^0]] ⧗ Binary → Binary → Binary
abs [c (0 z a¹ a⁰)]
c¹ [1 ↑⁰(3 0 b¹) ↑¹(3 0 b⁰)]
a¹ [[[1 (c¹ 1) c¹' c¹]]]
c¹' [up 1 (3 0 b¹)]
a⁰ [[[1 (c⁰ 1) c¹ c⁰]]]
c⁰ [up 1 (3 0 b⁰)]
z [[0 ++(→_1) →_1]]
c [[1 0 b⁰]]
…+… add
:test ((+0b) + (+0b) =? (+0b)) (true)
:test ((+0b) + (+3b) =? (+3b)) (true)
:test ((+1b) + (+2b) =? (+3b)) (true)
:test ((+42b) + (+1b) =? (+43b)) (true)
:test ((+1b) + (+42b) =? (+43b)) (true)
# subs two binary numbers (can introduce leading 0s)
# second argument gets abstracted (performance)
# TODO: fix sub*; implementation is completely wrong
sub* [[abs 1 →^0]] ⧗ Binary → Binary → Binary
abs [c (0 z a¹ a⁰)]
c¹ [1 ↑¹(3 0 b⁰) ↑¹(3 0 b⁰)]
a¹ [[[1 (c¹ 1) c¹' c¹]]]
c¹ [1 ↑⁰(3 0 b¹) ↑¹(3 0 b⁰)]
c¹' [1 ↑¹(3 0 b⁰) ↑¹(3 0 b⁰)]
a⁰ [[[1 (c⁰ 1) c⁰' c⁰]]]
c⁰ [1 ↑¹(3 0 b⁰) ↑⁰(3 0 b⁰)]
c⁰' [1 (3 0 b⁰) ↑¹(3 0 b¹)]
z [[0 --(→_1) →_1]]
c [[1 0 b⁰]]
# subs two binary numbers
# uses addition but with two's complement
# TODO: isn't very performant ⇒ replace with sub*
# TODO: gives fun results if b>a in a-b
sub [[(0 =? 1) (+0b) -((pad 1 0) + -(pad 0 1))]] ⧗ Binary → Binary → Binary
…-… sub
:test ((+42b) - (+12b) =? (+30b)) (true)
:test ((+3b) - (+0b) =? (+3b)) (true)
:test ((+3b) - (+2b) =? (+1b)) (true)
# muls two binary numbers (can introduce leading 0s)
mul [[1 z a¹ a⁰]] ⧗ Number → Number → Number
z (+0b)
a¹ [↑⁰0 + 1]
a⁰ [↑⁰0]
…⋅… mul
:test ((+42b) ⋅ (+0b) =? (+0b)) (true)
:test ((+3b) ⋅ (+11b) =? (+33b)) (true)
# ceiled integer log2 by counting bits
# also counts leading 0s
log2* [0 (+0b) inc inc] ⧗ Binary → Binary
# ceiled integer log2 by counting bits
log2 log2* ∘ strip ⧗ Binary → Binary
:test ((log2 (+1b)) =? (+1b)) (true)
:test ((log2 (+2b)) =? (+2b)) (true)
:test ((log2 (+3b)) =? (+2b)) (true)
:test ((log2 (+4b)) =? (+3b)) (true)
:test ((log2 (+32b)) =? (+6b)) (true)
:test ((log2 (+48b)) =? (+6b)) (true)