function cholmod_make (metis_path)
%CHOLMOD_MAKE compiles the CHOLMOD mexFunctions
%
% Example:
%   cholmod_make
%
% CHOLMOD relies on AMD and COLAMD, and optionally CCOLAMD, CAMD, and METIS.
% You must type the cholmod_make command while in the CHOLMOD/MATLAB directory.
%
% See also analyze, bisect, chol2, cholmod2, etree2, lchol, ldlchol, ldlsolve,
%   ldlupdate, metis, spsym, nesdis, septree, resymbol, sdmult, sparse2,
%   symbfact2, mread, mwrite, ldlrowmod

%   Copyright 2006-2015, Timothy A. Davis, http://www.suitesparse.com

details = 0 ;	    % 1 if details of each command are to be printed

v = version ;
try
    % ispc does not appear in MATLAB 5.3
    pc = ispc ;
    mac = ismac ;
catch                                                                       %#ok
    % if ispc fails, assume we are on a Windows PC if it's not unix
    pc = ~isunix ;
    mac = 0 ;
end

flags = '' ;
is64 = ~isempty (strfind (computer, '64')) ;
if (is64)
    % 64-bit MATLAB
    flags = '-largeArrayDims' ;
end

% MATLAB 8.3.0 now has a -silent option to keep 'mex' from burbling too much
if (~verLessThan ('matlab', '8.3.0'))
    flags = ['-silent ' flags] ;
end

include = '-I. -I../../AMD/Include -I../../COLAMD/Include -I../../CCOLAMD/Include -I../../CAMD/Include -I../Include -I../../SuiteSparse_config' ;

if (verLessThan ('matlab', '7.0'))
    % do not attempt to compile CHOLMOD with large file support
    include = [include ' -DNLARGEFILE'] ;
elseif (~pc)
    % Linux/Unix require these flags for large file support
    include = [include ' -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE'] ;
end

if (verLessThan ('matlab', '6.5'))
    % logical class does not exist in MATLAB 6.1 or earlie
    include = [include ' -DMATLAB6p1_OR_EARLIER'] ;
end

 % Determine if METIS is available
if (nargin < 1)
    metis_path = '../../metis-5.1.0' ;
end
have_metis = exist (metis_path, 'dir') ;

if (have_metis)
    fprintf ('Compiling CHOLMOD with METIS 5.1.0 for MATLAB Version %s\n', v) ;
    include = [include ' -I' metis_path '/include'] ;
    include = [include ' -I' metis_path '/GKlib'] ;
    include = [include ' -I' metis_path '/libmetis'] ;
else
    fprintf ('Compiling CHOLMOD without METIS for MATLAB Version %s\n', v) ;
    include = ['-DNPARTITION ' include] ;
end


 %---------------------------------------------------------------------------
 % BLAS option
 %---------------------------------------------------------------------------

 % This is exceedingly ugly.  The MATLAB mex command needs to be told where to
 % fine the LAPACK and BLAS libraries, which is a real portability nightmare.

if (pc)
    if (verLessThan ('matlab', '7.5'))
        lapack = 'libmwlapack.lib' ;
    elseif (verLessThan ('matlab', '9.5'))
        lapack = 'libmwlapack.lib libmwblas.lib' ;
    else
        lapack = '-lmwlapack -lmwblas' ;
    end
else
    if (verLessThan ('matlab', '7.5'))
        lapack = '-lmwlapack' ;
    else
        lapack = '-lmwlapack -lmwblas' ;
    end
end

if (is64 && ~verLessThan ('matlab', '7.8'))
    % versions 7.8 and later on 64-bit platforms use a 64-bit BLAS
    fprintf ('with 64-bit BLAS\n') ;
    flags = [flags ' -DBLAS64'] ;
end

if (~(pc || mac))
    % for POSIX timing routine
    lapack = [lapack ' -lrt'] ;
end

 %-------------------------------------------------------------------------------

config_src = { '../../SuiteSparse_config/SuiteSparse_config' } ;

