Code Golf: Fractran
Asked Answered
D

25

49

The Challenge

Write a program that acts as a Fractran interpreter. The shortest interpreter by character count, in any language, is the winner. Your program must take two inputs: The fractran program to be executed, and the input integer n. The program may be in any form that is convenient for your program - for example, a list of 2-tuples, or a flat list. The output must be a single integer, being the value of the register at the end of execution.

Fractran

Fractran is a trivial esoteric language invented by John Conway. A fractran program consists of a list of positive fractions and an initial state n. The interpreter maintains a program counter, initially pointing to the first fraction in the list. Fractran programs are executed in the following fashion:

  1. Check if the product of the current state and the fraction currently under the program counter is an integer. If it is, multiply the current state by the current fraction and reset the program counter to the beginning of the list.
  2. Advance the program counter. If the end of the list is reached, halt, otherwise return to step 1.

For details on how and why Fractran works, see the esolang entry and this entry on good math/bad math.

Test Vectors

Program: [(3, 2)]
Input: 72 (2332)
Output: 243 (35)

Program: [(3, 2)]
Input: 1296 (2434)
Output: 6561 (38)

Program: [(455, 33), (11, 13), (1, 11), (3, 7), (11, 2), (1, 3)]
Input: 72 (2332)
Output: 15625 (56)

Bonus test vector:

Your submission does not need to execute this last program correctly to be an acceptable answer. But kudos if it does!

Program: [(455, 33), (11, 13), (1, 11), (3, 7), (11, 2), (1, 3)]
Input: 60466176 (210310)
Output: 7888609052210118054117285652827862296732064351090230047702789306640625 (5100)

Submissions & Scoring

Programs are ranked strictly by length in characters - shortest is best. Feel free to submit both a nicely laid out and documented and a 'minified' version of your code, so people can see what's going on.

The language 'J' is not admissible. This is because there's already a well-known solution in J on one of the linked pages. If you're a J fan, sorry!

As an extra bonus, however, anyone who can provide a working fractran interpreter in fractran will receive a 500 reputation point bonus. In the unlikely event of multiple self-hosting interpreters, the one with the shortest number of fractions will receive the bounty.

Winners

The official winner, after submitting a self-hosting fractran solution comprising 1779 fractions, is Jesse Beder's solution. Practically speaking, the solution is too slow to execute even 1+1, however.

Incredibly, this has since been beaten by another fractran solution - Amadaeus's solution in only 84 fractions! It is capable of executing the first two test cases in a matter of seconds when running on my reference Python solution. It uses a novel encoding method for the fractions, which is also worth a close look.

Honorable mentions to:

  • Stephen Canon's solution, in 165 characters of x86 assembly (28 bytes of machine code)
  • Jordan's solution in 52 characters of ruby - which handles long integers
  • Useless's solution in 87 characters of Python, which, although not the shortest Python solution, is one of the few solutions that isn't recursive, and hence handles harder programs with ease. It's also very readable.
