-- Autovaca -- online simulator of contract bridge probabilities
-- Copyright (c) 2003 Alexandru Dan Corlan (http://dan.corlan.net)
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- 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.  See the
-- GNU General Public License for more details.
--
-- You could have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-- or visit http://www.gnu.org
--
-- to compile you need: gnat (gnu nyu ada translator) and aws (ada web server)
-- make with: gnatchop -w -r autovaca.ada ; gnatmake autovacad
--
-- it responds over http on port 20209 (you can change this)
-- it needs the bridge.corlan.net/avaca.html form for data input
--
--
-- History:
--   Jul  9, 2003 -- initial release
--   Mar  4, 2004 -- version 3067, logging added
--   Mar 16, 2004 -- version 3081, logging using a compact, URI-like scheme
--   Mar 19, 2004 -- version 3084, URI parsing, single click analyses possible
--   Dec 19, 2004 -- version 3359, changed log directory
--   Dec 11, 2005 -- version 3716, validating HTML

with Text_Io; use Text_Io;
with Ada.Numerics.Float_Random; use Ada.Numerics.Float_Random;
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

package Avaca is

   package Fio is new Float_Io(Float);

   type Delim is (PO,Pica,Cupa,Caro,Trefla);
   subtype Culoare is Delim range Pica..Trefla;

   type Valoare is (A,K,Q,J,N10,N9,N8,N7,N6,N5,N4,N3,N2);

   Valonam: array(Valoare) of Character :=
     ('A','K','Q','J','T','9','8','7','6','5','4','3','2');
   Valopct: array(Valoare) of Integer :=
     ( 4,  3,  2,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0);

   type Pozitie is (Nord,Sud,Est,Vest);
   Poznam: array(Pozitie) of Character := ('N','S','E','W');

   type Distri is array(Pozitie,Culoare,Valoare) of Uniformly_Distributed;
   type Len_Dist is array(Pozitie,Culoare,0..13) of Uniformly_Distributed;
   type Point_Dist is array(Pozitie,0..40) of Uniformly_Distributed;

   type Distri_Set is array(Integer range <>) of Distri;

   type Delimit is array(Delim) of Integer;

   type Delims is record
      Min: Delimit:= (others => 0);
      Max: Delimit:= (40,13,13,13,13);
   end record;

   type Delim_Spec is array(Pozitie) of Delims;

   type Feature_Dist is record
      D: Distri:= (others => (others => (others => 0.0)));
      L: Len_Dist:= (others => (others => (others => 0.0)));
      P: Point_Dist:= (others => (others => 0.0));
      Ds: Distri_Set(1..10);
      N_In_Set: Integer:= 10;
      Dsl: Integer:= 0;
   end record;


   procedure Autovaca_3(D: Distri:= (others => (others => (others => 0.0)));
                        L: Delim_Spec;
                        N_Tries: Integer;
                        Find: out Feature_Dist;
                        Hits: out Integer);

   function Compact_Form(D: Distri; L: Delim_Spec) return String;

   procedure Parse_Compact_Form(S: in String; D: out Distri; L: out Delim_Spec);

   function Show_Probabilities(R: Feature_Dist) return String;

   procedure Show_Probabilities(D: Feature_Dist);

   Too_Many_Cards: exception;
   Invalid_Problem_Form: exception;

end Avaca;

package body Avaca is

   function Ptrim(S: String) return String is

   begin
      for I in 1..S'Length loop
         if S(I)/=' ' then
            return S(I..S'Length);
         end if;
      end loop;
      return "";
   end Ptrim;

   function Ptrim(J: Integer) return String is

   begin
      return Ptrim(Integer'Image(J));
   end Ptrim;

   function Ptrim(M: Float) return String is

      Rr: String(1..10):= (others => ' ');

   begin
      if M=1.0 then
         return "1   ";
      elsif M=0.0 then
         return "0   ";
      end if;
      Fio.Put(Rr,M,2,0);
      return Ptrim(Rr);
   end Ptrim;

   procedure Parse_Compact_Form(S: in String; D: out Distri; L: out Delim_Spec) is

      Crtp: Pozitie:= Nord;
      Crtc: Delim:= PO;

      D0: Distri:= (others => (others => (others => 0.0)));
      L0: Delim_Spec;
      In_Dist: Boolean:= False;
      Parsed: Boolean:= False;
      J,K: Integer;

      function Valonamp(C: Character) return Boolean is

      begin
         for V in Valoare loop
            if Valonam(V)=C then
               return True;
            end if;
         end loop;
         return False;
      end Valonamp;

   begin
      D := D0;
      L := L0;

      for I in S'Range loop
         if I=1 then
            null; -- the first '/'
         elsif S(I)='/' then
            if Crtp=Vest then -- supplementary '/' following, ignore
               return ; -- could have more information here, such as n_tries
            end if;
            Crtc := PO;
            Crtp := Pozitie'Succ(Crtp);
            In_Dist:= False;
            Parsed := False;
         elsif S(I)=';' then
            Crtc := Delim'Succ(Crtc);
            In_Dist := False;
            Parsed := False;
         elsif S(I)=',' then
            In_Dist := True;
            Parsed := False;
         elsif Parsed then
            null;
         else -- first character of a new string
            if In_Dist then
               J := I;
               while J<=S'Length and Valonamp(S(J)) loop
                  for V in Valoare loop
                     if Valonam(V)=S(J) then
                        D(Crtp,Crtc,V) := 1.0;
                     end if;
                  end loop;
                  J := J+1;
               end loop;
               Parsed := True;
            else
               J := I;
               while J<S'Length and S(J)>='0' and S(J)<='9' loop
                  J := J+1;
               end loop;
               L(Crtp).Min(Crtc) := Integer'Value(S(I..(J-1)));
               K := J+1;
               J := K;
               while J<S'Length and S(J)>='0' and S(J)<='9' loop
                  J := J+1;
               end loop;
               L(Crtp).Max(Crtc) := Integer'Value(S(K..(J-1)));
               Parsed := True;
            end if;
         end if;
      end loop;
   exception
      when others =>
         raise Invalid_Problem_Form;
   end Parse_Compact_Form;

   function Compact_Form(D: Distri; L: Delim_Spec) return String is

      O: Unbounded_String:= To_Unbounded_String("");

      function Is_Void(P: Pozitie) return Boolean is

      begin
         if L(P).Min(PO)/=0 or L(P).Max(Po)/=40 then
            return False;
         end if;
         for C in Culoare loop
            if L(P).Min(C)/=0 or L(P).Max(C)/=13 then
               return False;
            end if;
            for V in Valoare loop
               if D(P,C,V)/= 0.0 then
                  return False;
               end if;
            end loop;
         end loop;
         return True;
      end Is_Void;

      procedure Append_Position(P: Pozitie) is

      begin
         if L(P).Min(PO)/=0 or L(P).Max(PO)/=40 then
            Append(O, Ptrim(L(P).Min(PO)) & "-" & Ptrim(L(P).Max(PO)));
         end if;
         for C in Culoare loop
            Append(O, ";");
            if L(P).Min(C)/=0 or L(P).Max(C)/=13 then
               Append(O, Ptrim(L(P).Min(C)) & "-" & Ptrim(L(P).Max(C)));
            end if;
            Append(O, ",");
            for V in Valoare loop
               if D(P,C,V)>0.0 then
                  Append(O, Valonam(V));
               end if;
            end loop;
         end loop;
      end Append_Position;

   begin
      for P in Pozitie loop
         Append(O, "/");
         if not Is_Void(P) then
            Append_Position(P);
         end if;
      end loop;
      return To_String(O);
   end Compact_Form;

   procedure Autovaca_3(D: Distri:= (others => (others => (others => 0.0)));
                        L: Delim_Spec;
                        N_Tries: Integer;
                        Find: out Feature_Dist;
                        Hits: out Integer) is

      Rg: Generator;
      Gd: Distri; -- next generated distribution
      R: Feature_Dist;

      procedure New_Distribution is

         Ncards: array(Pozitie) of Float:= (others => 0.0);
                 -- actually integers
                 -- but we want to avoid int/float conversions which are expensive
                 -- on pentiums

         Todeal: Float;
         Probs: array(Pozitie) of Float;
         Dice: Float;
         Newp: Pozitie;

      begin

         Gd := D;
         for P in Pozitie loop
            for C in Culoare loop
               for V in Valoare loop
                  if Gd(P,C,V)=1.0 then
                     Ncards(P) := Ncards(P)+1.0;
                  end if;
               end loop;
            end loop;
            if Ncards(P)>13.0 then
               raise Too_Many_Cards;
            end if;
         end loop;

         Todeal := 52.0 - Ncards(Nord) - Ncards(Sud) - Ncards(Est) - Ncards(Vest);

         if Todeal=0.0 then
            return;
         end if;

         for C in Culoare loop
            for V in Valoare loop
               if Gd(Nord,C,V)=0.0 and then Gd(Sud,C,V)=0.0 and then
                 Gd(Est,C,V)=0.0 and then Gd(Vest,C,V)=0.0 then
                  Probs(Nord) := (13.0-Ncards(Nord)) / Todeal;
                  Probs(Sud) := Probs(Nord) + (13.0 - Ncards(Sud)) / Todeal;
                  Probs(Est) := Probs(Sud) + (13.0 - Ncards(Est)) / Todeal;

                  Dice := Random(Rg);

                  if Probs(Nord)>0.0 and then Dice<=Probs(Nord) then
                     Newp := Nord;
                  elsif Probs(Sud)>Probs(Nord) and then
                    (Dice>Probs(Nord) and Dice<=Probs(Sud)) then
                     Newp := Sud;
                  elsif Probs(Est)>Probs(Sud) and then
                    (Dice>Probs(Sud) and Dice<=Probs(Est)) then
                     Newp := Est;
                  else
                     Newp := Vest;
                  end if;

                  Gd(Newp,C,V) := 1.0;
                  Ncards(Newp) := Ncards(Newp)+1.0;
                  Todeal := Todeal - 1.0;
               end if;
            end loop;
         end loop;

      end New_Distribution;

      function Co_Length(P: Pozitie; C: Culoare) return Integer is

         -- return number of cards in Gd of C for P

         R: Integer:= 0;

      begin
         for V in Valoare loop
            if Gd(P,C,V)=1.0 then
               R := R+1;
            end if;
         end loop;
         return R;
      end Co_Length;

      function Po_Length(P: Pozitie) return Integer is

         R: Integer:= 0;

      begin
         for C in Culoare loop
            for V in Valoare loop
               if Gd(P,C,V)=1.0 then
                  R := R+Valopct(V);
               end if;
            end loop;
         end loop;

         return R;
      end Po_Length;

      procedure Add_Distribution(Nth: Float) is

         Ln: Integer;

      begin
         for P in Pozitie loop
            for C in Culoare loop
               for V in Valoare loop
                  R.D(P,C,V) := (R.D(P,C,V)*(Nth-1.0) + Gd(P,C,V)) / Nth;
               end loop;
               Ln := Co_Length(P,C); -- in gd that is
               for I in 0..13 loop
                  if Ln=I then
                     R.L(P,C,I) := (R.L(P,C,I)*(Nth-1.0) + 1.0) / Nth;
                  else
                     R.L(P,C,I) := (R.L(P,C,I)*(Nth-1.0)) / Nth;
                  end if;
               end loop;
            end loop;
            Ln := Po_Length(P);
            for I in 0..40 loop
               if Ln=I then
                  R.P(P,I) := (R.P(P,I)*(Nth-1.0) + 1.0) / Nth;
               else
                  R.P(P,I) := (R.P(P,I)*(Nth-1.0)) / Nth;
               end if;
            end loop;
         end loop;
      end Add_Distribution;

      function Points(P: Pozitie) return Integer is

         Rr: Integer:= 0;

      begin
         for C in Culoare loop
            for V in Valoare loop
               if Gd(P,C,V)=1.0 then
                  Rr := Rr+Valopct(V);
               end if;
            end loop;
         end loop;

         return Rr;

      end Points;

      function Colength(P: Pozitie; C: Culoare) return Integer is

         Rr: Integer:= 0;

      begin
         for V in Valoare loop
            if Gd(P,C,V)=1.0 then
               Rr := Rr+1;
            end if;
         end loop;

         return Rr;
      end Colength;

      function Inside_Limits return Boolean is

         Pp: Integer;

      begin
         for I in Pozitie loop
            if L(I).Min(Po)>0 or else L(I).Max(Po)<40 then -- there is a limit to nr of PO
               Pp := Points(I);
               if Pp<L(I).Min(Po) then
                  return False;
               end if;
               if Pp>L(I).Max(Po) then
                  return False;
               end if;
            end if;
            for C in Culoare loop
               if L(I).Min(C)>0 or else L(I).Max(C)<13 then -- limit on length of this color
                  Pp := Colength(I,C);
                  if Pp<L(I).Min(C) then
                     return False;
                  end if;
                  if Pp>L(I).Max(C) then
                     return False;
                  end if;
               end if;
            end loop;
         end loop;

         return True;
      end Inside_Limits;

      N_Hits: Integer:= 0;

   begin
      Reset(Rg);
      for I in 1..N_Tries loop
         New_Distribution;
         if Inside_Limits then
            N_Hits := N_Hits+1;
            Add_Distribution(Float(N_Hits));
            if N_Hits<=R.N_In_Set then
               R.Ds(N_Hits) := Gd;
               R.Dsl := N_Hits;
            end if;
         end if;
      end loop;
      Hits := N_Hits;
      Find := R;
   end Autovaca_3;

   function Show_Probabilities(R: Feature_Dist) return String is

      O: Unbounded_String:= To_Unbounded_String("");

      function Colornam(C: Culoare) return String is

      begin
         case C is
            when Pica => return "<IMG SRC=""/pica.gif"" ALT=""S"">" ;
            when Cupa => return "<IMG SRC=""/cupa.gif"" ALT=""H"">" ;
            when Caro => return "<IMG SRC=""/caro.gif"" ALT=""D"">" ;
            when Trefla => return "<IMG SRC=""/trefla.gif"" ALT=""C"">" ;
         end case;
      end;

      function Show_Distri(D: Distri) return String is

         O: Unbounded_String:= To_Unbounded_String("");

      begin
         Append(O, "<TABLE><TR>");
         for P in Pozitie loop
            Append(O,"<TD></TD><TD WIDTH=""20%"">" & Poznam(P) & "</TD>" & Ascii.Lf);
         end loop;
         Append(O, "</TR>" & Ascii.Lf);
         for C in Culoare loop
            Append(O, "<TR>");
            for P in Pozitie loop
               Append(O,"<TD>" & Colornam(C) & "</TD><TD>");
               for V in Valoare loop
                  if D(P,C,V)=1.0 then
                     Append(O,Valonam(V));
                  end if;
               end loop;
               Append(O,"</TD>" & Ascii.Lf);
            end loop;
            Append(O, "</TR>" & Ascii.Lf);
         end loop;
         Append(O, "</TABLE>" & Ascii.Lf);
         return To_String(O);
      end Show_Distri;

   begin
      Append(O, "<CENTER><B>Simulated probabilities of each card in each hand</B><BR>"
             & "</CENTER>" & Ascii.Lf);
      Append(O, "<TABLE WIDTH=""100%""><TR>" & Ascii.Lf);
      for I in 1..4 loop
         Append(O, "<TD></TD><TD></TD>" &
                "<TD WIDTH=""4%"">N</TD><TD WIDTH=""4%"" ALIGN=CENTER>S</TD>");
         Append(O, "<TD WIDTH=""4%"" ALIGN=CENTER>E</TD><TD WIDTH=""4%"" ALIGN=CENTER>W</TD>"
                & Ascii.Lf);
      end loop;
      Append(O, "</TR>" & Ascii.Lf);
      for V in Valoare loop
         Append(O,"<TR>");
         for C in Culoare loop
            Append(O,"<TD WIDTH=""6%"" ALIGN=RIGHT>");
            Append(O, Valonam(V) & "</TD><TD>" & Colornam(C));
            Append(O,"</TD>");
            for P in Pozitie loop
               Append(O, "<TD>" & Ptrim(R.D(P,C,V)) & "</TD>");
            end loop;
         end loop;
         Append(O, "</TR>" & Ascii.Lf);
      end loop;
      Append(O, "</TABLE>" & Ascii.Lf);

      Append(O,
             "<CENTER><B>Simulated probabilities of each colour length in each hand</B><BR>"
             & "</CENTER>" & Ascii.Lf);
      Append(O, "<TABLE WIDTH=""100%""><TR>" & Ascii.Lf);
      for I in 1..4 loop
         Append(O, "<TD></TD><TD></TD>" &
                "<TD WIDTH=""4%"">N</TD><TD WIDTH=""4%"" ALIGN=CENTER>S</TD>");
         Append(O, "<TD WIDTH=""4%"" ALIGN=CENTER>E</TD><TD WIDTH=""4%"" ALIGN=CENTER>W</TD>"
                & Ascii.Lf);
      end loop;
      Append(O, "</TR>" & Ascii.Lf);
      for V in 0..13 loop
         Append(O,"<TR>");
         for C in Culoare loop
            Append(O,"<TD WIDTH=""6%"" ALIGN=RIGHT>");
            Append(O, Integer'Image(V) & "</TD><TD>" & Colornam(C));
            Append(O,"</TD>");
            for P in Pozitie loop
               Append(O, "<TD>" & Ptrim(R.L(P,C,V)) & "</TD>");
            end loop;
         end loop;
         Append(O, "</TR>" & Ascii.Lf);
      end loop;
      Append(O, "</TABLE>" & Ascii.Lf);

      Append(O, "<CENTER><B>Simulated probabilities of honor "&
             "point numbers in each hand</B><BR>" &
             "</CENTER>" & Ascii.Lf);
      Append(O, "<TABLE WIDTH=""100%""><TR>" & Ascii.Lf);
      Append(O, "<TD></TD><TD></TD>" &
             "<TD WIDTH=""4%"">N</TD><TD WIDTH=""4%"" ALIGN=CENTER>S</TD>");
      Append(O, "<TD WIDTH=""4%"" ALIGN=CENTER>E</TD><TD WIDTH=""4%"" ALIGN=CENTER>W</TD>"
             & Ascii.Lf);
      Append(O, "</TR>" & Ascii.Lf);
      for V in 0..40 loop
         Append(O,"<TR>");
         Append(O,"<TD WIDTH=""6%"" ALIGN=RIGHT>");
         Append(O, Integer'Image(V) & "</TD><TD>");
         Append(O,"</TD>");
         for P in Pozitie loop
            Append(O, "<TD>" & Ptrim(R.P(P,V)) & "</TD>");
         end loop;
         Append(O, "</TR>" & Ascii.Lf);
      end loop;
      Append(O, "</TABLE>" & Ascii.Lf);

      Append(O, "<CENTER><B>Simulated sample distributions</B><BR>" & Ascii.Lf);

      for I in 1..R.Dsl loop
         Append(O, "<P>" & Integer'Image(I) & "<BR>" & Ascii.Lf);
         Append(O, Show_Distri(R.Ds(I)));
      end loop;

      Append(O, "</CENTER><P>" & Ascii.Lf);

      return To_String(O);
   end Show_Probabilities;

   procedure Show_Probabilities(D: Feature_Dist) is

   begin
      Put_Line(Show_Probabilities(D));
   end Show_Probabilities;


end Avaca;


with Ada.Interrupts; use Ada.Interrupts;
with Ada.Interrupts.Names; use Ada.Interrupts.Names;

package Unisig is

   protected Termination_Flag is

      procedure Termi;
      pragma Interrupt_Handler(Termi);
      pragma Attach_Handler(Termi,SIGQUIT);

      function Istermi return Boolean;

   private

      Stopit: Boolean:= False;

   end Termination_Flag;

end Unisig;


package body Unisig is

   protected body Termination_Flag is

      procedure Termi is

      begin
         Stopit := True;
      end Termi;

      function Istermi return Boolean is

      begin
         return Stopit;
      end Istermi;

   end Termination_Flag;

end Unisig;


with Ada.Text_IO; use Ada.Text_Io;

with AWS.Response;
with AWS.Server;
with AWS.Status;
with AWS.Default;
with Aws.Parameters;
with Aws.Mime;
with Aws.Log;

with Unisig; use Unisig;
with Ada.Exceptions; use Ada.Exceptions;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Avaca; use Avaca;

procedure Autovacad is

   WS: AWS.Server.HTTP;
   Lobj: Aws.Log.Object;

   function Autovaca_Server (Request : in AWS.Status.Data)
                            return AWS.Response.Data is

      P: Aws.Parameters.List:= Aws.Status.Parameters(Request);
      Uri: String:= Aws.Status.Uri(Request);
      Urr: Unbounded_String:= To_Unbounded_String("");
      Npa: Integer:= Aws.Parameters.Count(P);

      function Build_Copyright return String is

         O: Unbounded_String:= To_Unbounded_String("");

      begin
         Append(O, "<!-- Generated by Autovaca>" & Ascii.Lf);
         Append(O, "<!-- Copyright (c) 1999-2003 Alexandru Dan Corlan MD PhD>" & Ascii.Lf);
         Append(O, Ascii.Lf);
         return To_String(O);
      end Build_Copyright;

      Dd: Distri:= (others => (others => (others => 0.0)));
      Rd: Feature_Dist; -- results
      Nh: Integer; -- number of hits
      Ll: Delim_Spec;
      Np: Pozitie;
      Nc: Delim;

      Ntries: Integer:= 10_000;

      O: Unbounded_String:= To_Unbounded_String("");

      type Byte is mod 256;
      type Byte_String is array(Integer range <>) of Byte;

      Cupa_Gif: Byte_String:= ( 71, 73, 70, 56, 57, 97, 13, 0, 11, 0, 179, 0, 0, 0, 0,
      0, 191, 0, 0, 0, 191, 0, 191, 191, 0, 0, 0, 191, 191, 0,
      191, 0, 191, 191, 192, 192, 192, 128, 128, 128, 255, 0, 0, 0, 255,
      0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 0, 255, 255, 255, 255,
      255, 33, 249, 4, 1, 0, 0, 7, 0, 44, 0, 0, 0, 0, 13,
      0, 11, 0, 0, 4, 29, 240, 156, 68, 229, 172, 146, 234, 187, 185,
      254, 73, 6, 130, 226, 216, 121, 164, 101, 98, 234, 104, 189, 233, 219,
      134, 178, 76, 191, 17, 0, 59);

      Pica_Gif: Byte_String:= ( 71, 73, 70, 56, 57, 97, 13, 0, 11, 0, 179, 0, 0, 0, 0,
      0, 191, 0, 0, 0, 191, 0, 191, 191, 0, 0, 0, 191, 191, 0,
      191, 0, 191, 191, 192, 192, 192, 128, 128, 128, 255, 0, 0, 0, 255,
      0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 0, 255, 255, 255, 255,
      255, 33, 249, 4, 1, 0, 0, 7, 0, 44, 0, 0, 0, 0, 13,
      0, 11, 0, 0, 4, 28, 240, 200, 9, 166, 181, 32, 95, 156, 245,
      238, 224, 5, 142, 212, 72, 30, 230, 153, 134, 146, 87, 161, 175, 232,
      125, 230, 20, 1, 0, 59);

      Caro_Gif: Byte_String:= ( 71, 73, 70, 56, 57, 97, 13, 0, 11, 0, 179, 0, 0, 0, 0,
      0, 191, 0, 0, 0, 191, 0, 191, 191, 0, 0, 0, 191, 191, 0,
      191, 0, 191, 191, 192, 192, 192, 128, 128, 128, 255, 0, 0, 0, 255,
      0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 0, 255, 255, 255, 255,
      255, 33, 249, 4, 1, 0, 0, 7, 0, 44, 0, 0, 0, 0, 13,
      0, 11, 0, 0, 4, 26, 240, 200, 153, 166, 181, 41, 95, 156, 245,
      238, 224, 5, 142, 212, 72, 74, 102, 39, 154, 219, 113, 182, 106, 75,
      109, 17, 0, 59);

      Trefla_Gif: Byte_String:= (71, 73, 70, 56, 57, 97, 13, 0, 11, 0, 179, 0, 0, 0, 0,
      0, 191, 0, 0, 0, 191, 0, 191, 191, 0, 0, 0, 191, 191, 0,
      191, 0, 191, 191, 192, 192, 192, 128, 128, 128, 255, 0, 0, 0, 255,
      0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 0, 255, 255, 255, 255,
      255, 33, 249, 4, 1, 0, 0, 7, 0, 44, 0, 0, 0, 0, 13,
      0, 11, 0, 0, 4, 30, 240, 72, 9, 234, 188, 180, 90, 156, 53,
      159, 30, 183, 1, 25, 232, 145, 26, 121, 164, 236, 120, 174, 33, 76,
                                 149, 226, 86, 179, 83, 4, 0, 59);


      function To_String(B: Byte_String) return String is -- restored from clear

         R: String(1..B'Length);

      begin
         for I in B'Range loop
            R(I-B'First+1) := Character'Val(Integer(B(I)));
         end loop;
         return R;
      end To_String;


      Invalid_Form: exception;
      Wrong_Card: exception;
      Duplicate_Card: exception;
      Wrong_Length: exception;
      Wrong_Number_Of_Points: exception;

   begin

      if Uri="/pica.gif" then
         return Aws.Response.Build("image/gif", To_String(Pica_Gif));
      end if;
      if Uri="/cupa.gif" then
         return Aws.Response.Build("image/gif", To_String(Cupa_Gif));
      end if;
      if Uri="/caro.gif" then
         return Aws.Response.Build("image/gif", To_String(Caro_Gif));
      end if;
      if Uri="/trefla.gif" then
         return Aws.Response.Build("image/gif", To_String(Trefla_Gif));
      end if;

      if Uri="/" then -- data is expected in POST or GET form
         for Zz in 1..Npa loop
            declare
               Nm: String:= Aws.Parameters.Get_Name(P,Zz);
               Vl: String:= Aws.Parameters.Get_Value(P,Zz);
            begin
               if Nm="NTRIES" then
                  Ntries := Integer'Value(Vl);
                  if Ntries>200_000 then
                     Ntries := 200_000;
                  end if;
               else
                  if Nm(1)='N' then
                     Np := Nord;
                  elsif Nm(1)='S' then
                     Np := Sud;
                  elsif Nm(1)='E' then
                     Np := Est;
                  elsif Nm(1)='W' then
                     Np := Vest;
                  else
                     raise Invalid_Form;
                  end if;

                  if Nm(2)='S' then
                     Nc := Pica;
                  elsif Nm(2)='H' then
                     Nc := Cupa;
                  elsif Nm(2)='D' then
                     Nc := Caro;
                  elsif Nm(2)='C' then
                     Nc := Trefla;
                  elsif Nm(2)='P' then
                     Nc := PO;
                  else
                     raise Invalid_Form;
                  end if;

                  if Nm(3)='D' then
                     for Ji in 1..Vl'Length loop
                        declare
                           Found: Boolean:= False;
                        begin
                           for I in Valoare loop
                              if Vl(Ji)=Valonam(I) then
                                 for Pp in Pozitie loop
                                    if Dd(Pp,Nc,I)=1.0 then
                                       raise Duplicate_Card;
                                    end if;
                                 end loop;
                                 Dd(Np,Nc,I) := 1.0;
                                 Found := True;
                              end if;
                           end loop;
                           if not Found then
                              raise Wrong_Card;
                           end if;
                        end;
                     end loop;
                  elsif Nm(3)='M' and then Nm(4)='I' then
                     Ll(Np).Min(Nc) := Integer'Value(Vl);
                     if Nc/=Po and then (Ll(Np).Min(Nc)>13 or else Ll(Np).Min(Nc)<0
                                         or else Ll(Np).Min(Nc)>Ll(Np).Max(Nc)) then
                        raise Wrong_Length;
                     end if;
                     if Nc=Po and then (Ll(Np).Min(Nc)>40 or else Ll(Np).Min(Nc)<0
                                        or else Ll(Np).Min(Nc)>Ll(Np).Max(Nc)) then
                        raise Wrong_Number_Of_Points;
                     end if;
                  elsif Nm(3)='M' and then Nm(4)='A' then
                     Ll(Np).Max(Nc) := Integer'Value(Vl);
                     if Nc/=Po and then (Ll(Np).Max(Nc)>13 or else Ll(Np).Max(Nc)<0
                                         or else Ll(Np).Min(Nc)>Ll(Np).Max(Nc)) then
                        raise Wrong_Length;
                     end if;
                     if Nc=Po and then (Ll(Np).Max(Nc)>40 or else Ll(Np).Max(Nc)<0
                                        or else Ll(Np).Min(Nc)>Ll(Np).Max(Nc)) then
                        raise Wrong_Number_Of_Points;
                     end if;
                  else
                     raise Invalid_Form;
                  end if;
               end if;
            end;
         end loop;
      else -- data is given in uri form
           --         Aws.Log.Write(Lobj, Request, "parsing: " & Uri);
         Parse_Compact_Form(Uri, Dd, Ll);
         --         Aws.Log.Write(Lobj, Request, "... parsed");
      end if;

      Aws.Log.Write(Lobj, Request, Compact_Form(Dd,Ll));
      Autovaca_3(Dd,Ll,Ntries,Rd,Nh);
      return Aws.Response.Build("text/html",                                 "<!DOCTYPE html PUBLIC ""-//W3C//DTD HTML 4.01 Transitional//EN""" & Ascii.Lf &
   """http://www.w3.org/TR/html4/loose.dtd"">" & ascii.lf &
                                "<HTML><HEAD>" & Ascii.Lf &
                                "<meta http-equiv=""Content-Type"" content=""text/html; charset=iso-8859-1"">" & Ascii.Lf &
                                "<TITLE>Autovaca results</TITLE>" &
                                "</HEAD><BODY>" & Ascii.Lf &
                                Show_Probabilities(Rd) &
                                "<P><CENTER>Found " & Integer'Image(Nh) &
                                " distributions from " & Integer'Image(Ntries) &
                                " tries</CENTER><P>" & Ascii.Lf
                                & "</BODY></HTML>" & Ascii.Lf);

   exception
      when Too_Many_Cards =>
         return Aws.Response.Build("text/html", "<P><CENTER>You entered more " &
                                   "than 13 cards in one hand</CENTER>" & Ascii.Lf);
      when Invalid_Form =>
         return Aws.Response.Build("text/html", "<P><CENTER>The web page from which " &
                                   "you called this has errors</CENTER>" & Ascii.Lf);
      when Invalid_Problem_Form =>
         return Aws.Response.Build("text/html", "<P><CENTER>URI syntax error" &
                                   "</CENTER>" & Ascii.Lf);
      when Wrong_Card =>
         return Aws.Response.Build("text/html", "<P><CENTER>You entered an unrecognised " &
                                   "card character. Use: AKQJT98765432</CENTER>" &
                                   Ascii.Lf);
      when Duplicate_Card =>
         return Aws.Response.Build("text/html", "<P><CENTER>You entered a card " &
                                   "twice.</CENTER>" & Ascii.Lf);
      when Wrong_Length =>
         return Aws.Response.Build("text/html", "<P><CENTER>You entered an impossible " &
                                   "colour length range.</CENTER>" & Ascii.Lf);
      when Wrong_Number_Of_Points =>
         return Aws.Response.Build("text/html", "<P><CENTER>You entered an impossible " &
                                   "number of honor points.</CENTER>" & Ascii.Lf);
      when Error: others =>
         Put_Line("Some error in the server, sorry" & Ascii.Lf &
                  Exception_Name(Error) & Ascii.Lf &
                  Exception_Message(Error) & Ascii.Lf &
                  Exception_Information(Error) & Ascii.Lf );
         return Aws.Response.Build("text/html",
                                   "<P> Sorry, internal server error" & Ascii.Lf &
                                   Exception_Name(Error) & Ascii.Lf &
                                   Exception_Message(Error) & Ascii.Lf &
                                   Exception_Information(Error) & Ascii.Lf);
   end Autovaca_Server;

begin
   AWS.Server.Start (WS, "Autovaca",
                     Max_Connection => 10,
                     Port => 20209, -- 5 for testing
                     Callback       => Autovaca_Server'Unrestricted_Access);
   Aws.Log.Start(Lobj, File_Directory => "/web/corlan.net/logs/",
                Auto_Flush => True);
   loop
      delay 1.0;
      if Termination_Flag.Istermi then
         exit;
      end if;
   end loop;

   Put_Line("OK, Autovaca leaving gracefully.");
   Aws.Log.Stop(Lobj);
   AWS.Server.Shutdown (WS);
end Autovacad;

-- the following utilities allow stoping the server graciously with kill -3

with Ada.Interrupts; use Ada.Interrupts;
with Ada.Interrupts.Names; use Ada.Interrupts.Names;

package Unisig is

   protected Termination_Flag is

      procedure Termi;
      pragma Interrupt_Handler(Termi);
      pragma Attach_Handler(Termi,SIGQUIT);

      function Istermi return Boolean;

   private

      Stopit: Boolean:= False;

   end Termination_Flag;

end Unisig;


package body Unisig is

   protected body Termination_Flag is

      procedure Termi is

      begin
         Stopit := True;
      end Termi;

      function Istermi return Boolean is

      begin
         return Stopit;
      end Istermi;

   end Termination_Flag;

end Unisig;

