Thursday, May 29, 2014

New Progress Table

Below is the table of my progress on this Language Challenge. The numbers below link to my code for the solution, statements of the problems can be found on projecteuler.net. I changed the arrangement of the table in a way that will hopefully make it more useful: previously, the table was formed in chronological order relative to when I used the language: so the language I used most recently would always be at the end of the table. Now, it is arranged in order Project Euler problems: the languages below are listed in order of the problem they are currently being used to solve. Languages with multiple numbers listed indicate languages that were used to solve problems, and then were swapped out with another language, and then were used to solve another problem. Other changes/details: A * indicates a language that was used at some point but has yet to be reused to solve anything, and P&P denotes the problems that have been solved with pencil and paper, no coding necessary.
BrainFuck 1*
Ook! 6*
Whenever 5,2
Cat 3
FALSE 4
GLSL 7
ArnoldC 9
LOLCODE 10
PHP 11
Shakespeare 12
Smalltalk 13
Clojure 14
BASIC 16
INTERCAL 17
WIND 18
F# 19
E 20
COBOL 21
SwiftScript 22
Ceylon 23
Erlang 24
Kotlin 26
Befunge 27
Boo 29
K 8,30
ALGOL68 31
Go 12,32
Bash 33
Batch 34
ChucK 35
Whitespace 36
Haskell 2,37
Lua 38
Gosu 39
Rust 40
Ada 41
sML 10,42
Coffeescript 43
Scala 18,44
Rexx 45
Julia 46
x86-64 47
ELM 17,48
OCaml 49
Postscript 7,50
Cobra 51
APL 52
C++ 14,53
Chapel 54
Elixir 55
Ruby 4,40,56
Racket 7,26,57
WARM 58
C# 26,59
Javascript 9,31,60
Pascal 61
cLisp 13,62
Rebol 63
Tcl 64
Dart 63,65
Python 16,24,39,65,66
Prolog 18,67
Fantom 68
Perl 19,70
Processing 71
J 3,24,35,69,72
Groovy 73
Genie 74
Vala 75
Forth 9,76
Hack 77
R 45,78
Java 20,79
Frink 16,80
Dogescript 81
Fortran95 3,15,82
Zimbu 83
Red 84
Idris 85
Squirrel 21,52,55,86
D 22,87
C 48,50,88
P&P 1,5,6,8
P&P 15,25,28,69

Thursday, May 22, 2014

Problem 86 - Squirrel (and done with finals)

I finished my last final earlier this evening, so I will be free for a while to possibly solve Project Euler problems. I may find other things to pass my time, but nonetheless I expect I will get a good number done in the next couple of weeks. Anyway, as promised in my last post, I freed up Squirrel, so I used Squirrel on problem 86. Squirrel really is an easy language to script in. It's basically like python with curly braces. No wonder I keep coming back to it. Now that I am climbing in numbers, the code I write is starting to follow less and less directly from the problem. As the goal of this blog is to share my experiences using a bunch of programming languages, using Project Euler as the medium of the challenge, not to talk about Project Euler problems, I will not go into too much depth about why the code below produces a correct solution, though the reader can feel free to investigate themselves or read about it elsewhere. I will give a short summary of what's going on though. The problem asks about shortest paths on cuboids, but these shortest paths are along the hypotenuse of a right triangle if you unfold the cuboid. Anyway, with this, Squirrel is now my fourth most used language (behind only Python and J, which each have 5 problems solved, Squirrel is up to 4), and here is my solution, which runs in about 9.7s on my machine:
local cuboids = [];

for (local i = 0; i < 2000; ++i) {
    cuboids.append(0);
}

function gcd(a, b) {
    if (b == 0) {
        return a;
    } else {
        return gcd(b, a%b);
    }
}

function coprime(a, b) {
    return gcd(a,b) == 1;
}

function EuclidLeg1(m, n) {
    return square(m) - square(n);
}

function EuclidLeg2(m, n) {
    return 2*m*n;
}

function odd(x) {
    return x % 2 == 1;
}

function square(x) {
    return x * x;
}
function min(x, y) {
    if (x < y) {
        return x;
    } else {
        return y;
    }
}

function max(x, y) {
    return -min(-x, -y);
}

function countCuboids(l1k, l2k) {
    local pydiag = square(l1k) + square(l2k);
    if (l1k < 2000) {
        for (local i = (l2k/2); i > 0; --i) {
            if (l2k-i >= 2000) {
                return;
            } else {
                local d1 = square(l2k-i+l1k) + square(i);
                local d2 = square(l1k+i) + square(l2k-i);
                if (pydiag <= min(d1, d2)) {
                    cuboids[max(l2k-i, l1k)] += 1;
                }
            }
        }
    }
}

