{ VERIFICATION PROGRAM for STAT problem for NEERC'2001 }
{ (C) Roman Elizarov }
{$APPTYPE CONSOLE}
{.$DEFINE DEBUG}
program STAT_CHECK;
uses
  sysutils, testlib;

const
  MAX_ANSWERS = 10;
  MAX_ROWS = 1 + 3 * (MAX_ANSWERS + 1);
  MAX_COLS = MAX_ANSWERS + 2;
  FIELD_WIDTH = 6;

type
  TTable = record
    rows, cols: integer;
    t: array[1..MAX_ROWS, 1..MAX_COLS] of string;
  end;

procedure ReadTable(const st: InStream; out t: TTable); 
var
  s, cell: string;
  i: integer;
begin
  t.rows := 0;
  t.cols := -1;
  while true do begin
    s := st.readString;
    {$IFDEF DEBUG} if @st = @ans then writeln('"' + s + '"'); {$ENDIF}
    if s = '' then
      break;
    if length(s) mod FIELD_WIDTH <> 0 then
      st.Quit(_PE, 'Invalid number of characters in crosstable line: ' + s);
    if t.cols < 0 then
      t.cols := length(s) div FIELD_WIDTH
    else if t.cols <> length(s) div FIELD_WIDTH then
      st.Quit(_PE, 'Inconsistent number of characters in crosstable line: ' + s);
    inc(t.rows);
    for i := 1 to t.cols do begin
      cell := copy(s, (i - 1) * FIELD_WIDTH + 1, FIELD_WIDTH);
      assert(length(cell) = FIELD_WIDTH);
      t.t[t.rows, i] := TrimLeft(cell);
      if (t.t[t.rows, i] <> '') and (cell[FIELD_WIDTH] = ' ') then
        st.Quit(_PE, 'Data is not aligned to the right in cell "' + cell + '"');
      if length(t.t[t.rows, i]) = FIELD_WIDTH then 
        st.Quit(_PE, 'Data is too wide in cell "' + cell + '"');
    end;
  end;
  if (t.rows - 1) mod 3 <> 0 then
    st.Quit(_PE, 'Invalid number of rows in crosstable: ' + IntToStr(t.rows));
end;

function GetPercent(s: string): integer;
var
  code: integer;
begin
  if s = '-' then begin
    result := -1;
    exit;
  end;
  if s = '' then 
    Quit(_PE, 'Not a percent: ""');
  if s[length(s)] <> '%' then
    Quit(_PE, 'Not a percent: "' + s + '"');
  val(copy(s, 1, length(s) - 1), result, code);
  if (code <> 0) or (result < 0) or (result > 100) then
    Quit(_PE, 'Not a percent: "' + s + '"');
end;

var
  sans, souf: string;
  tans, touf: TTable;
  wa: boolean;
  wm: string;
  i, ii, j, n, code, sum, tot: integer;
  v, rw, cw: array[1..MAX_ANSWERS + 1, 1..MAX_ANSWERS + 1] of integer;

begin
  try 
    wa := false;
    while not ans.seekeof do begin
      { Skip descriptions }
      repeat
        sans := ans.readString;
        souf := ouf.readString;
        {$IFDEF DEBUG} writeln('"' + sans + '"'); {$ENDIF}
        if souf <> sans then
          Quit(_PE, '"' + souf + '" <> "' + sans + '"');
      until sans = '';
      { Read crosstable }
      ReadTable(ans, tans);
      ReadTable(ouf, touf);
      { Compare crosstables }
      if (touf.rows <> tans.rows) or (touf.cols <> tans.cols) then 
        Quit(_PE, 'Wrong number of rows/columns in crosstable');
      for j := 1 to tans.cols do
        if touf.t[1, j] <> tans.t[1, j] then
          Quit(_PE, 'Column headers do not match: ' + touf.t[1, j] + ' <> ' + tans.t[1, j]);
      for i := 2 to tans.rows do
        if touf.t[i, 1] <> tans.t[i, 1] then
          Quit(_PE, 'Row headers do not match: ' + touf.t[i, 1] + ' <> ' + tans.t[i, 1]);
      for ii := 1 to tans.rows div 3 do
        for j := 2 to tans.cols do begin
          i := ii * 3 - 1;
          val(touf.t[i, j], n, code);
          if code <> 0 then
            Quit(_PE, 'Not a number: "' + touf.t[i, j] + '"');
          if not wa and (touf.t[i, j] <> tans.t[i, j]) then begin
            wa := true;
            wm := 'Wrong value: ' + touf.t[i, j] + ' <> ' + tans.t[i, j];
          end;
          v[ii, j - 1] := n;
          rw[ii, j - 1] := GetPercent(touf.t[i + 1, j]);
          cw[ii, j - 1] := GetPercent(touf.t[i + 2, j]);
        end;
      { Check rw/cw values }
      if not wa then begin
        { Row-wise }
        for i := 1 to tans.rows div 3 do begin
          tot := v[i, tans.cols - 1];
          if tot = 0 then begin
            for j := 1 to tans.cols - 1 do
              if not wa and (rw[i, j] <> -1) then begin
                wa := true;
                wm := 'Undefined row-wise percents expected';
              end;
          end else begin
            sum := rw[i, tans.cols - 1];
            if not wa and (sum <> 100) then begin
              wa := true;
              wm := 'Total is not 100%: ' + IntToStr(sum);
            end;
            sum := 0;
            for j := 1 to tans.cols - 2 do begin
              inc(sum, rw[i, j]);
              if 100*v[i, j] mod tot = 0 then begin
                if not wa and (rw[i, j] <> 100*v[i, j] div tot) then begin
                  wa := true;
                  wm := 'Invalid row-wise percent: ' + IntToStr(rw[i, j]) + ' <> ' + IntToStr(100*v[i, j] div tot);
                end;
              end else begin
                if not wa and (rw[i, j] <> 100*v[i, j] div tot) and (rw[i, j] <> 1 + 100*v[i, j] div tot) then begin
                  wa := true;
                  wm := 'Invalid row-wise percent: ' + IntToStr(rw[i, j]) + ' is not a round of ' + FloatToStr(100*v[i, j] / tot);
                end;
              end;
            end;
            if not wa and (sum <> 100) then begin
              wa := true;
              wm := 'Row sum is not 100%: ' + IntToStr(sum);
            end;
          end;
        end;
        { Column-wise }
        for j := 1 to tans.cols - 1 do begin
          tot := v[tans.rows div 3, j];
          if tot = 0 then begin
            for i := 1 to tans.rows div 3 do
              if not wa and (cw[i, j] <> -1) then begin
                wa := true;
                wm := 'Undefined column-wise percents expected';
              end;
          end else begin
            sum := cw[tans.rows div 3, j];
            if not wa and (sum <> 100) then begin
              wa := true;
              wm := 'Total is not 100%: ' + IntToStr(sum);
            end;
            sum := 0;
            for i := 1 to tans.rows div 3 - 1 do begin
              inc(sum, cw[i, j]);
              if 100*v[i, j] mod tot = 0 then begin
                if not wa and (cw[i, j] <> 100*v[i, j] div tot) then begin
                  wa := true;
                  wm := 'Invalid column-wise percent: ' + IntToStr(cw[i, j]) + ' <> ' + IntToStr(100*v[i, j] div tot);
                end;
              end else begin
                if not wa and (cw[i, j] <> 100*v[i, j] div tot) and (cw[i, j] <> 1 + 100*v[i, j] div tot) then begin
                  wa := true;
                  wm := 'Invalid column-wise percent: ' + IntToStr(cw[i, j]) + ' is not a round of ' + FloatToStr(100*v[i, j] / tot);
                end;
              end;
            end;
            if not wa and (sum <> 100) then begin
              wa := true;
              wm := 'Column sum is not 100%: ' + IntToStr(sum);
            end;
          end;
        end;
      end;
    end;
    if not ouf.seekeof then
      Quit(_PE, 'Extra data in file');
    if wa then
      Quit(_WA, wm);
    Quit(_OK, 'Ok');
  except on e: Exception do 
    Quit(_FAIL, e.Message);
  end;
end.
