diff options
| -rw-r--r-- | CHANGES.md | 1 | ||||
| -rw-r--r-- | api/api.md | 60 | ||||
| -rw-r--r-- | api/bytes.md | 30 | ||||
| -rw-r--r-- | api/bytes.yaml | 30 | ||||
| -rw-r--r-- | api/integers.md | 30 | ||||
| -rw-r--r-- | api/integers.yaml | 36 | ||||
| -rw-r--r-- | man/man3/tomo-Byte.get_bit.3 | 44 | ||||
| -rw-r--r-- | man/man3/tomo-Int.get_bit.3 | 44 | ||||
| -rw-r--r-- | src/environment.c | 10 | ||||
| -rw-r--r-- | src/stdlib/bytes.c | 8 | ||||
| -rw-r--r-- | src/stdlib/bytes.h | 1 | ||||
| -rw-r--r-- | src/stdlib/integers.c | 20 | ||||
| -rw-r--r-- | src/stdlib/integers.h | 2 | ||||
| -rw-r--r-- | test/bytes.tm | 9 | ||||
| -rw-r--r-- | test/integers.tm | 18 |
15 files changed, 340 insertions, 3 deletions
@@ -13,6 +13,7 @@ - Added Path.sibling() - Added Path.has_extension() - Added Table.with_fallback() +- Added Int*.get_bit() and Byte.get_bit() - Fixed bugs: - Negative integers weren't converting to text properly. - Mutation of a collection during iteration was violating value semantics. @@ -210,6 +210,36 @@ text | `Text` | The string containing the boolean value. | - ``` # Byte +## Byte.get_bit + +```tomo +Byte.get_bit : func(i: Byte, bit_index: Int -> Bool) +``` + +In the binary representation of a byte, check whether a given bit index is set to 1 or not. + +The bit index must be between 1-8 or a runtime error will be produced. + +Argument | Type | Description | Default +---------|------|-------------|--------- +i | `Byte` | The byte whose bits are being inspected. | - +bit_index | `Int` | The index of the bit to check (1-indexed, range 1-8). | - + +**Return:** Whether or not the given bit index is set to 1 in the byte. + + +**Example:** +```tomo +>> Byte(6).get_bit(1) += no +>> Byte(6).get_bit(2) += yes +>> Byte(6).get_bit(3) += yes +>> Byte(6).get_bit(4) += no + +``` ## Byte.hex ```tomo @@ -402,6 +432,36 @@ n | `Int` | The integer to compute the factorial of. | - = 3628800 ``` +## Int.get_bit + +```tomo +Int.get_bit : func(i: Int, bit_index: Int -> Bool) +``` + +In the binary representation of an integer, check whether a given bit index is set to 1 or not. + +For fixed-size integers, the bit index must be between 1 and the number of bits in that integer (i.e. 1-64 for `Int64`). For `Int`, the bit index must be between 1 and `Int64.max`. Values outside this range will produce a runtime error. + +Argument | Type | Description | Default +---------|------|-------------|--------- +i | `Int` | The integer whose bits are being inspected. | - +bit_index | `Int` | The index of the bit to check (1-indexed). | - + +**Return:** Whether or not the given bit index is set to 1 in the binary representation of the integer. + + +**Example:** +```tomo +>> (6).get_bit(1) += no +>> (6).get_bit(2) += yes +>> (6).get_bit(3) += yes +>> (6).get_bit(4) += no + +``` ## Int.hex ```tomo diff --git a/api/bytes.md b/api/bytes.md index 598c92b7..908d78e2 100644 --- a/api/bytes.md +++ b/api/bytes.md @@ -3,6 +3,36 @@ # Builtins # Byte +## Byte.get_bit + +```tomo +Byte.get_bit : func(i: Byte, bit_index: Int -> Bool) +``` + +In the binary representation of a byte, check whether a given bit index is set to 1 or not. + +The bit index must be between 1-8 or a runtime error will be produced. + +Argument | Type | Description | Default +---------|------|-------------|--------- +i | `Byte` | The byte whose bits are being inspected. | - +bit_index | `Int` | The index of the bit to check (1-indexed, range 1-8). | - + +**Return:** Whether or not the given bit index is set to 1 in the byte. + + +**Example:** +```tomo +>> Byte(6).get_bit(1) += no +>> Byte(6).get_bit(2) += yes +>> Byte(6).get_bit(3) += yes +>> Byte(6).get_bit(4) += no + +``` ## Byte.hex ```tomo diff --git a/api/bytes.yaml b/api/bytes.yaml index 52f48528..2785513d 100644 --- a/api/bytes.yaml +++ b/api/bytes.yaml @@ -1,3 +1,33 @@ +Byte.get_bit: + short: check whether a bit is set + description: > + In the binary representation of a byte, check whether a given bit index is + set to 1 or not. + note: > + The bit index must be between 1-8 or a runtime error will be produced. + return: + type: 'Bool' + description: > + Whether or not the given bit index is set to 1 in the byte. + args: + i: + type: 'Byte' + description: > + The byte whose bits are being inspected. + bit_index: + type: 'Int' + description: > + The index of the bit to check (1-indexed, range 1-8). + example: | + >> Byte(6).get_bit(1) + = no + >> Byte(6).get_bit(2) + = yes + >> Byte(6).get_bit(3) + = yes + >> Byte(6).get_bit(4) + = no + Byte.hex: short: convert to hexidecimal description: > diff --git a/api/integers.md b/api/integers.md index 0865e93f..efb891bf 100644 --- a/api/integers.md +++ b/api/integers.md @@ -90,6 +90,36 @@ n | `Int` | The integer to compute the factorial of. | - = 3628800 ``` +## Int.get_bit + +```tomo +Int.get_bit : func(i: Int, bit_index: Int -> Bool) +``` + +In the binary representation of an integer, check whether a given bit index is set to 1 or not. + +For fixed-size integers, the bit index must be between 1 and the number of bits in that integer (i.e. 1-64 for `Int64`). For `Int`, the bit index must be between 1 and `Int64.max`. Values outside this range will produce a runtime error. + +Argument | Type | Description | Default +---------|------|-------------|--------- +i | `Int` | The integer whose bits are being inspected. | - +bit_index | `Int` | The index of the bit to check (1-indexed). | - + +**Return:** Whether or not the given bit index is set to 1 in the binary representation of the integer. + + +**Example:** +```tomo +>> (6).get_bit(1) += no +>> (6).get_bit(2) += yes +>> (6).get_bit(3) += yes +>> (6).get_bit(4) += no + +``` ## Int.hex ```tomo diff --git a/api/integers.yaml b/api/integers.yaml index f927c75f..a91a21ce 100644 --- a/api/integers.yaml +++ b/api/integers.yaml @@ -81,7 +81,41 @@ Int.factorial: example: | >> (10).factorial() = 3628800 - + +Int.get_bit: + short: check whether a bit is set + description: > + In the binary representation of an integer, check whether a given bit index + is set to 1 or not. + note: > + For fixed-size integers, the bit index must be between 1 and the number of + bits in that integer (i.e. 1-64 for `Int64`). For `Int`, the bit index must + be between 1 and `Int64.max`. Values outside this range will produce a + runtime error. + return: + type: 'Bool' + description: > + Whether or not the given bit index is set to 1 in the binary + representation of the integer. + args: + i: + type: 'Int' + description: > + The integer whose bits are being inspected. + bit_index: + type: 'Int' + description: > + The index of the bit to check (1-indexed). + example: | + >> (6).get_bit(1) + = no + >> (6).get_bit(2) + = yes + >> (6).get_bit(3) + = yes + >> (6).get_bit(4) + = no + Int.hex: short: convert to hexidecimal description: > diff --git a/man/man3/tomo-Byte.get_bit.3 b/man/man3/tomo-Byte.get_bit.3 new file mode 100644 index 00000000..85d6c2ae --- /dev/null +++ b/man/man3/tomo-Byte.get_bit.3 @@ -0,0 +1,44 @@ +'\" t +.\" Copyright (c) 2025 Bruce Hill +.\" All rights reserved. +.\" +.TH Byte.get_bit 3 2025-06-26 "Tomo man-pages" +.SH NAME +Byte.get_bit \- check whether a bit is set +.SH LIBRARY +Tomo Standard Library +.SH SYNOPSIS +.nf +.BI Byte.get_bit\ :\ func(i:\ Byte,\ bit_index:\ Int\ ->\ Bool) +.fi +.SH DESCRIPTION +In the binary representation of a byte, check whether a given bit index is set to 1 or not. + + +.SH ARGUMENTS + +.TS +allbox; +lb lb lbx lb +l l l l. +Name Type Description Default +i Byte The byte whose bits are being inspected. - +bit_index Int The index of the bit to check (1-indexed, range 1-8). - +.TE +.SH RETURN +Whether or not the given bit index is set to 1 in the byte. + +.SH NOTES +The bit index must be between 1-8 or a runtime error will be produced. + +.SH EXAMPLES +.EX +>> Byte(6).get_bit(1) += no +>> Byte(6).get_bit(2) += yes +>> Byte(6).get_bit(3) += yes +>> Byte(6).get_bit(4) += no +.EE diff --git a/man/man3/tomo-Int.get_bit.3 b/man/man3/tomo-Int.get_bit.3 new file mode 100644 index 00000000..e0b98909 --- /dev/null +++ b/man/man3/tomo-Int.get_bit.3 @@ -0,0 +1,44 @@ +'\" t +.\" Copyright (c) 2025 Bruce Hill +.\" All rights reserved. +.\" +.TH Int.get_bit 3 2025-06-25 "Tomo man-pages" +.SH NAME +Int.get_bit \- check whether a bit is set +.SH LIBRARY +Tomo Standard Library +.SH SYNOPSIS +.nf +.BI Int.get_bit\ :\ func(i:\ Int,\ bit_index:\ Int\ ->\ Bool) +.fi +.SH DESCRIPTION +In the binary representation of an integer, check whether a given bit index is set to 1 or not. + + +.SH ARGUMENTS + +.TS +allbox; +lb lb lbx lb +l l l l. +Name Type Description Default +i Int The integer whose bits are being inspected. - +bit_index Int The index of the bit to check (1-indexed). - +.TE +.SH RETURN +Whether or not the given bit index is set to 1 in the binary representation of the integer. + +.SH NOTES +For fixed-size integers, the bit index must be between 1 and the number of bits in that integer (i.e. 1-64 for `Int64`). For `Int`, the bit index must be between 1 and `Int64.max`. Values outside this range will produce a runtime error. + +.SH EXAMPLES +.EX +>> (6).get_bit(1) += no +>> (6).get_bit(2) += yes +>> (6).get_bit(3) += yes +>> (6).get_bit(4) += no +.EE diff --git a/src/environment.c b/src/environment.c index 5fb6714a..eb2d28be 100644 --- a/src/environment.c +++ b/src/environment.c @@ -90,10 +90,11 @@ env_t *global_env(bool source_mapping) )}, {"Byte", Type(ByteType), "Byte_t", "Byte$info", TypedList(ns_entry_t, {"max", "Byte$max", "Byte"}, + {"get_bit", "Byte$get_bit", "func(x:Byte, bit_index:Int -> Bool)"}, {"hex", "Byte$hex", "func(byte:Byte, uppercase=yes, prefix=no -> Text)"}, - {"is_between", "Byte$is_between", "func(x:Byte,low:Byte,high:Byte -> Bool)"}, + {"is_between", "Byte$is_between", "func(x:Byte, low:Byte, high:Byte -> Bool)"}, {"min", "Byte$min", "Byte"}, - {"to", "Byte$to", "func(first:Byte,last:Byte,step:Int8?=none -> func(->Byte?))"}, + {"to", "Byte$to", "func(first:Byte, last:Byte, step:Int8?=none -> func(->Byte?))"}, )}, {"Int", Type(BigIntType), "Int_t", "Int$info", TypedList(ns_entry_t, {"abs", "Int$abs", "func(x:Int -> Int)"}, @@ -105,6 +106,7 @@ env_t *global_env(bool source_mapping) {"divided_by", "Int$divided_by", "func(x,y:Int -> Int)"}, {"factorial", "Int$factorial", "func(x:Int -> Int)"}, {"gcd", "Int$gcd", "func(x,y:Int -> Int)"}, + {"get_bit", "Int$get_bit", "func(x,bit_index:Int -> Bool)"}, {"hex", "Int$hex", "func(i:Int, digits=0, uppercase=yes, prefix=yes -> Text)"}, {"is_between", "Int$is_between", "func(x:Int,low:Int,high:Int -> Bool)"}, {"is_prime", "Int$is_prime", "func(x:Int,reps=50 -> Bool)"}, @@ -137,6 +139,7 @@ env_t *global_env(bool source_mapping) {"divided_by", "Int64$divided_by", "func(x,y:Int64 -> Int64)"}, {"gcd", "Int64$gcd", "func(x,y:Int64 -> Int64)"}, {"parse", "Int64$parse", "func(text:Text -> Int64?)"}, + {"get_bit", "Int64$get_bit", "func(x:Int64, bit_index:Int -> Bool)"}, {"hex", "Int64$hex", "func(i:Int64, digits=0, uppercase=yes, prefix=yes -> Text)"}, {"is_between", "Int64$is_between", "func(x:Int64,low:Int64,high:Int64 -> Bool)"}, {"max", "Int64$max", "Int64"}, @@ -158,6 +161,7 @@ env_t *global_env(bool source_mapping) {"divided_by", "Int32$divided_by", "func(x,y:Int32 -> Int32)"}, {"gcd", "Int32$gcd", "func(x,y:Int32 -> Int32)"}, {"parse", "Int32$parse", "func(text:Text -> Int32?)"}, + {"get_bit", "Int32$get_bit", "func(x:Int32, bit_index:Int -> Bool)"}, {"hex", "Int32$hex", "func(i:Int32, digits=0, uppercase=yes, prefix=yes -> Text)"}, {"is_between", "Int32$is_between", "func(x:Int32,low:Int32,high:Int32 -> Bool)"}, {"max", "Int32$max", "Int32"}, @@ -179,6 +183,7 @@ env_t *global_env(bool source_mapping) {"divided_by", "Int16$divided_by", "func(x,y:Int16 -> Int16)"}, {"gcd", "Int16$gcd", "func(x,y:Int16 -> Int16)"}, {"parse", "Int16$parse", "func(text:Text -> Int16?)"}, + {"get_bit", "Int16$get_bit", "func(x:Int16, bit_index:Int -> Bool)"}, {"hex", "Int16$hex", "func(i:Int16, digits=0, uppercase=yes, prefix=yes -> Text)"}, {"is_between", "Int16$is_between", "func(x:Int16,low:Int16,high:Int16 -> Bool)"}, {"max", "Int16$max", "Int16"}, @@ -200,6 +205,7 @@ env_t *global_env(bool source_mapping) {"divided_by", "Int8$divided_by", "func(x,y:Int8 -> Int8)"}, {"gcd", "Int8$gcd", "func(x,y:Int8 -> Int8)"}, {"parse", "Int8$parse", "func(text:Text -> Int8?)"}, + {"get_bit", "Int8$get_bit", "func(x:Int8, bit_index:Int -> Bool)"}, {"hex", "Int8$hex", "func(i:Int8, digits=0, uppercase=yes, prefix=yes -> Text)"}, {"is_between", "Int8$is_between", "func(x:Int8,low:Int8,high:Int8 -> Bool)"}, {"max", "Int8$max", "Int8"}, diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index b5c10aa2..48c8b93b 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -49,6 +49,14 @@ public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) { return text; } +public bool Byte$get_bit(Byte_t x, Int_t bit_index) { + if (Int$compare_value(bit_index, I(1)) < 0) + fail("Invalid bit index (expected 1 or higher): ", bit_index); + if (Int$compare_value(bit_index, I(8)) > 0) + fail("Bit index is too large! There are only 8 bits in a byte, but index is: ", bit_index); + return ((x & (Byte_t)(1L << (Int64$from_int(bit_index, true)-1L))) != 0); +} + #ifdef __TINYC__ #define __builtin_add_overflow(x, y, result) ({ *(result) = (x) + (y); false; }) #endif diff --git a/src/stdlib/bytes.h b/src/stdlib/bytes.h index 5c64687d..e733c274 100644 --- a/src/stdlib/bytes.h +++ b/src/stdlib/bytes.h @@ -30,5 +30,6 @@ extern const Byte_t Byte$max; extern const TypeInfo_t Byte$info; Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix); +bool Byte$get_bit(Byte_t x, Int_t bit_index); // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/integers.c b/src/stdlib/integers.c index 7c623663..018798ec 100644 --- a/src/stdlib/integers.c +++ b/src/stdlib/integers.c @@ -359,6 +359,19 @@ public OptionalInt_t Int$sqrt(Int_t i) return Int$from_mpz(result); } +public bool Int$get_bit(Int_t x, Int_t bit_index) +{ + mpz_t i; + mpz_init_set_int(i, x); + if (Int$compare_value(bit_index, I(1)) < 0) + fail("Invalid bit index (expected 1 or higher): ", bit_index); + if (Int$compare_value(bit_index, Int$from_int64(INT64_MAX)) > 0) + fail("Bit index is too large! ", bit_index); + + int is_bit_set = mpz_tstbit(i, (mp_bitcnt_t)(Int64$from_int(bit_index, true)-1)); + return (bool)is_bit_set; +} + typedef struct { OptionalInt_t current, last; Int_t step; @@ -620,6 +633,13 @@ public void Int32$deserialize(FILE *in, void *outval, List_t *pointers, const Ty } \ return bit_list; \ } \ + public bool KindOfInt ## $get_bit(c_type x, Int_t bit_index) { \ + if (Int$compare_value(bit_index, I(1)) < 0) \ + fail("Invalid bit index (expected 1 or higher): ", bit_index); \ + if (Int$compare_value(bit_index, Int$from_int64(sizeof(c_type)*8)) > 0) \ + fail("Bit index is too large! There are only ", sizeof(c_type)*8, " bits, but index is: ", bit_index); \ + return ((x & (c_type)(1L << (Int64$from_int(bit_index, true)-1L))) != 0); \ + } \ typedef struct { \ Optional##KindOfInt##_t current, last; \ KindOfInt##_t step; \ diff --git a/src/stdlib/integers.h b/src/stdlib/integers.h index 4eaac916..beb26bd6 100644 --- a/src/stdlib/integers.h +++ b/src/stdlib/integers.h @@ -29,6 +29,7 @@ Text_t type_name ## $hex(c_type i, Int_t digits, bool uppercase, bool prefix); \ Text_t type_name ## $octal(c_type i, Int_t digits, bool prefix); \ List_t type_name ## $bits(c_type x); \ + bool type_name ## $get_bit(c_type x, Int_t bit_index); \ Closure_t type_name ## $to(c_type first, c_type last, Optional ## type_name ## _t step); \ Closure_t type_name ## $onward(c_type first, c_type step); \ PUREFUNC Optional ## type_name ## _t type_name ## $parse(Text_t text); \ @@ -105,6 +106,7 @@ Int_t Int$abs(Int_t x); Int_t Int$power(Int_t base, Int_t exponent); Int_t Int$gcd(Int_t x, Int_t y); OptionalInt_t Int$sqrt(Int_t i); +bool Int$get_bit(Int_t x, Int_t bit_index); #define BIGGEST_SMALL_INT 0x3fffffff #define SMALLEST_SMALL_INT -0x40000000 diff --git a/test/bytes.tm b/test/bytes.tm index 25efbeb8..a207c633 100644 --- a/test/bytes.tm +++ b/test/bytes.tm @@ -14,3 +14,12 @@ func main() = "0x0F" >> b.hex(uppercase=no) = "0f" + + >> Byte(0x06).get_bit(1) + = no + >> Byte(0x06).get_bit(2) + = yes + >> Byte(0x06).get_bit(3) + = yes + >> Byte(0x06).get_bit(4) + = no diff --git a/test/integers.tm b/test/integers.tm index 6ba1559c..3035ad3b 100644 --- a/test/integers.tm +++ b/test/integers.tm @@ -139,3 +139,21 @@ func main() = yes >> (3).is_between(100, 200) = no + + >> (6).get_bit(1) + = no + >> (6).get_bit(2) + = yes + >> (6).get_bit(3) + = yes + >> (6).get_bit(4) + = no + + >> Int64(6).get_bit(1) + = no + >> Int64(6).get_bit(2) + = yes + >> Int64(6).get_bit(3) + = yes + >> Int64(6).get_bit(4) + = no |
