-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add capability bit for bit manipulation, tests
A bunch of basics and a pretty shitty implementation. I don't have any motivation to write a better one because all this bit stuff is soo boooring. * Some decimal fixes/etc. Remove the weird `%` definition. Can't remember why I did that there.
- Loading branch information
1 parent
66c7850
commit e763581
Showing
4 changed files
with
322 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
module Novika::Capabilities::Impl | ||
class Bit | ||
include Capability | ||
|
||
def self.id : String | ||
"bit" | ||
end | ||
|
||
def self.purpose : String | ||
"allows to interpret decimals as sequences of bits and manipulate those bits" | ||
end | ||
|
||
def self.on_by_default? : Bool | ||
true | ||
end | ||
|
||
def inject(into target : Block) | ||
target.at("bit:fromLeft", <<-END | ||
( D I -- B ): leaves Index-th Bit from left in the given Decimal, | ||
represented as `0` or `1`. Dies if Decimal has a fractional part. | ||
The sign of decimal is ignored. | ||
Note: we consider the *left*most bit to be the most significant bit, | ||
and the *right*most bit the least significant bit. Leading zeroes | ||
do not count. | ||
``` | ||
0b00010001 0 bit:fromLeft leaves: 1 | ||
0b00010001 1 bit:fromLeft leaves: 0 | ||
0b00010001 2 bit:fromLeft leaves: 0 | ||
0b00010001 3 bit:fromLeft leaves: 0 | ||
0b00010001 4 bit:fromLeft leaves: 1 | ||
``` | ||
END | ||
) do |_, stack| | ||
index = stack.drop.a(Decimal).posint | ||
decimal = stack.drop.a(Decimal).int | ||
unless bit = decimal.nth_ms_bit?(index) | ||
index.die("bit index out of bounds") | ||
end | ||
bit.onto(stack) | ||
end | ||
|
||
target.at("bit:fromRight", <<-END | ||
( D I -- B ): leaves Index-th Bit from right in the given Decimal, | ||
represented as `0` or `1`. Dies if Decimal has a fractional part. | ||
The sign of decimal is ignored. | ||
Note: we consider the *left*most bit to be the most significant bit, | ||
and the *right*most bit the least significant bit. Leading zeroes | ||
do not count. | ||
``` | ||
0b00010001 0 bit:fromRight leaves: 1 | ||
0b00010001 1 bit:fromRight leaves: 0 | ||
0b00010001 2 bit:fromRight leaves: 0 | ||
0b00010001 3 bit:fromRight leaves: 0 | ||
0b00010001 4 bit:fromRight leaves: 1 | ||
``` | ||
END | ||
) do |_, stack| | ||
index = stack.drop.a(Decimal).posint | ||
decimal = stack.drop.a(Decimal).int | ||
decimal.nth_ls_bit(index).onto(stack) | ||
end | ||
|
||
target.at("bit:count", <<-END | ||
( D -- Bc ): leaves Bit count, the number of bits in the given | ||
Decimal. Dies if Decimal has a fractional part. | ||
``` | ||
0b00010001 bit:count leaves: 4 | ||
``` | ||
END | ||
) do |_, stack| | ||
decimal = stack.drop.a(Decimal).int | ||
decimal.bitcount.onto(stack) | ||
end | ||
|
||
target.at("bit:or", <<-END | ||
( D D -- D ): combines two Decimal numbers using bitwise or, leaves | ||
the resulting Decimal. Dies if either of decimal has a fractional part. | ||
``` | ||
0b00010001 | ||
0b10001000 bit:or leaves: | ||
0b10011001 | ||
``` | ||
END | ||
) do |_, stack| | ||
b = stack.drop.a(Decimal).int | ||
a = stack.drop.a(Decimal).int | ||
(a | b).onto(stack) | ||
end | ||
|
||
target.at("bit:and", <<-END | ||
( D D -- D ): combines two Decimal numbers using bitwise and, leaves | ||
the resulting Decimal. Dies if either of decimal has a fractional part. | ||
``` | ||
0b10011001 | ||
0b00011000 bit:and leaves: | ||
0b00011000 | ||
``` | ||
END | ||
) do |_, stack| | ||
b = stack.drop.a(Decimal).int | ||
a = stack.drop.a(Decimal).int | ||
(a & b).onto(stack) | ||
end | ||
|
||
target.at("bit:bits", <<-END | ||
( D -- Bb ): leaves Bits block for the given Decimal, which contains | ||
the binary representation of the *absolute value* of Decimal, starting | ||
with the most-significant bit. | ||
``` | ||
0b10011001 bit:bits leaves: [ 1 0 0 1 1 0 0 1 ] | ||
``` | ||
END | ||
) do |_, stack| | ||
decimal = stack.drop.a(Decimal).int | ||
bits = Block.new | ||
decimal.each_bit &.onto(bits) | ||
bits.onto(stack) | ||
end | ||
|
||
target.at("bit:fromBits", <<-END | ||
( Bb -- D ): converts Bits block to a Decimal. Bits block should | ||
contain binary digits (represented by `0` or `1`), and should | ||
begin with the most significant bit. | ||
``` | ||
0b10011001 bit:bits leaves: [[ 1 0 0 1 1 0 0 1 ]] | ||
bit:fromBits leaves: 0b10011001 | ||
``` | ||
END | ||
) do |_, stack| | ||
bits = stack.drop.a(Block) | ||
acc = Decimal.new(0) | ||
pow = Decimal.new(bits.count - 1) | ||
one = Decimal.new(1) | ||
two = Decimal.new(2) | ||
bits.each do |bit| | ||
acc += bit.a(Decimal).int.in(0..1) * two ** pow | ||
pow -= one | ||
end | ||
acc.onto(stack) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
describe 'bit:fromLeft' [ | ||
in lang | ||
|
||
it should 'leave the indexth most significant bit, ignores sign' [ | ||
0b10001 0 bit:fromLeft 1 assert= | ||
0b10001 1 bit:fromLeft 0 assert= | ||
0b10001 2 bit:fromLeft 0 assert= | ||
0b10001 3 bit:fromLeft 0 assert= | ||
0b10001 4 bit:fromLeft 1 assert= | ||
|
||
0b10001 flipSign 0 bit:fromLeft 1 assert= | ||
0b10001 flipSign 1 bit:fromLeft 0 assert= | ||
0b10001 flipSign 2 bit:fromLeft 0 assert= | ||
0b10001 flipSign 3 bit:fromLeft 0 assert= | ||
0b10001 flipSign 4 bit:fromLeft 1 assert= | ||
] | ||
|
||
it should 'die if decimal has a fractional part' [ | ||
[ 12.34 0 bit:fromLeft ] 'decimal is not an integer' assertDies | ||
] | ||
|
||
it should 'die if index is invalid' [ | ||
[ 0b00010001 1000 bit:fromLeft ] 'bit index out of bounds' assertDies | ||
[ 0b00010001 -1000 bit:fromLeft ] 'decimal is not a positive integer' assertDies | ||
[ 0b00010001 1000.123 bit:fromLeft ] 'decimal is not a positive integer' assertDies | ||
] | ||
] | ||
|
||
|
||
describe 'bit:fromRight' [ | ||
in lang | ||
|
||
it should 'leave the indexth least significant bit, ignores sign' [ | ||
0b00010001 0 bit:fromRight 1 assert= | ||
0b00010001 1 bit:fromRight 0 assert= | ||
0b00010001 2 bit:fromRight 0 assert= | ||
0b00010001 3 bit:fromRight 0 assert= | ||
0b00010001 4 bit:fromRight 1 assert= | ||
0b00010001 5 bit:fromRight 0 assert= | ||
0b00010001 1000 bit:fromRight 0 assert= | ||
|
||
0b10001 flipSign 0 bit:fromRight 1 assert= | ||
0b10001 flipSign 1 bit:fromRight 0 assert= | ||
0b10001 flipSign 2 bit:fromRight 0 assert= | ||
0b10001 flipSign 3 bit:fromRight 0 assert= | ||
0b10001 flipSign 4 bit:fromRight 1 assert= | ||
0b10001 flipSign 1000 bit:fromRight 0 assert= | ||
] | ||
|
||
it should 'die if decimal has a fractional part' [ | ||
[ 12.34 0 bit:fromRight ] 'decimal is not an integer' assertDies | ||
] | ||
|
||
it should 'die if index is invalid' [ | ||
[ 0b00010001 -1000 bit:fromRight ] 'decimal is not a positive integer' assertDies | ||
[ 0b00010001 1000.123 bit:fromRight ] 'decimal is not a positive integer' assertDies | ||
] | ||
] | ||
|
||
describe 'bit:count' [ | ||
in lang | ||
|
||
it should 'leave the number of bits in decimal' [ | ||
0b00010001 bit:count 5 assert= | ||
0xffffffff bit:count 32 assert= | ||
] | ||
|
||
it should 'die if decimal has a fractional part' [ | ||
[ 12.34 bit:count ] 'decimal is not an integer' assertDies | ||
] | ||
] | ||
|
||
|
||
describe 'bit:or' [ | ||
in lang | ||
|
||
it should 'combine two decimals using bitwise or' [ | ||
0b00010001 0b10001000 bit:or 0b10011001 assert= | ||
] | ||
|
||
it should 'die if any of the decimals has a fractional part' [ | ||
[ 12.34 100 bit:or ] 'decimal is not an integer' assertDies | ||
[ 100 12.34 bit:or ] 'decimal is not an integer' assertDies | ||
] | ||
] | ||
|
||
describe 'bit:and' [ | ||
in lang | ||
|
||
it should 'combine two decimals using bitwise and' [ | ||
0b10011001 0b00011000 bit:and 0b00011000 assert= | ||
] | ||
|
||
it should 'die if any of the decimals has a fractional part' [ | ||
[ 12.34 100 bit:and ] 'decimal is not an integer' assertDies | ||
[ 100 12.34 bit:and ] 'decimal is not an integer' assertDies | ||
] | ||
] | ||
|
||
|
||
describe 'bit:bits/bit:fromBits' [ | ||
in lang | ||
|
||
it should 'leave bit block of bits in a decimal' [ | ||
0b00010001 bit:bits $: bits | ||
bits orphan? true assert= | ||
bits [ 1 0 0 0 1 ] assert= | ||
] | ||
|
||
it should 'leave decimal from a block of bits' [ | ||
[ 1 0 0 0 1 ] $: bits | ||
bits bit:fromBits 0b00010001 assert= | ||
] | ||
] |