for (local m = 1; m < 60; ++m) {
    for (local n = 1; n < m; ++n) {
        if (odd(m-n) && coprime(m, n)) {
            local leg1 = EuclidLeg1(m, n);
            local leg2 = EuclidLeg2(m, n);
            for (local k=1; k * min(leg1, leg2) < 2000; ++k) {
                countCuboids(leg1 * k, leg2 * k);
                countCuboids(leg2 * k, leg1 * k);
            }
        }
    }
}

local count = 0;
for (local i = 0; i < 2000; ++i) {
    count += cuboids[i];
    if (count > 1000000) {
        print(i + "\n");
        break
    }
}


Problem 55 - Elixir (I should get back to finals)

So, I am in the middle of finals period and have one final exam left to take, so clearly today was the perfect day to solve another Project Euler problem in the morning. I have a solution written down in Python for problem 86 that I am ready to port to another language, but its the kind of thing that I want to have a very nice and capable language to have to port into. So, I decided to free up Squirrel (which will mean that next time I use it, I will have used Squirrel 4 times). I had used Squirrel for Problem 55, which is on finding Lycherel numbers. Elixir is a functional language (with some OO features) that runs on the Erlang VM. So, it was a very good language for solving Problem 55, as reversing lists, checking for palindromes, etc are very natural things to do in a functional programming language. Anyway, Elixir was a pretty nice language: most of my issues came from practical use issues, not from the language (I had to install a newer version of Erlang, which I had to compile from source, so I now have both Erlang 1.5 and 1.7 on my machine, then for some reason the elixir scripts didn't like being run from outside of the bin directory...little stuff like that). The language itself is a functional programming language with pretty nice syntax. Perhaps the most annoying thing was how verbose the pattern matching syntax is, but there might be a better way that I just did not bother to learn. Because I compiled the code to a .beam file then ran the ans() function from the interpreter to get the answer, I don't have very good timing information, but this definitely runs in <1s:
defmodule E55 do
    def lrev(lst) when length(lst) == 0 do
        []
    end

    def lrev(lst) do
        lrev(tl(lst)) ++ [hd(lst)]
    end

    def numrev(x) do
        numrevHelper(x, 0)
    end

    def numrevHelper(0, r) do
        r
    end

    def numrevHelper(x, r) do
        numrevHelper(div(x,10), r*10 + rem(x,10))
    end

    def digits(x) when x == 0 do
        []
    end

    def digits(x) do
        digits(div(x, 10)) ++ [rem(x, 10)]
    end

    def isNumPalindrome(x) do
        isListPalindrome(digits(x))
    end

    def isListPalindrome(lst) when length(lst) < 2 do
        true
    end

    def isListPalindrome(lst) do
        isListPalindromeHelper(lst, lrev(lst), div(length(lst), 2), 0)
    end

    def isListPalindromeHelper(lst, tsl, target, k) when k >= target do
        true
    end
    
    def isListPalindromeHelper(lst, tsl, target, k) do
        if hd(lst) == hd(tsl) do
            isListPalindromeHelper(tl(lst),tl(tsl),target,k+1)
        else
            false
        end
    end

    def isLycherel(x) do
        isLycherelHelper(x, 0)
    end

    def isLycherelHelper(x, i) when i >= 50 do
        true
    end

    def isLycherelHelper(x, i) do
        y = x + numrev(x)
        if isNumPalindrome(y) do
            false
        else
            isLycherelHelper(y, i+1)
        end
    end

    def ans() do
        ansHelper(0, 1)
    end

    def ansHelper(acc, i) when i >= 10000 do
        acc
    end

    def ansHelper(acc, i) do
        if isLycherel(i) do
            ansHelper(1+acc,1+i)
        else
            ansHelper(acc,1+i)
        end
    end
end 

Sunday, May 11, 2014

Problem 85 - Idris (Also, I should be doing a problem set)

So, Problem 85 involves counting the number of rectangles that can be found in a rectangular grid. Finding a formula to compute this was kind of fun, and the resulting formula is kind of nice. Once that formula was found, writing up a solution isn't too bad. I solved this problem in Idris, a functional programming language with fully dependent types. The theory behind all that stuff seems cool and I will probably study it more fully later (I am taking a course in Functional Programming next semester), but for now, it was just a language with Haskell like syntax that was easy enough to use for this problem, and I have been trying to find a home for it for a while. And oh yeah, fun fact: two of the main authors of Idris are the guys who designed Whitespace, if that helps increase any confidence in the quality of the language. I do like that it is, like Haskell, a compiled language, despite being highly theoretical, that is a nice feature, solution runs in .7s on my machine...only .1s slower than my python solution (both run on the same rather exaggerated search space):
tri : Int -> Int

tri n = divInt (n * (n + 1)) 2

rectangles : Int -> Int -> Int

rectangles n 0 = 0
rectangles n k = ((tri n) * k) + (rectangles n (k - 1))

N : Int
N = 2000000
findans : Int -> Int -> Int -> Int -> Int

findans 1000 h optarea optcount = optarea
findans w 1000 optarea optcount = findans (w + 1) 1 optarea optcount
findans w h optarea optcount = 
    let numrects = rectangles w h in
        if (abs (N - numrects)) < (abs (N - optcount)) then
            findans w h (w * h) numrects else
            if numrects > N then
                findans (w + 1) 1 optarea optcount else
                findans w (h + 1) optarea optcount
main : IO ()
main = putStrLn (show (findans 1 1 0 0))

Thursday, May 8, 2014

Problem 84 - Red: Monopoly, bugs in a beta, RNG issues, oh my

Continuing in my trend that I began with Zimbu, I decided to use another "fledgling" language for problem 84. This language is Red. Red is based on the REBOL language, that I used earlier in this challenge. Therefore it has a weird love for square brackets, but otherwise a very nice, readable, easy to learn syntax. However, the language is currently at version 0.4.2. This caused...some issues. Notably, many things are in the language spec that have not been implemented. Some examples of issues I ran into:

Horrible bug with array indexing. If you have a list L, you can access the first element with L/1. Indeed, you can even do fancy stuff like L/(3 + 4 - 6). However, you cannot do L/x, where x is any variable. The solution? L/(x + 0) does work, though it took me quite a while to find.

Switch statements are nice, and the syntax for them is quite simple in Red. However, though switch statements have been implemented in the language, switch statements with a default clause have not been implemented...great...this explains my switch cases that do nothing basically for a lot of inputs.

I decided that I would use Red for this problem, which involves running simulations of Monopoly. In order to simulate Monopoly, one must roll dice. In order to roll dice, one must generate random numbers. I started with the bare knowledge I have of Lehmer's algorithm / Linear Congruential generators for forming random numbers (Using a sequence z_n = a^n + b mod m) for some parameters a,b, m. However, everywhere I looked online gave good parameters that would have required integers larger than 32 bits (or at least unsigned 32 bit integers), which Red does not support. Then, I thought I would implement the suggestion of a math professor, which was to take the ones digit of cos(n^2) for some n, and this generates a pretty random sequence. However, this would be just about impossible: not only does Red not have a library cosine function, Red does not yet support floating point arithmetic. So, I went back to the drawing board, until I randomly guessed LCG parameters that gave the right answer. This eventually led to me coming across the right answer by sheer luck. Of course, I felt bad about that, so I went back to the drawing board to get more legitimately random numbers. These were my conditions for my random numbers:

1) Consistency across number of trials: the answer should converge relatively uniformly, I shouldn't get the right results after 1000 trials, and then have very wrong results after 100000, that would be a bad sign.