Dalmatian answered 17/11, 2009 at 16:7 Comment(13)
To the inevitable close brigade: There are over 100 other code-golf tagged questions on Stack Overflow. It's a legitimate post, and leads to interesting and enlightening answers. And, it's a community wiki, so nobody's whoring reputation from it.Dalmatian
A contest is still not a question. Trivially changing it to "What is the shortest program to do X?" still doesn't make it an appropriate questionSwec
"If you have one, do not mark your post as a wiki." That's impossible since the question is wiki. But I think bounty points still work even so.Amongst
@R. Pate: Go have the argument with the other 104 code golfs before me, or bring it up on meta, then - the community consensus is generally favorable. @mmyers: Thanks, I'll update the question.Dalmatian
That you felt the need to pre-emptively comment with "please don't close me" shows how strong that "consensus" is.Swec
The only problem I have is that one of the links in the challenge includes a 20 character J program for solving it...Decimalize
@R.Pate: The current consensus seems fairly strong (+20 votes for "it's ok, ignore it if you don't like it"): meta.stackexchange.com/questions/20912/so-weekly-code-golf. Of course, you (and anyone else that cares) should vote, and also state your viewpoint if it's not covered.Pronunciation
I didn't pre-emptively comment - I only commented after someone had voted to close it.Dalmatian
@Aaron: Hm, good point. I edited t the text to explicitly exclude J. It seems nasty, but also the only way to stop the winner being something copied and pasted.Dalmatian
Just my 2 cents: This is a great question, both because I generally like Code Golf, and even more so because it introduced me to a really interesting thing (never heard of this "language" before, but it's awesome!).Worcestershire
+1 for showing me that something like Fractran exists. Now excuse me while I go scoop up my brain and put it back into my skull.Weighty
Proof that 2^x3^y with (3,2) yields 3^(x+y): Clearly it will run through x times, because the 3^y part will never become divisible by 2 regardless of the value of y, but 2^x is divisible by 2 x times. 2^x*1.5^x = 3^x. 2^x*1.5^x*3^y=3^(x+y).Fugacity
Another [code-golf] question on meta that may be relevant: meta.stackexchange.com/questions/24242/…Crescin
T
45

Fractran - 1779 fractions

(Edit: fixed)

(I hope people are still following this thread, because this took a while!)

It appears SO won't let me post something as long as this, so I posted the Fractran source here.

Input is specified as follows:

First, we encode a fraction m/n = p_0^a0... p_k^ak by:

  • Start with 1. Then, for each ai:
  • Multiply by p_2i^ai if ai > 0
  • Multiply by p_2i+1^{-ai} if a_i < 0

This way, we encode any fraction as a positive integer. Now, given a progoram (sequence of encoded fractions F0, F1, ...), we encode that by

p_0^F0 p1^F1 ...

Finally, input to the interpreter is given by:

2^(program) 3^(input) 5

where program and input are encoded as above. For example, in the first test problem, 3/2 gets encoded to 15, so the program gets encoded to 2^15; and 108 gets encoded to 500. So, we pass

2^{2^15} 3^500 5

to the program. The output, then is of the form

2^(program) 3^(output)

so in the first example, it'll be

2^{2^15} 3^3125

How does it work?

I wrote a meta-language that compiles down to Fractran. It allows for functions (simple Fractran and sequences of other functions), and a while loop and if statement (for convenience!). The code for that can be found here.

If you want to compile that code down to Fractran yourself, my (C++) program can be found here [tar.gz]. In a stunning display of dogfooding (and showing off), I used my C++ YAML parser yaml-cpp, so you'd have to download and link with that. For both yaml-cpp and the "compiler", you'll need CMake for cross-platform makefile generating.

The usage of this program is:

./fracc interpreter.frp

The it reads the name of a function from standard input, and writes the corresponding "pseudo-Fraction" (I'll explain that in a second) to standard output. So to compile the interpreter (the Interpret function), you could run

echo "Interpret" | ./fracc interpreter.frp > interpret

The output ("pseudo-Fractran") will be a sequence of lines, each with a string of space-separated digits. A line corresponds to a fraction: if the nth digit in the line is an, then the fraction is the product of p_n^an.

It's very easy to convert this to Fractran, but if you're lazy, you can use to-fractions.py. [Note: earlier I had a C++ program, and I had carelessly ignored integer overflow. I translated it to Python to avoid this.]

Note about input: if you want to test out a different function this way, the convention is always the same. It has a number of parameters (usually the comment above the function explains this) in pseudo-Fractran, so give it what it wants, plus a 1 on the very next slot (so in ordinary Fractran, multiply once by the first prime that it won't use). This is a "signal" bit to the function to start going.


However,

I don't recommend actually trying to run the Fractran interpreter (alas). I tested many of its components, and, for example, the function IncrementPrimes, which takes a pair of primes and returns the next two primes, takes about 8 minutes to run, using my silly C++ interpreter (no need to post that :). Plus, it goes (at least) quadratically in the number of function calls - doubling the number of function calls makes it take at least four times as long (more if there are while loops or if statements). So I'm guessing that running the interpreter will take at least days, if not years :(

So how do I know it works? Well, of course I'm not 100% certain, but I'm pretty close. First of all, I tested many, many of its components, and in particular, I tested all of the elements of the meta-language (sequences of functions and if and while statements) very thoroughly.

Also, the meta-language is easy to translate into your favorite language, and even easier to translate to C++, since all parameters of functions are passed by reference. If you're feeling lazy again, you can download my translation here [tar.gz] (there's no makefile; it's just two .cpp files, so directly calling gcc is fine).

So you can compare the two interpreters, run the C++ version (it also takes input/output in pseudo-Fractran), check that that works, and then convince yourself that the meta-language works too.


Or!

If you're feeling inspired, and really want to see this interpreter interpreted, you can write a "clever" Fractran interpreter based around the type of Fractran output that we get. The output is very structured - sequences of functions are implemented using signals, so if you somehow cache where the interpreter was, you could jump there immediately if nothing important changed. This, I think, would dramatically speed up the program (perhaps cutting down running time by one or more powers).

But, I'm not really sure how to do this, and I'm happy with what's done, so I'll leave it as an exercise for the reader.

Tillandsia answered 17/11, 2009 at 16:7 Comment(10)
Whoa. Really impressive. Still trying to get the test vector to run - if it does, you get 500 points. ;) What did you use to compile the meta-language down to fractran statements?Dalmatian
So, using Useless's Python implementation, with the test vector you suggest, the output I get is not the expected output: data = (2**(215))*(3500)*5; output = f(data, program); expected = (2**(215))*(33125) # output != expected and output % expected != 0. What's wrong?Dalmatian
Hmmm... see my latest update - I didn't expect it to actually finish (it doesn't with my C++ interpreter). How long did it take? Also, what's the actual output it gives?Tillandsia
I should probably add, when I ask for the output, can you give me the prime factorization?Tillandsia
@Nick, I figured out the problem - my conversion to Fractran ignored integer overflow (stupid C++). I translated the last step to Python, and re-uploaded the main Fractran code. You'll notice that it's got much bigger numbers!Tillandsia
Trying the fixed version on the input 2^(2^15) 3^10 5 (the program 3/2 with input 2^1 3^1) results in the output 2^(2^15) 3^10 after about 2 and a half hours. Any idea why the output appears to be unmodified?Dalmatian
Aha! I really should've tested the if statement more. I just copied over the while statement, made the obvious modification, but it turned out that my if statement zeroed its conditional. I rethought the if statement, and it ended up much simpler, so it cut down the number of fractions necessary. Reuploaded.Tillandsia
I was in the planning stages of a Fractran generator like this, with an initial prototype in Haskell, but it looks like you got a working result first :)Ululate
@ephemient, Ah, see, but I've had the flu for the last week, so I've had nothing to do but sit in bed, drink tea, and code. An unfair advantage :)Tillandsia
Oh, congratulations! I'm in the middle of something similar: I wrote a compiler for a minski register machine, but not added the conversion to fractran or any control flow operations yet, but am really disappointed someone else got there first :)Kiruna
H
41

Fractran: 84 fractions

