Erlang Back to the Win32 Shootout
Back to dada's perl lab

[The Original Shootout]   [NEWS]   [FAQ]   [Methodology]   [Platform Details]   [Acknowledgements]   [Scorecard]  
All Source For erlang
Ackermann's Function
%%% -*- mode: erlang -*-
%%% $Id: ackermann.erlang,v 1.6 2000/10/07 08:41:43 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(ackermann).
-export([main/1]).

main() -> main(['1']).
main([Arg]) ->
    Num = list_to_integer(atom_to_list(Arg)),
    io:fwrite("Ack(3,~w): ~w\n", [Num, ack(3, Num)]),
    halt(0).

ack(0, N) -> N+1;
ack(M, 0) -> ack(M-1, 1);
ack(M, N) -> ack(M-1, ack(M, N-1)).
Array Access
%%% -*- mode: erlang -*-
%%% $Id: ary3.erlang,v 1.3 2001/06/18 18:39:33 doug Exp $
%%% http://www.bagley.org/~doug/shootout/
%%% from James Hague

-module(ary3).
-export([main/1]).

main() -> main(['1']).
main([Arg]) ->
    Num = list_to_integer(atom_to_list(Arg)),
    ets:new(y, [set, private, named_table]),
    clear_y_array(Num),
    X = list_to_tuple(lists:seq(1, Num)),
    repeat(X, Num, 1000),
    [{_,First}] = ets:lookup(y, 1),
    [{_,Last}]  = ets:lookup(y, Num),
    io:fwrite("~w ~w~n", [First, Last]),
    ets:delete(y),
    halt(0).

clear_y_array(0) -> ok;
clear_y_array(I) ->
    ets:insert(y, {I,0}),
    clear_y_array(I - 1).

repeat(X, N, 0) -> ok;
repeat(X, N, K) ->
    calc(X, N),
    repeat(X, N, K - 1).

calc(X, 0) -> ok;
calc(X, N) ->
    ets:update_counter(y, N, element(N, X)),
    calc(X, N - 1).
Count Lines/Words/Chars
%%% -*- mode: erlang -*-
%%% $Id: wc.erlang,v 1.5 2001/06/26 05:09:25 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(wc).
-export([main/0, main/1]).

% TBD - this program should not assume lines are less than 10000 characters long

main() -> main(['1']).
main(Args) ->
    Port = open_port({fd, 0, 1}, [eof, {line, 10000}]),
    wc(Port, 0, 0, 0),
    halt(0).

wc(Port, NL, NW, NC) ->
    receive
    {Port, {_, {_, Line}}} ->
        wc(Port, NL + 1, NW + cw(Line, out, 0), NC + length(Line) + 1);
    {Port, eof} ->
        io:format("~w ~w ~w~n", [NL, NW, NC])
    end.

% count words in a line (list)
cw([],        _, Count) -> Count;
cw([$\ |T],   _, Count) -> cw(T, out, Count);
cw([$\t|T],   _, Count) -> cw(T, out, Count);
cw([_|T],   out, Count) -> cw(T, in, 1 + Count);
cw([_|T],    in, Count) -> cw(T, in, Count).
Echo Client/Server
%%% -*- mode: erlang -*-
%%% $Id: echo.erlang,v 1.6 2001/06/09 00:02:04 doug Exp $
%%% http://www.bagley.org/~doug/shootout/
%%% with help from Sebastian Strollo

%%% TBD - need to add check for valid response.

-module(echo).
-export([main/0, main/1, client/2, server/1]).

-define(DATA, <<"Hello there sailor\n">>).

main() -> main(['1']).
main([Arg]) ->
    N = list_to_integer(atom_to_list(Arg)),
    ServerSock = create_server_sock(),
    spawn(?MODULE, client, [N, socket_port(ServerSock)]),
    server(ServerSock),
    init:stop().

create_server_sock() ->
    {ok, LSock} = gen_tcp:listen(0, [binary]),
    LSock.

socket_port(Sock) ->
    {ok, Port} = inet:port(Sock),
    Port.