2) Consistency across input problems: When I first found a bad LCG that gave the right answer to the euler problem, it did not give the correct answer in the class 6-sided dice monopoly case, so it was clearly illegitimate.

3) Consistency in theory: I did not allow myself to guess parameters. I went with large primes for m, 0 for b (thus I used Lehmer's algorithm, not a general LCG), and for a, I chose the primitive root of m, as this is supposed to be a good choice.

Faced with these conditions, I tried to look for more help from the internet, maybe finding some magical small LCG. But no, I found a solution in a somewhat obvious observation in a text file somewhere on the internet (ftp://ftp.ietf.org/rfc/rfc1750.txt): if you have two kind of random, uncorrelated sequences, and you xor them together, you get a theoretically more random sequence. So, I took three large primes with different primitive roots (3, 5, 17), and I started them at different points in the sequence (to decrease any chance for correlation), and I xored the results of the three LCGs together. These gave random numbers that led to the correct results in a pretty consistent manner. I will admit that this is probably not the world's best sequence of random numbers, but it did pretty well, and I am fairly happy with the theory behind it: for example, adding another "correlated" LCG (one with the same a), never helped my results, which supports the theory behind all this. Anyway, after all of these difficulties that led to many insights, my solution is below. Runs in 14s on my machine (I am doing 200000 trials). Also on the time: Red is apparently supposed to  be able to be compiled, but every Red program that I have written that can be legally interpreted is treated as an "Invalid Red Program" by the compiler, so I gave up. Code is here:
lastrand: 25
lastrand2: 17
lastrand3: 59049
rand: func [ 
    return: [integer!] 
] [
    lastrand: (lastrand * 5) // 163847
    lastrand2: (lastrand2 * 17) // 8191
    lastrand3: (lastrand3 * 3) // 104743
    lastrand xor lastrand2 xor lastrand3
]
 
squares: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

pos: 0
turn: 0
ccn: 0
chn: 0
while [turn < 200000] [
    goagain: true
    dubcount: 0
    while [goagain] [
        d1: 1 + ((rand) % 4)
        d2: 1 + ((rand) % 4)
        either d1 = d2 [
            goagain: true
            dubcount: dubcount + 1
        ] [
            goagain: false
        ]
        either dubcount = 3 [
            pos: 11
            goagain: false
        ] [
            pos: ((pos + d1 + d2) % 40)
            switch pos [
                0 [pos: 40]
                31 [pos: 11
                    goagain: false]
                3 18 34 [
                    switch ccn [
                        0 [pos: 1]
                        1 [pos: 11
                           goagain: false]
                        2 3 4 5 6 7 8 9 10 11 12 13 14 15 [pos: pos]
                    ]
                    ccn: (ccn + 1) % 16
                ]
                8 23 37 [
                    switch chn [
                        0 [pos: 1]
                        1 [pos: 11
                           goagain: false]
                        2 [pos: 12]
                        3 [pos: 25]
                        4 [pos: 40]
                        5 [pos: 6]
                        6 7 [switch pos [
                                    8 [pos: 16]
                                    23 [pos: 26]
                                    37 [pos: 6]
                            ]]
                        8 [switch pos [
                                    8 37 [pos: 13]
                                    23 [pos: 29]
                            ]]
                        9 [pos: pos - 3]
                        10 11 12 13 14 15 [pos: pos]
                    ]
                    chn: (chn + 1) % 16
                ]
                1 2 4 5 6 7 9 10 11 12 13 14 15 16 17 19 20 [pos: pos]
                21 22 24 25 26 27 28 29 30 32 33 34 35 36 38 39 40 [pos: pos]
            ]
        ]
    ]
    squares/(pos + 0): squares/(pos + 0) + 1
    turn: turn + 1  
]
caps: [1000000 0 0 0]
ans: [0 0 0]
i: 1
while [i < 4][
    j: 1
    while [j < 41][
        if all [squares/(j + 0) < caps/(i + 0) squares/(j + 0) > caps/(i + 1)][
            caps/(i + 1): squares/(j + 0)
            ans/(i + 0): j - 1
        ]
        j: j + 1
    ]
    i: i + 1
]
print squares
print ans

Tuesday, May 6, 2014

Problem 83 - Zimbu, and no more path sums!

So, problem 83 is just a harder version of the previous 2 problems, 81, and 82. As far as figuring out a solution, my main difficulty was in misreading the problem and not realizing that you have to go from corner to corner (note: with the restriction removed, the answer to this problem is the answer to problem 82!). However, finding a language took a while. By this point, I have used a lot of languages. And while it might be tempting to say that that means that there are not many programming languages left out there, that is simply not true. However, some languages are...difficult to work with. In particular, I found this: http://fll.presidentbeef.com/, which is a list of "fledgling" languages. Many of these are still in development (Kitten, which I really wanted to use because a) it seems like a neat functional concatenative langauge and b) it would have been a nice follow up to Cat, is in this category), or have code that just simply doesn't seem like it will compile (brace was like that), or have incredibly arcane build systems (Lobster, which is set up for OS X or win32, but for Linux would have required downloading more libraries than I wanted to deal with). But, I found one that worked (the developer sayed it is in the proof of concept to beta stage, but when "proof of concept" included the compiler being able to compile itself, things seem pretty safe.

This language was Zimbu (http://www.zimbu.org/). Two things stood out about Zimbu that made me want to use it: a) it was at the bottom of the list, being last in alphabetical order and b) it was written by the author of vim, my favorite command line text editor. So, using Zimbu. It was easy to build, run, yada yada, its a compiled language that compiles to C with pretty C-like syntax, yep, pretty solid in the basics. Neat advantage: it was written by the author of vim: therefore, even though it is crazily uncommon, it has beautiful syntax highlighting / autocorrect / autoindenting in vim. And I kind of like the language in principle, it has some neat little things about it, including bringing us back to the 70s with ALL CAPS reserved words, which, actually, I found to be something of a nice feature, the caps help to make them stand out. However, the one main complaint I had with the language that it is aggressively whitespace sensitive. The language description seems to say this is so that code is easy to read, but this language takes whitespace sensitivity to kind of a pedantic level that enforces an exact style of code. The main issue here is that all operators must be separated by whitespace. So in the below, where I have updated[row - 1][col], it is a syntax error to write updated[row-1][col]. Similarly, there must be a space after the comma in a list. So yeah, just annoying stuff like that...this discussion makes me think that maybe I should at some point try to kind of formally review the languages I use, maybe I will implement something like a grading system, 1 to 5 stars on various categories...we shall see. Anyway, my code is below, runs in about .13s on my machine, and now I can start using languages that don't have easy ways of defining matrices again.
FUNC Main() int
  list<list<int>> matrix = [[4445, 2697, 5115, 718, 2209, ...]]
  list<list<int>> updated = NEW(80, NEW(80, 0))
  FOR row IN 0 TO 79
    updated[row] = NEW(80, 0)
  }
  updated[0][0] = matrix[0][0]
  FOR diag IN 1 TO 159
    int h = diag
    IF h > 79
      h = 79
    }
    int l = diag - 79
    IF h < 0
      h = 0
    }
    FOR col IN l TO h
      int row = diag - col
      int lp = matrix[row][col]
      IF row > 0
        lp += updated[row - 1][col]
      ELSE
        lp += 1000000
      }
      int rp = matrix[row][col]
      IF col > 0
        rp += updated[row][col - 1]
      ELSE
        rp += 1000000
      }
      IF rp < lp
        updated[row][col] = rp
      ELSE
        updated[row][col] = lp
      }
    }
  }
  DO
    bool mchanged = FALSE
    FOR col IN 0 TO 79
      DO
        bool changed = FALSE
        FOR row IN 0 TO 79
          IF row > 0
            IF updated[row - 1][col] < updated[row][col] - matrix[row][col]
              updated[row][col] = matrix[row][col] + updated[row - 1][col]
              changed = TRUE
            }
          }
          IF row < 79
            IF updated[row + 1][col] < updated[row][col] - matrix[row][col]
              updated[row][col] = matrix[row][col] + updated[row + 1][col]
              changed = TRUE
            }
          }
        }
        mchanged = mchanged || changed
      UNTIL !changed
    }
    FOR row IN 0 TO 79
      DO
        bool changed = FALSE
        FOR col IN 0 TO 79
          IF col > 0
            IF updated[row][col - 1] < (updated[row][col] - matrix[row][col])
              updated[row][col] = matrix[row][col] + updated[row][col - 1]
              changed = TRUE
            }
          }
          IF col < 79
            IF updated[row][col + 1] < updated[row][col] - matrix[row][col]
              updated[row][col] = matrix[row][col] + updated[row][col + 1]
              changed = TRUE
            }
          }
        }
        mchanged = mchanged || changed
      UNTIL !changed
    }
  UNTIL !mchanged
  IO.write(updated[79][79])
  IO.write("\n")
  RETURN 0
}