ordering_src = { ...
    '../../AMD/Source/amd_1', ...
    '../../AMD/Source/amd_2', ...
    '../../AMD/Source/amd_aat', ...
    '../../AMD/Source/amd_control', ...
    '../../AMD/Source/amd_defaults', ...
    '../../AMD/Source/amd_dump', ...
    '../../AMD/Source/amd_global', ...
    '../../AMD/Source/amd_info', ...
    '../../AMD/Source/amd_order', ...
    '../../AMD/Source/amd_postorder', ...
    '../../AMD/Source/amd_post_tree', ...
    '../../AMD/Source/amd_preprocess', ...
    '../../AMD/Source/amd_valid', ...
    '../../CAMD/Source/camd_1', ...
    '../../CAMD/Source/camd_2', ...
    '../../CAMD/Source/camd_aat', ...
    '../../CAMD/Source/camd_control', ...
    '../../CAMD/Source/camd_defaults', ...
    '../../CAMD/Source/camd_dump', ...
    '../../CAMD/Source/camd_global', ...
    '../../CAMD/Source/camd_info', ...
    '../../CAMD/Source/camd_order', ...
    '../../CAMD/Source/camd_postorder', ...
    '../../CAMD/Source/camd_preprocess', ...
    '../../CAMD/Source/camd_valid', ...
    '../../COLAMD/Source/colamd', ...
    '../../CCOLAMD/Source/ccolamd' } ;

if (have_metis)

    metis_src = {
        'GKlib/b64', ...
        'GKlib/blas', ...
        'GKlib/csr', ...
        'GKlib/error', ...
        'GKlib/evaluate', ...
        'GKlib/fkvkselect', ...
        'GKlib/fs', ...
        'GKlib/getopt', ...
        'GKlib/gkregex', ...
        'GKlib/graph', ...
        'GKlib/htable', ...
        'GKlib/io', ...
        'GKlib/itemsets', ...
        'GKlib/mcore', ...
        'GKlib/memory', ...
        'GKlib/omp', ...
        'GKlib/pdb', ...
        'GKlib/pqueue', ...
        'GKlib/random', ...
        'GKlib/rw', ...
        'GKlib/seq', ...
        'GKlib/sort', ...
        'GKlib/string', ...
        'GKlib/timers', ...
        'GKlib/tokenizer', ...
        'GKlib/util', ...
        'libmetis/auxapi', ...
        'libmetis/balance', ...
        'libmetis/bucketsort', ...
        'libmetis/checkgraph', ...
        'libmetis/coarsen', ...
        'libmetis/compress', ...
        'libmetis/contig', ...
        'libmetis/debug', ...
        'libmetis/fm', ...
        'libmetis/fortran', ...
        'libmetis/frename', ...
        'libmetis/gklib', ...
        'libmetis/graph', ...
        'libmetis/initpart', ...
        'libmetis/kmetis', ...
        'libmetis/kwayfm', ...
        'libmetis/kwayrefine', ...
        'libmetis/mcutil', ...
        'libmetis/mesh', ...
        'libmetis/meshpart', ...
        'libmetis/minconn', ...
        'libmetis/mincover', ...
        'libmetis/mmd', ...
        'libmetis/ometis', ...
        'libmetis/options', ...
        'libmetis/parmetis', ...
        'libmetis/pmetis', ...
        'libmetis/refine', ...
        'libmetis/separator', ...
        'libmetis/sfm', ...
        'libmetis/srefine', ...
        'libmetis/stat', ...
        'libmetis/timing', ...
        'libmetis/util', ...
        'libmetis/wspace', ...
    } ;

    for i = 1:length (metis_src)
        metis_src {i} = [metis_path '/' metis_src{i}] ;
    end
end

cholmod_matlab = { 'cholmod_matlab' } ;

cholmod_src = {
    '../Core/cholmod_aat', ...
    '../Core/cholmod_add', ...
    '../Core/cholmod_band', ...
    '../Core/cholmod_change_factor', ...
    '../Core/cholmod_common', ...
    '../Core/cholmod_complex', ...
    '../Core/cholmod_copy', ...
    '../Core/cholmod_dense', ...
    '../Core/cholmod_error', ...
    '../Core/cholmod_factor', ...
    '../Core/cholmod_memory', ...
    '../Core/cholmod_sparse', ...
    '../Core/cholmod_transpose', ...
    '../Core/cholmod_triplet', ...
    '../Check/cholmod_check', ...
    '../Check/cholmod_read', ...
    '../Check/cholmod_write', ...
    '../Cholesky/cholmod_amd', ...
    '../Cholesky/cholmod_analyze', ...
    '../Cholesky/cholmod_colamd', ...
    '../Cholesky/cholmod_etree', ...
    '../Cholesky/cholmod_factorize', ...
    '../Cholesky/cholmod_postorder', ...
    '../Cholesky/cholmod_rcond', ...
    '../Cholesky/cholmod_resymbol', ...
    '../Cholesky/cholmod_rowcolcounts', ...
    '../Cholesky/cholmod_rowfac', ...
    '../Cholesky/cholmod_solve', ...
    '../Cholesky/cholmod_spsolve', ...
    '../MatrixOps/cholmod_drop', ...
    '../MatrixOps/cholmod_horzcat', ...
    '../MatrixOps/cholmod_norm', ...
    '../MatrixOps/cholmod_scale', ...
    '../MatrixOps/cholmod_sdmult', ...
    '../MatrixOps/cholmod_ssmult', ...
    '../MatrixOps/cholmod_submatrix', ...
    '../MatrixOps/cholmod_vertcat', ...
    '../MatrixOps/cholmod_symmetry', ...
    '../Modify/cholmod_rowadd', ...
    '../Modify/cholmod_rowdel', ...
    '../Modify/cholmod_updown', ...
    '../Supernodal/cholmod_super_numeric', ...
    '../Supernodal/cholmod_super_solve', ...
    '../Supernodal/cholmod_super_symbolic', ...
    '../Partition/cholmod_ccolamd', ...
    '../Partition/cholmod_csymamd', ...
    '../Partition/cholmod_camd', ...
    '../Partition/cholmod_metis', ...
    '../Partition/cholmod_nesdis' } ;