client(N, ServerPort) ->
    {ok, Sock} = gen_tcp:connect("localhost", ServerPort, [binary]),
    client_loop(N, Sock),
    gen_tcp:close(Sock).

client_loop(0, Sock) -> ok;
client_loop(N, Sock) ->
    ok = gen_tcp:send(Sock, ?DATA),
    receive
    {tcp, Sock, _} -> client_loop(N-1, Sock);
    {tcp_closed, Sock} -> ok
    end.

server(LSock) ->
    {ok, Sock} = gen_tcp:accept(LSock),
    server_loop(Sock, 0),
    gen_tcp:close(LSock).

server_loop(Sock, Bytes) ->
    receive
    {tcp, Sock, Packet} ->
        ok = gen_tcp:send(Sock, Packet),
        server_loop(Sock, Bytes + size(Packet));
    {tcp_closed, Sock} ->
        io:format("server processed ~w bytes~n", [Bytes]),
        gen_tcp:close(Sock)
    end.
Exception Mechanisms
%% Used destructive assignment in the process dictionary 
%% to keep count of handled exceptions.
%%
%% Usage: start from command line with
%%     erlc exceptions.erl
%%     erl -noinput -s exceptions main 200000

-module(except). 
-export([main/1]). 


blowup(N) when N rem 2 == 0 -> throw({lo_exception, N});
blowup(N) -> throw({hi_exception, N}).


lo_fun(N) ->
    case catch blowup(N) of
        {lo_exception, N1} -> put(lo_count, get(lo_count) + 1);
        {hi_exception, N2} -> throw({hi_exception, N2})
    end.


hi_fun(N) -> 
    case catch lo_fun(N) of
        {hi_exception, N1} -> put(hi_count, get(hi_count) + 1);
        _ -> true
    end.


some_fun(N) when N > 0 ->
    case catch hi_fun(N) of
        {lo_exception, N1} -> io:fwrite("~s~n", ["lo_exception should not get here."]);
        {hi_exception, N2} -> io:fwrite("~s~n", ["hi_exception should not get here."]);
        _ -> true
    end,
    some_fun(N - 1);
some_fun(0) -> true.


main([Arg]) ->
    Num = list_to_integer(atom_to_list(Arg)),
    put(hi_count, 0),
    put(lo_count, 0),
    some_fun(Num),
    io:fwrite("~s~w ~s~w~n", ["Exceptions: HI=", get(hi_count),"/ LO=", get(lo_count)]),
    halt(0).


Fibonacci Numbers
%%% -*- mode: erlang -*-
%%% $Id: fibo.erlang,v 1.2 2000/12/24 19:10:50 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(fibo).
-export([main/1]).

main() -> main(['1']).
main([Arg]) ->
    Num = list_to_integer(atom_to_list(Arg)),
    io:fwrite("~w\n", [fib(Num)]),
    halt(0).

fib(N) when N < 2 -> 1;
fib(N) -> fib(N-2) + fib(N-1).
Hash (Associative Array) Access
%%% -*- mode: erlang -*-
%%% $Id: hash.erlang,v 1.3 2001/05/21 01:28:48 doug Exp $
%%% http://www.bagley.org/~doug/shootout/
%%% Tweaked by James Hague.

