% Copyright 2015 by Jonathan D. Hauenstein

% Deflate Caprasse system from Section 5.2 of 
% "Certifying isolated singular points and their multiplicity structure" 
% by J.D. Hauenstein, B. Mourrain, and A. Szanto

function [Fmm,Vmm,Fnf,Vnf] = deflate52_structure()
    % Output: Fmm - polynomial system from Macaulay matrix deflation
    %         Vmm - variables arising from Macaulay matrix deflation
    %         Fnf - polynomial system from normal form deflation
    %         Vnf - variables arising from normal form deflation

    % setup the original system
    syms x1 x2 x3 x4;
    X = [x1 x2 x3 x4];
    F = [x1^3*x3-4*x1^2*x2*x4-4*x1*x2^2*x3-2*x2^3*x4-4*x1^2-4*x1*x3+10*x2^2+10*x2*x4-2; 
    	 x1*x3^3-4*x1*x3*x4^2-4*x2*x3^2*x4-2*x2*x4^3-4*x1*x3+10*x2*x4-4*x3^2+10*x4^2-2; 
         2*x1*x2*x4+x2^2*x3-2*x1-x3; 
         x1*x4^2+2*x2*x3*x4-x1-2*x3];
    
    % setup the Macaulay matrix up to degree 2
    MM = constructMM(F,X,2);

    % setup the null space -- dimension 3
    NullCols = 3; % # of columns of null space
    Cols = size(MM,2); % # of columns of MM = # of rows of null space
    
    A = zeros(Cols-NullCols,NullCols)*x1;
    for j = (NullCols+1):Cols
        for k = 1:NullCols
            c = ['n_',num2str(j),'_',num2str(k)];
            eval(['syms ',c]);
            A(j-NullCols,k) = eval(c);
        end;
    end;
    Vmm = [X symvar(A)];
    N = randi([-10,10],Cols,Cols)*[eye(NullCols,NullCols);A];
    
    % setup Fmm
    Q = MM*N;
    Fmm = F;
    for j = 1:NullCols
        Fmm = [Fmm;Q(:,j)];
    end;
        
    % display summary of MM deflation
    disp(['Macaulay matrix']);
    disp(['  functions: ',num2str(length(Fmm))]);
    disp(['  variables: ',num2str(length(Vmm))]);
    
    % setup E -- leading terms
    E = [0 0 0 0; 1 0 0 0; 0 1 0 0; 1 1 0 0]; % 1, x1, x2, x1*x2
    
    % setup normal form deflation
    [Fnf,Vnf] = deflate(F,X,E);

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

return;
    
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] = deflate(F,X,E)

    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 j = 2:mu
        for k = 1:j-1  
          if norm(E(j,:) - E(k,:) - IX(l,:)) == 0 
            % entry is 1
            M(j,k,l) = 1;
          else
            % test to see if in E
            inE = 0;
            for m = 1:mu
              if norm(E(m,:) - E(k,:) - IX(l,:)) == 0
                inE = 1;
              end;
            end;

            if inE == 0
              % 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:(mu-1)
      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)
          Q = Q*M(:,:,l)^Deg(k,l);
          p = diff(p,X(l),Deg(k,l));
        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;


