Skip to content

Euclidean algorithm in brainfuck #407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,4 @@ __pycache__/

-# Other
-*.xcf
listproj.py
4 changes: 4 additions & 0 deletions book.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@
{
"lang": "f90",
"name": "Fortran90"
},
{
"lang": "bf",
"name": "Brainfuck"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
>,>,[<[>->+<[>]>[<+>-]<<[<]>-]>[-<+>]>[-<+<+>>]<]<.
2 changes: 2 additions & 0 deletions contents/euclidean_algorithm/code/brainfuck/euclidean_sub.bf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
,>,>>>+[[-]<<<<[->>>+<<<]>[->>>+<<<]>>[-<+<<+>>>]>[-<+<<+>>>]<<[->->[-]+<[>-]>[->>]<<<]>[>]<[<<[-]>>[-<<+>>>+<]]<[<<[-]>>[-<<+>>>>+<<]]>>>[-]+>[-]<<[>-]>[<<<<.>>>>->>]<<]

114 changes: 114 additions & 0 deletions contents/euclidean_algorithm/euclidean_algorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ The algorithm is a simple way to find the *greatest common divisor* (GCD) of two
[import:13-24, lang="nim"](code/nim/euclid_algorithm.nim)
{% sample lang="f90" %}
[import:1-19, lang="Fortran"](code/fortran/euclidean.f90)
{% sample lang="bf" %}
[import:1-19, lang="Brainfuck"](code/brainfuck/euclidean_sub.bf)
{% endmethod %}

Here, we simply line the two numbers up every step and subtract the lower value from the higher one every timestep. Once the two values are equal, we call that value the greatest common divisor. A graph of `a` and `b` as they change every step would look something like this:
Expand Down Expand Up @@ -88,6 +90,8 @@ Modern implementations, though, often use the modulus operator (%) like so
[import:1-11, lang="nim"](code/nim/euclid_algorithm.nim)
{% sample lang="f90" %}
[import:21-34, lang="Fortran"](code/fortran/euclidean.f90)
{% sample lang="bf" %}
[import:1-19, lang="Brainfuck"](code/brainfuck/euclidean_mod.bf)
{% endmethod %}

Here, we set `b` to be the remainder of `a%b` and `a` to be whatever `b` was last timestep. Because of how the modulus operator works, this will provide the same information as the subtraction-based implementation, but when we show `a` and `b` as they change with time, we can see that it might take many fewer steps:
Expand Down Expand Up @@ -140,6 +144,116 @@ The Euclidean Algorithm is truly fundamental to many other algorithms throughout
[import, lang="nim" %](code/nim/euclid_algorithm.nim)
{% sample lang="f90" %}
[import, lang="Fortran"](code/fortran/euclidean.f90)
{% sample lang="bf" %}
#### Subtraction varient
##### Code
[import, lang="Brainfuck"](code/brainfuck/euclidean_sub.bf)
##### Explanation
Basic plan: get |a-b|, check if 0

So the program does something like
```
a (b) 0 0 1 0 0
a b a b (0) 0 0
if(a>b) a b a-b 0 (a-b) 0 0
else a b 0 a-b (a-b) 0 0
if(a-b==0)print and break
else
if(a>b) a-b b 0 0 (a-b) 0 0
else a a-b 0 0 (a-b) 0 0
```

More detail:

`scan a,b`: `>,>,`
State: `a (b) 0 0 0 0 0`
```
>>>+
[
[-]<<<
<[->>>+<<<]>[->>>+<<<]>>
```
State: `0 0 0 (a) b 0 0`
```
[-<+<<+>>>]
>[-<+<<+>>>]
```
State: `a b a b (0) 0 0`
```
<<
[->- subtracts a from b, assuming a>b
>[-]+
<[
>-]>
[->>]<<< if a is 0, stop
]
```
So basically the state will either be
a_b (0) 0 0
or
(0) a_b 0 0
but it's hard to do when states may be different, so `>[>]` moves the pointer to cell 4
```
<[<<[-]>>[-<<+>>>+<]]
<[<<[-]>>[-<<+>>>>+<<]]
```
basically cell 4 will contain the difference
```
>>>[-]+
>[-]<<
[>-]>
[<<<<.>>> testing if difference is 0, if so return
>->>]<<
]
```

#### Modulo varient
##### Code
[import, lang="Brainfuck"](code/brainfuck/euclidean_mod.bf)
##### Explanation
`scan a,b`: `>,>,`

State: `0 a >b 0 0 0`

`while(b!=0)`: `[`

`a,b,0=0,b-a%b,a%b`:
```
<[
>->+<[>]
>[<+>-]<
<[<]>-
]
```

so basically this is the modulo algorithm in brainfuck, it slowly shifts cell 2 to cell 3, while subtracting 1 from cell 1
then when cell 2 goes to 0, it shifts cell 3 to 2 and continues, this is like just constantly subtracting cell 2 from cell 1, until you cant subtract anymore then return at cell 3

State: `0 >0 b-a%b a%b 0 0`

shifting: `>[-<+>]`

State: `0 b-a%b >0 a%b 0 0`

Currently we have a,b,0=b-a%b,0,a%b, and we need a,b,0=b,a%b,0, so we just add the third cell to the first and second cell

adding thing: `>[-<+<+>>]<`

State: `0 b >(a%b) 0 0 0`

So now we have a,b=b,a%b, we continue the loop

`]`

After the second cell is 0, the loop terminates and we obtain the GCD

State: `0 >GCD(a b) 0 0 0 0`

Now we print the GCD

print: `<.`


{% endmethod %}


Expand Down
112 changes: 112 additions & 0 deletions contents/gaussian_elimination/code/javascript/gaussian_elimination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
function gaussian_elimination(a){
var rows = a.length
var cols = a[0].length
var row = 0;

for (let col = 0; col < cols - 1; ++col) {

let pivot = row;
for (let i = row + 1; i < rows; ++i) {
if (Math.abs(a[i][col]) > Math.abs(a[pivot][col])) {
pivot = i;
}
}

if (a[pivot][col] == 0) {
console.log("The matrix is singular.\n");
continue;
}

if (col != pivot) {
let t=a[col];
a[col]=a[pivot];
a[pivot]=t;
}

for (let i = row + 1; i < rows; ++i) {
let scale = a[i][col] / a[row][col];

for (let j = col + 1; j < cols; ++j) {
a[i][j] -= a[row][j] * scale;
}

a[i][col] = 0;
}

++row;
}
return a;
}

function back_substitution(a){
var rows = a.length;
var cols = a[0].length;
var sol=new Array(rows);

for (let i = rows - 1; i >= 0; --i) {

let sum = 0;
for (let j = cols - 2; j > i; --j) {
sum += sol[j] * a[i][j];
}

sol[i] = (a[i][cols - 1] - sum) / a[i][i];
}
return sol;
}

function gauss_jordan(a) {
var rows = a.length;
var cols = a[0].length;
var row = 0;

for (let col = 0; col < cols - 1; ++col) {
if (a[row][col] != 0) {
for (let i = cols - 1; i > col - 1; --i) {
a[row][i] /= a[row][col];
}

for (let i = 0; i < row; ++i) {
for (let j = cols - 1; j > col - 1; --j) {
a[i][j] -= a[i][col] * a[row][j];
}
}

++row;
}
}
}

var a = [[3, 2 , -4, 3 ],
[ 2, 3 , 3 , 15],
[ 5, -3, 1 , 14]];

gaussian_elimination(a);
console.log("Gaussian elimination:\n");
for(let i=0;i<a.length;++i){
let txt=""
for(let j=0;j<a[i].length;++j){
txt+=a[i][j]<0?" ":" ";
txt+=a[i][j].toPrecision(8);
}
console.log(txt);
}

gauss_jordan(a);
console.log("\nGauss-Jordan:\n");
for(let i=0;i<a.length;++i){
let txt=""
for(let j=0;j<a[i].length;++j){
txt+=a[i][j]<0?" ":" ";
txt+=a[i][j].toPrecision(8);
}
console.log(txt);
}

var sol=back_substitution(a),txt="";
console.log("\nSolutions are:\n");
for(let i=0;i<sol.length;++i){
txt+=sol[i]<0?" ":" ";
txt+=sol[i].toPrecision(8);
}
console.log(txt)
8 changes: 8 additions & 0 deletions contents/gaussian_elimination/gaussian_elimination.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ In code, this looks like:
[import:10-36, lang:"haskell"](code/haskell/gaussianElimination.hs)
{% sample lang="py" %}
[import:3-28, lang:"python"](code/python/gaussian_elimination.py)
{% sample lang="js" %}
[import:1-39, lang:"javascript"](code/javascript/gaussian_elimination.js)
{% endmethod %}

Now, to be clear: this algorithm creates an upper-triangular matrix.
Expand Down Expand Up @@ -397,6 +399,8 @@ This code does not exist yet in rust, so here's Julia code (sorry for the inconv
[import:38-46, lang:"haskell"](code/haskell/gaussianElimination.hs)
{% sample lang="py" %}
[import:31-49, lang:"python"](code/python/gaussian_elimination.py)
{% sample lang="js" %}
[import:58-78, lang:"javascript"](code/javascript/gaussian_elimination.js)
{% endmethod %}

## Back-substitution
Expand Down Expand Up @@ -429,6 +433,8 @@ In code, this involves keeping a rolling sum of all the values we substitute in
[import:48-53, lang:"haskell"](code/haskell/gaussianElimination.hs)
{% sample lang="py" %}
[import:52-64, lang:"python"](code/python/gaussian_elimination.py)
{% sample lang="js" %}
[import:41-56, lang:"javascript"](code/javascript/gaussian_elimination.js)
{% endmethod %}

## Conclusions
Expand All @@ -453,6 +459,8 @@ As for what's next... Well, we are in for a treat! The above algorithm clearly h
[import, lang:"haskell"](code/haskell/gaussianElimination.hs)
{% sample lang="py" %}
[import, lang:"python"](code/python/gaussian_elimination.py)
{% sample lang="js" %}
[import, lang:"javascript"](code/javascript/gaussian_elimination.js)
{% endmethod %}


Expand Down