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

From Esolang
Jump to navigation Jump to search

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!