program a2;
uses classes,
  utils in '..\common\utils.pas';

const IGNORED_COUNT=3;
      SOLUTIONS_COUNT=3;
type TValue=integer;
     TValues=array of array[0..2] of integer;
     TFactor=TValue;
     TOperators=char;
     TPossibleSolution=record
                         f1,f2,f3:TFactor;
                         op:TOperators;
                         absDifference,absIgnoredDifference:TValue;
                         differences:array of TValue;
                         ignoredDifferences:array[0..IGNORED_COUNT-1] of integer;
                         ignoredLines:integer;
                       end;

  function readValues(const fileName:string):tvalues;
  var loadedfile:TStringList;
      i,pos:integer;
  begin
    loadedfile:=TStringList.create;
    try
      loadedfile.LoadFromFile(fileName);
      SetLength(result,loadedfile.Count-1);
      for i:=1 to loadedfile.Count-1 do
        if loadedfile[i]<>'' then begin
          pos:=0;
          readNumber(loadedfile[i],pos); //Index
          Result[i-1][0]:=readNumber(loadedfile[i],pos);
          Result[i-1][1]:=readNumber(loadedfile[i],pos);
          Result[i-1][2]:=readNumber(loadedfile[i],pos);
        end else begin
          SetLength(result,i-1);
          break;
        end;
    finally
      loadedfile.free;
    end;
  end;

var bestSolutions:array[0..SOLUTIONS_COUNT-1] of TPossibleSolution;
    values:TValues;

function ggT(a,b:integer):integer;
var t:integer;
begin
  while b>0 do begin
    t:=a;
    a:=b;
    b:=a mod b;
  end;
  result:=a;
end;

procedure calculateDifferences(var sol:TPossibleSolution);
var i,j:integer;
begin
  SetLength(sol.differences,length(values));
  sol.absDifference:=0;
  sol.absIgnoredDifference:=0;
  sol.ignoredLines:=0;
  FillChar(sol.ignoredDifferences,sizeof(sol.ignoredDifferences),$FF); //=-1
  for i:=0 to high(values) do begin
    if sol.op='+' then
      sol.differences[i]:=sol.f1*values[i][0]+sol.f2*values[i][1] - sol.f3*values[i][2]
     else
      sol.differences[i]:=sol.f1*values[i][0]-sol.f2*values[i][1] - sol.f3*values[i][2];
    sol.absDifference:=sol.absDifference+abs(sol.differences[i]);
    if sol.differences[i]<>0 then begin
      j:=IGNORED_COUNT-1;
      while (j>=0) do begin
        if (sol.ignoredDifferences[j]<>-1)and(abs(sol.differences[sol.ignoredDifferences[j]])>=abs(sol.differences[i])) then break
        else if j<IGNORED_COUNT-1 then sol.ignoredDifferences[j+1]:=sol.ignoredDifferences[j];
        dec(j);
      end;
      if j<IGNORED_COUNT-1 then sol.ignoredDifferences[j+1]:=i;
    end;
  end;
  for j:=0 to IGNORED_COUNT-1 do
    if sol.ignoredDifferences[j]<>-1 then begin
      sol.absIgnoredDifference:=sol.absIgnoredDifference+abs(sol.differences[sol.ignoredDifferences[j]]);
      inc(sol.ignoredLines);
    end;
end;

function createSolution(f2,f3,divisor:TFactor;out solutions:TPossibleSolution):boolean;
begin
  Result:=false;
  if f3<0 then exit;
  if f2<0 then solutions.op:='-'
  else solutions.op:='+';
  f2:=abs(f2);
  if divisor=1 then begin
    solutions.f1:=1;
    solutions.f2:=abs(f2);
    solutions.f3:=f3;
  end else begin
    divisor:=ggt(ggt(abs(f2),f3),divisor);
    solutions.f1:=divisor;
    solutions.f2:=abs(f2);
    solutions.f3:=f3;
  end;
  calculateDifferences(solutions);
  Result:=true;
end;

