% Numerical computation of the Hilbert function of a zero-scheme
% Z. Griffin, J. Hauenstein, A. Sommese, and C. Peterson
% Copyright July 2011

function [reg, hilbertFunc, stdMon] = HilbertFunc(numVars, pointLoc, dualLoc, tol, compMon) 
    % compute the Hilbert function of the points and dual bases
    
    % INPUT
    % numVars = number of variables
    % pointLoc = string of the name of the file containing the points
    % dualLoc = string of the name of the file containing the dual bases
    % tol = tolerance for rank determination
    % compMon = boolean which determines whether to compute the standard monomials
    
    % OUTPUT
    % reg = index of regularity
    % hilbertFunc = Hilbert function up to index of regularity
    % stdMon = standard monomials
    
    % setup the points
    [numPts, Pts, rV] = readInMultPoints(numVars, pointLoc);
    
    % check for errors in the points
    if rV == -1
        reg = 0;
        hilbertFunc = 0;
        stdMon = 0;
        return;
    end;

    % setup the dual bases
    [mult, ~, ~, colSize, basis, rV] = readInMultStructs(numPts, dualLoc);

    % check for errors in the dual bases
    if rV == -1
        reg = 0;
        hilbertFunc = 0;
        stdMon = 0;
        return;
    end;
    
    % initialize
    k = 1;
    
    % preallocate h
    h = zeros(10,1);

    % setup A_0
    [Ak] = setupAk(numVars, numPts, Pts, mult, colSize, basis, k - 1);
    h(1) = rank(Ak,tol);

    while (1)
        % increment k
        k = k + 1;

        % setup A_{k-1} = [A_{k-2} B_{k-1}]
        Ak = [Ak, setupBk(numVars, numPts, Pts, mult, colSize, basis, k - 1)];

        % compute its rank
        h(k) = rank(Ak,tol);   

        % see if we have the correct rank
        if h(k) == size(Ak,1)
            break;
        end;
    end;

    % copy h to hilbertFunc
    hilbertFunc = h(1:k);

    % setup reg
    reg = k - 1;

    if compMon
        % setup the monomials
        M = setupMonomials(numVars, reg);

        % compute the pivot columns
        [~, jb] = rref(Ak, tol);

        % setup the monomial support
        stdMon = M(jb,:);
    else
        stdMon = 0;
    end;

return;

