- Bubble sort and insertion sort against json sort
- Binary search against a linear scan
- Recursion: a script that calls itself
- Fibonacci with a memo stored in a node
- Stack and queue with atom lists
- Palindromes and anagrams
- Word frequency with an index and json sort
- Sieve of Eratosthenes
- Euclid's GCD, fractions and exact decimals
- Matrix product: triple loop against matrix mul
Bubble sort and insertion sort against json sort
Sort 12 values three ways: a bubble sort with repeat and for that swaps neighbours with json uarray, an insertion sort with a while loop that shifts the bigger values, and the built-in json sort. The script counts passes, swaps and shifts, times each method with date current_ms and checks the three arrays are equal.
#1. Load the same list three times; -> "[data]" "[42, 7, 93, 15, 61, 3, 88, 27, 54, 70, 11, 36]"; json load "tuto_alg_bubble" [data]; json load "tuto_alg_insert" [data]; json load "tuto_alg_builtin" [data]; -> "[n]" (json count "tuto_alg_bubble" "/"); #2. Bubble sort: swap neighbours until a pass makes no swap; -> "[t0]" (date current_ms); -> "[swaps]" 0; -> "[passes]" 0; -> "[swapped]" 0; #repeat runs its body before testing the condition; repeat (> [swapped] 0) { -> "[swapped]" 0; ++ "[passes]"; for (-> "[i]" 0) (< [i] (- [n] 1)) (++ "[i]") { -> "[a]" (json select "tuto_alg_bubble" (concat "/[" [i] "]")); -> "[b]" (json select "tuto_alg_bubble" (concat "/[" (+ [i] 1) "]")); if (> [a] [b]) { json uarray "tuto_alg_bubble" / [i] [b] INT; json uarray "tuto_alg_bubble" / (+ [i] 1) [a] INT; ++ "[swaps]"; -> "[swapped]" 1; }; }; }; -> "[ms_bubble]" (- (date current_ms) [t0]); #3. Insertion sort: shift the bigger values right, then drop the key in the hole; -> "[t0]" (date current_ms); -> "[shifts]" 0; for (-> "[i]" 1) (< [i] [n]) (++ "[i]") { -> "[key]" (json select "tuto_alg_insert" (concat "/[" [i] "]")); -> "[j]" (- [i] 1); -> "[go]" 1; while (and (>= [j] 0) (== [go] 1)) { -> "[v]" (json select "tuto_alg_insert" (concat "/[" [j] "]")); if (> [v] [key]) { json uarray "tuto_alg_insert" / (+ [j] 1) [v] INT; ++ "[shifts]"; -> "[j]" (- [j] 1); } {-> "[go]" 0;}; }; json uarray "tuto_alg_insert" / (+ [j] 1) [key] INT; }; -> "[ms_insert]" (- (date current_ms) [t0]); #4. The built-in sort; -> "[t0]" (date current_ms); json sort "tuto_alg_builtin" "/" "" ASC; -> "[ms_sort]" (- (date current_ms) [t0]); #5. Compare and free the memory; -> "[r1]" (json doc "tuto_alg_bubble"); -> "[r2]" (json doc "tuto_alg_insert"); -> "[r3]" (json doc "tuto_alg_builtin"); json unload "tuto_alg_bubble"; json unload "tuto_alg_insert"; json unload "tuto_alg_builtin"; concat "Bubble sort: " [passes] " passes, " [swaps] " swaps, " [ms_bubble] " ms" [_n_] "Insertion sort: " [shifts] " shifts, " [ms_insert] " ms" [_n_] "json sort: " [ms_sort] " ms" [_n_] "Same result: " (and (equal [r1] [r3]) (equal [r2] [r3])) [_n_] (string regexp_replace [r3] "\s+" "");
Bubble sort: 9 passes, 33 swaps, 1 ms Insertion sort: 33 shifts, 1 ms json sort: 0 ms Same result: 1 [3,7,11,15,27,36,42,54,61,70,88,93]
- Sort an array of objects by a field:
json sort "key" "/" "price" DESC - Count the inversions of a list: it is exactly the number of bubble swaps
Binary search against a linear scan
Find an invoice number in a sorted array of 500 ids. Binary search halves the interval low..high with arith iquo and a case on the comparison, while the linear scan stops with break. The script compares the number of comparisons with log2 of 500.
#1. Build a sorted array of 500 invoice numbers: 1000, 1007, 1014...; json load "tuto_alg_ids" "[]"; for (-> "[i]" 0) (< [i] 500) (++ "[i]") { json iarray "tuto_alg_ids" / (+ 1000 (* [i] 7)) INT; }; -> "[target]" 4283; #2. Binary search: halve the interval low..high at each step; -> "[low]" 0; -> "[high]" (- (json count "tuto_alg_ids" "/") 1); -> "[found]" -1; -> "[steps]" 0; while (and (<= [low] [high]) (== [found] -1)) { ++ "[steps]"; -> "[mid]" (arith iquo (+ [low] [high]) 2); -> "[v]" (json select "tuto_alg_ids" (concat "/[" [mid] "]")); case (== [v] [target]) {-> "[found]" [mid];} (< [v] [target]) {-> "[low]" (+ [mid] 1);} {-> "[high]" (- [mid] 1);} ; }; #3. Linear search for comparison; -> "[scan]" 0; for (-> "[i]" 0) (< [i] 500) (++ "[i]") { ++ "[scan]"; if (== (json select "tuto_alg_ids" (concat "/[" [i] "]")) [target]) {break;}; }; json unload "tuto_alg_ids"; concat "Value " [target] " found at index " [found] [_n_] "Binary search: " [steps] " comparisons, log2 of 500 = " (math round (math log_base 500 2) 2) [_n_] "Linear search: " [scan] " comparisons";
Value 4283 found at index 469 Binary search: 9 comparisons, log2 of 500 = 8.97 Linear search: 470 comparisons
- Return the insertion point when the value is missing: it is [low] at the end of the loop
- Use
math log_baseto predict the worst case for 1 million values
Recursion: a script that calls itself
A tuto script computes n! by calling itself with execute, multiplying with bi* so that 50! keeps its 65 digits. The result is checked against arith factorial, then the script shows the difference of scope: execute runs in a new environment, call and include run in the caller's environment and overwrite [n]. The script is deleted at the end.
#1. A script that calls itself: n! = n * (n-1)!; script create exe "tuto.alg.fact" false 1 (param (var "[n]" {type is_int [n]} "A positive integer" is_null:false is_empty:false "5") ) "Factorial by recursion" { if (<= [n] 1) { 1; } { bi* [n] (execute "tuto.alg.fact.exe" "[n]" (- [n] 1)); }; } "n! as a big integer"; #2. Recursion depth 50, with big integers; -> "[t0]" (date current_ms); -> "[f50]" (execute "tuto.alg.fact.exe" "[n]" 50); -> "[ms]" (- (date current_ms) [t0]); #3. Check against the built-in function; -> "[same]" (equal [f50] (arith factorial 50)); #4. execute runs in a new environment, call and include in the parent one; -> "[n]" 99; execute "tuto.alg.fact.exe" "[n]" 4; -> "[after_execute]" [n]; call "tuto.alg.fact.exe" "[n]" 4; -> "[after_call]" [n]; -> "[n]" 99; include "tuto.alg.fact.exe" "[n]" 3; -> "[after_include]" [n]; script delete "tuto.alg.fact.exe"; concat "50! = " [f50] [_n_] (string length [f50]) " digits in " [ms] " ms, same as arith factorial: " [same] [_n_] "[n] after execute: " [after_execute] ", after call: " [after_call] ", after include: " [after_include];
50! = 30414093201713378043612608166064768844377641568960512000000000000 65 digits in 2 ms, same as arith factorial: 1 [n] after execute: 99, after call: 4, after include: 3
- Try
execute "tuto.alg.fact.exe" "[n]" 200: recursion depth 200 still works - Use
includewhen the called script must see the caller's variables
Fibonacci with a memo stored in a node
Naive recursive Fibonacci makes thousands of calls; storing each result in a persistent node turns it into a linear algorithm. A sequence counts the calls (its value is in base 36, converted with math base_to_base), the node keeps one field per rank, and the script compares fib 16 naive with fib 90 memoized. Script, node and sequence are deleted at the end.
#1. A persistent memo node and a call counter; node create "tuto_alg_fib"; sequence add "tuto_alg_calls" 0; #2. fib n = fib n-1 + fib n-2, results cached in the node when memo is 1; script create exe "tuto.alg.fib" false 1 (param (var "[n]" {type is_int [n]} "The rank" is_null:false is_empty:false "10") (var "[memo]" {type is_bool [memo] 1 0;} "Use the memo node" is_null:false is_empty:false "1") ) "Fibonacci by recursion, with an optional memo in a node" { sequence increment "tuto_alg_calls"; -> "[cached]" (node select "tuto_alg_fib" (concat "/f" [n])); case (< [n] 2) {[n];} (and (== [memo] 1) (is not null [cached])) {[cached];} { -> "[r]" (bi+ (execute "tuto.alg.fib.exe" "[n]" (- [n] 1) "[memo]" [memo]) (execute "tuto.alg.fib.exe" "[n]" (- [n] 2) "[memo]" [memo])); if (== [memo] 1) {node iobject "tuto_alg_fib" / (concat "f" [n]) [r] STR;}; [r]; } ; } "fib n as a big integer"; #3. Naive recursion: the number of calls explodes; -> "[t0]" (date current_ms); -> "[naive]" (execute "tuto.alg.fib.exe" "[n]" 16 "[memo]" 0); -> "[naive_calls]" (sequence get current "tuto_alg_calls"); -> "[naive_ms]" (- (date current_ms) [t0]); #4. With the memo, rank 90 needs few calls; sequence update "tuto_alg_calls" 0; -> "[t0]" (date current_ms); -> "[fast]" (execute "tuto.alg.fib.exe" "[n]" 90 "[memo]" 1); -> "[memo_calls]" (sequence get current "tuto_alg_calls"); -> "[memo_ms]" (- (date current_ms) [t0]); -> "[cached]" (node count "tuto_alg_fib" "/"); #5. Sequence values are in base 36: convert them, then clean up; -> "[naive_calls]" (math base_to_base [naive_calls] 36 10); -> "[memo_calls]" (math base_to_base [memo_calls] 36 10); script delete "tuto.alg.fib.exe"; node delete "tuto_alg_fib"; sequence remove "tuto_alg_calls"; concat "Naive fib 16 = " [naive] ": " [naive_calls] " calls, " [naive_ms] " ms" [_n_] "Memo fib 90 = " [fast] ": " [memo_calls] " calls, " [memo_ms] " ms, " [cached] " fields in the node";
Naive fib 16 = 987: 3193 calls, 241 ms Memo fib 90 = 2880067194370816120: 179 calls, 23 ms, 90 fields in the node
- Keep the node between runs: the second call of fib 90 is a single node select
- Count the fields of the memo with
node count
Stack and queue with atom lists
An atom list is a ready-made data structure. As a stack with a # at the bottom, it checks that the brackets of an expression are balanced: switch on the character code pushes the expected closing bracket, atom get_last and atom before_exclud pop it. As a queue, it runs a round-robin scheduler with a quantum of 2 units.
#1. A stack as an atom list: # is the bottom, push adds the expected closing bracket at the end, pop removes the last atom; -> "[expr]" "{[(a+b)*c]-(d/e)}"; -> "[stack]" "#"; -> "[ok]" 1; -> "[max_depth]" 0; -> "[closing]" 0; for (-> "[i]" 0) (and (< [i] (string length [expr])) (== [ok] 1)) (++ "[i]") { -> "[c]" (string substr [expr] [i] (+ [i] 1)); switch (string ascii [c]) (40) {-> "[stack]" (concat [stack] ",)");} (91) {-> "[stack]" (concat [stack] ",]");} (123) {-> "[stack]" (concat [stack] ",}");} (41) {-> "[closing]" 1;} (93) {-> "[closing]" 1;} (125) {-> "[closing]" 1;} {-> "[closing]" 0;} ; -> "[max_depth]" (math max [max_depth] (- (atom size [stack] ",") 1)); if (== [closing] 1) { #pop: the last atom must be the expected closing bracket; if (equal (atom get_last [stack] ",") [c]) { -> "[stack]" (atom before_exclud [stack] (atom size [stack] ",") ","); } {-> "[ok]" 0;}; -> "[closing]" 0; }; }; -> "[balanced]" (and [ok] (equal [stack] "#")); #2. A queue as an atom list: round robin with a quantum of 2 units; -> "[queue]" "A:5,B:2,C:4"; -> "[order]" ""; while (not (is empty [queue])) { -> "[job]" (atom get_first [queue] ","); -> "[queue]" (string substr [queue] (math min (+ (string length [job]) 1) (string length [queue]))); -> "[name]" (atom get [job] 1 ":"); -> "[left]" (- (atom get [job] 2 ":") 2); -> "[order]" (concat [order] [name]); if (> [left] 0) { if (is empty [queue]) {-> "[queue]" (concat [name] ":" [left]);} {-> "[queue]" (concat [queue] "," [name] ":" [left]);}; }; }; concat "Expression " [expr] " balanced: " [balanced] ", max depth " [max_depth] [_n_] "Round robin order: " [order];
Expression {[(a+b)*c]-(d/e)} balanced: 1, max depth 3
Round robin order: ABCACA- Use
atom sizeto show the depth of the stack at each step - Replace the queue with a JSON array:
json iarrayto enqueue,json darray ... 0to dequeue
Palindromes and anagrams
Normalize each phrase with string lower, string unaccent and a regex, then test it with two pointers walking to the middle and cross-check with string reverse. For anagrams, the key of a word is its letters split, sorted with string order and joined: words with the same key are anagrams.
#1. Normalize: lower case, no accents, letters only; -> "[phrases]" "Ésope reste ici et se repose|A man, a plan, a canal: Panama|MentDB is a database"; -> "[report]" ""; for (-> "[k]" 1) (<= [k] (atom size [phrases] "|")) (++ "[k]") { -> "[p]" (atom get [phrases] [k] "|"); -> "[clean]" (string regexp_replace (string unaccent (string lower [p])) "[^a-z]" ""); #2. Two pointers walk to the middle; -> "[i]" 0; -> "[j]" (- (string length [clean]) 1); -> "[pal]" 1; while (and (< [i] [j]) (== [pal] 1)) { if (not (equal (string substr [clean] [i] (+ [i] 1)) (string substr [clean] [j] (+ [j] 1)))) {-> "[pal]" 0;}; ++ "[i]"; -> "[j]" (- [j] 1); }; #3. Cross-check with string reverse; -> "[report]" (concat [report] [p] " -> two pointers: " [pal] ", reverse: " (equal [clean] (string reverse [clean])) [_n_]); }; #4. Anagrams: same key once the letters are sorted and joined; -> "[words]" "listen,silent,enlist,tinsel,lentil,inlets"; -> "[ref]" ""; -> "[anagrams]" ""; for (-> "[k]" 1) (<= [k] (atom size [words] ",")) (++ "[k]") { -> "[w]" (atom get [words] [k] ","); -> "[key]" (string regexp_replace (string order (string split [w] "" -1)) "[^a-z]" ""); if (== [k] 1) {-> "[ref]" [key];}; if (equal [key] [ref]) {-> "[anagrams]" (concat [anagrams] " " [w]);}; }; concat [report] "Key of listen: " [ref] [_n_] "Anagrams:" [anagrams];
Ésope reste ici et se repose -> two pointers: 1, reverse: 1 A man, a plan, a canal: Panama -> two pointers: 1, reverse: 1 MentDB is a database -> two pointers: 0, reverse: 0 Key of listen: eilnst Anagrams: listen silent enlist tinsel inlets
- Group a whole dictionary by anagram key in a JSON object of arrays
- Measure how far two words are with
string levenshtein_distance
Word frequency with an index and json sort
Count the words of a text: clean it with a regex, split it, skip stop words with atom find, keep an index object word -> position and an array of counters updated with json uobject. json sort by the counter field gives the top 3.
#1. Clean the text and split it into words; -> "[text]" "The stack runs the script, the job runs the stack. A script calls a script; the job waits, the stack retries the script."; -> "[words]" (string split (string trim (string regexp_replace (string lower [text]) "[^a-z ]" "")) " +" -1); json load "tuto_alg_words" [words]; -> "[stop]" "the,a,an,of"; #2. An index object word -> position and an array of counters; json load "tuto_alg_index" "{}"; json load "tuto_alg_freq" "[]"; -> "[nb]" (json count "tuto_alg_words" "/"); for (-> "[i]" 0) (< [i] [nb]) (++ "[i]") { -> "[w]" (json select "tuto_alg_words" (concat "/[" [i] "]")); if (== (atom find [stop] [w] ",") 0) { -> "[pos]" (json select "tuto_alg_index" (concat "/" [w])); if (is null [pos]) { json iobject "tuto_alg_index" / [w] (json count "tuto_alg_freq" "/") INT; json iarray "tuto_alg_freq" / (concat "{\"word\": \"" [w] "\", \"n\": 1}") OBJ; } { -> "[path]" (concat "/[" [pos] "]"); json uobject "tuto_alg_freq" [path] n (+ (json select "tuto_alg_freq" (concat [path] "/n")) 1) INT; }; }; }; #3. Most frequent first; json sort "tuto_alg_freq" "/" "n" DESC; -> "[top]" ""; for (-> "[i]" 0) (< [i] 3) (++ "[i]") { -> "[top]" (concat [top] (json select "tuto_alg_freq" (concat "/[" [i] "]/word")) "=" (json select "tuto_alg_freq" (concat "/[" [i] "]/n")) " "); }; -> "[distinct]" (json count "tuto_alg_freq" "/"); json unload "tuto_alg_words"; json unload "tuto_alg_index"; json unload "tuto_alg_freq"; concat [nb] " words, " [distinct] " distinct without stop words" [_n_] "Top 3: " (string trim [top]);
23 words, 7 distinct without stop words Top 3: script=4 stack=3 runs=2
- Store the index in a node to count words over many documents
- Show the frequencies as a bar chart with the Plot family
Sieve of Eratosthenes
Find all primes up to 300 with the classic sieve: a JSON array of flags, a while loop on p while p*p <= N and a for loop with a custom step that crosses out the multiples. The count is checked by trial division with arith is_prime, and arith next_prime gives the next one.
#1. One flag per integer 0..N, all set to 1; -> "[N]" 300; json load "tuto_alg_sieve" "[]"; for (-> "[i]" 0) (<= [i] [N]) (++ "[i]") {json iarray "tuto_alg_sieve" / 1 INT;}; #2. Cross out the multiples of each prime p from p*p, while p*p <= N; -> "[t0]" (date current_ms); -> "[p]" 2; while (<= (* [p] [p]) [N]) { if (== (json select "tuto_alg_sieve" (concat "/[" [p] "]")) 1) { for (-> "[m]" (* [p] [p])) (<= [m] [N]) (-> "[m]" (+ [m] [p])) { json uarray "tuto_alg_sieve" / [m] 0 INT; }; }; ++ "[p]"; }; #3. Collect the primes; -> "[primes]" ""; -> "[count]" 0; for (-> "[i]" 2) (<= [i] [N]) (++ "[i]") { if (== (json select "tuto_alg_sieve" (concat "/[" [i] "]")) 1) { ++ "[count]"; -> "[last]" [i]; if (<= [count] 10) {-> "[primes]" (concat [primes] [i] " ");}; }; }; -> "[ms_sieve]" (- (date current_ms) [t0]); json unload "tuto_alg_sieve"; #4. Cross-check by trial division with arith is_prime; -> "[t0]" (date current_ms); -> "[check]" 0; for (-> "[i]" 2) (<= [i] [N]) (++ "[i]") {if (arith is_prime [i]) {++ "[check]";};}; -> "[ms_check]" (- (date current_ms) [t0]); concat "Sieve up to " [N] ": " [count] " primes in " [ms_sieve] " ms, first ten: " (string trim [primes]) ", last: " [last] [_n_] "arith is_prime finds " [check] " in " [ms_check] " ms, next prime after " [N] ": " (arith next_prime [N]);
Sieve up to 300: 62 primes in 2 ms, first ten: 2 3 5 7 11 13 17 19 23 29, last: 293 arith is_prime finds 62 in 1 ms, next prime after 300: 307
- Stop the outer loop at
arith isqrtof N - Factor the composite numbers with
arith factors
Euclid's GCD, fractions and exact decimals
Euclid's algorithm in a while loop with a trace of each step, then on two consecutive 21-digit Fibonacci numbers (the worst case) with arith irem. The GCD reduces a fraction with bi/ and gives the LCM, checked against arith gcd, arith to_fraction and arith lcm. Finally repeat adds 0.1 ten times with + and with bd+ to show why money needs exact decimals.
#1. Euclid: gcd a b = gcd b, a mod b until b is 0; -> "[a]" 1071; -> "[b]" 462; -> "[trace]" ""; while (> [b] 0) { -> "[trace]" (concat [trace] "(" [a] "," [b] ") "); -> "[r]" (math mod [a] [b]); -> "[a]" [b]; -> "[b]" [r]; }; -> "[g]" [a]; #2. Worst case: two consecutive Fibonacci numbers of 21 digits, with big integers; -> "[x]" 218922995834555169026; -> "[y]" 135301852344706746049; -> "[steps]" 0; while (not (equal [y] 0)) { -> "[r]" (arith irem [x] [y]); -> "[x]" [y]; -> "[y]" [r]; ++ "[steps]"; }; #3. Reduce a fraction and check with the built-ins; -> "[num]" 1071; -> "[den]" 462; -> "[reduced]" (concat (bi/ [num] [g]) "/" (bi/ [den] [g])); -> "[lcm]" (bi/ (bi* [num] [den]) [g]); #4. Why money needs exact decimals: add 0.1 ten times; -> "[d]" 0; -> "[e]" 0; -> "[k]" 0; repeat (< [k] 10) { -> "[d]" (+ [d] 0.1); -> "[e]" (bd+ [e] 0.1); ++ "[k]"; }; concat "Euclid steps: " [trace] "-> gcd " [g] ", arith gcd: " (arith gcd 1071 462) [_n_] "Fibonacci pair: gcd " [x] " after " [steps] " steps" [_n_] "1071/462 = " [reduced] ", arith to_fraction: " (arith to_fraction (/ 1071 462)) ", lcm " [lcm] " = arith lcm " (arith lcm 1071 462) [_n_] "0.1 x 10 with +: " [d] ", with bd+: " [e];
Euclid steps: (1071,462) (462,147) (147,21) -> gcd 21, arith gcd: 21 Fibonacci pair: gcd 1 after 97 steps 1071/462 = 51/22, arith to_fraction: 51/22, lcm 23562 = arith lcm 23562 0.1 x 10 with +: 0.9999999999999999, with bd+: 1.0
- Add fractions exactly with
arith frac_add "1/3" "1/6" - Extended Euclid gives the modular inverse: compare with
arith invmod
Matrix product: triple loop against matrix mul
Build two 8x8 matrices as JSON strings with nested for loops, multiply them with the textbook triple loop and matrix get, then with matrix mul. Both are timed with date current_ms, and the Frobenius norm of their difference proves they are identical.
#1. Build two 8x8 matrices as JSON strings: A i,j = i+j and B i,j = i-j; -> "[n]" 8; -> "[A]" "["; -> "[B]" "["; for (-> "[i]" 1) (<= [i] [n]) (++ "[i]") { -> "[ra]" ""; -> "[rb]" ""; for (-> "[j]" 1) (<= [j] [n]) (++ "[j]") { -> "[sep]" (if (== [j] 1) {"";} {",";}); -> "[ra]" (concat [ra] [sep] (+ [i] [j])); -> "[rb]" (concat [rb] [sep] (- [i] [j])); }; -> "[sep]" (if (== [i] 1) {"";} {",";}); -> "[A]" (concat [A] [sep] "[" [ra] "]"); -> "[B]" (concat [B] [sep] "[" [rb] "]"); }; -> "[A]" (concat [A] "]"); -> "[B]" (concat [B] "]"); #2. Triple loop: C i,j = sum over k of A i,k * B k,j; -> "[t0]" (date current_ms); -> "[C]" "["; for (-> "[i]" 1) (<= [i] [n]) (++ "[i]") { -> "[row]" ""; for (-> "[j]" 1) (<= [j] [n]) (++ "[j]") { -> "[s]" 0; for (-> "[k]" 1) (<= [k] [n]) (++ "[k]") { -> "[s]" (+ [s] (* (matrix get [A] [i] [k]) (matrix get [B] [k] [j]))); }; -> "[row]" (concat [row] (if (== [j] 1) {"";} {",";}) [s]); }; -> "[C]" (concat [C] (if (== [i] 1) {"";} {",";}) "[" [row] "]"); }; -> "[C]" (concat [C] "]"); -> "[ms_loops]" (- (date current_ms) [t0]); #3. The built-in product; -> "[t0]" (date current_ms); -> "[M]" (matrix mul [A] [B]); -> "[ms_mul]" (- (date current_ms) [t0]); #4. Same matrix if the norm of the difference is 0; concat "Loops: " (math pow [n] 3) " multiplications in " [ms_loops] " ms" [_n_] "matrix mul: " [ms_mul] " ms" [_n_] "Norm of loops - mul: " (matrix norm_frobenius (matrix sub [C] [M])) [_n_] "Trace: " (matrix trace [M]) ", C 1,1 = " (matrix get [C] 1 1) ", C 8,8 = " (matrix get [M] 8 8);
Loops: 512 multiplications in 9 ms matrix mul: 0 ms Norm of loops - mul: 0 Trace: 0, C 1,1 = 196, C 8,8 = -308
- Raise a matrix to a power with
matrix powand compare with repeated loops - Solve A x = b with
matrix solveinstead of computing the inverse