//-1: s1 ist besser, 0 gleich, +1 s2 ist besser
function compareSolutions(const s1,s2:TPossibleSolution):integer;
begin
  //Lösungsexistens
  if s1.op=#0 then begin
    result:=1;
    exit;
  end else if s2.op=#0 then begin
    result:=-1;
    exit;
  end;

  //Abweichung von den Messergebnissen soll minimal sein
  if s1.absDifference-s1.absIgnoredDifference<s2.absDifference-s2.absIgnoredDifference then begin
    result:=-1;
    exit;
  end else if s1.absDifference-s1.absIgnoredDifference>s2.absDifference-s2.absIgnoredDifference then begin
    result:=1;
    exit;
  end;

  //Anzahl der ignorierten Zeilen soll minimal sein
  if s1.ignoredLines<s2.ignoredLines then begin
    result:=-1;
    exit;
  end else if s1.ignoredLines>s2.ignoredLines then begin
    result:=1;
    exit;
  end;

  //Faktorengröße soll minimal sein
  if abs(s1.f1)+abs(s1.f2)+abs(s1.f3)<abs(s2.f1)+abs(s2.f2)+abs(s2.f3) then
    Result:=-1
  else if abs(s1.f1)+abs(s1.f2)+abs(s1.f3)>abs(s2.f1)+abs(s2.f2)+abs(s2.f3) then
    Result:=1
  else
    Result:=0;
end;

procedure addSolution(const sol:TPossibleSolution);
var j:integer;
begin
  for j:=0 to SOLUTIONS_COUNT-1 do
    if (sol.f1=bestSolutions[j].f1)and(sol.f2=bestSolutions[j].f2)and
       (sol.f3=bestSolutions[j].f3)and(sol.op=bestSolutions[j].op) then exit; 
  j:=SOLUTIONS_COUNT-1;
  while (j>=0) do begin
    case compareSolutions(sol,bestSolutions[j]) of
      1: break;
      -1:  if j<SOLUTIONS_COUNT-1 then
             bestSolutions[j+1]:=bestSolutions[j];
    end;
    dec(j);
  end;
  if j<SOLUTIONS_COUNT-1 then bestSolutions[j+1]:=sol;
end;

procedure createAndAddSolutions(const f2,f3,divisor:TFactor);
var sol:TPossibleSolution;
begin
  if createSolution(f2,f3,divisor,sol) then
    addSolution(sol);
end;

var
    i,j,k,len:integer;
    f1,f2,f3:TFactor;
    divi,min,max:TValue;
begin
  FillChar(bestSolutions,sizeof(bestSolutions),0);
  values:=readValues(ParamStr(1));

  //Suche nach einer perfekten Lösung
  //h1 + f_2*a1 = f_3*m1
  //h2 + f_2*a2 = f_3*m2
  //f_2 = (h2·m1 - h1·m2)/(a1·m2 - a2·m1)
  //f_3 = (a1·h2 - a2·h1)/(a1·m2 - a2·m1)
  len:=length(values);
  for i:=0 to high(values) do
    for j:=0 to high(values) do begin
      divi:=(values[i][1]*values[j][2] - values[j][1]*values[i][2]);
      if divi<>0 then begin
       f2 := values[j][0]*values[i][2] - values[i][0]*values[j][2];
       f3 := values[i][1]*values[j][0] - values[j][1]*values[i][0];
       createAndAddSolutions(f2,f3,divi);
      end;
    end;

  min:=MAXINT;
  max:=0;
  for i:=0 to high(values) do begin
    if values[i][2]>max then max:=values[i][2];
    if values[i][2]<min then min:=values[i][2];
  end;


  for i:=0 to SOLUTIONS_COUNT-1 do begin
    Writeln(bestSolutions[i].f1,' * H-Wert ',bestSolutions[i].op,' ',bestSolutions[i].f2,' * A-Wert = ',bestSolutions[i].f3,' * M-Wert');
    Writeln('  Es wurde gestrichen: ');
    for j:=0 to IGNORED_COUNT-1 do
      if bestSolutions[i].ignoredDifferences[j]<>-1 then
        writeln('    ',bestSolutions[i].ignoredDifferences[j]+1,' mit Abweichung: ',bestSolutions[i].differences[bestSolutions[i].ignoredDifferences[j]]);
    Writeln('  Dies ist eine schöne Formel, da:');
    if bestSolutions[i].absDifference-bestSolutions[i].absIgnoredDifference<(max-min) then
      Writeln('    - Die Summe der absoluten Abweichungen nur ',bestSolutions[i].absDifference-bestSolutions[i].absIgnoredDifference,' ist');
    if abs(bestSolutions[i].f1)+abs(bestSolutions[i].f2)+abs(bestSolutions[i].f3)<100 then
      Writeln('    - Die Faktoren ','(',bestSolutions[i].f1,', ',bestSolutions[i].f2,', ',bestSolutions[i].f3,') alle klein sind');
  end;
  Readln;
end.