%% Use ETS tables (Erlang's associative store).

-module(hash).
-export([main/0, main/1]).

main() -> main(['1']).
main([Arg]) ->
    N = list_to_integer(atom_to_list(Arg)),
    H = ets:new(ok, [set]),
    doinserts(0, N, H),
    Count = dolookups(N, 0, H),
    io:format("~w~n", [Count]),
    halt(0).

format_hex(0) -> $0;
format_hex(X) -> format_hex(X, []).
format_hex(0, Hex) -> Hex;
format_hex(X, Hex) ->
    N = X band 16#f,
    if
       N < 10 -> format_hex(X bsr 4, [N+$0 | Hex]);
       true ->   format_hex(X bsr 4, [N+($a-10) | Hex])
    end.

doinserts(N, N, H) -> ok;
doinserts(I, N, H) ->
    Hx = format_hex(I),
    ets:insert(H, {Hx, I}),
    doinserts(I+1, N, H).

dolookups(0, C, H) -> C;
dolookups(I, C, H) ->
    Nx = integer_to_list(I),
    case ets:lookup(H, Nx) of
        []-> dolookups(I-1, C, H);
        _ -> dolookups(I-1, C+1, H)
    end.

Hashes, Part II
%%% -*- mode: erlang -*-
%%% $Id: hash2.erlang,v 1.1 2001/05/21 02:22:57 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

%% Use ETS tables (Erlang's associative store).

-module(hash2).
-export([main/0, main/1]).

main() -> main(['1']).
main([Arg]) ->
    N = list_to_integer(atom_to_list(Arg)),
    H1 = ets:new(one, [set]),
    H2 = ets:new(two, [set]),
    doinserts1(0, H1),
    doinserts2(N, H1, H2),
    io:format("~w ~w ~w ~w~n", [value(H1, list_to_atom("foo_1")),
                value(H1, list_to_atom("foo_9999")),
                value(H2, list_to_atom("foo_1")),
                value(H2, list_to_atom("foo_9999"))]),
    halt(0).

doinserts1(10000, _) -> ok;
doinserts1(I, H) ->
    Key = list_to_atom(lists:append("foo_", integer_to_list(I))),
    ets:insert(H, { Key, I }),
    doinserts1(I+1, H).

doinserts2(0, _, _) -> ok;
doinserts2(I, H1, H2) ->
    addTables(H1, H2),
    doinserts2(I-1, H1, H2).

addTables(H1, H2) ->
    Key = ets:first(H1),
    addTables(Key, H1, H2).

value(Tab, Key) -> { _, V } = hd(ets:lookup(Tab, Key)), V.

addTables('$end_of_table', _, _) -> ok;
addTables(Key, H1, H2) ->
    Val1 = value(H1, Key),
    case (catch ets:update_counter(H2, Key, Val1)) of
    {'EXIT', {badarg, _}} -> ets:insert(H2, {Key, Val1});
    _                     -> true
    end,
    addTables(ets:next(H1, Key), H1, H2).

Heapsort
%% $Id: heapsort.erlang,v 1.0 2002/09/24 12:16:00 dada Exp $
%%
%% contributed by Isaac Gouy
%%
%% Quick and Dirty transliteration from the Mercury solution
%% with +1 adjustment for array indexes. 
%% Mercury uses 0..N-1 and Erlang uses 1..N
%%
%% Usage: start from command line with
%%     erlc heapsort.erl
%%     erl -noinput -s heapsort main 10000

-module(heapsort). 
-export([main/1]). 


random_heap(I, Seed, H) ->
    if 
        I < size(H) -> 
            {NextSeed, R} = gen_random(Seed),
            random_heap(I+1, NextSeed, up_heap(I, R, H));
        true -> H
    end.


up_heap(N, Y, H) ->
    HalfN = N div 2,
    X = element(HalfN+1, H), %%%% +1
    Condition = 0 < N andalso X < Y,
    if 
        Condition -> up_heap(HalfN, Y, setelement(N+1, H, X)); %%%% +1
        true -> setelement(N+1, H, Y) %%%% +1
    end.


heapsort(0, H) -> H;
heapsort(N, H) -> heapsort(N-1, remove_greatest(N, H)).


remove_greatest(N, H) ->
    X = element(0+1, H), %%%% +1
    Y = element(N+1, H), %%%% +1
    down_heap(0, N-1, Y, setelement(N+1, H, X)). %%%% +1


down_heap(I, N, X, H) -> 
    L = I + I + 1,
    R = L + 1,
    if
        N < L -> 
            setelement(I+1, H, X); %%%% +1
        true ->
            Condition = R < N andalso element(R+1, H) > element(L+1, H), %%%% +1
            J = if 
                   Condition -> R;
                   true -> L
                end,
            Y = element(J+1, H),
            if
                X > Y -> setelement(I+1, H, X); %%%% +1
                true -> down_heap(J, N, X, setelement(I+1, H, Y)) %%%% +1
            end
    end.


gen_random(Seed) ->
    IM = 139968, IA = 3877, IC = 29573,
    S = ((Seed * IA) + IC) rem IM,
    {S, S/IM}.


main([Arg]) ->
    N = list_to_integer(atom_to_list(Arg)),
    Seed = 42,
    RandomHeap = random_heap(0, Seed, erlang:make_tuple(N, 0.0)),
    SortedHeap = heapsort(N-1, RandomHeap),
    io:fwrite("~.10f~n", [element(N, SortedHeap)]),            
    halt(0).






Hello World
%%% -*- mode: erlang -*-
%%% $Id: hello.erlang,v 1.1 2001/06/17 22:00:34 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(hello).
-export([main/0]).

main() -> io:fwrite("hello world\n", []), halt(0).
Matrix Multiplication
%% Doesn't check matrix sizes match but 
%% not restricted to square matrices.
%%
%% Represent matrix as tuple of tuples.
%% Doesn't use destructive assignment so building a
%% matrix creates an awful lot of temporary lists.
%%
%% Usage: start from command line with
%%     erlc multiply.erl
%%     erl -noinput -s multiply main 1

-module(matrix). 
-export([main/1]). 
-import(lists, [reverse/1]).


sumprod(0, _, _, Sum, _, _) -> Sum;
sumprod(I, C, R, Sum, M1, M2) -> 
    NewSum = Sum + (element(I,element(R,M1)) * element(C,element(I,M2))),
    sumprod(I-1, C, R, NewSum, M1, M2).


rowmult(_, 0, _, L, _, _) -> list_to_tuple(L);
rowmult(I, C, R, L, M1, M2) -> 
    SumProd = sumprod(I, C, R, 0, M1, M2),
    rowmult(I, C-1, R, [SumProd|L], M1, M2).


mmult(_, _, 0, MM, _, _) -> list_to_tuple(MM);
mmult(I, C, R, MM, M1, M2) ->
    NewRow = rowmult(I, C, R, [], M1, M2),
    mmult(I, C, R-1, [NewRow|MM], M1, M2).

mmult(M1, M2) -> 
    Inner = size(M2), % could optimize more by hardcoding the sizes
    NRows = size(M1), 
    mmult(Inner, NRows, NRows,[], M1, M2).


repeatmmult(1, M1, M2) -> mmult(M1, M2);
repeatmmult(NTimes, M1, M2) -> 
    mmult(M1, M2),
    repeatmmult(NTimes-1, M1, M2).


mkrow(0, L, Count) -> {list_to_tuple(reverse(L)), Count};
mkrow(N, L, Count) -> mkrow(N-1, [Count|L], Count+1).


mkmatrix(0, _, _, M) -> list_to_tuple(reverse(M));
mkmatrix(NR, NC, Count, M) ->
    {Row, NewCount} = mkrow(NC, [], Count),
    mkmatrix(NR-1, NC, NewCount, [Row|M]).

mkmatrix(NR, NC) -> mkmatrix(NR, NC, 1, []).


main([Arg]) ->
    NTimes = list_to_integer(atom_to_list(Arg)),
    Size = 30,
    M1 = mkmatrix(Size, Size),
    M2 = mkmatrix(Size, Size),
    MM = repeatmmult(NTimes, M1, M2),
    Val1 = element(1,element(1, MM)), 
    Val2 = element(4,element(3, MM)), % get col 4 out of row 3
    Val3 = element(3,element(4, MM)), % get col 3 out of row 4
    Val4 = element(5,element(5, MM)), 
    io:fwrite("~w ~w ~w ~w~n", [Val1, Val2, Val3, Val4]),
    halt(0).            



Nested Loops
%%% -*- mode: erlang -*-
%%% $Id: nestedloop.erlang,v 1.1 2001/05/15 07:00:27 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(nestedloop).
-export([main/1]).

main([Arg]) ->
    Num = list_to_integer(atom_to_list(Arg)),
    io:fwrite("~w\n", [loopA(Num, Num, 0)]),
    halt(0).


loopA(0, M, N) -> N;
loopA(I, M, N) -> loopA(I - 1, M, loopB(M, M, N)).

loopB(0, M, N) -> N;
loopB(I, M, N) -> loopB(I - 1, M, loopC(M, M, N)).

loopC(0, M, N) -> N;
loopC(I, M, N) -> loopC(I - 1, M, loopD(M, M, N)).

loopD(0, M, N) -> N;
loopD(I, M, N) -> loopD(I - 1, M, loopE(M, M, N)).

loopE(0, M, N) -> N;
loopE(I, M, N) -> loopE(I - 1, M, loopF(M, N)).

loopF(0, N) -> N;
loopF(I, N) -> loopF(I - 1, 1 + N).
Producer/Consumer Threads
%%% $Id: prodcons.erlang,v 1.0 2002/10/21 16:58:00 dada Exp $
%%% http://dada.perl.it/shootout
%%%
%%% contributed by James Hague

-module(prodcons).
-export([main/0, main/1, producer/2, consumer/1]).

main() -> main(['1']).
main([Arg]) ->
    N = list_to_integer(atom_to_list(Arg)),
    spawn(prodcons, producer, [self(), N]),
    receive
        {Produced, Consumed} ->
            io:fwrite("~w ~w~n", [Produced, Consumed]),
            halt(0)
    end.

producer(From, N) ->
    Result = producer_loop(spawn(prodcons, consumer, [0]), 0, N),
    From ! Result.

producer_loop(Consumer, Produced, 0) ->
    Consumer ! {self(), stop},
    receive
        Consumed -> {Produced, Consumed}
    end;
producer_loop(Consumer, Produced, N) ->
    Consumer ! {self(), N},
    receive
        ok -> producer_loop(Consumer, Produced + 1, N - 1)
    end.

consumer(Consumed) ->
    receive
        {From, stop} ->
            From ! Consumed;
        {From, I} ->
            From ! ok,
            consumer(Consumed + 1)
    end.

Random Number Generator
%%% -*- mode: erlang -*-
%%% $Id: random.erlang,v 1.3 2001/05/18 07:39:45 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(random).
-export([main/1]).

main() -> main(['1']).
main([Arg]) ->
    N = list_to_integer(atom_to_list(Arg)),
    io:fwrite("~.9f\n", [rand(N, 42, 0.0, 100.0)]),
    halt(0).


-define(IM, 139968).
-define(IA, 3877).
-define(IC, 29573).


rand(0, _, Rand, _) -> Rand;
rand(N, Seed, Rand, Max) ->
    NewSeed = (Seed * ?IA + ?IC) rem ?IM,
    NewRand = Max * NewSeed / ?IM,
    rand(N-1, NewSeed, NewRand, Max).
Regular Expression Matching
%%% -*- mode: erlang -*-
%%% $Id: regexmatch.erlang,v 1.7 2001/01/06 22:35:23 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(regexmatch).
-export([main/0, main/1]).

%% get the program argument, which is how many test iterations to run
main() -> main(['1']).
main([Arg]) ->
    Num = list_to_integer(atom_to_list(Arg)),
    {ok, Re} = regexp:parse(
         "(^|[^0-9\\(])"        % preceeding non-digit or bol
         "("                % area code
         "\\([0-9][0-9][0-9]\\)"    % is either 3 digits in parens
         "|"                % or
         "[0-9][0-9][0-9]"        % just 3 digits
         ")"                % end of area code
         " "                % area code is followed by one space
         "[0-9][0-9][0-9]"        % exchange is 3 digits
         "[ -]"                % separator is either space or dash
         "[0-9][0-9][0-9][0-9]"        % last 4 digits
         "($|[^0-9])"            % must be followed by a non-digit
        ),
    Plist = readlines(),
    test(Num, Re, Plist),
    halt(0).


test(1, Regexp, Plist) -> 
    % display output on last iteration
    Nums = match_phones(Regexp, Plist),
    print_phones(1, Nums),
    true;
test(N, Regexp, Plist) ->
    match_phones(Regexp, Plist),
    test(N-1, Regexp, Plist).


print_phones(Count, [H|T]) ->
    [A,E,N] = H,
    % A,E,N is a list of the matching sub-expressions, which are:
    % Areacode (3 digits), Exchange (3 digits), Number (4 digits)
    io:fwrite("~w: (~s) ~s-~s~n", [Count, A,E,N]),
    print_phones(Count+1, T);
print_phones(_, []) ->
    true.


match_phones(Regexp, List) ->
    mapfilter(
      fun(String) ->
          case regexp:matches(String, Regexp) of
          {match, []}      -> false;
          {match, Matches} -> parse_phone(String, Matches);
          _                -> false
          end
      end,
      List).


parse_phone(Str, [H|T]) ->
    {Start, Len} = H,
    % Numstr is something that looks like a complete phone #
    Numstr = string:substr(Str, Start, Len),
    case regexp:matches(Numstr, "[0-9][0-9][0-9][0-9]*") of
    {match, []}      -> false;
    {match, Matches} ->
        lists:map(fun({Offset, Length}) ->
                  string:substr(Numstr, Offset, Length) end,
              Matches);
    _                -> false
    end;
parse_phone(Str, []) -> [].


mapfilter(Fun, [H|T]) ->
    case Fun(H) of
    false -> mapfilter(Fun, T);
    New   -> [New | mapfilter(Fun, T)]
    end;
mapfilter(_, []) -> [].


readlines() ->
    Port = open_port({fd, 0, 1}, [eof, {line, 512}]),
    readlines_from_stream([], Port).

readlines_from_stream(Lines, Port) ->
    receive
    {Port, eof} ->
        lists:reverse(Lines);
    {Port, {_, {_, Line}}} ->
        readlines_from_stream([Line|Lines], Port)
    end.
Reverse a File
%%% -*- mode: erlang -*-
%%% $Id: reversefile.erlang,v 1.1 2001/05/15 08:05:58 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(reversefile).
-export([main/0, main/1]).
-export([init/0]).

main() -> main(['1']).
main(Args) ->
    init(),
    halt(0).

init() ->
    Port = open_port({fd, 1, 0}, [eof, {line, 128}]),
    printlist(rev(Port, [])).

rev(Port, Lines) ->
    receive
    {Port, {_, {_, Line}}} ->
        rev(Port, [Line|Lines]);
    {Port, eof} ->
        Lines
    end.

printlist([])     -> [];
printlist([H|T])  -> io:format("~s~n", [H]), printlist(T).
Sieve of Erathostenes
%%% -*- mode: erlang -*-
%%% $Id: sieve.erlang,v 1.12 2000/10/07 08:41:44 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(sieve).
-export([main/0, main/1]).
-import(io, [fwrite/2]).
-import(lists, [seq/2]).
-import(math, [sqrt/1]).

%% get the program argument, which is how many test iterations
%% to run
main() -> main(['1']).
main(Arg) ->
    [Tmp] = Arg,
    Num = list_to_integer(atom_to_list(Tmp)),
    test(Num),
    halt(0).

test(N) ->
    Primes = primes(8192),
    Count = length(Primes),
    case N > 1 of
    true  -> test(N-1);
    false -> fwrite("Count: ~w\n", [Count])
    end.

primes(Size) ->
    era(sqrt(Size), seq(2,Size)).


%% modified from Maurice Castro's original (below), sped it up 
%% a fraction by reordering clauses and args, but if it's less
%% clear now, that's my fault.
%% -Doug

era(Max, [H|T]) when H =< Max ->
    [H | era(Max, sieve([H|T], H))];
era(Max, L) -> 
    L.

sieve([H|T], N) when H rem N =/= 0 ->
    [H | sieve(T, N)];
sieve([H|T], N) ->
    sieve(T, N);
sieve([], N) ->
    [].


%%% eratosthenes algorithm from Maurice Castro, with permission, 
%%% from his book, _Erlang in Real Time_, ISBN: 0864447434
%%% http://www.serc.rmit.edu.au/~maurice/erlbk/eg/choice/erasto.erl
%
%era(Max, L) when hd(L) =< Max ->
%    Prime = hd(L),
%    [Prime | era(Max, sieve(Prime, L))];
%era(Max, L) -> 
%    L.
%
%sieve(N, []) ->
%    [];
%sieve(N, [H|T]) when H rem N == 0 ->
%    sieve(N, T);
%sieve(N, [H|T]) ->
%    [H | sieve(N, T)].
Spell Checker
%%% -*- mode: erlang -*-
%%% $Id: spellcheck.erlang,v 1.1 2001/05/26 05:05:04 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(spellcheck).
-export([main/0, main/1]).


main() -> main(['1']).
main(Args) ->
    % load dictionary into hash table (erlang ets table)
    Dict = load_dict(),
    % read words from stin and print those not in dictionary
    spell(Dict),
    halt(0).


load_dict() ->
    Dict = ets:new(i_am_a_carrot, [set]),
    {ok, In} = file:open("Usr.Dict.Words", [raw]),
    % I'm just guessing that the file opened above has fd=3
    % this is a total hack, but I could find nothing in the Erlang
    % documention on how to do this.
    Port = open_port({fd, 3, 1}, [eof, {line, 64}]),
    read_dict(Port, Dict),
    file:close(In),
    Dict.


load_dict_1() ->
    Dict = ets:new(i_am_a_carrot, [set]),
    %{ok, In} = file:open("Usr.Dict.Words", [read]),
    %Port = open_port({fd, ????, 1}, [eof, {line, 64}]),
    Port = open_port({spawn, "cat Usr.Dict.Words"}, [eof, {line, 64}]),
    read_dict(Port, Dict),
    %file:close(In),
    Dict.


read_dict(Port, Dict) ->
    receive
    {Port, {_, {_, Word}}} ->
        Atom = list_to_atom(Word),
        ets:insert(Dict, { Atom, 1 }),
        read_dict(Port, Dict);
    {Port, eof} -> ok
    end.


spell(Dict) ->
    Port = open_port({fd, 0, 1}, [eof, {line, 64}]),
    spell(Port, Dict).

spell(Port, Dict) ->
    receive
    {Port, {_, {_, Word}}} ->
        Atom = list_to_atom(Word),
        case ets:lookup(Dict, Atom) of
        [] -> io:format("~w~n",[Atom]);
        _  -> ok
        end,
        spell(Port, Dict);
    {Port, eof} -> ok
    end.
Statistical Moments
%% $Id: moments.erl,v 1.0 2002/09/24 12:09:00 dada Exp $
%%
%% Code contributed by Isaac Gouy
%%
%% Usage: start from command line with
%%     erlc moments.erl
%%     erl -noinput -s moments main 200000

-module(moments). 
-export([main/0]). 
-import(lists, [sort/1, foldl/3, nth/2]).


accumulate([], Mean, Ad, Av, As, Ak) ->
    {Ad, Av, As, Ak};
accumulate([H|T], Mean, Ad, Av, As, Ak) ->
    D = H - Mean,
    D2 = D * D,
    accumulate(T, Mean, Ad + abs(D), Av + D2, As + (D2 * D), Ak + (D2 * D2)).


median(L, N) -> medianS(sort(L), N).


medianS(L, N) ->
    Mid = N div 2,
    case N rem 2 of
        0 -> (nth(Mid, L) + nth(Mid + 1, L)) / 2;
        1 -> nth(Mid, L)
    end.


skew(N, As, V, SD) when V > 0.0 -> As / (N * V * SD);
skew(N, As, V, SD) -> 0.


kurtosis(N, Ak, V) when V > 0.0 -> Ak / (N * V * V) - 3;
kurtosis(N, Ak, V) -> 0.


stats(L) -> 
    N = length(L),
    io:fwrite("~s~w~n", ["n:                  ", N]),
    io:fwrite("~s~f~n", ["median:             ", median(L, N)]),

    Mean = foldl(fun(X, Sum) -> X + Sum end, 0.0, L) / N,
    io:fwrite("~s~f~n", ["mean:               ", Mean]),

    {Ad, Av, As, Ak} = accumulate(L, Mean, 0.0, 0.0, 0.0, 0.0),
    io:fwrite("~s~f~n", ["average_deviation:  ", Ad / N]),

    Variance = Av / (N - 1),
    Standard_deviation = math:sqrt(Variance),
    io:fwrite("~s~f~n", ["standard_deviation: ", Standard_deviation]),
    io:fwrite("~s~f~n", ["variance:           ", Variance]),
    io:fwrite("~s~f~n", ["skew:               ", skew(N, As, Variance, Standard_deviation)]),
    io:fwrite("~s~f~n", ["kurtosis:           ", kurtosis(N, Ak, Variance)]),
    io:fwrite("~n ", []).


main() ->
    stats(lists:seq(1,500,1)),
    halt(0).

String Concatenation
%%% -*- mode: erlang -*-
%%% $Id: strcat.erlang,v 1.1 2001/05/15 07:37:04 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(strcat).
-export([main/1]).

main([Arg]) ->
    Num = list_to_integer(atom_to_list(Arg)),
    io:fwrite("~w\n", [length(string:copies("hello\n", Num))]),
    halt(0).
Sum a Column of Integers
%%% -*- mode: erlang -*-
%%% $Id: sumcol.erlang,v 1.2 2000/12/31 18:56:43 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(sumcol).
-export([main/0, main/1]).


main() -> main(['1']).
main(Args) ->
    io:put_chars( sumcol( io:fread( '', "~d" ), 0) ),
    halt(0).

sumcol( eof, Sum ) ->
        io_lib:format( "~w~n", [Sum] );
sumcol( {ok, [N]}, Sum ) ->
    io:put_chars( io_lib:format( "~w~n", [Sum] ) ),
    sumcol( io:fread( '', "~d" ), Sum + N ).
Word Frequency Count
%%% -*- mode: erlang -*-
%%% $Id: wordfreq.erlang,v 1.8 2000/12/31 22:54:17 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

%% Use ETS tables (Erlang's associative store).

-module(wordfreq).
-export([main/0, main/1]).

%% ignore program argument
main() -> main(['1']).
main(Args) ->
    OutL = fun ({Word, Count}) ->
        io:format("~7w\t~s~n", [Count, Word])
       end,
    lists:foreach(OutL, sortedfreqs()),
    halt(0).


% sort the results, descending
sortedfreqs() -> 
    Port = open_port({fd, 0, 1}, [eof, {line, 512}]),
    WordCountList = count_words_from_stream(ets:new(freqtab, [ordered_set]), Port),
    lists:reverse(lists:keysort(2, WordCountList)).


count_words_from_stream(Table, Port) ->
    receive
    {Port, eof} ->
        ets:delete(Table, ''),
        ets:tab2list(Table);
    {Port, {_, {_, Line}}} ->
        count_words([], Line, Table),
        count_words_from_stream(Table, Port)
    end.

count_word(Word, Table) ->
    WordAtom = list_to_atom(Word),
    case (catch ets:update_counter(Table, WordAtom, 1)) of
    {'EXIT', {badarg, _}} ->
        ets:insert(Table, {WordAtom, 1});
    _ ->
        true
    end.

% count_words(Word_Accumulator, Line_of_Chars, Table)
count_words([], [], Table) -> true;
count_words(Word, [], Table) ->
    count_word(Word, Table);
count_words(Word, [H|T], Table) when H >= $a, H=< $z ->
    NewWord = lists:append(Word, [H]),
    count_words(NewWord, T, Table);
count_words(Word, [H|T], Table) when H >= $A, H=< $Z ->
    NewWord = lists:append(Word, [(H - $A) + $a]),
    count_words(NewWord, T, Table);
% we hit a non-word character so count previous word and continue
count_words(Word, [H|T], Table) ->
    count_word(Word, Table),
    count_words([], T, Table).