diff --git a/Benchmark.ark b/Benchmark.ark new file mode 100644 index 0000000..9290101 --- /dev/null +++ b/Benchmark.ark @@ -0,0 +1,44 @@ +# @brief Measure the time it takes to run some given code, in milliseconds +# @param tag Identifier for the code block (string) +# @param code Node of code to run +# @author https://github.com/SuperFola +(macro measureOnce (tag code) { + (mut _start (time)) + { code } + (print (format "{} took {:.4} ms" tag (* 1000 (- (time) _start)))) }) + +(import std.List) + +# @brief Benchmark some given code by running it a given number of times +# @param code code to run, eg. a function call +# @param times number of times to run the code +# =begin +# (let fib (fun (n) +# (if (< n 2) +# n +# (+ (fib (- n 1)) (fib (- n 2)))))) +# (bench (fib 23) 10) +# # (fib 23), 10 times +# # ↪︎ range: [4.7 - 5.02] ms +# # ↪︎ mean: 4.85ms +# # ↪︎ median: 4.86ms +# =end +# @author https://github.com/SuperFola +(macro bench (code times) { + (mut _i 0) + (mut _data []) + (while (< _i times) { + (let _start (time)) + { ($as-is code) } + (let _end (time)) + (append! _data (* 1000 (- _end _start))) + (set _i (+ 1 _i)) }) + + (mut _mean (/ (list:sum _data) (len _data))) + (mut _median (list:median _data)) + (mut _max (list:max _data)) + (mut _min (list:min _data)) + (print + (format + "{}, {} times\n ↪︎ range: [{:.3} - {:.3}] ms\n ↪︎ mean: {:.3}ms\n ↪︎ median: {:.3}ms" + ($repr code) times _min _max _mean _median)) }) diff --git a/Cli.ark b/Cli.ark index ff726dd..a4dfe1b 100644 --- a/Cli.ark +++ b/Cli.ark @@ -108,7 +108,7 @@ (+ headers synopsis "\n\nOPTIONS\n" options) })) -(let _match_group (fun (parsed args param) { +(let _match_group (fun (parsed (mut args) param) { (mut missing_param false) (mut i 0) (while (and (not missing_param) (< i (len param.children))) { diff --git a/Dict.ark b/Dict.ark index 7ced589..edef425 100644 --- a/Dict.ark +++ b/Dict.ark @@ -24,6 +24,7 @@ # @brief Checks if the dictionary has a given key # @param _D dictionary +# @param _key key to check for its presence in the dict # =begin # (let data (dict "key" "value")) # (print (dict:contains? data "key")) # true diff --git a/IO.ark b/IO.ark index e3338ed..6d72341 100644 --- a/IO.ark +++ b/IO.ark @@ -24,6 +24,14 @@ # @author https://github.com/SuperFola (let readFile (fun (_name) (builtin__io:readFile _name))) +# @brief Read the content from a file as a List of Strings +# @param filename the path of the file to read +# =begin +# (io:readLinesFile "hello.json") +# =end +# @author https://github.com/SuperFola +(let readLinesFile (fun (_name) (builtin__io:readLinesFile _name))) + # @brief Check if a file exists, return True or False # @param filename the path of the file # =begin diff --git a/List.ark b/List.ark index 28a477d..668ab12 100644 --- a/List.ark +++ b/List.ark @@ -78,41 +78,60 @@ # @author https://github.com/SuperFola (let setAt (fun (_L _index _x) (builtin__list:setAt _L _index _x))) +(let _stopIteration_helper (fun () (assert false "This shouldn't be called"))) + +# @brief Value to return from functions passed to forEach, enumerate, window... to stop iteration early +# @author https://github.com/SuperFola +(let stopIteration _stopIteration_helper) + # @brief Iterate over a given list and run a given function on every element. # @param _L the list to iterate over -# @param _func the function to call on each element -# @details The original list is not modified. +# @param _func the function to call on each element. It can return list:stopIteration to stop iteration early +# @details The original list is not modified. Returns true if it returns early, false otherwise # =begin # (let collection [1 2 5 12]) # (list:forEach collection (fun (element) { # (print element) })) # =end # @author https://github.com/SuperFola -(let forEach (fun (_L _func) { +(let forEach (fun ((ref _L) _func) { (mut _index 0) + (mut _early false) (while (< _index (len _L)) { (mut _element (@ _L _index)) - (_func _element) - (set _index (+ 1 _index)) }) })) + (if (= (_func _element) stopIteration) + { + (set _index (len _L)) + (set _early true) }) + (set _index (+ 1 _index)) }) + + _early })) # @brief Iterate over a given list and run a given function on every element, passing its index as well. # @param _L the list to iterate over -# @param _func a binary function to call on each element with (index, element) -# @details The original list is not modified. +# @param _func a binary function to call on each element with (index, element). It can return list:stopIteration to stop iteration early +# @details The original list is not modified. Returns true if it returns early, false otherwise # =begin # (let collection [1 2 5 12]) # (list:enumerate collection (fun (idx element) { # (print idx " " element) })) # =end # @author https://github.com/SuperFola -(let enumerate (fun (_L _func) { +(let enumerate (fun ((ref _L) _func) { (mut _index 0) + (mut _early false) (while (< _index (len _L)) { (mut _element (@ _L _index)) - (_func _index _element) - (set _index (+ 1 _index)) }) })) + (if (= (_func _index _element) stopIteration) + { + (set _index (len _L)) + (set _early true) + }) + (set _index (+ 1 _index)) }) + + _early })) # @brief Iterate over a given list and multiply all the elements with the others. # @param _L the list to iterate over @@ -122,7 +141,7 @@ # (let p (list:product collection)) # => 120 # =end # @author https://github.com/Unactived -(let product (fun (_L) { +(let product (fun ((ref _L)) { (mut _index 0) (mut _output 1) @@ -139,7 +158,7 @@ # (let p (list:sum collection)) # => 20 # =end # @author https://github.com/Unactived -(let sum (fun (_L) { +(let sum (fun ((ref _L)) { (mut _index 0) (mut _output 0) @@ -155,7 +174,7 @@ # (let value (list:min [0 1 2 3 5 8])) # 0 # =end # @author https://github.com/SuperFola -(let min (fun (_L) { +(let min (fun ((ref _L)) { (mut _index 0) (mut _output nil) @@ -172,7 +191,7 @@ # (let value (list:max [0 1 2 3 5 8])) # 8 # =end # @author https://github.com/SuperFola -(let max (fun (_L) { +(let max (fun ((ref _L)) { (mut _index 0) (mut _output nil) @@ -182,7 +201,24 @@ (set _index (+ 1 _index)) }) _output })) -(import std.Math :min :max) +(import std.Math :min :max :even) + +# @brief Find the median in a list of numbers +# @param _L list of numbers +# @details The original list is not modified. +# =begin +# (let value (list:median [0 1 2 3 5 8])) # 2.5 +# =end +# @author https://github.com/SuperFola +(let median (fun ((ref _L)) { + (let _n (len _L)) + (if (= 0 _n) + nil + { + (let _s (sort _L)) + (if (math:even _n) + (/ (+ (@ _s (- (/ _n 2) 1)) (@ _s (/ _n 2))) 2) + (@ _s (/ _n 2))) }) })) # @brief Drop the first n elements of a list # @param _L the list to work on @@ -193,7 +229,7 @@ # (print (list:drop cool-stuff 4)) # [5 6 7 8 9] # =end # @author https://github.com/rstefanic, https://github.com/SuperFola -(let drop (fun (_L _n) +(let drop (fun ((ref _L) _n) (if (< _n (/ (len _L) 2)) (if (> _n 0) (drop (tail _L) (- _n 1)) @@ -216,7 +252,7 @@ # (print (list:dropWhile cool-stuff (fun (a) (< a 4)))) # [4 5 6 7 8 9] # =end # @author https://github.com/SuperFola -(let dropWhile (fun (_L _f) { +(let dropWhile (fun ((ref _L) _f) { (mut _index 0) (mut _output []) @@ -237,7 +273,7 @@ # (print (list:filter [1 2 3 4 5 6 7 8 9] math:even?)) # [2 4 6 8] # =end # @author https://github.com/rstefanic -(let filter (fun (_L _f) { +(let filter (fun ((ref _L) _f) { (mut _index 0) (mut _output []) @@ -256,15 +292,16 @@ # (print sorted) # [[3 5] [10 14] [12 18] [16 20]] # =end # @author https://github.com/SuperFola -(let sortByKey (fun (_L _key) { +(let sortByKey (fun ((ref _L) _key) { (if (empty? _L) [] { (let _pivot_val (head _L)) (let _pivot (_key _pivot_val)) - (mut _less (sortByKey (filter (tail _L) (fun (e) (< (_key e) _pivot))) _key)) - (let _more (sortByKey (filter (tail _L) (fun (e) (>= (_key e) _pivot))) _key)) + (mut _rest (tail _L)) + (mut _less (sortByKey (filter _rest (fun (e) (< (_key e) _pivot))) _key)) + (let _more (sortByKey (filter _rest (fun (e) (>= (_key e) _pivot))) _key)) (concat! _less [_pivot_val] _more) _less }) })) @@ -277,7 +314,7 @@ # (print (list:map [1 2 3 4 5 6 7 8 9] (fun (e) (* e e)))) # [1 4 9 25 36 49 64 81] # =end # @author https://github.com/rstefanic -(let map (fun (_L _f) { +(let map (fun ((ref _L) _f) { (mut _index 0) (mut _output []) @@ -295,7 +332,7 @@ # (print (list:reduce cool (fun (a b) (+ a b)))) # 45 # =end # @author https://github.com/Unactived -(let reduce (fun (_L _f) { +(let reduce (fun ((ref _L) _f) { (mut _index 1) (mut _output (@ _L 0)) @@ -312,7 +349,7 @@ # (print (list:flatten cool)) # [1 2 3 4 5 6 7 8 9] # =end # @author https://github.com/SuperFola -(let flatten (fun (_L) { +(let flatten (fun ((ref _L)) { (mut _index 0) (mut _output []) @@ -334,12 +371,12 @@ # (print (list:flatMap cool (fun (a) [a a]))) # [1 1 2 2 3 3 4 4] # =end # @author https://github.com/SuperFola -(let flatMap (fun (_L _f) { +(let flatMap (fun ((ref _L) _f) { (mut _index 0) (mut _output []) (while (< _index (len _L)) { - (mut _res (_f (@ _L _index))) + (let _res (_f (@ _L _index))) (if (= "List" (type _res)) (concat! _output _res) @@ -355,7 +392,7 @@ # (print (list:take [1 2 3 4 5 6 7 8 9] 4)) # [1 2 3 4] # =end # @author https://github.com/rstefanic -(let take (fun (_L _n) { +(let take (fun ((ref _L) (mut _n)) { (mut _index 0) (mut _output []) (set _n (math:min _n (len _L))) @@ -373,17 +410,17 @@ # (print (list:takeWhile [1 2 3 4 5 6 7 8 9 10] (fun (a) (< a 4)))) # [1 2 3] # =end # @author https://github.com/rakista112 -(let takeWhile (fun (_L _f) { +(let takeWhile (fun ((ref _L) _f) { (mut _index 0) (mut _output []) - (mut continue true) + (mut _continue true) - (while (and (< _index (len _L)) continue) + (while (and (< _index (len _L)) _continue) (if (_f (@ _L _index)) { (append! _output (@ _L _index)) (set _index (+ 1 _index)) } - (set continue false))) + (set _continue false))) _output })) # @brief Partition a list in two, given a predicate @@ -395,7 +432,7 @@ # (print (list:partition a (fun (c i) (= 0 (mod c 2))))) # [[2] [1 3]] # =end # @author https://github.com/rakista112 -(let partition (fun (_L _f) { +(let partition (fun ((ref _L) _f) { (mut _index 0) (mut _pass []) (mut _fail []) @@ -417,7 +454,7 @@ # (print (list:unzip zipped)) # [[1 2 3 4] [5 6 7 8]] # =end # @author https://github.com/Unactived -(let unzip (fun (_L) { +(let unzip (fun ((ref _L)) { (let _m (len _L)) (mut _list1 []) (mut _list2 []) @@ -440,7 +477,7 @@ # (print (list:zip a b)) # [[1 5] [2 6] [3 7] [4 8]] # =end # @author https://github.com/Unactived -(let zip (fun (_a _b) { +(let zip (fun ((ref _a) (ref _b)) { (let _m (math:min (len _a) (len _b))) (mut _c []) (mut _index 0) @@ -458,7 +495,7 @@ # (print (list:zipWithIndex a)) # [[0 5] [1 6] [2 7] [3 8]] # =end # @author https://github.com/SuperFola -(let zipWithIndex (fun (_L) { +(let zipWithIndex (fun ((ref _L)) { (mut _output []) (mut _index 0) (let _len (len _L)) @@ -477,7 +514,7 @@ # (print (list:foldLeft a 0 (fun (a b) (+ a b)))) # 10 # =end # @author https://github.com/SuperFola -(let foldLeft (fun (_L _init _f) { +(let foldLeft (fun ((ref _L) _init _f) { (mut _index 0) (mut _val _init) @@ -495,7 +532,7 @@ # (print (list:forAll a f)) # true # =end # @author https://github.com/Gryfenfer97 -(let forAll (fun (_L _f) { +(let forAll (fun ((ref _L) _f) { (mut _verified true) (mut _index 0) @@ -514,7 +551,7 @@ # (print (list:any a f)) # true # =end # @author https://github.com/Gryfenfer97 -(let any (fun (_L _f) { +(let any (fun ((ref _L) _f) { (mut _verified false) (mut _index 0) @@ -534,7 +571,7 @@ # (print (list:none [4 5 6] f)) # true # =end # @author https://github.com/SuperFola -(let none (fun (_L _f) (not (any _L _f)))) +(let none (fun ((ref _L) _f) (not (any _L _f)))) # @brief Count the number of elements in a list that match a condition # @param _L the list to work on @@ -545,7 +582,7 @@ # (print (list:countIf lst is_even)) # 4 # =end # @author https://github.com/SuperFola -(let countIf (fun (_L _f) { +(let countIf (fun ((ref _L) _f) { (let _inner (fun (lst cond acc) (if (not (empty? lst)) (_inner @@ -595,7 +632,7 @@ # (print (list:chunkBy indices 3)) # [[1 2 3] [4 5 6] [7 8 9]] # =end # @author https://github.com/SuperFola -(let chunkBy (fun (_L _length) { +(let chunkBy (fun ((ref _L) _length) { (assert (> _length 0) "list:chunkBy needs a chunk size of at least 1") (mut _output []) (mut _current []) @@ -627,7 +664,7 @@ # (print (list:insert b 1 [1 2])) # [0 1 2 9] # =end # @author https://github.com/SuperFola -(let insert (fun (_L _index _value) { +(let insert (fun ((ref _L) _index (mut _value)) { (let _size (len _L)) (assert (<= _index _size) "list:insert can not insert a value outside the list") @@ -646,10 +683,10 @@ (concat (head _parts) _value (@ _parts 1)) })) })) # @brief Create a sliding window of a given size on a list -# @details The original list is not modified +# @details The original list is not modified. Returns true if it returns early, false otherwise # @param _L list to iterate over # @param _size window size, must be at least 1 -# @param _f function to call with the window +# @param _f function to call with the window. It can return list:stopIteration to stop iteration early # =begin # (let f (fun (lst) (print lst)) # (list:window [1 2 3 4 5] 3 f) @@ -658,9 +695,10 @@ # # [3 4 5] # =end # @author https://github.com/SuperFola -(let window (fun (_L _size _f) { +(let window (fun ((ref _L) _size _f) { (assert (> _size 0) "window size must be at least 1") + (mut _early false) (mut _i 0) (while (< _i (len _L)) { (mut _win []) @@ -669,11 +707,17 @@ (append! _win (@ _L (+ _i _j))) (set _j (+ 1 _j)) }) - (_f _win) + (if (= (_f _win) stopIteration) + { + (set _i (len _L)) + (set _early true) + }) (if (>= (+ _i _size) (len _L)) (set _i (len _L))) - (set _i (+ 1 _i)) }) })) + (set _i (+ 1 _i)) }) + + _early })) # @brief Transpose a list of lists or list of strings # @details The original list is not modified. Each element should have the same length @@ -700,3 +744,21 @@ (set _i (+ 1 _i)) }) _output })) +# @brief Get the unique values in a given list +# @details The original list is not modified. +# @param _L list to extract unique values from +# =begin +# (let data [1 1 2 3 4 3 4 5]) +# (print (list:unique data)) # [1 2 3 4 5] +# =end +# @author https://github.com/SuperFola +(let unique (fun ((ref _L)) { + (mut _vals (dict)) + (mut _i 0) + (while (< _i (len _L)) { + (let v (@ _L _i)) + (if (not (builtin__dict:contains _vals v)) + (builtin__dict:add _vals v _i)) + (set _i (+ 1 _i)) }) + + (builtin__dict:keys _vals) })) diff --git a/Macros.ark b/Macros.ark index 35c34a6..fab345f 100644 --- a/Macros.ark +++ b/Macros.ark @@ -116,3 +116,20 @@ # =end # @author https://github.com/SuperFola (macro until (cond body) (while (not cond) body)) + +# @brief Unpack a pair of two elements into two new variables +# @param pair list of two elements to unpack +# @param outx name of the variable which will hold the first element +# @param outy name of the variable which will hold the second element +# =begin +# (let data [[1 2] [2 3] [5 8]]) +# (list:forEach data +# (fun (pair) { +# (unpackPair pair x y) +# (print (+ x y)) })) +# =end +# @author https://github.com/SuperFola +(macro unpackPair (pair outx outy) { + (let outx ($as-is (@ pair 0))) + (let outy ($as-is (@ pair 1))) }) + diff --git a/Math.ark b/Math.ark index d2058a6..47ce917 100644 --- a/Math.ark +++ b/Math.ark @@ -428,3 +428,17 @@ (set _i (+ 1 _i)) }) _result })) + +# @brief Compute a percentage of how much b is better than a +# @details Returns a positive number when _a is bigger than _b, negative otherwise +# @param _a the base +# @param _b new measure to compare +# =begin +# (let base 55) # something takes 55ms to run +# (let new 43) # now it takes 43ms +# (print (math:improvementRatioPercentage base new)) # 27.9069767442 +# # 'base' is 27%~ slower than 'new' +# =end +# @author https://github.com/SuperFola +(let improvementRatioPercentage (fun (_a _b) + (* 100 (- (/ _a _b) 1)))) diff --git a/Random.ark b/Random.ark index ad38f18..6ecf4c0 100644 --- a/Random.ark +++ b/Random.ark @@ -21,7 +21,7 @@ # (let randomized (random:shuffle data)) # =end # @author https://github.com/SuperFola -(let shuffle (fun (_L) { +(let shuffle (fun ((mut _L)) { (mut _output []) (while (not (empty? _L)) { diff --git a/Range.ark b/Range.ark index 0a6b146..bec4f7c 100644 --- a/Range.ark +++ b/Range.ark @@ -12,7 +12,7 @@ # (print (obj.asList)) # [1 2 3 4 5 6 7 8 9] # =end # @author https://github.com/SuperFola -(let range (fun (i _b) { +(let range (fun ((mut i) _b) { (let asList (fun () { # i and _b are going to be captured by the caller (mut _output []) diff --git a/String.ark b/String.ark index 6e30120..dc8a154 100644 --- a/String.ark +++ b/String.ark @@ -276,7 +276,7 @@ # @brief Split a string in multiple substrings in a list, given a separator # @param _string the string to split # @param _separator the separator to use for splitting -# @details Returns a list of strings. Example : +# @details Returns a list of strings # =begin # (let message "hello world, I like boats") # (let as-list (string:split message " ")) diff --git a/Testing.ark b/Testing.ark index ef06abc..b597f85 100644 --- a/Testing.ark +++ b/Testing.ark @@ -197,10 +197,17 @@ # (test:expect (return_true) "return true"}) # =end # @author https://github.com/SuperFola -(macro test:expect (_cond ..._desc) { - (if (!= true ($as-is _cond)) - (testing:_report_error true ($as-is _cond) "true" ($repr _cond) _desc) - (testing:_report_success)) }) +(macro test:expect (_cond ..._desc) {{ + (set testing:_cond_res ($as-is _cond)) + (if (!= true testing:_cond_res) + (testing:_report_error true testing:_cond_res "true" ($repr _cond) _desc) + (testing:_report_success)) }}) + +(mut _cond_res nil) +(mut _expr_res nil) +(mut _expected_res nil) +(mut _unexpected_res nil) +(mut _value_res nil) # @brief Compare two values that should be equal and generate a test case # @param _expr computed value to test @@ -212,10 +219,12 @@ # (test:eq 5 (foo) "foo should return 5")}) # =end # @author https://github.com/SuperFola -(macro test:eq (_expr _expected ..._desc) { - (if (= ($as-is _expected) ($as-is _expr)) +(macro test:eq (_expr _expected ..._desc) {{ + (set testing:_expected_res ($as-is _expected)) + (set testing:_expr_res ($as-is _expr)) + (if (= testing:_expected_res testing:_expr_res) (testing:_report_success) - (testing:_report_error ($as-is _expected) ($as-is _expr) ($repr _expected) ($repr _expr) _desc)) }) + (testing:_report_error testing:_expected_res testing:_expr_res ($repr _expected) ($repr _expr) _desc)) }}) # @brief Compare two values that should **not** be equal and generate a test case # @param _value tested value @@ -226,10 +235,12 @@ # (test:neq 0 (my_function 1 2 3))}) # =end # @author https://github.com/SuperFola -(macro test:neq (_value _unexpected ..._desc) { - (if (!= ($as-is _unexpected) ($as-is _value)) +(macro test:neq (_value _unexpected ..._desc) {{ + (set testing:_unexpected_res ($as-is _unexpected)) + (set testing:_value_res ($as-is _value)) + (if (!= testing:_unexpected_res testing:_value_res) (testing:_report_success) - (testing:_report_error ($as-is _unexpected) ($as-is _value) ($repr _unexpected) ($repr _value) _desc)) }) + (testing:_report_error testing:_unexpected_res testing:_value_res ($repr _unexpected) ($repr _value) _desc)) }}) # @brief Generate the code for a test suite # @details Create two variables: _name-output (a list: [successes, failures]) and _name-status (boolean, true on success) diff --git a/tests/io-tests.ark b/tests/io-tests.ark index 2d034a6..1d0a706 100644 --- a/tests/io-tests.ark +++ b/tests/io-tests.ark @@ -25,6 +25,7 @@ (io:writeFile "test.txt" "hello, world!") (test:expect (io:fileExists? "test.txt")) (test:eq (io:readFile "test.txt") "hello, world!") + (test:eq (io:readLinesFile "test.txt") ["hello, world!"]) (io:appendToFile "test.txt" "bis") (test:eq (io:readFile "test.txt") "hello, world!bis") (test:expect (> (len (io:listFiles "./")) 0)) diff --git a/tests/list-tests.ark b/tests/list-tests.ark index 3f0e118..8933b2f 100644 --- a/tests/list-tests.ark +++ b/tests/list-tests.ark @@ -64,6 +64,14 @@ (test:eq (list:max b) 6) (test:eq (list:max [-1]) -1) }) + (test:case "median" { + (test:eq (list:median []) nil) + (test:eq (list:median [1]) 1) + (test:eq (list:median [1 2]) 1.5) + (test:eq (list:median [2 1 3]) 2) + (test:eq (list:median [1 2 2 1]) 1.5) + (test:eq (list:median [1 2 2 1 3]) 2) }) + (test:case "drop" { (test:eq (list:drop a 0) [1 2 3]) (test:eq (list:drop a 1) [2 3]) @@ -196,5 +204,11 @@ (test:eq (list:transpose data) [["1" " "] ["2" "4"] ["3" "5"] [" " " "] ["3" "6"] ["2" "4"] ["8" " "] [" " " "] [" " "3"] ["5" "8"] ["1" "7"]]) (test:eq (list:transpose (list:transpose zipped)) zipped) (test:eq (list:transpose [[]]) []) - (test:eq (list:transpose [[1]]) [[1]]) }) }) + (test:eq (list:transpose [[1]]) [[1]]) }) + + (test:case "unique" { + (test:eq (list:unique []) []) + (test:eq (list:unique [1 1 1 1]) [1]) + (test:eq (list:unique [1 -1 1 -1]) [1 -1]) + (test:eq (list:unique ["a" "b" "a" "b" "c" "d"]) ["a" "b" "c" "d"]) }) }) diff --git a/tests/math-tests.ark b/tests/math-tests.ark index 3271bbc..4db46f1 100644 --- a/tests/math-tests.ark +++ b/tests/math-tests.ark @@ -127,4 +127,8 @@ (test:case "vectors" { (test:eq 32 (math:dotProduct [1 2 3] [4 5 6])) - (test:eq 3 (math:dotProduct [1 3 -5] [4 -2 -1])) })}) + (test:eq 3 (math:dotProduct [1 3 -5] [4 -2 -1])) }) + + (test:eq 0 (math:improvementRatioPercentage 12 12)) + (test:eq 100 (math:improvementRatioPercentage 24 12)) + (test:eq 50 (math:improvementRatioPercentage 18 12)) }) diff --git a/tests/string-tests.ark b/tests/string-tests.ark index cbc3bfc..f0992d9 100644 --- a/tests/string-tests.ark +++ b/tests/string-tests.ark @@ -7,6 +7,7 @@ (test:eq (builtin__string:find "aaacd" "a" 1) (string:findAfter "aaacd" "a" 1)) (test:eq (builtin__string:removeAt "abcd" 1) (string:removeAt "abcd" 1)) + (test:eq (builtin__string:removeAt "abcd" -1) "abc") (test:eq (builtin__string:setAt "abcd" 1 "z") (string:setAt "abcd" 1 "z")) (test:expect (string:contains? "hello, world" "h"))