https://wiki.xxiivv.com/site/fractran.html XXIIVV * assembly * two dimensional * concatenative * functional * imperative * rewriting * fractran * interaction nets * thue * modal Fractran is a computer architecture based entirely on the multiplication of fractions. A prime is a number that can only be divided by itself one, since these numbers can't be divided, they can considered the DNA of other numbers. The factoring of a number into prime numbers, for example: 18 = 2 x 3^2, exposes values which Fractran utilizes as registers. There are two parts to a Fractran program: 1. The Accumulator 2. The Fractions Liu Xing Tong Xin Typical Fractran Programmer The Accumulator +------------------------+ | | Registers | |Accumulator |-----------| | |r2|r3|r5|r7| |------------+--+--+--+--| | 6 |1 |1 | | | |------------+--+--+--+--| | 18 |1 |2 | | | |------------+--+--+--+--| | 1008 |4 |2 | |1 | |------------+--+--+--+--| | 5402250 |1 |2 |3 |4 | +------------------------+ The Accumulator is a single number whose prime factorization holds the value of registers(2, 3, 5, 7, 11, 13, 17, ..). For example, if the state of the accumulator is 1008(24 x 32 x 7), r2 has the value 4, r3 has the value 2, r7 has the value 1, and all other registers are unassigned. The Fraction [fractran] A Fraction represents an instruction that tests one or more registers by the prime factors of its numerator and denominator. To evaluate the result of a rule we take the the accumulator, if multiplying it by this fraction will give us an integer, we will update the accumulator with the result. +-----------------------------------------------------------+ | 2/3 | 15/256 | 21/20 | |----------------+-----------------+------------------------| |(2^1)/(3^1) |(3^1 x 5^1)/(2^6)|(3^1 x 7^1)/(2^2 x 5^1) | |----------------+-----------------+------------------------| | |if(r2 >= 6){ |if(r2 >= 2 && r5 >= 1){ | |if(r3 >= 1){ | r2 -= 6; | r2 -= 2; | | r3 -= 1;| r3 += 1; | r5 -= 1; | | r2 += 1;| r5 += 1; | r3 += 1; | | return; | return; | r7 += 1; | |} |} | return; | | | |} | +-----------------------------------------------------------+ Operations become more readable when broken down into their primes. We can think of every prime number as having a register which can take on non-negative integer values. Each fraction is an instruction that operates on some of the registers. A Notation While Fractran is commonly reduced to just another opaque esoteric language, portraying it as such is doing a disservice to the relatively simple idea at its core and to the researchers who might otherwise benefit by venturing deeper into a relatively unexplored field of computation. Wryl, who created Modal, demonstrated to me an interesting connection between Fractran and rewriting languages. We need only to compile our rules and point the prime registers to symbols in a dictionary to see this relationship more clearly. :: left side > right side 15/6 left.2 side.3 > side.3 right.5 AC 6 left side accumulator 00 6 x 15/6 = 15, side right result This documentation will represent registers with names(x, y, foo-bar, baz, ..). Fractions will be written as rewrite rules starting with ::, a left-side, a spacer(>) and a right-side. The notation indicates which registers to replace on the left-side, and what to replace them with on the right-side. [fractran] Programming In Fractran In a rule definition, which is a fraction where prime factorization is written as symbols, we find symbols to the left-side of the spacer (>) to be rewritten by symbols found on the right-side. Each new symbol is added to the dictionary and represented internally as a prime number. :: flour sugar apples > apple-cake :: apples oranges cherries > fruit-salad :: fruit-salad apple-cake > fruit-cake sugar oranges apples cherries flour apples Rules are tested in a sequence from the first to the last, when a valid rewrite rule is encountered, the accumulator is updated by the product of the multiplication of the accumulator with the fraction, and search for the next rule starts back again from the beginning. :: 7/30 flour.2 sugar.3 apples.5 > apple-cake.7 :: 17/715 apples.5 oranges.11 cherries.13 > fruit-salad.17 :: 19/119 apple-cake.7 fruit-salad.17 > fruit-cake.19 AC 21450 flour sugar apples apples oranges cherries 00 21450 x 7/30 = 5005, apples apple-cake oranges cherries 01 5005 x 17/715 = 119, apple-cake fruit-salad 02 119 x 19/119 = 19, fruit-cake In other words, it helps to visualize the fractions in a program as a list of rewrite rules that tests the accumulator against its left-side, and starts back at the top of the list after updating the accumulator when it is a match, or keep going when it does not. `Fractran has a single operation, and can be explained in 10 seconds.' * For each fraction in a list for which the multiplication of the accumulator and the fraction is an integer, replace the accumulator by the result of that multiplication. * Repeat this rule until no fraction in the list produces an integer when multiplied by the accumulator, then halt. That's all! [fractran] The Book of Numbers, John Conway Logic & Arithmetic Logic in rewrite rules is typically implemented as multiple rules, where each one is a potential location in the truth table, here is logical and between two registers(x&y) as example: :: x y and > true :: x and > false :: y and > false AC 30 x y and 00 30 x 7/30 = 7, true You can get the sum of two registers(x+y) by moving the value of one register into the other. The naming of the x register in advance ensures that the highest number will be stored in the lowest register: :: x :: y > x AC 144 x^4 y^2 00 144 x 2/3 = 96, x^5 y 00 96 x 2/3 = 64, x^6 You can get the difference between two registers(x-y) by consuming the value of two registers at once, and moving the remains to the first: :: x y > :: y > x AC 576 x^6 y^2 00 576 x 1/6 = 96, x^5 y 00 96 x 1/6 = 16, x^4 You can get the product of two registers(x*y) by keeping an intermediate result and state register. Keeping the resulting product, by naming the first register for the result, prevents the accumulator grow too much in size: :: r acc x y :: iter acc > x iter :: iter > :: x y > r acc y :: y > iter :: x > AC 8575 x^2 y^3 .. r^6 Similarly to the multiplication, you can get the quotient and remainder of two registers(x/y) by doing a series of subtractions: :: x y res rem div1 acc1 div2 acc2 :: x y div1 > rem acc1 :: acc1 > div1 :: y div1 > :: div1 > res div2 :: rem div2 > y acc2 :: acc2 > div2 :: div2 > div1 :: y > AC x^7 y^2 div1 .. res^3 rem To explore Fractran further, have a look at some of the examples: * Fizzbuzz * Tic-Tac-Toe * Game Of Life(video) Example: Tic-Tac-Toe To handle output, nothing special is to be done, the resulting accumulator at the end of an evaluation is a valid output value, some have offered schemes like assigning an alphabet to a series of registers. The advantage with symbolic rewriting is that registers are already assigned whole tokens, so we shall use those instead. To handle input, we can type in new symbol tokens and appending their value to the accumulator when evaluation halts. We can implement a tic-tac-toe in a mere 16 rules: Reserve the first registers for the player moves: :: x#a o#a x#b o#b x#c o#c :: x#d o#d x#e o#e x#f o#f :: x#g o#g x#h o#h x#i o#i This register remains active until the game ends: game A symbol to draw the value of registers in a grid: " Set move in the format x#a, o#b, x#c, etc: a b c | {x#a o#a .} {x#b o#b .} {x#c o#c .} d e f | {x#d o#d .} {x#e o#e .} {x#f o#f .} g h i | {x#g o#g .} {x#h o#h .} {x#i o#i .} " Rules for each possible victory states: :: game x#a x#b x#c > x#a x#b x#c "Player X wins!" :: game o#a o#b o#c > o#a o#b o#c "Player O wins!" :: game x#d x#e x#f > x#d x#e x#f "Player X wins!" :: game o#d o#e o#f > o#d o#e o#f "Player O wins!" :: game x#g x#h x#i > x#g x#h x#i "Player X wins!" :: game o#g o#h o#i > o#g o#h o#i "Player O wins!" :: game x#a x#e x#i > x#a x#e x#i "Player X wins!" :: game o#a o#e o#i > o#a o#e o#i "Player O wins!" :: game x#g x#e x#c > x#g x#e x#c "Player X wins!" :: game o#g o#e o#c > o#g o#e o#c "Player O wins!" :: game x#a x#d x#g > x#a x#d x#g "Player X wins!" :: game o#a o#d o#g > o#a o#d o#g "Player O wins!" :: game x#b x#e x#h > x#b x#e x#h "Player X wins!" :: game o#b o#e o#h > o#b o#e o#h "Player O wins!" :: game x#c x#f x#i > x#c x#f x#i "Player X wins!" :: game o#c o#f o#i > o#c o#f o#i "Player O wins!" Program don't need to specify anything other than these 16 rules, as players can already input their moves in the format of its register names: x#a, o#b, x#c, etc. Set move in the format x#a, o#b, x#c, etc: a b c | x o o d e f | . x . g h i | . . x Player X wins! Notes Fractran operators are reversible, meaning that some programs can be run backward, back to its original state. To undo an operation, evaluation is undone by inverting the numerator and denumerator: AC 19, fruit-cake 02 19 x 119/19 = 119, apple-cake fruit-salad 01 119 x 715/17 = 5005, apples apple-cake oranges cherries 00 5005 x 30/7 = 21450, apples apples flour sugar oranges cherries Implementation The rewriting implementation of the runtime can be implemented in about 300 lines. cc fractran.c -o fractran view raw `The wise marvels at the commonplace.' Confucius * Fractran Interpreter(C89), used for this documentation. * Fractran Interpreter(Web) * Intro to Fractran * Remembering John Conway * On Esolang incoming phutball firth primes fractions WebringMerveillesNoNazis!UxnPowered Devine Lu Linvega (c) 2008-2024 BY-NC-SA 4.0CreativeCommons ---------------------------------------------------------------------