# 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)) # 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)) # 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⁰) # 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 (and! (+1b) (+0b)) ((+0b)) :test (and! (+5b) (+4b)) ((+4b)) :test (and! (+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 (or! (+10b) (+12b)) ((+14b)) # :test (or! (+1b) (+0b)) ((+1b)) # :test (or! (+5b) (+3b)) ((+7b)) # 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) # returns true if two binary numbers are not equal not-eq? not! ∘∘ eq? ⧗ Binary → Binary → Boolean …≠?… not-eq? :test ((+0b) ≠? (+0b)) (false) :test ([[[1 (0 2)]]] ≠? [[[1 (0 2)]]]) (false) :test ([[[1 (0 2)]]] ≠? (+2b)) (true) # prefix for comparing functions ?‣ &eq? # 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)) # 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)) # 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) # 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) # 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) # 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) # returns true if the number is odd (remainder mod 2 == 1) odd? lsb ⧗ Binary → Boolean ≠²?‣ odd? :test (odd? (+0b)) (false) :test (odd? (+1b)) (true) :test (odd? (+41b)) (true) :test (odd? (+42b)) (false) # 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 odd? 0 (2 (1 ∘ T.inc) (dec 0)) (2 (1 ∘ (T.mul (+2t))) (div² 0)) case-end 1 # returns true if number is greater than other number # TODO: remove ternary conversion gre? T.gre? ⋔ binary→ternary ⧗ Binary → Binary → Boolean …>?… gre? :test ((+1b) >? (+2b)) (false) :test ((+2b) >? (+2b)) (false) :test ((+3b) >? (+2b)) (true) # returns true if number is less than other number les? \gre? ⧗ Binary → Binary → Boolean …b, -1 if a… compare <=>‣ &compare :test (compare (+2b) (+2b)) ((+0)) :test (compare (+2b) (+1b)) ((+1)) :test (compare (+1b) (+2b)) ((-1)) # prefix for comparing functions # returns max number of two max [[(1 ≤? 0) 0 1]] ⧗ Binary → Binary → Binary :test (max (+5b) (+2b)) ((+5b)) # returns min number of two min [[(1 ≤? 0) 1 0]] ⧗ Binary → Binary → Binary :test (min (+5b) (+2b)) ((+2b))