cholmod_mex_src = { ...
    'analyze', ...
    'bisect', ...
    'chol2', ...
    'cholmod2', ...
    'etree2', ...
    'lchol', ...
    'ldlchol', ...
    'ldlsolve', ...
    'ldlupdate', ...
    'ldlrowmod', ...
    'metis', ...
    'spsym', ...
    'nesdis', ...
    'septree', ...
    'resymbol', ...
    'sdmult', ...
    'sparse2', ...
    'symbfact2', ...
    'mread', ...
    'mwrite', ...
    'lxbpattern', 'lsubsolve' } ;   % <=== these 2 are just for testing

if (pc)
    % Windows does not have drand48 and srand48, required by METIS.  Use
    % drand48 and srand48 in CHOLMOD/MATLAB/Windows/rand48.c instead.
    % Also provide Windows with an empty <strings.h> include file.
    obj_extension = '.obj' ;
    cholmod_matlab = [cholmod_matlab {'Windows/rand48'}] ;
    include = [include ' -IWindows'] ;
else
    obj_extension = '.o' ;
end

 % compile each library source file
obj = '' ;

source = [ordering_src config_src cholmod_src cholmod_matlab] ;
if (have_metis)
    source = [metis_src source] ;
end

kk = 0 ;

for f = source
    ff = f {1} ;
    if (isequal (ff, [metis_path '/GKlib/util']))
        % special case, since a file with the same name also exists in libmetis
        copyfile ([ff '.c'], 'GKlib_util.c', 'f') ;
        ff = 'GKlib_util' ;
        o = 'GKlib_util' ;
    elseif (isequal (ff, [metis_path '/GKlib/graph']))
        % special case, since a file with the same name also exist in libmetis
        copyfile ([ff '.c'], 'GKlib_graph.c', 'f') ;
        ff = 'GKlib_graph' ;
        o = 'GKlib_graph' ;
    else
        slash = strfind (ff, '/') ;
        if (isempty (slash))
            slash = 1 ;
        else
            slash = slash (end) + 1 ;
        end
        o = ff (slash:end) ;
    end
    % fprintf ('%s\n', o) ;
    o = [o obj_extension] ;
    obj = [obj  ' ' o] ;					            %#ok
    s = sprintf ('mex %s -DDLONG -O %s -c %s.c', flags, include, ff) ;
    kk = do_cmd (s, kk, details) ;
end

 % compile each mexFunction
for f = cholmod_mex_src
    s = sprintf ('mex %s -DDLONG -O %s %s.c', flags, include, f{1}) ;
    s = [s obj ' ' lapack] ;						    %#ok
    kk = do_cmd (s, kk, details) ;
end

 % clean up
s = ['delete ' obj] ;
do_cmd (s, kk, details) ;
fprintf ('\nCHOLMOD successfully compiled\n') ;

% remove the renamed METIS files, if they exist
if (exist ('GKlib_util.c', 'file'))
    delete ('GKlib_util.c') ;
end
if (exist ('GKlib_graph.c', 'file'))
    delete ('GKlib_graph.c') ;
end

 %------------------------------------------------------------------------------
function kk = do_cmd (s, kk, details)
 %DO_CMD: evaluate a command, and either print it or print a "."
if (details)
    fprintf ('%s\n', s) ;
else
    if (mod (kk, 60) == 0)
	fprintf ('\n') ;
    end
    kk = kk + 1 ;
    fprintf ('.') ;
end
eval (s) ;
