-- Reads in strings, counts the number of distinct vowels
-- For strings with 0..3 vowels, stores the head (2 chars)
-- For strings with 4..6 vowels, stores the tail (3 chars)
-- Prints all heads, followed by all tails
-- Example input:
--  abcde
--  aeiou
--  bbb
-- Example output:
--  ab
--  bb
--  iou

pragma Assertion_Policy (Check);  -- Turn on Dynamic_Predicate checking

with ada.text_io; use ada.text_io; 
 
procedure vowels_variations  is 
   type Distinct_Vowel_Count is range 0 .. 6;

   -- Count number of distinct vowels in s
   -- A second version to consider
   function number_distinct_vowels(s: String) return Distinct_Vowel_Count is
      check: array(Character) of Boolean := (others => false);
   begin
      for c of s loop
         check(c) := true;
      end loop;

      return (if check('a') then 1 else 0)
           + (if check('e') then 1 else 0)
           + (if check('i') then 1 else 0)
           + (if check('o') then 1 else 0)
           + (if check('u') then 1 else 0)
           + (if check('y') then 1 else 0);

      --  Another way to calculate the answer
      --  for b of check loop
      --     if b then
      --        ans := ans + 1;
      --     end if;
      --  end loop;
      --  return ans;
          
      --  Yet Another way to calculate the answer
      --  subtype vowels is Character
           --  with static_predicate => vowels in 'a'|'e'|'i'|'o'|'u'|'y';
      --  for v in vowels loop
      --     if check(v) then
      --        ans := ans + 1;
      --     end if;
      --  end loop;
      --  return ans;
   end number_distinct_vowels;
  
   -- Count number of distinct vowels in s
   -- A third version to consider
   function number_distinct_vowels2(s: String) return Distinct_Vowel_Count is
      type vowels is (a, e, i, o, u, y, not_a_vowel);

      function char2vowel(c: Character) return vowels is
         (case c is when 'a' => a, when 'e' => e, when 'i' => i, when 'o' => o,
                    when 'u' => u, when 'y' => y, when others => not_a_vowel);

      subtype Binary_Distinct_Vowel_COunt is Distinct_Vowel_Count range 0 .. 1;
      function bool2BDVC(b: Boolean) return Binary_Distinct_Vowel_COunt is
         (if b then 1 else 0);

      check: array(vowels) of Boolean;
      ans: Distinct_Vowel_Count := 0;

   begin
      for c of s loop
         check(char2vowel(c)) := true;
      end loop;
      for v in vowels loop
         ans := ans + bool2BDVC(check(v));
         --  ans := ans + Boolean'Pos(check(v));
         --  The above works since False is at position 0, True at position 1!
         --  Not necessarily a good idea!
      end loop;
      return ans;
   end number_distinct_vowels2;

   subtype Long_String is String with
      Dynamic_Predicate => Long_String'length >= 3;
      -- Predicate_Failure => raise Constraint_Error with "String too short!";
      -- Unfortunately Predicate_Failure is not yet implemented

   HEAD_LENGTH: Constant := 2;
   TAIL_LENGTH: COnstant := 3;

   MAX_NUM_STRINGS: Constant := 100;
   type String_Count is range 0 .. MAX_NUM_STRINGS;
   subtype String_Index is String_Count range 1 .. String_Count'last;

   package String_Count_IO is new Ada.Text_IO.Integer_IO(String_Count);
   use String_Count_IO;

   type Heads_Array is array(String_Index) of String(1 .. HEAD_LENGTH);
   type Tails_Array is array(String_Index) of String(1 .. TAIL_LENGTH);

   procedure process_one_string(
      s: Long_String;
      num_heads: in out String_Count; 
      num_tails: in out String_Count; 
      heads: in out Heads_Array; 
      tails: in out Tails_Array) 
   is
      HEAD_END: Constant Natural := s'first + HEAD_LENGTH - 1;
      TAIL_START: Constant Natural := s'last - TAIL_LENGTH + s'first;

      subtype Few_Vowels is Distinct_Vowel_Count range 0 .. 3;
   begin
      if number_distinct_vowels(s) in Few_Vowels then
            num_heads := num_heads + 1;
            heads(num_heads) := s(s'first .. HEAD_END);
      else
            num_tails := num_tails + 1;
            tails(num_tails) := s(TAIL_START .. s'last);
      end if;
   end process_one_string;

   -- Slightly shorter version
   procedure get_and_process_all_strings(
      num_heads: out String_Count; 
      num_tails: out String_Count; 
      heads: out Heads_Array; 
      tails: out Tails_Array) 
   is
   begin
      num_heads := 0;
      num_tails := 0;
      while not end_of_file loop
         process_one_string(get_line, num_heads, num_tails, heads, tails);
      end loop;
   end get_and_process_all_strings;

   procedure put(num_heads: String_Count; num_tails: String_Count; 
                 heads: Heads_Array; tails: Tails_Array) 
   is
   begin
      for i in 1 .. num_heads loop
         put_line(heads(i));
      end loop;

      for i in 1 .. num_tails loop
         put_line(tails(i));
      end loop;
   end put;

   heads: Heads_Array;            -- Array for heads
   tails: Tails_Array;            -- Array for tails
   num_heads, 
   num_tails: String_Count := 0;  -- Number of heads and tails
begin
   get_and_process_all_strings(num_heads, num_tails, heads, tails);
   put(num_heads, num_tails, heads, tails);

   put(num_heads);  -- Where is this put from?
   put(num_tails);
end vowels_variations;