FTEVAL = [197*103/(2^11*101), 101/103, 103*127/(2*101), 101/103, 109/101,
  2*23/(197*109), 109/23, 29/109,197*41*47/(31*59), 11^10*53/(127*197), 197/53,
  37/197, 7^10*43/(11^10*37), 37/43, 59/(37*47), 59/47, 41*61/59, 31*67/(41*61),
  61/67, 7*67/(127*61), 61/67,101/71, 73/(127^9*29), 79/(127^2*73),
  83/(127*73), 89/(2*29), 163/29, 127^11*89/79, 337/83, 2*59/89, 71/61,
  7*173/(127*163), 163/173, 337*167/163, 347/(31*337), 337/347, 151/337,
  1/71,19*179/(3*7*193), 193/179, 157/(7*193), 17*181/193, 7*211/(19*181),
  181/211, 193/181, 157/193, 223/(7*157), 157/223, 281*283/239,
  3*257*269/(7*241), 241/269, 263/241, 7*271/(257*263), 263/271, 281/263,
  241/(17*281), 1/281, 307/(7*283), 283/307, 293/283, 71*131/107, 193/(131*151),
  227/(19*157), 71*311/227, 233/(151*167*311), 151*311/229, 7*317/(19*229),
  229/317, 239*331/217, 71*313/157, 239*251/(151*167*313), 239*251/(151*313),
  149/(251*293), 107/(293*331), 137/199, 2^100*13^100*353/(5^100*137),
  2*13*353/(5*137), 137/353, 349/137, 107/349, 5^100*359/(13^100*149),
  5*359/(13*149), 149/359, 199/149]

This is written entirely by hand. I did make up a pseudo language to be able to express things more clearly, but I did not write a compiler and opted to write optimized Fractran code directly.

FTEVAL takes input 3^initial_state * 5^encoded_program * 199, produces intermediate results 3^interpreted_program_state * 199, and completely halts at a number divisible by 233.

The interpreted program is embeded as a list of base 10 digits inside a single base 11 number, using the digit "a" to mark the boundary except at the very end. The addition program [3/2] is encoded as

int("3a2", 11) = 475.

The multiplication program [455/33, 11/13, 1/11, 3/7, 11/2, 1/3] is encoded as

int("1a3a11a2a3a7a1a11a11a13a455a33", 11) = 3079784207925154324249736405657

which is a truly large number.

The first test vector finished in less than one second, produced the desired result after 4545 iterations and halted after 6172 iterations. Here is the complete output.

Unfortunately, sage segfaulted when I tried the second test vector (but I think it'll work under Nick's implementation using prime exponent vectors).

The space here is really too small to explain everything. But here is my pseudocode. I will write up my process in a couple of days, hopefully.

# Notations:
# %p
#     designates the exponent of prime factor p that divides the
#     current state.
# mov x y
#     directly translates to the fraction y/x; its meaning: test if x divides
#     the current state, if so divide the current state by x and multiply it by
#     y.  In effect, the prime exponents of x and y are exchanged.  A Fractran
#     program only comprises of these instructions; when one is executable, the
#     program continues from the beginning.
# dec x => mov x, 1
#     wipes out all factors of x
# inc x => mov 1, x
#     this form is here for the sake of clarity, it usually appears in a
#     loop's entry statement and is merged as such when compiled
# sub n ret m {...}
#     conceptually represents a Fractran sub-program, which will execute just
#     like a normal Fractran program, that is, the sub-program's statements
#     execute when triggered and loop back.  The sub-program only exits when none of
#     its statement is executable, in which occasion we multiply the program's
#     state by m.  We can also explicitly break out early on some conditions.
#     It is also possible to enter a sub-prorgram via multiple entry points and
#     we must take care to avoiding this kind of behavior (except in case where
#     it is desirable).

# entry point 101: return 29
# Divide %2 modulo 11:
#   - quotient is modified in-place
#   - remainder goes to %127
sub 101 ret 101 { mov 2^11, 197 }
sub 101 ret 109 { mov 2, 127 }
sub 109 ret 29 { mov 197, 2 }

# entry point 59: return 61
# Multiply %127 by 10^%31 then add result to %7,
# also multiply %31 by 10 in-place.
sub 59 ret 41*61 {
  mov 31, 197*41
  sub 197 ret 37 { mov 127, 11^10 }
  sub 37 { mov 11^10, 7^10 }
}
sub 61 ret 61 { mov 41, 31 }
sub 61 ret 61 { mov 127, 7 } # the case where %31==0

# entry point 71: return 151 if success, 151*167 if reached last value
# Pop the interpreted program stack (at %2) to %7.
sub 71 {
  # call sub 101
  inc 101

  # if remainder >= 9:
  mov 29*127^9, 73
  #     if remainder == 11, goto 79
  mov 73*127^2, 79
  #     else:
  #         if remainder == 10, goto 83
  mov 73*127, 83
  #         else:
  #             if quotient >= 1: goto 89
  mov 29*2, 89
  #             else: goto 163
  mov 29, 163

  # 79: restore remainder to original value, then goto 89
  mov 79, 127^11*89

  # 83: reached a border marker, ret
  mov 83, 337

  # 89: the default loop branch
  # restore quotient to original value, call 59 and loop when that rets
  mov 2*89, 59
  mov 61, 71

  # 163: reached stack bottom,
  # ret with the halt signal
  sub 163 ret 337*167 { mov 127, 7 }

  # 337: clean up %31 before ret
  sub 337 ret 151 { dec 31 }
}

# entry point 193, return 157
# Divide %3 modulo %7:
#   - quotient goes to %17
#   - remainder goes to %19
sub 193 ret 17*181 {
  mov 3*7, 19
}
mov 7*193, 157
sub 181 ret 193 { mov 19, 7 }
mov 193, 157
sub 157 ret 157 { dec 7 }

# entry point 239: return 293
# Multiply %17 by %7, result goes to %3
mov 239, 281*283
sub 241 { mov 7, 3*257 }
sub 263 ret 281 { mov 257, 7 }
mov 281*17, 241
sub 283 ret 293 { dec 7 }

