Profiles / Programming
AI

Algorithms in MQL

Sorting, searching, recursion, data structures and number algorithms: MQL is a real programming language.

10 recipes on this page · open in the interactive view

Recipes
  1. Bubble sort and insertion sort against json sort
  2. Binary search against a linear scan
  3. Recursion: a script that calls itself
  4. Fibonacci with a memo stored in a node
  5. Stack and queue with atom lists
  6. Palindromes and anagrams
  7. Word frequency with an index and json sort
  8. Sieve of Eratosthenes
  9. Euclid's GCD, fractions and exact decimals
  10. Matrix product: triple loop against matrix mul
executed on MentDB

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.

Pipeline
Script
admin
#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+" "");
Result
mentdb
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]
Go further
  • 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
executed on MentDB

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.

Pipeline
Script
admin
#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";
Result
mentdb
Value 4283 found at index 469
Binary search: 9 comparisons, log2 of 500 = 8.97
Linear search: 470 comparisons
Go further
  • Return the insertion point when the value is missing: it is [low] at the end of the loop
  • Use math log_base to predict the worst case for 1 million values
executed on MentDB

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.

Pipeline
Script
admin
#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];
Result
mentdb
50! = 30414093201713378043612608166064768844377641568960512000000000000
65 digits in 2 ms, same as arith factorial: 1
[n] after execute: 99, after call: 4, after include: 3
Go further
  • Try execute "tuto.alg.fact.exe" "[n]" 200: recursion depth 200 still works
  • Use include when the called script must see the caller's variables
executed on MentDB

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.

Pipeline
Script
admin
#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";
Result
mentdb
Naive fib 16 = 987: 3193 calls, 241 ms
Memo fib 90 = 2880067194370816120: 179 calls, 23 ms, 90 fields in the node
Go further
  • 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
executed on MentDB

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.

Pipeline
Script
admin
#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];
Result
mentdb
Expression {[(a+b)*c]-(d/e)} balanced: 1, max depth 3
Round robin order: ABCACA
Go further
  • Use atom size to show the depth of the stack at each step
  • Replace the queue with a JSON array: json iarray to enqueue, json darray ... 0 to dequeue
executed on MentDB

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.

Pipeline
Script
admin
#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];
Result
mentdb
É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
Go further
executed on MentDB

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.

Pipeline
Script
admin
#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]);
Result
mentdb
23 words, 7 distinct without stop words
Top 3: script=4 stack=3 runs=2
Go further
  • Store the index in a node to count words over many documents
  • Show the frequencies as a bar chart with the Plot family
executed on MentDB

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.

Pipeline
Script
admin
#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]);
Result
mentdb
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
Go further
executed on MentDB

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.

Pipeline
Script
admin
#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];
Result
mentdb
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
Go further
  • Add fractions exactly with arith frac_add "1/3" "1/6"
  • Extended Euclid gives the modular inverse: compare with arith invmod
executed on MentDB

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.

Pipeline
Script
admin
#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);
Result
mentdb
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
Go further
  • Raise a matrix to a power with matrix pow and compare with repeated loops
  • Solve A x = b with matrix solve instead of computing the inverse

Also in Programming

MB
MQL beginner
Variables, operators, if, loops and your first try/exception: learn MQL step by step.
10 recipes
MD
MQL developer
Atom lists, data types, constants, translations, session variables and error patterns.
10 recipes
BW
Backend & web service developer
Typed scripts, execute / call / include, generated calls and URLs, parameters and logs.
10 recipes
DT
Date & time developer
Calendars, business days, time zones, ISO and epoch, durations, quarters and week numbers.
10 recipes
SE
String & encoding developer
Cleaning, case styles, regex, URL/HTML/Base64 encodings, number bases and encryption.
10 recipes
DV
Data validation developer
IBAN, BIC, SIRET, Luhn, email, IP, UUID, JSON and date checks gathered in a validation report.
10 recipes
JD
JSON & document store developer
In-memory JSON documents, persistent nodes and sequences: build, walk, sort, clean up.
10 recipes
SD
Script developer
Full script lifecycle in the MQL editor: typed parameters, read back, generate, patch, deploy, copy, grant, delete.
10 recipes
EM
Execution modes
execute, call, include, sub_include, parallel, stack: which environment, which checks, which logs.
11 recipes
WS
Web service developer
A small REST resource with get, post, put and delete scripts: validation, JSON answers, codes, grants, URLs and API docs.
10 recipes
AD
Async developer
Asynchronous processing with the stack: push runs now or later, priorities and parallel limits, errors and replays, flows and cleanup.
10 recipes
SD
Scheduler developer
Cron jobs and delayed runs: add, resume, pause, update and remove jobs, prove they ran, and postpone stack runs with a delay condition.
10 recipes
ED
Event developer
Event-driven scripts: file watchers that push each file event to the stack, validation, archiving, errors and safe restarts.
10 recipes
CC
Configuration & connections
Parameters (private, described, locked, deployed), environment variables, sessions and connections used by your scripts.
12 recipes
VD
Versioning & deployment
Group your scripts, commit, compare, roll back, export, import and package a release.
10 recipes
DE
Debugging & error handling
Coded errors with try/exception, logs with correlation ids, timing, metrics and defensive checks.
10 recipes
SD
SQL developer in MQL
Connections, DDL and DML with sql encode, transactions, cursors, parse, and exports to JSON, CSV, Excel and PDF on a real local database. Run the recipes in order: the first one creates the tuto_db_h2 database.
10 recipes
CD
CRUD & data API developer
Generate SCRUD scripts from a table, patch and install them, write typed read and write APIs, check data quality and manage connections per environment. Uses the tuto_db_h2 database of the SQL developer recipes; the last recipe removes it.
10 recipes
WA
Web application developer
Create a web app, its host name, pages, ajax forms, menu and grants, then remove it all.
10 recipes
CF
Control flow
if, if force, case, switch, mode, for, while, repeat, break, continue, parallel and synchronized on real cases.
10 recipes
TM
The MQL language in depth
Data types and null, booleans, comments, variables and scope, eval and mql blocks, coded errors and try.
10 recipes
SU
Screen & UI developer (sajax)
Build the screens of MentDB web apps in MQL: Bootstrap grids, forms and every control, ajax buttons, alerts, refreshes and charts.
10 recipes