Saturday, May 3, 2014

Problem 82 - Fortran: huh, maybe this isn't so bad

So, when I decided that I would use Cat in order to free up FORTRAN for problem number 82, I thought oh no, Fortran, that language is supposed to be awful, I remember having issues with it before when all I needed to do was something simple, this must be bad. Well, for the purposes I needed it for, Fortran was actually kind of pleasant to work with for problem 82 (note for anyone who knows Fortran, I am using Fortran 95, which certainly has something to do with it not being as terrible as the stories of fortran 70, etc). The main difficulty was just in loading the matrix: at first I thought it would be best to be lazy and just declare the big 80x80 grid of numbers in code, but it appears that Fortran doesn't like array literals above a certain relatively small size. So, I read them in from a file, which actually was a surprisingly painless task. I will note that I did not have to write any methods (subroutines), which I think contributed a lot to my positive outlook on this problem. Anyway, problem 82 is solved, and Fortran proves itself again to be blazingly fast, my solution (including reading in the 31K text file) runs in 10ms on my machine.
!Program
program euler
implicit none
integer, dimension(80, 80) :: original, updated
integer :: row, col, changed, ans
!Commas replaced with spaces in matrix.txt
open(unit = 11, file="matrix.txt")
do row = 1,80
    read(11,*) (original(row, col),col=1,80)
