-module(dsquares).
-export([sumsquares/1,square/0]).

getNode(_) -> "127.0.0.1".         %% using the same IP for all actors,
                                   %% could also be obtained from a list of IPs.

totalNodes() -> 3.                 %% Total number of nodes (M).
                                   %% could also be read from a file.

%% using 'sX@127.0.0.1' as the node name for actor N 
%% where X is N rem M and M is the number of nodes (up to 9).
getNodeName(N) ->                 
    X = N rem totalNodes(),
    list_to_atom(lists:append(lists:append("s",[X+$0]), lists:append("@",getNode(N)))).

sumsquares(N) ->
    Workers = create_actors(N),
    send_squares(Workers,N,self()),
    Sum = receive_results(N),
    io:format("The sum of first ~w squares is: ~w~n",[N,Sum]).

create_actors(0) -> [];
create_actors(N) ->
    [spawn(getNodeName(N),dsquares,square,[])|create_actors(N-1)].

send_squares([],_,_) -> true;
send_squares([Worker|Workers], N, Farmer) ->
    Worker!{Farmer,N},
    send_squares(Workers,N-1,Farmer).

receive_results(N) -> 
    receive_results_aux(N,0).

receive_results_aux(0,Acc) -> Acc;
receive_results_aux(N,Acc) ->
    receive
	S -> receive_results_aux(N-1,Acc+S)
    end.

square() -> 
    receive 
	{Farmer,N} -> io:format("Computing square of: ~w at node ~w~n",[N,node()]),
		      Farmer!N*N
    end.

%% Usage:

%% Start M nodes as:
%% % erl -name s0@127.0.0.1
%% % erl -name s1@127.0.0.1
%% % ...
%% % erl -name s(M-1)@127.0.0.1

%% Start sum of squares node as:

%% % erl -name ss@127.0.0.1

%% (ss@127.0.0.1)1> dsquares:sumsquares(N).