function [numPts, Pts, rV] = readInMultPoints(numVars, fileLocation)
    % read in the points from a file
    
    % open file
    RF = fopen(fileLocation, 'r');

    % make sure the file exists
    if RF == -1
        % file does not exist
        disp(['The file ''', fileLocation, ''' does not exist!']);
        numPts = 0;
        Pts = 0;
        rV = -1;
        return;
    end;

    % read in the number of points
    numPts = fscanf(RF, '%d', 1);

    % setup Pts
    Pts = zeros(numPts, numVars);

    % loop through to read in the points
    for k = 1:numPts
        % read in the point
        for j = 1:numVars
            tempR = fscanf(RF, '%e', 1);
            tempI = fscanf(RF, '%e', 1);
            Pts(k,j) = tempR + 1i * tempI;
        end;
    end;

    % close file
    fclose(RF);
    
    % setup return value
    rV = 0;
return;

function [mult, depth, hilbertFunc, colSize, basis, rV] = readInMultStructs(numPts, fileLocation)
    % read in the dual bases from a file

    % open file
    RF = fopen(fileLocation, 'r');

    % make sure the file exists
    if RF == -1
        % file does not exist
        disp(['The file ''', fileLocation, ''' does not exist!']);
        mult = 0; 
        depth = 0; 
        hilbertFunc = 0; 
        colSize = 0; 
        basis = 0;
        rV = -1;
        return;
    end;

    % setup mult, depth & colSize
    mult = zeros(numPts, 1);
    depth = zeros(numPts, 1);
    colSize = zeros(numPts, 1);
    hilbertFunc = zeros(numPts, 0);
    basis = zeros(numPts, 0, 0);

    % loop through to read in the dual bases
    for k = 1:numPts
        % read in the path number & the length
        [num] = fscanf(RF, '%d', 2);
        % store the length
        len = num(2);
        % setup the depth = len - 1
        depth(k) = len - 1;
        % read in the Hilbert function
        [hFunc] = fscanf(RF, '%d', len);
        % setup hilbertFunc
        hilbertFunc(k,1:len) = hFunc;
        % read in the size of the dual basis (mult == rows)
        [num] = fscanf(RF, '%d', 2);
        mult(k) = num(1);
        colSize(k) = num(2);
        % read in the basis
        for j = 1:mult(k)
            for l = 1:colSize(k)
                [b] = fscanf(RF, '%e', 2);
                basis(k,j,l) = b(1) + 1i*b(2);
            end;
        end;
    end;

    % close file
    fclose(RF);
    
    % setup return value
    rV = 0;
return;

function [Ak] = setupAk(numVars, numPts, Pts, mult, colSize, basis, k)
    % setup the matrix A_k(0)
    
    % compute the sum of the multiplicity & number of monomials
    m = sum(mult);
    q = nchoosek(numVars + k, k);

    % setup the monomials up to order k
    monomials = setupMonomials(numVars, k);

    % initialize Ak
    Ak = zeros(m, q);

    % setup Ak
    p = 1;
    for j = 1:numPts
        for k = 1:mult(j)     
            for l = 1:q
                % setup the lth column based on the lth monomial, the jth point
                % and its kth dual basis vector
                Ak(p, l) = setupAkEntry(monomials(l,:), Pts(j,:), basis(j,k,1:colSize(j)), colSize(j), monomials);
            end;
            % update p
            p = p + 1;
        end;
    end;

return;

function [Ak] = setupAkEntry(currMon, currPt, currBasis, basisSize, basisMon)
    % setup an entry of A_k(0)
    
    % find the number that we need to do
    num = min(basisSize, size(basisMon, 1));

    % initialize
    Ak = 0;

    for j = 1:num
        % d_a (x^b)(\hat(x)) = combination(b,a) * \hat(x)^(b-a)
        [c, d] = combination(currMon, basisMon(j,:));

        if not(c == 0)
            % compute and add on
            Ak = Ak + currBasis(j) * c * prod(currPt .^ d);        
        end;
    end;
return;

function [c, d] = combination(I, K)
    % compute \binom{I}{K}
    
    % compute I - K
    d = I - K;

    % make sure I >= K
    if min(d) >= 0
        % compute nchoosek(I,K)
        c = 1;
        n = max(size(d));
        for j = 1:n
            c = c * nchoosek(I(j),K(j));
        end;
    else
        c = 0;
    end;
return;
    
function [Bk] = setupBk(numVars, numPts, Pts, mult, colSize, basis, k)
    % setup the matrix Bk
    
    % compute the sum of the multiplicity & number of monomials
    m = sum(mult);
    q = nchoosek(numVars - 1 + k, k);

    % setup the monomials up to order k
    allMon = setupMonomials(numVars, k);

    % setup the monomials up to order k
    monomials = setupMonomialsOrder(numVars, k);

    % initialize Bk
    Bk = zeros(m, q);

    % setup Bk
    p = 1;
    for j = 1:numPts
        for k = 1:mult(j)     
            for l = 1:q
                % setup the lth column based on the lth monomial, the jth point
                % and its kth dual basis vector
                Bk(p, l) = setupAkEntry(monomials(l,:), Pts(j,:), basis(j,k,1:colSize(j)), colSize(j), allMon);
            end;
            % update p
            p = p + 1;
        end;
    end;
return;

function [monomials] = setupMonomials(numVars, order)
    % setup the monomials up to the given order

    q = nchoosek(numVars + order, order);
    monomials = zeros(q,numVars);

    % setup the monomials for each order
    loc = 1;
    for j = 0:order
        % compute the number of monomials of order j
        M = setupMonomialsOrder(numVars, j);
        rows = size(M,1);
        monomials(loc:loc+rows-1,1:numVars) = M;
        % update loc
        loc = loc + rows;
    end;
return;
    
function [M] = setupMonomialsOrder(numVars, order)
    % setup the monomials of the given order

    if (order < 0)
        M = 0;
    elseif (order == 0)
        M = zeros(1, numVars);
    else % order >= 1
        q = nchoosek(numVars - 1 + order, order);
        M = zeros(q, numVars);

        % setup the first one
        M(1,1) = order;
        % loop through the other ones
        for j = 2:q
            % copy over M(j-1,:)
            M(j,:) = M(j-1,:);
        
            % find where we can increment
            target = numVars - 1;
            while (target >= 0)
                if not(M(j,target) == 0)
                    break;
                else
                    target = target - 1;
                end;
            end;
        
            % increment
            M(j,target) = M(j,target) - 1;
            M(j,target+1) = M(j,target+1) + 1;
        
            if not(target + 1 == numVars)
                M(j,target+1) = M(j,target+1) + M(j,numVars);
                M(j,numVars) = 0;
            end;
        end;
    end;
return;

