We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
Quaternions
Quaternions is an esolang by User:Cleverxia based on quaternions.
Introduction to quaternions
A quaternion is a number with form where , , are imaginary units, , , , , , , . NOTE: This means quaternions' multiplication is not commutative.
A quaternion has norm , conjugate , reciprocal and negation .
Quaternion's addition do what you expect: .
Quaternion's multiplication is defined as .
Subtracting a quaternion is equal to adding its negation; dividing a quaternion is equal to multiplying its reciporial.
Program flow
Quaternions is not stack based, but rather works on a array shaped like a surface of a 4d unit hypersphere called S. There is a data pointer DP which is initially 1. Let SP=. It also has an accumulator.
| command | meaning |
|---|---|
^ |
load S[SP] onto accumulator
|
1, i, j, k |
load corresponding imaginary unit onto accumulator |
? |
load random thing from [1,i,j,k] onto accumulator |
+, -, *, / |
accumulator (+=|-=|*=|/=) S[SP] |
= |
stores accumulator into S[SP]
|
` |
DP*=acc. THIS IS THE ONLY WAY TO MODIFY DP. this is equivalent to rotating the SP
|
(...), [...], {...} |
if statements, while loops, and until loops. condition: acc==0
|
. |
output. implementation defined |
, |
input. implementation defined |
Examples
Truth machine
1=+=+=+=+=++=,.-{+.-}
(assuming IO inputs/outputs the character codes on the real part)
unary Looping counter
1=+=++=1+*=i`1=+=++++=i`{1+=i*`=i*={1+=i`^.i```^}i``^.i`}
may be shorter... who knows? this version requires js Number can be arbitrarily large.
explanation: this uses 4 points on the unit circle on the complex plane to store things, SP=1 stores 42, SP=i stores 10, SP=-1 stores the outer counter (counts up), SP=-i stores the negation of the inner counter (to make things easier).
Interpreter
This interpreter returns -1 for EOF, however a flag may be set to return i for EOF. It uses floating point arithmetic which is inaccurate
code='1=+=+=+=+=++=,.-{+.-}'
input='1'
EOF=[[-1,0],[0,0]] // change to [[0,1],[0,0]] for i on EOF... or use the -i flag
if(argv.includes('-i'))EOF=[[0,1],[0,0]];
function add(a,b){return typeof a=='number'?a+b:[add(a[0],b[0]),add(a[1],b[1])];}
function neg(a,b){return typeof a=='number'?-a:[neg(a[0]),neg(a[1])];}
function sub(a,b){return add(a,neg(b));}
function conj(a){return typeof a=='number'?a:[conj(a[0]),neg(a[1])];}
function mul(a,b){return typeof a=='number'?a*b:[sub(mul(a[0],b[0]),mul(conj(b[1]),a[1])),add(mul(b[1],a[0]),mul(a[1],conj(b[0])))];}//cayley dickson definition
function mul_scalar(a,b){return typeof a=='number'?a*b:[mul_scalar(a[0],b),mul_scalar(a[1],b)];}
function norm(a){return typeof a=='number'?Math.abs(a):Math.hypot(norm(a[0]),norm(a[1]));}
function recip(a){return mul_scalar(conj(a),norm(a)**-2);}
function div(a){return mul(a,recip(b));}
function unit(a){return mul_scalar(a,1/norm(a));}
function strf(x,d=33){return typeof x=='number'?(0+x).toExponential(8):strf(x[0],d+1)+String.fromCodePoint(d)+strf(x[1],d+1)}//avoid -0 problems
function str_(x){function a(a){return a>=1e5||a<=.001?a.toExponential(3):a.toPrecision(7)}return a(x[0][0])+`+${a(x[0][1])}i+${a(x[1][0])}j+${a(x[1][1])}k`}
s={};iP=0;i=[[0,1],[0,0]];j=[[0,0],[1,0]];k=[[0,0],[0,1]];_1=[[1,0],[0,0]];
function gi(){if(iP>=input.length)return EOF;return [[input.codePointAt(iP++),0],[0,0]];};function po(x){process.stdout.write(String.fromCodePoint(x[0][0]))};
e='acc=[[0,0],[0,0]];dp=[[1,0],[0,0]];';for(let i of code)switch(i){
case'^':e+='acc=s[strf(unit(dp))]||[[0,0],[0,0]];';break;
case'=':e+='s[strf(unit(dp))]=acc;';break;
case'1':e+='acc=_1;';break;
case'i':e+='acc=i;';break;
case'j':e+='acc=j;';break;
case'k':e+='acc=k;';break;
case'?':e+='acc=[i,j,k][~~(Math.random()*3)];';break;
case'+':e+='acc=add(acc,s[strf(unit(dp))]||[[0,0],[0,0]]);';break;
case'-':e+='acc=sub(acc,s[strf(unit(dp))]||[[0,0],[0,0]]);';break;
case'*':e+='acc=mul(acc,s[strf(unit(dp))]||[[0,0],[0,0]]);';break;
case'/':e+='acc=div(acc,s[strf(unit(dp))]||[[0,0],[0,0]]);';break;
case'`':e+='dp=mul(dp,acc);';break;
case'(':e+='if(norm(acc)==0){';break;
case'[':e+='while(norm(acc)==0){';break;
case'{':e+='while(norm(acc)!=0){';break;
case')':case']':case'}':e+='};';break;
case',':e+='acc=gi();';break;
case'.':e+='po(acc);';break;
///debug commands
case'"':e+='process.stdout.write(strf(acc));';break;
case'p':e+='process.stdout.write(str_(acc));';break;
}eval(e)
Also there is a Python interpretor by User:Blashyrkh here. It uses -1 as EOF, doesn't rely on floating point arithmetics and uses rational numbers for all intermediate calculations.
Dialects
Octonions
This is very self-explanitory: it uses octonions instead of quaternions. Octonions' multiplication is defined subject to the following multiplication table
| × | 1 | i | j | k | l | il | jl | kl |
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | i | j | k | l | il | jl | kl |
| i | i | −1 | k | −j | il | −l | −kl | jl |
| j | j | −k | −1 | i | jl | kl | −l | −il |
| k | k | j | −i | −1 | kl | −jl | il | −l |
| l | l | −il | −jl | −kl | −1 | i | j | k |
| il | il | l | −kl | jl | −i | −1 | −k | j |
| jl | jl | kl | l | −il | −j | k | −1 | −i |
| kl | kl | −jl | il | l | −k | −j | i | −1 |
Commands remain the same (except the ? loads one of 7 imaginary units, and the new commands l, I (il), J (jl), K (kl) loads the corresponding im. unit into acc). However octonions' multiplication is not associative nor commutative, so beware!