# entry point 107: return 149 if success, 233 if stack empty
# Pop the stack to try execute each fraction
sub 107 {
  # pop the stack
  inc 71*131

  # 151: popped a value
  # call divmod %3 %7
  mov 131*151, 193

  # if remainder > 0:
  mov 19*157, 227
  #     pop and throw away the numerator
  mov 227, 71*311
  #     if stack is empty: halt!
  mov 151*167*311, 233
  #     else: call 239 to multiply back the program state and gave loop signal
  mov 151*311, 229
  sub 229 ret 239*331 { mov 19, 7 }
  # else: (remainder == 0)
  #     pop the numerator
  mov 157, 71*313
  #     clear the stack empty signal if present
  #     call 239 to update program state and gave ret signal
  mov 151*167*313, 239*251
  mov 151*313, 239*251

  # after program state is updated
  # 313: ret
  mov 293*251, 149
  # 331: loop
  mov 293*331, 107
}

# main
sub 199 {
  # copy the stack from %5 to %2 and %13
  sub 137 ret 137 { mov 5^100, 2^100*13^100 }
  sub 137 ret 349 { mov 5, 2*13 }

  # call sub 107
  mov 349, 107

  # if a statement executed, restore stack and loop
  sub 149 ret 149 { mov 13^100, 5^100 }
  sub 149 ret 199 { mov 13, 5 }
}
Hurlyburly answered 17/11, 2009 at 16:7 Comment(2)
Wow – that's spectacular. Could you describe how to came to that answer? I'd love to see!Fret
Just saw this one, quite beautiful!Gooseberry
G
20

x86_64 assembly, 165 characters (28 bytes of machine code).

State is passed in %rdi, Program (pointer to null-terminated array of fractions) is in %rsi. Results are returned in %rax per the usual C-style calling conventions. Using non-standard calling conventions or Intel syntax (this is AT&T syntax) would drop a few more characters, but I'm lazy; someone else can do that. An instruction or two can almost certainly be saved by re-arranging the control flow, if someone wants to do that, feel free.

Intermediate computations (state*numerator) can be up to 128 bits wide, but only 64 bit state is supported.

_fractran:
0:  mov     %rsi,   %rcx    // set aside pointer to beginning of list
1:  mov    (%rcx),  %rax    // load numerator
    test    %rax,   %rax    // check for null-termination of array
    jz      9f              // if zero, exit
    mul     %rdi
    mov   8(%rcx),  %r8     // load denominator
    div     %r8
    test    %rdx,   %rdx    // check remainder of division
    cmovz   %rax,   %rdi    // if zero, keep result
    jz      0b              // and jump back to program start
    add     $16,    %rcx    // otherwise, advance to next instruction
    jmp     1b
9:  mov     %rdi,   %rax    // copy result for return
    ret

Delete comments, extraneous whitespace, and the verbose label _fractran for minimized version.

Gooseberry answered 17/11, 2009 at 16:7 Comment(0)
M
16

Ruby, 58 57 56 53 52 characters

This is my first-ever code golf entry, so please be gentle.

def f(n,c)d,e=c.find{|i,j|n%j<1};d ?f(n*d/e,c):n end

Usage:

irb> f 108, [[455, 33], [11, 13], [1,11], [3,7], [11,2], [1,3]]
=> 15625

irb> f 60466176, [[455, 33], [11, 13], [1, 11], [3, 7], [11, 2], [1, 3]]
=> 7888609052210118054117285652827862296732064351090230047702789306640625

Pretty version (252):

def fractran(instruction, program)
  numerator, denominator = program.find do |numerator, denominator|
    instruction % denominator < 1
  end

  if numerator
    fractran(instruction * numerator / denominator, program)
  else
    instruction
  end
end

Ruby, 53 52 using Rational

Inspired by gnibbler's solution I was able to get a solution using Rational down to 53 52 characters. Still one longer than the (less elegant) solution above.

def f(n,c)c.map{|i|return f(n*i,c)if i*n%1==0};n end

Usage:

irb> require 'rational'
irb> f 60466176, [Rational(455, 33), Rational(11, 13), Rational(1, 11), Rational(3, 7), Rational(11, 2), Rational(1, 3)]
=> Rational(7888609052210118054117285652827862296732064351090230047702789306640625, 1)

(A to_i call for prettier output would add 5 more characters.)

Moyer answered 17/11, 2009 at 16:7 Comment(4)
@Andy Dent: I had to track down one of your other posts to give you +1 for that.Gooseberry
you can use map instead of each so it ties at 52Downandout
def f(n,c)(j=c.find{|i|i*n%1==0})?f(n*j,c):n end - 48Downandout
def f(n,c)c.find{|i|($j=i*n)%1==0}?f($j,c):n end - 48Downandout
I
12

