with Sets_of_Unknowns;          use Sets_of_Unknowns;

package body Partitions_of_Sets_of_Unknowns is

-- CREATORS :

  procedure Create ( p : in out Partition; n : in natural ) is
  begin
    for i in p'range loop
      p(i) := Create(n);
    end loop;
  end Create;

  function Create ( p : Partition ) return Partition is

    res : Partition(p'range);

  begin
    for i in p'range loop
      res(i) := Create(p(i));
    end loop;
    return res;
  end Create;

-- CONSTRUCTOR :

  procedure Generate_Partitions ( s : in Set ) is

  -- NOTE :
  --   The algorithm below is a rather unelegant construction.
  --   The VADS compiler for IBM RS/6000 had problems with the nested
  --   generics, so the generation of all subsets is repeated here in full.

    n : constant natural := Dimension(s);
    continue : boolean := true;
    p : Partition(1..n);
    cnt : natural := 0;

    procedure Generate ( v : in Set; cont : out boolean );

    -- DESCRIPTION :
    --   Generation of all partitions makes use of a double recursive process.

    procedure Empty_Subsets ( w : in Set; cont : out boolean ) is
  
      rest : Set := Difference(w,p(cnt));

    begin
      if Extent_of(rest) = 0
       then Process(p(1..cnt),cont);
       else Generate(rest,cont);
      end if;
      Clear(rest);
    end Empty_Subsets;

    procedure All_Subsets ( w : in Set; cont : out boolean ) is

      sb : Set := Create(n);

      procedure Create_Partition ( sub : in Set; cont : out boolean ) is

        rest : Set;
        back : Set := Create(p(cnt));   -- back up copy needed to restore

      begin
        Union(p(cnt),sub);
        rest := Difference(w,p(cnt));
        if Extent_Of(rest) = 0
         then Process(p(1..cnt),cont);
         else Generate(rest,cont);
        end if;
        Clear(p(cnt)); p(cnt) := Create(back);
        Clear(rest); Clear(back);
      end Create_Partition;

      procedure Generate_Subset ( level,start : in natural ) is
      begin
        if level > 0
         then for i in start..n loop
                if Is_In(w,i)
                 then Add(sb,i);
                      Create_Partition(sb,continue);
                      if continue
                       then Generate_Subset(level-1,i+1);
                            Remove(sb,i);
                      end if;
                end if;
                exit when not continue;
              end loop;
              cont := continue;
        end if;
      end Generate_Subset;

    begin
      Generate_Subset(n,1);
      Clear(sb);
    end All_Subsets;

    procedure Generate ( v : in Set; cont : out boolean ) is
    begin
      for i in 1..n loop
        if Is_In(v,i)
         then cnt := cnt + 1;
              p(cnt) := Create(n); Add(p(cnt),i); 
              Empty_Subsets(v,continue);
              if continue
               then declare
                      w : Set := Create(v);
                    begin
                      Remove(w,i);
                      All_Subsets(w,cont);
                      Clear(w);
                    end;
              end if;
              Clear(p(cnt)); cnt := cnt - 1;
              cont := continue;
        end if;
        exit when Is_In(v,i);
      end loop;
    end Generate;

  begin
    Generate(s,continue);
  end Generate_Partitions;

-- SELECTOR :

  function Number_of_Partitions ( n : natural ) return natural is

    sum : natural;

    function comb ( n,i : natural ) return natural is
      n1,n2 : natural := 1;
    begin
      if (i = 0) or (i = n)
       then return 1;
       else for k in 1..i loop
              n1 := n1 * (n - k + 1);
              n2 := n2 * k;
            end loop;
            return (n1/n2);
      end if;
    end comb;

  begin
    if (n = 0) or (n = 1)
     then return 1;
     else sum := 0;
          for k in 0..(n-1) loop
            sum := sum + comb(n-1,k) * Number_Of_Partitions(n-1-k);
          end loop;
          return sum;
    end if;
  end Number_of_Partitions;

-- DESTRUCTOR :

  procedure Clear ( p : in out Partition ) is
  begin
    for i in p'range loop
      Clear(p(i));
    end loop;
  end Clear;

end Partitions_of_Sets_of_Unknowns;