end do
do row= 1,80
    updated(row, 1) = original(row, 1)
end do
do col = 2,80
    do row = 1,80
        updated(row, col) = original(row,col) + updated(row, col-1)
    end do
    changed = 1
    do while (changed .eq. 1)
        changed = 0
        do row = 1,80
            if (row > 1) then
                if (updated(row-1,col) < updated(row,col) - original(row,col)) then
                    updated(row,col) = original(row,col) + updated(row-1,col)
                    changed = 1
                end if
            end if
            if (row < 80) then
                if (updated(row+1,col) < updated(row,col) - original(row,col)) then
                    updated(row,col) = original(row,col) + updated(row+1,col)
                    changed = 1
                end if
            end if
        end do
    end do
end do
ans = 10000000
do row = 1,80
    if ( updated(row, 80) < ans) then 
        ans = updated(row, 80)
    end if
end do
print *, ans
end program euler


Progress

Below is the table of my progress. It includes all of the languages that I have solved problems in, as well as which problem I solved in that language. Statements of the problems can be found at projecteuler.net, and all of the solutions are linked to from the table, in addition to being directly in posts on this blog.
BrainFuck 1*
Haskell 2*,37
J 3*,24*,35*
J 69*,72
Ruby 4*,40*,56
Whenever 2,5*
Ook! 6*
Racket 7*,26*,57
K 8*,30
Javascript 9*,31*,60
sML 10*,42
PHP 11
Go 12*,32
cLisp 13*,62
C 48*,50
Fortran95 3*,15*,82
Python 16*,24*,39*
Python 65*,66
ELM 17*,48
Scala 18*,44
Perl 19*,70
Java 20*,79
FALSE 4
Squirrel 21*,52*
Squirrel 55*,86
D 22
Ceylon 23
Postscript 7
Befunge 27
Boo 29
Frink 16*,80
Forth 9*,76
Shakespeare 12
Bash 33
Batch 34
Whitespace 36
LOLCODE 10
Lua 38
Erlang 24
Rust 40
Ada 41
Clojure 14
Coffeescript 43
Prolog 18*,67
R 45*,78
Julia 46
x86-64 47
INTERCAL 17
OCaml 49
COBOL 21
Cobra 51
C++ 14*,53
APL 52
Chapel 54
C# 26*,59
WARM 58
Kotlin 26
Pascal 61
ALGOL68 31
Smalltalk 13
Dart 63*,65
Tcl 64
Gosu 39
Rebol 63
WIND 18
ChucK 35
F# 19
Fantom 68
Processing 71
Groovy 73
Genie 74
Vala 75
ArnoldC 9
Hack 77
Rexx 45
E 20
BASIC 16
Dogescript 81
Cat 3
Zimbu 83
Red 84
Idris 85
Elixir 55
Analytic 1,5,6,8
Analytic 15,25,28,69

