From cefe6c3ec50dd6e0e32bb806da8545b27f638722 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 11 May 2026 14:47:26 +0200 Subject: [PATCH 1/4] fix(math): math:pow should use a better exponentiation method when the inputs are integers --- Math.ark | 18 +++++++++++++++++- tests/math-tests.ark | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Math.ark b/Math.ark index 05a2eba..e696c36 100644 --- a/Math.ark +++ b/Math.ark @@ -214,7 +214,23 @@ # @param _x the number to pow # @param _a the exponent # @author https://github.com/SuperFola -(let pow (fun (_x _a) (exp (* _a (ln _x))))) +(let pow (fun ((mut _x) (mut _a)) { + (if (= 0 (mod _a 1)) + (if (= 1 _a) + _x + (if (= 0 _a) + 1 + { + (let _c _x) + (if (> _a 0) + (while (> _a 1) { + (set _x (* _c _x)) + (set _a (- _a 1)) }) + (while (<= _a 0) { + (set _x (/ _x _c)) + (set _a (+ _a 1)) })) + _x })) + (exp (* _a (ln _x)))) })) # @brief Get the square root of a number # @details Square roots can't be taken for negative numbers for obvious reasons. diff --git a/tests/math-tests.ark b/tests/math-tests.ark index 20de42d..babc020 100644 --- a/tests/math-tests.ark +++ b/tests/math-tests.ark @@ -80,6 +80,8 @@ (test:case "pow" { (test:eq (math:pow 2 2) 4) + (test:eq (math:pow 2 3) 8) + (test:eq (math:pow 2 -3) 0.125) (test:eq (math:pow 4 0.5) 2) }) (test:case "clamp" { From 4c7bfb0cd07d19bfd0a5a27a452f7c91d7f20860 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Wed, 13 May 2026 13:01:14 +0200 Subject: [PATCH 2/4] feat(testing): accept strings as test suites names --- Testing.ark | 4 +++- tests/string-tests.ark | 2 +- tests/switch-tests.ark | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Testing.ark b/Testing.ark index 796bac5..9cecffa 100644 --- a/Testing.ark +++ b/Testing.ark @@ -261,7 +261,9 @@ (set testing:_suite (testing:_make_suite ($repr _name))) (let ($symcat _name "-output") (testing:_runner - ($repr _name) + ($if (= "String" ($type _name)) + _name + ($repr _name)) (fun () ($as-is { _body })))) diff --git a/tests/string-tests.ark b/tests/string-tests.ark index 9c0f598..378df90 100644 --- a/tests/string-tests.ark +++ b/tests/string-tests.ark @@ -75,7 +75,7 @@ (test:eq (string:toUpper "ABCDEFGHIJKLMNOPQRSTUVWXYZ") "ABCDEFGHIJKLMNOPQRSTUVWXYZ") }) (test:case "reverse" { - (test:eq (string:reverse "dlrow olleh") "hello world" ) + (test:eq (string:reverse "dlrow olleh") "hello world") (test:eq (string:reverse "") "") (test:eq (string:reverse "a") "a") }) diff --git a/tests/switch-tests.ark b/tests/switch-tests.ark index 865523a..85491de 100644 --- a/tests/switch-tests.ark +++ b/tests/switch-tests.ark @@ -9,7 +9,7 @@ (test:expect true)) called })) -(test:suite switch { +(test:suite "switch" { (switch 12 0 (test:expect false) -1 (test:expect false) From db0c7a9654c52f80ae9b7d9ed853c2d8ddec9126 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Wed, 13 May 2026 15:54:03 +0200 Subject: [PATCH 3/4] feat(math): add new bitwise builtins --- Math.ark | 81 ++++++++++++++++++++++++++++++++++++++++++++ tests/math-tests.ark | 27 ++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/Math.ark b/Math.ark index e696c36..6e6d23a 100644 --- a/Math.ark +++ b/Math.ark @@ -158,6 +158,87 @@ # @author https://github.com/SuperFola (let NaN builtin__math:NaN) +# @brief Count the number of '1' bits in the given value +# @param value the Number, must be an integer +# =begin +# (math:countOnes 5) # 2 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let countOnes (fun (_x) (builtin__math:countOnes _x))) + +# @brief Count the number of '0' bits in the given value +# @param value the Number, must be an integer +# =begin +# (math:countZeros 5) # 1 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let countZeros (fun (_x) (builtin__math:countZeros _x))) + +# @brief Invert a number on 64 bits +# @param value Number, must be an integer +# =begin +# (math:bitNot 5) # -6 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let bitNot (fun (_x) (builtin__math:bitNot _x))) + +# @brief Bitwise and +# @param lhs Number, must be an integer +# @param rhs Number, must be an integer +# =begin +# (math:bitAnd 4 12) # 4 +# (math:bitAnd 4 10) # 0 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let bitAnd (fun (_a _b) (builtin__math:bitAnd _a _b))) + +# @brief Bitwise or +# @param lhs Number, must be an integer +# @param rhs Number, must be an integer +# =begin +# (math:bitOr 4 10) # 14 +# (math:bitOr 4 1) # 5 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let bitOr (fun (_a _b) (builtin__math:bitOr _a _b))) + +# @brief Bitwise exclusive or of lhs and rhs +# @param lhs Number, must be an integer +# @param rhs Number, must be an integer +# =begin +# (math:bitXor 4 12) # 8 +# (math:bitXor 4 10) # 14 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let bitXor (fun (_a _b) (builtin__math:bitXor _a _b))) + +# @brief Left bit shift +# @param lhs Number, must be an integer +# @param rhs Number, must be an integer +# =begin +# (math:lshift 3 2) # 12 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let lshift (fun (_a _b) (builtin__math:lshift _a _b))) + +# @brief Right bit shift +# @param lhs Number, must be an integer +# @param rhs Number, must be an integer +# =begin +# (math:rshift 13 2) # 3 +# (math:rshift -14 2) # -4 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let rshift (fun (_a _b) (builtin__math:rshift _a _b))) + # @brief Return the absolute value of a number # @param _x the number to get the absolute value of # @author https://github.com/rstefanic diff --git a/tests/math-tests.ark b/tests/math-tests.ark index babc020..7ea1288 100644 --- a/tests/math-tests.ark +++ b/tests/math-tests.ark @@ -29,7 +29,32 @@ (test:eq (builtin__math:tanh 5) (math:tanh 5)) (test:eq (builtin__math:acosh 5) (math:acosh 5)) (test:eq (builtin__math:asinh 5) (math:asinh 5)) - (test:eq (builtin__math:atanh 0.5) (math:atanh 0.5)) }) + (test:eq (builtin__math:atanh 0.5) (math:atanh 0.5)) + (test:eq (builtin__math:countOnes 1234) (math:countOnes 1234)) + (test:eq (builtin__math:countZeros 1234) (math:countZeros 1234)) + (test:eq (builtin__math:bitNot 1234) (math:bitNot 1234)) + (test:eq (builtin__math:bitAnd 1234 5678) (math:bitAnd 1234 5678)) + (test:eq (builtin__math:bitOr 1234 5678) (math:bitOr 1234 5678)) + (test:eq (builtin__math:bitXor 1234 5678) (math:bitXor 1234 5678)) + (test:eq (builtin__math:lshift 1234 2) (math:lshift 1234 2)) + (test:eq (builtin__math:rshift 1234 6) (math:rshift 1234 6)) }) + + (test:case "bitwise" { + (test:eq (math:countOnes 5) 2) + (test:eq (math:countOnes 63) 6) + (test:eq (math:countZeros 63) 58) + (test:eq (math:countZeros 65535) 48) + + (test:eq (math:bitNot 5) -6) + (test:eq (math:bitAnd 4 12) 4) + (test:eq (math:bitAnd 4 10) 0) + (test:eq (math:bitOr 4 10) 14) + (test:eq (math:bitOr 4 1) 5) + (test:eq (math:bitXor 4 12) 8) + (test:eq (math:bitXor 4 10) 14) + (test:eq (math:lshift 3 2) 12) + (test:eq (math:rshift 13 2) 3) + (test:eq (math:rshift -14 2) -4) }) (test:case "abs" { (test:eq (math:abs -1) 1) From 887aef5c004a49574e0fbcead41326840f1dea94 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Thu, 14 May 2026 13:07:45 +0200 Subject: [PATCH 4/4] feat(math): add more bit operations, base conversion, circular bit shifting --- Math.ark | 147 +++++++++++++++++++++++++++++++++++++++++++ tests/math-tests.ark | 37 ++++++++++- 2 files changed, 182 insertions(+), 2 deletions(-) diff --git a/Math.ark b/Math.ark index 6e6d23a..2b11c8f 100644 --- a/Math.ark +++ b/Math.ark @@ -239,6 +239,153 @@ # @require 4.6.0 Requires ArkScript 4.6.0 or later (let rshift (fun (_a _b) (builtin__math:rshift _a _b))) +# @brief Finds the smallest integral power of 2 not less than the given value +# @param value Number, must be an integer +# =begin +# (math:bitCeil 4) # 4 +# (math:bitCeil 7) # 8 +# (math:bitCeil 8) # 8 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let bitCeil (fun (_x) (builtin__math:bitCeil _x))) + +# @brief Finds the largest integral power of 2 not greater than the given value +# @param value Number, must be an integer +# =begin +# (math:bitFloor 4) # 4 +# (math:bitFloor 7) # 4 +# (math:bitFloor 8) # 8 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let bitFloor (fun (_x) (builtin__math:bitFloor _x))) + +# @brief Finds the smallest number of bits needed to represent the given value +# @param value Number, must be an integer +# =begin +# (math:bitWidth 2) # 2 +# (math:bitWidth 7) # 3 +# (math:bitWidth 8) # 4 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let bitWidth (fun (_x) (builtin__math:bitWidth _x))) + +# @brief Counts the number of consecutive 0 bits, starting from the most significant bit +# @param n Number, must be an integer +# @param bits Number of bits to represent n in, must be an integer +# =begin +# (math:countLeftZeros 30 8) # 3 +# (math:countLeftZeros 227 8) # 0 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let countLeftZeros (fun (_n _bits) (builtin__math:countLeftZeros _n _bits))) + +# @brief Counts the number of consecutive 1 bits, starting from the most significant bit +# @param n Number, must be an integer +# @param bits Number of bits to represent n in, must be an integer +# =begin +# (math:countLeftOnes 30 8) # 4 +# (math:countLeftOnes 227 8) # 3 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let countLeftOnes (fun (_n _bits) (builtin__math:countLeftOnes _n _bits))) + +# @brief Counts the number of consecutive 0 bits, starting from the least significant bit +# @param n Number, must be an integer +# @param bits Number of bits to represent n in, must be an integer +# =begin +# (math:countRightZeros 30 8) # 1 +# (math:countRightZeros 227 8) # 0 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let countRightZeros (fun (_n _bits) (builtin__math:countRightZeros _n _bits))) + +# @brief Counts the number of consecutive 1 bits, starting from the least significant bit +# @param n Number, must be an integer +# @param bits Number of bits to represent n in, must be an integer +# =begin +# (math:countRightOnes 30 8) # 0 +# (math:countRightOnes 227 8) # 2 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let countRightOnes (fun (_n _bits) (builtin__math:countRightOnes _n _bits))) + +# @brief Computes the result of bitwise left-rotation of _x by _count +# @param _x Number, must be an integer +# @param _count Number, must be an integer +# @param _bytecount Number of bytes to shift on, must be an integer +# =begin +# (math:circularLeftShift 29 4 1) # 209 +# (math:circularLeftShift 29 9 1) # 58 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let circularLeftShift (fun (_x (mut _count) _bytecount) { + (assert (and (>= _bytecount 1) (<= _bytecount 8)) "bytecount must be in [1, 8]") + (let _max (- (* 8 _bytecount) 1)) + (let _mask (- (lshift 1 (* 8 _bytecount)) 1)) + (set _count (bitAnd _count _max)) + (bitAnd (bitOr (lshift _x _count) (rshift _x (bitAnd (* -1 _count) _max))) _mask) })) + +# @brief Computes the result of bitwise left-rotation of _x by _count +# @param _x Number, must be an integer +# @param _count Number, must be an integer +# @param _bytecount Number of bytes to shift on, must be an integer +# =begin +# (math:circularRightShift 29 9 1) # 142 +# (math:circularRightShift 29 2 1) # 71 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let circularRightShift (fun (_x (mut _count) _bytecount) { + (assert (and (>= _bytecount 1) (<= _bytecount 8)) "bytecount must be in [1, 8]") + (let _max (- (* 8 _bytecount) 1)) + (let _mask (- (lshift 1 (* 8 _bytecount)) 1)) + (set _count (bitAnd _count _max)) + (bitAnd (bitOr (rshift _x _count) (lshift _x (bitAnd (* -1 _count) _max))) _mask) })) + +(let _base_conversion_alphabet "0123456789abcdefghijklmnopqrstuvwxyz") + +# @brief Convert a number in a another base (in [2, 36[) +# @param _x Number, must be an integer +# @param _base Number, must be an integer +# =begin +# (math:toBase 10 2) # "1010" +# (math:toBase 165 16) # "a5" +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let toBase (fun ((mut _x) _base) { + (assert (and (> _base 1) (< _base 36)) "Base must be in [2, 36[") + (mut _out "") + (while _x { + (set _out (+ (@ _base_conversion_alphabet (mod _x _base)) _out)) + (set _x (floordiv _x _base)) }) + _out })) + +# @brief Count the digits of a number in a given base (in [2, 36[) +# @param _x Number, must be an integer +# @param _base Number, must be an integer +# =begin +# (math:countDigits 10 2) # 4 +# (math:countDigits 165 16) # 2 +# =end +# @author https://github.com/SuperFola +# @require 4.6.0 Requires ArkScript 4.6.0 or later +(let countDigits (fun ((mut _x) _base) { + (assert (and (> _base 1) (< _base 36)) "Base must be in [2, 36[") + (mut _out 0) + (while _x { + (set _out (+ 1 _out)) + (set _x (floordiv _x _base)) }) + _out })) + # @brief Return the absolute value of a number # @param _x the number to get the absolute value of # @author https://github.com/rstefanic diff --git a/tests/math-tests.ark b/tests/math-tests.ark index 7ea1288..6c9e36f 100644 --- a/tests/math-tests.ark +++ b/tests/math-tests.ark @@ -37,7 +37,14 @@ (test:eq (builtin__math:bitOr 1234 5678) (math:bitOr 1234 5678)) (test:eq (builtin__math:bitXor 1234 5678) (math:bitXor 1234 5678)) (test:eq (builtin__math:lshift 1234 2) (math:lshift 1234 2)) - (test:eq (builtin__math:rshift 1234 6) (math:rshift 1234 6)) }) + (test:eq (builtin__math:rshift 1234 6) (math:rshift 1234 6)) + (test:eq (builtin__math:bitCeil 1234) (math:bitCeil 1234)) + (test:eq (builtin__math:bitFloor 1234) (math:bitFloor 1234)) + (test:eq (builtin__math:bitWidth 1234) (math:bitWidth 1234)) + (test:eq (builtin__math:countLeftZeros 1234 32) (math:countLeftZeros 1234 32)) + (test:eq (builtin__math:countLeftOnes 1234 32) (math:countLeftOnes 1234 32)) + (test:eq (builtin__math:countRightZeros 1234 32) (math:countRightZeros 1234 32)) + (test:eq (builtin__math:countRightOnes 1234 32) (math:countRightOnes 1234 32)) }) (test:case "bitwise" { (test:eq (math:countOnes 5) 2) @@ -54,7 +61,33 @@ (test:eq (math:bitXor 4 10) 14) (test:eq (math:lshift 3 2) 12) (test:eq (math:rshift 13 2) 3) - (test:eq (math:rshift -14 2) -4) }) + (test:eq (math:rshift -14 2) -4) + + (test:eq (math:countLeftZeros 30 8) 3) + (test:eq (math:countLeftZeros 227 8) 0) + (test:eq (math:countLeftOnes 30 5) 4) + (test:eq (math:countLeftOnes 30 8) 0) + (test:eq (math:countLeftOnes 227 8) 3) + (test:eq (math:countRightZeros 30 8) 1) + (test:eq (math:countRightZeros 227 8) 0) + (test:eq (math:countRightOnes 30 8) 0) + (test:eq (math:countRightOnes 227 8) 2) + + (test:eq (math:circularLeftShift 29 4 1) 209) + (test:eq (math:circularLeftShift 29 9 1) 58) + (test:eq (math:circularRightShift 29 9 1) 142) + (test:eq (math:circularRightShift 29 2 1) 71) }) + + (test:case "toBase" { + (test:eq (math:toBase 10 10) "10") + (test:eq (math:toBase 10 16) "a") + (test:eq (math:toBase 10 2) "1010") + (test:eq (math:toBase 165 16) "a5") }) + + (test:case "countDigits" { + (test:eq (math:countDigits 10 2) 4) + (test:eq (math:countDigits 10 10) 2) + (test:eq (math:countDigits 165 16) 2) }) (test:case "abs" { (test:eq (math:abs -1) 1)