{
    This file is part of calcz5
    Copyright (c) 2008 by Riccardo Iaconelli

    A calculator in ZN

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

}

program calcZn;

  uses crt;

  var n, argc : integer;
  var operation : char;
  var numbers : array[0..2] of integer;

function sum (a:integer; b:integer) : integer; { a, b: numbers to sum. if b = -1,
                                                       find the inverse of a }
begin
  if (b = -1) then
    sum := n-a
  else
    sum := (a+b) mod n;
end;

function multiply (a : integer; b : integer) : integer; { a, b: numbers to multiply. if b = -1,
                                                                find the inverse of a }
  var i : integer;
begin
  if (b = -1) then
    for i := 1 to n do
    begin
      { WriteLn('With i = ', i, ' and a = ', a, ' then the product is ', (a*i) mod n); }
      if (((a*i) mod n) = 1) then
      begin
        multiply := i;
        break;
      end;
    end
  else
    multiply := (a*b) mod n
end;

procedure getData; {TODO: Make me nicer, if possible}
  var i : integer;
begin
  numbers[1] := -1;
  numbers[2] := -1;

  for i := 1 to argc do
  begin

    if argc = 1 then
      WriteLn('Scrivi il numero:')
    else
      WriteLn('Scrivi il ', i, ' numero:');

//     Write('| ');

    Read(numbers[i]);

//     Write(' |');
//     if i <> argc then
//       WriteLn(' ', operation, ' ');

    while not ((numbers[i] < n) and (numbers[i] > -1)) do
    begin
      WriteLn('Numero fuori dal range. Riprova.');
      if argc = 1 then
        WriteLn('Scrivi il numero:')
      else
        WriteLn('Scrivi il ', i, ' numero:');
      Read(numbers[i]);
    end
  end;

end;

procedure eval; { TODO: parse expressions }
  var result : integer;
begin

  if (operation = '+') then
    result := sum(numbers[1], numbers[2])
  else if (operation = 'x') then
    result := multiply(numbers[1], numbers[2]);

  WriteLn();
  WriteLn('===============================================');

  if argc = 1 then
    WriteLn('L''inverso di ', numbers[1], ' e'': ', result)
  else
    WriteLn('In Z', n, ', l''operazione ', numbers[1], ' ', operation, ' ', numbers[2], ' da come  risultato ', result);

  WriteLn('===============================================');
  WriteLn();

end;

procedure changeN;
begin
  clrscr;
  WriteLn('*** ATTENZIONE! *** Se il numero non e'' primo, il calcolatore puo'' fornire risultati errati!');
  WriteLn();
  WriteLn('Il valore corrente per n e'': ', n);
  Write('Cambiare in? ');
  Read(n);

  WriteLn('Il valore è stato cambiato con successo! Stai ora operando in Z', n, '.');
end;

function engage : boolean; { handles the main loop, if quitting returns 0}
  var scelta, operationCount, quitNumber, changeNNumber : integer;
begin

// Pascal is not optimal, fill in some data about the menu so that we don't get crazy
// with debugging if we decide to add an operation of something

  operationCount := 4;
  changeNNumber := 5;
  quitNumber := 6;

// Paint

  clrscr;

  WriteLn('La matematica dell''orologio, in Z', n);
  WriteLn('-----------------------------------');
  WriteLn('1 - Somma');
  WriteLn('2 - Moltiplicazione');
  WriteLn('3 - Inverso della somma');
  WriteLn('4 - Inverso della moltiplicazione');
  WriteLn('5 - Cambia n');
  WriteLn('6 - Esci');
  WriteLn();
  Write('calcz', n, '@localhost> ');
  Read(scelta);

// Set proprieties for the evaluation

  if (scelta = quitNumber) then
    engage := false
  else
    engage := true;

  if scelta = changeNNumber then
    changeN;

  if scelta < (operationCount+1) then
  begin
    if (scelta < 3) then
      argc := 2
    else
      argc := 1;

    if ((scelta = 1) or (scelta = 3)) then
      operation := '+'
    else if ((scelta = 2) or (scelta = 4)) then
      operation := 'x';

    getData;
    eval;
  end;

end;

procedure letHimSee;
begin
    WriteLn('Premi invio per tornare al menu principale.');
    ReadLn();
    ReadLn(); { Don't ask why I need two of these... }
end;

begin
  n := 5;

  while (engage) do
  begin
    letHimSee;
  end;

  WriteLn('To boldly go where no calculator in Zn has gone before!');
  WriteLn();
  WriteLn('<Press Enter to quit>');
  ReadLn();
  ReadLn();
end.