Golfscript - 32

    
    {:^{1=1$\%!}?.1={~@\/*^f}{}if}:f

    ; 108 [[3 2]] f p
    # 243
    ; 1296 [[3 2]] f p
    # 6561
    ; 108 [[455 33][11 13][1 11][3 7][11 2][1 3]] f p
    # 15625
    ; 60466176 [[455 33][11 13][1 11][3 7][11 2][1 3]] f p
    # 7888609052210118054117285652827862296732064351090230047702789306640625
Indeclinable answered 17/11, 2009 at 16:7 Comment(1)
Golfscript seems like it should be cheating, but it wasn't excluded, and it even gets the bonus vector - so I'm marking this one as the winner.Dalmatian
U
7

Haskell, 102 characters

import List
import Ratio
l&n=maybe n((&)l.numerator.(n%1*).(!!)l)$findIndex((==)1.denominator.(n%1*))l
$ ghci
Prelude> :m List Ratio
Prelude List Ratio> let l&n=maybe n((&)l.numerator.(n%1*).(!!)l)$findIndex((==)1.denominator.(n%1*))l
Prelude List Ratio> [3%2]&108
243
Prelude List Ratio> [3%2]&1296
6561
Prelude List Ratio> [455%33,11%13,1%11,3%7,11%2,1%3]&108
15625

88 with relaxed restrictions on the input/output format.

import List
import Ratio
l&n=maybe n((&)l.(*)n.(!!)l)$findIndex((==)1.denominator.(*)n)l
Prelude List Ratio> let l&n=maybe n((&)l.(*)n.(!!)l)$findIndex((==)1.denominator
Prelude List Ratio> [455%33,11%13,1%11,3%7,11%2,1%3]&108
15625 % 1
Ululate answered 17/11, 2009 at 16:7 Comment(0)
W
6

Python, 83 82 81 72 70 characters.

It's convenient to have input as fractions.Fraction objects. Same idea as in Ruby solution.

def f(n,c):d=[x for x in c if x*n%1==0];return d and f(n*d[0],c) or n

# Test code:
from fractions import Fraction as fr
assert f(108, [fr(3, 2)]) == 243
assert f(1296, [fr(3, 2)]) == 6561
assert f(108, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 15625
assert f(60466176, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 7888609052210118054117285652827862296732064351090230047702789306640625
Weir answered 17/11, 2009 at 16:7 Comment(2)
You can leave out the space before or and you don't have to count the newline at the end, so should be 68Downandout
you can take d[0] directly on x. this also allows for a lambda function. and using generators you can make the list comprehension stop at the first hit for efficiency. altogether 53 characters (also posted at gnibblers solution): g=lambda n,c:next((g(n*x,c)for x in c if x*n%1==0),n)Europa
U
6

C, 159 153 151 131 111 110 characters

v[99],c,d;main(){for(;scanf("%d",v+c++););while(d++,v[++d])
*v%v[d]?0:(*v=*v/v[d]*v[d-1],d=0);printf("%d",*v);}
$ cc f.c
$ echo 108 3 2 . | ./a.out; echo
243
$ echo 1296 3 2 . | ./a.out; echo
6561
$ echo 108 455 33 11 13 1 11 3 7 11 2 1 3 . | ./a.out; echo
15625
Ululate answered 17/11, 2009 at 16:7 Comment(5)
Interesting choice of language.Pre
Why are you using long long in golf? Do you lose that much precision from a regular int?Voluble
@Goody: "poor choice", you mean. It's certainly got little chance of winning. @Chris: the third example has an intermediate calculation with 2299171875 > 2^31. I didn't realize it when I first wrote it, but I can rearrange the program to avoid that...Ululate
maybe you can use _int64 or _int128Downandout
Nah: since the fractions are assumed to be in reduced form, I just switched the order of the division and the multiplication, and the first three tests fit within an int now :)Ululate
W
5

F#: 80 chars

let rec f p=function|x,(e,d)::t->f p (if e*x%d=0I then(e*x/d,p)else(x,t))|x,_->x

Here's an expanded version using match pattern with |cases instead of function:

//program' is the current remainder of the program
//program is the full program
let rec run program (input,remainingProgram) =
    match input, remainingProgram with
        | x, (e,d)::rest -> 
            if e*x%d = 0I then //suffix I --> bigint
                run program (e*x/d, program) //reset the program counter
            else    
                run program (x, rest) //advance the program
        | x, _ -> x //no more program left -> output the state   

Test code:

let runtests() = 
    [ f p1 (108I,p1) = 243I
      f p1 (1296I,p1) = 6561I
      f p2 (108I,p2) = 15625I
      f p2 (60466176I,p2) = pown 5I 100] 

And result (tested in F# interactive):

> runtests();;
val it : bool list = [true; true; true; true]

Edit let's have some more fun with this, and calculate some primes (see linked page in the starting post). I've written a new function g that yields the intermediate values of the state.

//calculate the first n primes with fractran
let primes n = 
    let ispow2 n = 
        let rec aux p = function
            | n when n = 1I -> Some p
            | n when n%2I = 0I -> aux (p+1) (n/2I)
            | _ -> None
        aux 0 n

    let pp = [(17I,91I);(78I,85I);(19I,51I);(23I,38I);(29I,33I);(77I,29I);(95I,23I); 
              (77I,19I);(1I,17I);(11I,13I);(13I,11I);(15I,14I);(15I,2I);(55I,1I)]

    let rec g p (x,pp) =
        seq { match x,pp with 
                |x,(e,d)::t -> yield x
                               yield! g p (if e*x%d=0I then (e*x/d,p) else (x,t))
                |x,_ -> yield x }

    g pp (2I,pp)
    |> Seq.choose ispow2
    |> Seq.distinct
    |> Seq.skip 1 //1 is not prime
    |> Seq.take n
    |> Seq.to_list

Takes a whopping 4.7 seconds to cough up the first 10 prime numbers:

> primes 10;;
Real: 00:00:04.741, CPU: 00:00:04.005, GC gen0: 334, gen1: 0, gen2: 0
val it : int list = [2; 3; 5; 7; 11; 13; 17; 19; 23; 29]

This is, without doubt, the most bizarre and slow prime number generator I've ever written. I'm not sure whether that's a good thing or a bad thing.

Weighty answered 17/11, 2009 at 16:7 Comment(0)
I
5

Python - 53

Improvement thanks to Paul

f=lambda n,c:next((f(n*x,c)for x in c if x*n%1==0),n)

testcases

from fractions import Fraction as fr
assert f(108, [fr(3, 2)]) == 243
assert f(1296, [fr(3, 2)]) == 6561
assert f(108, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 15625
assert f(60466176, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 7888609052210118054117285652827862296732064351090230047702789306640625

Python - 54 Without using Fraction

f=lambda n,c:next((f(n*i/j,c)for i,j in c if n%j<1),n)

Python - 55

This one is somewhat theoretical. The first two cases run ok, but the other two fail from recursion depth. Maybe someone can get it to work with a generator expression

f=lambda n,c:([f(n*i/j,c)for i,j in c if n%j<1]+[n])[0]

Here's one possibility, but grows to 65 even without including the import

from itertools import chain
f=lambda n,c:(chain((f(n*i/j,c)for i,j in c if n%j<1),[n])).next()
Indeclinable answered 17/11, 2009 at 16:7 Comment(3)
53: g=lambda n,c:next((g(n*x,c)for x in c if x*n%1==0),n)Europa
54 if you insist on not using fraction g=lambda n,c:next((g(n*i/j,c)for i,j in c if n%j<1),n)Europa
@Paul..brilliant, next is perfect hereDownandout
S
4

A Javascript one: 99 characters. No bonus vector :(

function g(n,p,q,i,c){i=0;while(q=p[i],c=n*q[0],(c%q[1]?++i:(n=c/q[1],i=0))<p.length){};return n;};

Input is in the format [[a,b],[c,d]]. I took advantage of Javascript's lenience: instead of doing var x=0, y=0;, you can add as many parameters as you like. It doesn't matter whether you actually pass them or not, since they default to null.

Pretty version:

function g(n,p) {
    var q, c, i=0;
    while(i < p.length) {
        q = p[i];
        c = n * q[0];
        if(c % q[1] != 0) {
            ++i;
        } else {
            n = c % q[1];
            i = 0;
        }
    }
    return n;
};
Slender answered 17/11, 2009 at 16:7 Comment(3)
No, they default to undefined, which is subtly different.Chaffer
Also, the trailing semicolon is unnecessary.Chaffer
@bcat: Indeed, they default to undefined. Fortunately it doesn't matter in this case since I assign values before use anyway. Thanks for the reminder.Slender
C
4

C#:

Tidy version:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            int ip = 1;
            decimal reg = Convert.ToInt32(args[0]);
            while (true)
            {
                if (ip+1 > args.Length)
                {
                    break;
                }
                decimal curfrac = Convert.ToDecimal(args[ip]) / Convert.ToDecimal(args[ip+1]);
                if ((curfrac * reg) % 1 == 0)
                {
                    ip = 1;
                    reg = curfrac * reg;
                }
                else
                {
                    ip += 2;
                }
            }
            Console.WriteLine(reg);
            Console.ReadKey(true);
        }
    }
}

Cut down version weighing in at 201 chars (without the namespace declarations or any of that, just the single using statement (not system) and the Main function):

using System;namespace T{using b=Convert;static class P{static void Main(string[] a){int i=1;var c=b.ToDecimal(a[0]);while(i+1<=a.Length){var f=b.ToDecimal(a[i])/b.ToDecimal(a[i+1]);if((f*c)%1==0){i=1;c*=f;}else{i+=2;}}Console.Write(c);}}}

Examples (input is through command line arguments):

input: 108 3 2
output: 243.00
input: 1296 3 2
output: 6561.0000
input: 108 455 33 11 13 1 11 3 7 11 2 1 3
output: 45045.000000000000000000000000
Colis answered 17/11, 2009 at 16:7 Comment(5)
You can save some chars by putting i+1<=a.Length as condition in the while statement and by writing c*=f instead of c=f*cEndothelioma
Wouldn't count things like "using" and "namepace" and "class". You should count only chars in a function that does all the work. Other things not important. Also you can remove needless braces in "if" and "else"Oleg
I'd also dump the Console.Read() command. Run the executable from a command prompt and you'll be able to see the result.Lippizaner
Thanks guys, it's down under 200, though just barely now!Colis
You can save four more characters by using Console.Write instead of Console.WriteLineLippizaner
S
4

Python, 110 103 95 87 characters

frc.py

def f(n,c):
 d=c
 while len(d):
  if n%d[1]:d=d[2:]
  else:n=d[0]*n/d[1];d=c
 return n

test.py

(shows how to drive it)

from frc import f
def test():
 """
 >>> f(108, [3,2])
 243
 >>> f(1296, [3,2])
 6561
 >>> f(108, [455,33,11,13,1,11,3,7,11,2,1,3])
 15625
 >>> f(60466176, [455, 33,11, 13,1, 11,3, 7,11, 2,1, 3])
 7888609052210118054117285652827862296732064351090230047702789306640625L
 """
 pass
import doctest
doctest.testmod()
Seizing answered 17/11, 2009 at 16:7 Comment(1)
put i=0 on the same line as n=(n*c[i])/c[i+1] separated by a semicolon. It'll save three characters. You might also be able to do if n%c[i+1]:i+=2 and similar tricks to get rid of all that nasty whitespace.Voluble
D
2

Reference implementation in Python

This implementation operates on prime factorizations.

First, it decodes a list of fraction tuples by encoding the numerator and denominator as a list of (idx, value) tuples, where idx is the number of the prime (2 is prime 0, 3 is prime 1, and so forth).

The current state is a list of exponents for each prime, by index. Executing an instruction requires first iterating over the denominator, checking if the indexed state element is at least the specified value, then, if it matches, decrementing state elements specified in the denominator, and incrementing those specified in the numerator.

This approach is about 5 times the speed of doing arithmetic operations on large integers in Python, and is a lot easier to debug!

A further optimisation is provided by constructing an array mapping each prime index (variable) to the first time it is checked for in the denominator of a fraction, then using that to construct a 'jump_map', consisting of the next instruction to execute for each instruction in the program.

def primes():
  """Generates an infinite sequence of primes using the Sieve of Erathsones."""
  D = {}
  q = 2
  idx = 0
  while True:
    if q not in D:
      yield idx, q
      idx += 1
      D[q * q] = [q]
    else:
      for p in D[q]:
        D.setdefault(p + q, []).append(p)
      del D[q]
    q += 1

def factorize(num, sign = 1):
  """Factorizes a number, returning a list of (prime index, exponent) tuples."""
  ret = []
  for idx, p in primes():
    count = 0
    while num % p == 0:
      num //= p
      count += 1
    if count > 0:
      ret.append((idx, count * sign))
    if num == 1:
      return tuple(ret)

def decode(program):
  """Decodes a program expressed as a list of fractions by factorizing it."""
  return [(factorize(n), factorize(d)) for n, d in program]

def check(state, denom):
  """Checks if the program has at least the specified exponents for each prime."""
  for p, val in denom:
    if state[p] < val:
      return False
  return True

def update_state(state, num, denom):
  """Checks the program's state and updates it according to an instruction."""
  if check(state, denom):
    for p, val in denom:
      state[p] -= val
    for p, val in num:
      state[p] += val
    return True
  else:
    return False

def format_state(state):
  return dict((i, v) for i, v in enumerate(state) if v != 0)

def make_usage_map(program, maxidx):
  firstref = [len(program)] * maxidx
  for i, (num, denom) in enumerate(program):
    for idx, value in denom:
      if firstref[idx] == len(program):
        firstref[idx] = i
  return firstref

def make_jump_map(program, firstref):
  jump_map = []
  for i, (num, denom) in enumerate(program):
    if num:
      jump_map.append(min(min(firstref[idx] for idx, val in num), i))
    else:
      jump_map.append(i)
  return jump_map

def fractran(program, input, debug_when=None):
  """Executes a Fractran program and returns the state at the end."""
  maxidx = max(z[0] for instr in program for part in instr for z in part) + 1
  state = [0]*maxidx

  if isinstance(input, (int, long)):
    input = factorize(input)

  for prime, val in input:
    state[prime] = val

  firstref = make_usage_map(program, maxidx)
  jump_map = make_jump_map(program, firstref)

  pc = 0
  length = len(program)
  while pc < length:
    num, denom = program[pc]
    if update_state(state, num, denom):
      if num:
        pc = jump_map[pc]
      if debug_when and debug_when(state):
        print format_state(state)
    else:
      pc += 1

  return format_state(state)
Dalmatian answered 17/11, 2009 at 16:7 Comment(0)
W
2

Haskell: 116 109 characters

f p x[]=x
f p x((n,d):b)|x*n`mod`d==0=f p(x*n`div`d)p|True=f p x b
main=do{p<-readLn;x<-readLn;print$f p x p}

This ended up as somewhat of a knockoff of Dario's entry.

Wondrous answered 17/11, 2009 at 16:7 Comment(0)
F
2

Scheme: 326

I thought a Scheme submission was needed, for parity. I also just wanted the excuse to play with it. (Excuse my rudimentary knowledge, I'm sure this could be optimized, and I am open to suggestions!)

#lang scheme
(define fractran_interpreter
(lambda (state pc program)
    (cond
        ((eq? pc (length program)) 
            (print state))
        ((integer? (* state (list-ref program pc)))
            (fractran_interpreter (* state (list-ref program pc)) 0 program))
        (else
            (fractran_interpreter state (+ pc 1) program)))))

Tests:

(fractran_interpreter 108 0 '(3/2))

(fractran_interpreter 60466176 0 '(455/33 11/13 1/11 3/7 11/2 1/3))

I get the bonus vector! (using Dr. Scheme, allocating 256 mb)

Fret answered 17/11, 2009 at 16:7 Comment(0)
F
2

Perl 6: 77 Characters (experimental)

sub f(@p,$n is copy){
loop {my$s=first {!($n%(1/$_))},@p or return $n;$n*=$s}}

Newline is optional. Call as:

say f([3/2], 1296).Int;
say f([455/33, 11/13, 1/11, 3/7, 11/2, 1/3], 60466176).Int;

Readable version:

sub Fractran (@program, $state is copy) {
  loop {
    if my $instruction = first @program:
      -> $inst { $state % (1 / $inst) == 0 } {
      $state *= $instruction;
    } else {
      return $state.Int;
    }
  }
}

Notes:

  1. The colon notation first @program: pointy-sub doesn't work on current implementations; first BLOCK, @program has to be used instead.

  2. Rakudo appears to have a buggy Rat giving incorrect results. Current Niecza runs all of the test programs correctly and quickly, including the "bonus" fraction.

Fug answered 17/11, 2009 at 16:7 Comment(0)
C
2

Lua:

Tidy code:

a=arg;
ip=2;
reg=a[1];
while a[ip] do
    curfrac = a[ip] / a[ip+1];
    if (curfrac * reg) % 1 == 0 then
        ip=2;
        reg = curfrac * reg
    else
        ip=ip+2
    end
end
print(reg)

Compact code weighing in at 98 chars (reduction suggested by Scoregraphic on my other answer, and more suggested by gwell):

a=arg i=2 r=a[1]while a[i]do c=a[i]/a[i+1]v=c*r if v%1==0 then i=2 r=v else i=i+2 end end print(r)

Run from the command line, supplying the base number first then the series of fractions presented as numbers with space separation, like the following:

C:\Users\--------\Desktop>fractran.lua 108 3 2
243
C:\Users\--------\Desktop>fractran.lua 1296 3 2
6561
C:\Users\--------\Desktop>fractran.lua 108 455 33 11 13 1 11 3 7 11 2 1 3
15625

(manually typed some of that in because it's a pain to get stuff out of the command line, though that is the results returned)

Does NOT handle the bonus vector sadly :(

Colis answered 17/11, 2009 at 16:7 Comment(5)
tonumber() can be removed because of auto-conversion. Semi-colons and spaces can be removed after close brackets.Sofa
if v%1==0 then i=2 r=v else i=i+2 end can be replaced with if v%1==0 then i=0 r=v end i=i+2 to save five characters.Sofko
That doesn't work because then it doesn't reset to the start of the list completely.Colis
I'm sorry, but can you explain how those two snippets of code do not have the same effects? I've probably overlooked something, but then again, I did briefly test my change and it seemed to work.Sofko
It always incrments the instruction pointer, which means in the case of it resetting to the start of the list it will actually start at the second item in the list (it resets to the start and increments one fraction)Colis
R
2

Perl, 84 82 char

Uses standard input.

@P=<>=~/\d+/g;$_=<>;
($a,$%)=@P[$i++,$i++],$_*$a%$%or$i=0,$_*=$a/$%while$i<@P;
print

Takes 110 chars to pass the bonus test:

use Math'BigInt blcm;@P=<>=~/\d+/g;$_=blcm<>;
($%,$=)=@P[$i++,$i++],$_*$%%$=or$i=0,($_*=$%)/=$=while$i<@P;print
Revelatory answered 17/11, 2009 at 16:7 Comment(0)
V
2

Groovy, 136 117 107 characters.

Call as groovy fractal.groovy [input state] [program vector as list of numbers]

a=args.collect{it as int}
int c=a[0]
for(i=1;i<a.size;i+=2) if(c%a[i+1]==0){c=c/a[i+1]*a[i];i=-1}
println c

Sample

bash$ groovy fractal.groovy 108 455 33 11 13 1 11 3 7 11 2 1 3
Output: 15625
Vikki answered 17/11, 2009 at 16:7 Comment(1)
Are you able to add some details on how the input and code is accepted? It looks like they're command line arguments? Output for the sample vectors would be instructive, too.Dalmatian
J
1

A bit late... dc 84 chars

Just for fun a dc solution (OpenBSD)

[ss1sp]s1[Rlp1+sp]s2?l1xz2/sz[z2/ds_:bl_:az0<L]dsLx
1[;als*lp;b~0=1e2lpdlz!<L]dsLxlsp

It handles all the cases:

$ dc fractran.dc  
455 33 11 13 1 11 3 7 11 2 1 3 60466176
7888609052210118054117285652827862296732064351090230047702789306640625
Jabber answered 17/11, 2009 at 16:7 Comment(0)
M
1

Scheme 73 characters

My first attempt, at doing this with completely standard R5RS Scheme, came in at 104 characters:

(define(f p n)(let l((q p)(n n))(if(null? q)n(let((a(* n(car q))))(if(integer?
a)(l p a)(l(cdr q)n))))))

Running against a few items in the test vector:

> (f '(3/2) 1296)
6561
> (f '(455/33 11/13 1/11 3/7 11/2 1/3) 60466176)
7888609052210118054117285652827862296732064351090230047702789306640625

If you assume that λ is bound to lambda and let/cc is defined (as they are in PLT Scheme; see below for definitions for running this in Schemes that don't define those), then I can adapt Jordan's second Ruby solution to Scheme, which comes out to 73 characters (note that the argument order is the reverse of my first solution, but the same as Jordan's; in this version, that saves one character).:

(define(f n p)(let/cc r(map(λ(i)(if(integer?(* n i))(r(f(* n i)p))))p)n))

If I don't have λ and let/cc predefined, then this one comes in at 111 characters (88 if the fairly common call/cc abbreviation is defined):

(define(f n p)(call-with-current-continuation(lambda(r)(map(lambda(i)(if(integer?(*
n i))(r(f(* n i)p))))p)n)))

Definitions of λ and let/cc:

(define-syntax λ 
  (syntax-rules () 
    ((_ . body) (lambda . body)))

(define-syntax let/cc 
  (syntax-rules () 
    ((_ var . body) (call-with-current-continuation (lambda (var) . body)))))
Margarethe answered 17/11, 2009 at 16:7 Comment(0)
G
1

Java, 200 192 179 characters

I think everyone knows that Java would not have the shortest implementation, but I wanted to see how it would compare. It solves the trivial examples, but not the bonus one.

Here is the minimized version:

class F{public static void main(String[]a){long p=new Long(a[0]);for(int i=1;i<a.length;){long n=p*new Long(a[i++]),d=new Long(a[i++]);if(n%d<1){p=n/d;i=1;}}System.out.print(p);}}

java -cp . F 108 455 33 11 13 1 11 3 7 11 2 1 3

15625

java -cp . F 1296 3 2

6561

Here is the cleaned-up version:

public class Fractran {

    public static void main(String[] args) {
        long product = new Long(args[0]);

        for (int index = 1; index < args.length;) {
            long numerator = product * new Long(args[index++]);
            long denominator = new Long(args[index++]);

            if (numerator % denominator < 1) {
                product = numerator / denominator;
                index = 1;
            } // if
        } // for

        System.out.print(product);
    }
}
Goblin answered 17/11, 2009 at 16:7 Comment(2)
You can save a few more characters by switching to new Long instead of Long.decode.Amongst
Thanks for the tip - implemented! It's a challenge to take advantage of code techniques for saving characters that one would not normally employ.Goblin
A
1

Haskell, 142 characters

Without any additional libraries and full I/O.

t n f=case f of{(a,b):f'->if mod n b == 0then(\r->r:(t r f))$a*n`div`b else t n f';_->[]}
main=readLn>>=(\f->readLn>>=(\n->print$last$t n f))
Apropos answered 17/11, 2009 at 16:7 Comment(0)
G
0

I can't leave comments yet but here's a "slightly" shorter version of RCIX's C# version (i believe it's 7 chars shorter)

using System;namespace T{static class P{static void Main(string[] a){int i=1;Func<string,decimal> d=Convert.ToDecimal;var c=d(a[0]);while(i+1<=a.Length){var f=d(a[i])/d(a[++i]);if((f*c)%1==0){i=1;c*=f;}else i++;}Console.Write(c);}}}

which uses

Func<string,decimal> d=Convert.ToDecimal

and calls d(); instead of

using b=Convert;

and repeatedly calling b.ToDecimal();.

I also removed an unnecessary pair of curly braces around the else statement to gain 1 char :).

I also replaced the a[i+1] with a[++i] and in the following else body i replaced i+=2 with i++ to gain another char :P

Gardner answered 17/11, 2009 at 16:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.