% $Id: matrixNxN.pl,v 1.1 2006/12/04 18:56:54 nunomorgadinho Exp $

:- initialization(init).

% include the pm2 interface lib
:- include('lib').

rows(50).
cols(X):- rows(X).

determine(Rows, Rows):-
  argument_counter(2),
  argument_list(Args),
  append([RowsString|_], [], Args),
  write_to_chars(Chars, RowsString),
  number_chars(Rows, Chars).
  
determine(Rows, Cols):-
  rows(Rows),
  cols(Cols),
  write('Going for default row number of '),write(Rows),write('.'),nl.

start_prolog_workers(0):- !. % on master dont start
start_prolog_workers(VP):-
  % thread_create(+Goal, +VP, -Id)
  thread_create(_, VP, _),
  VP1 is VP - 1,
  start_prolog_workers(VP1).

stop_prolog_workers(0):- !. % on master dont stop
stop_prolog_workers(VP):-
  thread_send_message(vid(VP,0), unlock),
  VP1 is VP - 1,
  stop_prolog_workers(VP1).

test_prolog_workers(0):- !. % on master dont do it
test_prolog_workers(VP):-
  thread_send_message(vid(VP,0), teste),
  VP1 is VP - 1,
  test_prolog_workers(VP1).

read_test(0):- !. % on master dont do it
read_test(VP):-
  thread_get_message(X),
  VP1 is VP - 1,
  read_test(VP1).

% thread 0
init:-
  pm2_is_master,!,
  pm2_max_rank(MaxRank),
  pm2_config_size(ConfigSize),

  % read number of columns from console
  % or go with the default value
  determine(Rows, Cols),
  
  % Start_Prolog(0, 0) on each node
  start_prolog_workers(MaxRank),
  
  fill_matrix(Matrix1, Rows, Cols), %%% for t(0) comment
  fill_matrix(Matrix2, Rows, Cols), %%% for t(0) comment
%  test_prolog_workers(MaxRank),      %%% for t(0) uncomment
%  read_test(MaxRank),                %%% for t(0) uncomment
  
  % give workers work
  mult(Matrix1, Matrix2, Rows, MaxRank), %%% for t(0) comment
  read_results(Rows),                    %%% for t(0) comment

  stop_prolog_workers(MaxRank),
  
  finish_listeners.

% thread != 0
init:-
  spawn(sleep, ['1']),
  mutex_init,
  worker_work.

init.
%  write('init falhou'),nl.

% -------------------------------------------------------------------

worker_work:-
        mutex_lock,
        % entretanto o unlock e' feito quando for recebida uma msg
        !, thread_get_message_loop,
        worker_work.

worker_work:-
  write('worker_work falhou'),nl,!,fail.

% tirar todas as mensagens da queue
thread_get_message_loop:-
%  pm2_self(Rank),
  thread_get_message(Termo),
  !, trick(Termo, X),
%  write('Worker '),write(Rank),write(' -> '),
%  write(Termo), write(','), write(X), nl,
  thread_send_message(vid(0,0), X),
  thread_get_message_loop.

% -------------------------------------------------------------------

% For NxN matrix will have to read N lines
read_results(0):-!.
read_results(X):-
        thread_get_message(R),
        %write('Master -> '),write(R),nl,
        X1 is X - 1,
        !, read_results(X1).

read_results_loop:-
        thread_get_message(Result),
        write('Master -> '),write(Result),nl,
        !, read_results_loop.

% -----------------------------------------------------------------------

trick(unlock, ok):- !, mutex_unlock.
trick(teste, ok).

trick(query(NumberOfRow,Row,Matrix,NumRows), (NumberOfRow,NL)):-
%  pm2_self(Rank),
%  write('Hi, I\'m on node '),write(Rank),write(' !!! '),nl,
  repeat(50, multiply_row_per_matrix(Row, Matrix, 1, NumRows, _)),
  multiply_row_per_matrix(Row, Matrix, 1, NumRows, NL).

repeat(0):-!,fail.
repeat(_).
repeat(N):- N1 is N-1, repeat(N1).

repeat(N, G):-
  repeat(N), G, fail.
repeat(_, _).

%
%  multiply_matrix(+Matrix1, +Matrix2, +NumRows, -Result)
%
%  for each row in Matrix1 call multiply_row_per_col for all the
%  columns in Matrix2
%

mult([], _, 0, _):- !. % N messages were sent for a NxN matrix

% round-roubin the machines
mult(L, M, N, 0):-
  pm2_max_rank(MaxRank),
  !, mult(L, M, N, MaxRank).

mult([Row|Rows], Matrix, N, Rank):-
  length(Row,NumRows),
  thread_send_message(vid(Rank,0), query(N,Row,Matrix,NumRows)),
  N1 is N - 1,
  Rank1 is Rank - 1,
  mult(Rows, Matrix, N1, Rank1).


% -----------------------------------------------------------------------

% the maximum number when generating the matrix content.
maximum(7).

%
%  multiply_row_per_matrix(+Row, +Matrix, +NumCol, +NumRows, -Result)  
%
%  multiplies a line of the first matrix per all the columns of the
%  second and obtains the first line of the solution matrix.
%
multiply_row_per_matrix(_, _, N, M, []):- N > M, !.
  
multiply_row_per_matrix(Row, Matrix, NumCol, NumRows, [RowCol|Res]):-
  col_items(Matrix, NumCol, Col),
  multiply_row_per_col(Row, Col, RowCol),
  NumCol1 is NumCol + 1,
  multiply_row_per_matrix(Row, Matrix, NumCol1, NumRows, Res).

%
%  multiply_row_per_col(+Row, +Col, -Result)
%
%  multiply a row (list) per a column (list) like this:
%  Elem*Elem + Elem*Elem + .. + Elem*Elem
%
multiply_row_per_col(Row, Col, Res):-
  multiply_row_per_col(Row, Col, 0, Res).

multiply_row_per_col([], [], V, V):- !.
multiply_row_per_col([First|Rest], [Second|SecRest], Acc, Res):-
%  write('('),write(First),write(' * '),write(Second),write(') + '),
  Acc1 is (Acc + (First*Second)),
  multiply_row_per_col(Rest, SecRest, Acc1, Res).

%
%  col_items(+Matrix, +NumCol, -Col)
%
%  given a matrix unify Col with a list of all the elements in that
%  col on the matrix.
%
col_items([], _, []):- !.
col_items([First|Rest], NumCol, [X|Col]):-
  nth(NumCol, First, X),
  col_items(Rest, NumCol, Col).

%
%  fill_matrix(-Matrix, +Rows, +Cols)
%
%  given N and M return a list of lists (matrix) filled randomly.
%
fill_matrix([], 0, _):- !.
fill_matrix([First|Rest], Rows, Cols):-
  fill_row(First, Cols),
  Rows1 is Rows - 1,
  fill_matrix(Rest, Rows1, Cols).

%
%  fill_row(-List, +N)
% 
%  Given N unifies List with a list of length N filled randomly.
% 
fill_row([], 0):- !.
fill_row([First|Rest], Cols):-
  maximum(Max),
  random(2, Max, First),
  Cols1 is Cols - 1,
  fill_row(Rest, Cols1).

