% Copyright 2015 by Jonathan D. Hauenstein

% Deflate family of examples modified from Example 5.1 in [Li & Zhi JCS 2012]. 
% For any s ≥ 2, the system has s variables, s polynomials of degrees at most 3. 
% The system has a root at the origin with multiplicity 2^s and breadth 2, i.e. its Jacobian matrix at the origin has co-rank 2. 
% "Certifying isolated singular points and their multiplicity structure" 
% by J.D. Hauenstein, B. Mourrain, and A. Szanto

function [Fnf,Vnf] = deflate51b(s)
    % Input: d - integer >= 2
    % Output:
    %         Fnf - polynomial system from normal form deflation
    %         Vnf - variables arising from normal form deflation

    % error checking
    d = 2^(s-1);
    Fmm = []; Vmm = []; Fnf = []; Vnf = [];
    if nargin < 1 || d < 2
        disp(['The degree d must be at least 2!']);
        return;
    end;

    % setup the original system
    X = sym('x',[1,s]);
    F = [X(1)^3+X(1)^2-X(2)^2];
    for i = 2:s-1
        F = [F,X(i)^3+X(i)^2-X(i+1)];
    end;
    F = [F, X(s)^2]
    
     
    % setup E -- leading terms
    E = zeros(2*d,s);
    for j = 1:d
        E(2*j-1,1) =  j-1; % x1^(j-1)
        E(2*j,1) = j-1; % x2 * x1^(j-1)
         E(2*j,2) = 1;
    end;

    % setup normal form deflation
    [Fnf,Vnf, M] = deflate(F,X,E);

	% display summary of MM deflation
    disp(['Normal form']);
    disp(['  functions: ',num2str(length(Fnf))]);
    disp(['  variables: ',num2str(length(Vnf))]);

return;
 
% We din't use this function, because the Macaulay matrix becomes too large.   
function [MM] = constructMM(F,X,deg)
    % construct MM up to degree deg ignoring the trivial first column

    numF = length(F);
    numX = length(X);

    if deg <= 0 || numF < 1 || numX < 1
        MM = [];
        return;
    elseif deg == 1
        MM = jacobian(F,X);
        return;
    end;

    % deg >= 2
    numRows = numF*nchoosek(numX+deg-1,deg-1);
    numCols = nchoosek(numX+deg,deg)-1;

    MM = zeros(numRows,numCols)*X(1);

    % setup degrees
    DegC = [];
    for j = 1:(deg-1)
      DegC = [DegC;setupDegree(j,numX)];
    end;
    DegR = [setupDegree(0,numX);DegC];
    DegC = [DegC;setupDegree(deg,numX)];

    % setup MM
    currRow = 0;
    for j = 1:size(DegR,1)
        for k = 1:numF
            currRow = currRow + 1;
            for l = 1:size(DegC,1)
                f = F(k);
                for m = 1:size(DegR,2)
                    f = f*X(m)^DegR(j,m);
                end;
                mult = 1;
                for m = 1:size(DegC,2)
                    f = diff(f,X(m),DegC(l,m));
                    mult = mult*factorial(DegC(l,m));
                end;
                MM(currRow,l) = f/mult;
            end;
        end;
    end;

return;

function [System,Variables,M] = deflate(F,X,E)
% Computes the deflated system using the parametric normal form algorithm.
% It exploits the structure of the basis to minimize the number of new variables introduced.

    mu = size(E,1);

    % setup M & newVariables
    IX = eye(length(X),length(X));
    newVariables = zeros(0,0)*X(1);
    M = zeros(mu,mu,length(X))*X(1);
    for l = 1:length(X)
      for k = 1:mu-1
       % test to see if in E
        inE = 0;
        for j = k+1:mu  
            if norm(E(j,:) - E(k,:) - IX(l,:)) == 0 
            % entry is 1
               M(j,k,l) = 1;
               inE = 1;
            end;
        end;
        if inE == 0
           %test if we can use parameters introduced earlier
          NoNew=0;
          for a=1:k-1
               for b=1:l-1
                   if norm( E(k,:)-E(a,:)-IX(b,:)) == 0 
                      A=a;
                      B=b;
                      NoNew=1;
                      break;
                      break;
                   end;
                end;
           end;
           if NoNew == 1
              M(:,k,l)=M(:,:,B)*M(:,A,l);
           else
           for j = k+1:mu
              % need to create a variable
              c = ['u_',num2str(l),'_',num2str(j),'_',num2str(k)];
              eval(['syms ',c]);
              newVariables = [newVariables, eval(c)];
              M(j,k,l) = eval(c);           
            end;
          end;
        end;
      end;
    end;
    
    % setup commutation relations
    Comm = [];
    for j = 1:length(X)
      for k = j+1:length(X)
        Comm = [Comm; reshape(M(:,:,j)*M(:,:,k) - M(:,:,k)*M(:,:,j),[mu^2,1])]; 
      end; 
    end; 
    Comm = unique(Comm);

    % setup other polynomials
    Poly = [];
    Deg  = [];
    for j = 1:3
      Deg = [Deg;setupDegree(j,length(X))];
    end;

    for j = 1:length(F)
      % setup degree 0
      P = F(j)*[1;zeros(mu-1,1)];
      for k = 1:size(Deg,1)
        Mult = 1/prod(factorial(Deg(k,:)));
        Q = eye(mu,mu);
        p = F(j);
        for l = 1:length(X)
          p = diff(p,X(l),Deg(k,l));
          if p ==0  
            break;
          else Q = Q*M(:,:,l)^Deg(k,l);
          end
        end;
        P = P + Mult*p*(Q*[1;zeros(mu-1,1)]);
      end;
      Poly = [Poly;P];
    end;

    System = [Poly;Comm];
    Variables = transpose([X newVariables]);
    System = System(subs(System,Variables,rand(size(Variables))) ~= 0);

return;


function [A] = setupDegree(deg, n)
    % setup degrees for Veronese embedding

	numRows = nchoosek(n+deg-1,deg);
    A = zeros(numRows,n);
    A(1,1) = deg;

    for j = 1:numRows-1
        % copy
        A(j+1,:) = A(j,:);
        % find target entry
        target = -1;
        for k = n-1:-1:1
            if A(j+1,k) ~= 0
                target = k;
                break;
            end;
        end;
        % move entry
        A(j+1,target) = A(j+1,target) - 1;
        A(j+1,target+1) = A(j+1,target+1) + 1;
        % move all of final entry to target+1 if not the same!
        if target ~= n-1
            A(j+1,target+1) = A(j+1,target+1) + A(j+1,n);
            A(j+1,n) = 0;
        end;
    end;
return;