Problem 3 - Cat

So, notably 3 is a pretty small number, far smaller than the numbers I am dealing with right now in the 80s. However, problem 3 is kind of annoying, as it is a problem that involves factoring a large number, where large can be simply defined as just something that is too big to store in the 32-bit integers many languages use. Before today, my solution to problem 3 was in Fortran. While Fortran may not be my favorite language to work with, it looks like it does have pretty good support for rectangular arrays, which is what I need right now to work on the path-sum problems I have come upon. I solved this problem in a language which gave me some difficulty, but possibly that is only because I am a bit rusty. Cat is, as its name sort of implies, a concatenative language, or in more simple terms a stack-based language. After the haze in my mind cleared and I was able to think in this paradigm again, my main issues came from two facts: The "normal" Cat interpreter does not support large >32-bit integers, but the online interpreter does...the online interpreter, however is a bit hard to work with, being a little iffy at times, and indeed I don't entirely understand where the last major issue I had came from. Anyway, here is my solution to problem 3 in Cat, it runs in a pretty short amount of time, despite that amount of time being hard to measure due to its online-ness. Now this means reusing Fortran soon. Yay.
define dup2 { [dup] dip dup [swap] dip }
define func1 { dup2 dup * lt [pop writeln 0] 
[dup2 mod 0 eq [dup2 swap pop [div] dip] [inc] if] if }
600851475143 2 [func1] [dup 0 gt] while