aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.md1
-rw-r--r--api/api.md60
-rw-r--r--api/bytes.md30
-rw-r--r--api/bytes.yaml30
-rw-r--r--api/integers.md30
-rw-r--r--api/integers.yaml36
-rw-r--r--man/man3/tomo-Byte.get_bit.344
-rw-r--r--man/man3/tomo-Int.get_bit.344
-rw-r--r--src/environment.c10
-rw-r--r--src/stdlib/bytes.c8
-rw-r--r--src/stdlib/bytes.h1
-rw-r--r--src/stdlib/integers.c20
-rw-r--r--src/stdlib/integers.h2
-rw-r--r--test/bytes.tm9
-rw-r--r--test/integers.tm18
15 files changed, 340 insertions, 3 deletions
diff --git a/CHANGES.md b/CHANGES.md
index 1fba98fc..ce93bc8c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -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.
diff --git a/api/api.md b/api/api.md
index 464ec58a..9711ab44 100644
--- a/api/api.md
+++ b/api/api.md
@@ -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