function [status] = DrawGraphOneChunk3D(XStart, XEnd, YStart, YEnd, NumberOfPoints, cusFunction, currentChunk, MAXPOINT, TOTALPOINTS, settingFile)
    
    status = DrawGraphBasic(XStart, XEnd, YStart, YEnd, NumberOfPoints, cusFunction, currentChunk, MAXPOINT, TOTALPOINTS);
    if(status == 1)
        return;
    end
    status = UpdateAppSettings(currentChunk, settingFile);
    if(status == 1)
        return;
    end
end

function [status] = UpdateAppSettings(currentChunk, settingFile)
status = 0;
try
    xDoc = xmlread(settingFile);
    % get the 'Settings' node
    xRoot = xDoc.getDocumentElement;
    % get the 'CurrentChunk' node, then set the new value
    xRoot.getChildNodes.item(15).getFirstChild.setNodeValue(num2str(currentChunk));
    % write data to file AppSettings.xml
    xmlwrite(settingFile,xDoc);
    % remove space from that file
    status = RemoveSpace(settingFile);
    if(status == 1)
        return;
    end
catch err
    disp('Cannot update data to CondorMatlab_AppSettings.xml');
    status = 1;
    msg = getReport(err);
    disp(msg);
    return;
end

end



% can execute maximum 10,000 (MAX_POINT) points
function [status] = DrawGraphBasic(XStart, XEnd, YStart, YEnd, NumberOfPoints, cusFunction, currentChunk, MAXPOINT, TOTALPOINTS)
status = 0;
try
    
% validate the input parameters
status = CheckInput(XStart, XEnd, YStart, YEnd, NumberOfPoints, cusFunction);
if(status == 1)
    return;
end

% clear old data from the last crash time
status = ClearOldData();
if(status == 1)
    return;
end

% initialize variables of program
status = Initialize(XStart, XEnd, YStart, YEnd, NumberOfPoints, currentChunk);
% if the task fails, exit application
if(status == 1)
    return;
end

% submit data to Condor
status = SubmitDataToCondor(cusFunction, NumberOfPoints);
% if the task fails, exit application
if(status == 1)
    return;
end

% generate log file, create AppLog.xml
status = CreateLogFile(NumberOfPoints, cusFunction);
% if the task fails, exit application
if(status == 1)
    return;
end

% draw graph with the points are made from elements of XArray and YArray
status = DrawCurrentData(NumberOfPoints, currentChunk, MAXPOINT, TOTALPOINTS);
% if the task fails, exit application
if(status == 1)
    return;
end

% clean application
status = CleanApplication(NumberOfPoints);
% if the task fails, exit application
if(status == 1)
    return;
end
    
% display information about completed jobs and failed jobs
DisplayInfo();
    
catch err
     % display message when we have exceptions
     msg = getReport(err);
     disp(msg);
     % delete waitbar
     global progressBar;
     if(ishandle(progressBar) == 1)
         delete(progressBar);
     end
     % delete all the temporary files
    status = DeleteFiles();
    if(status == 1)
        return;
    end
end

end


% clear all data from the last crash time
function [status] = ClearOldData()
    status = 0;
   
    status = DeleteFiles();
    if(status == 1)
        return;
    end
end


% initialize variables of program
function [status] = Initialize(XStart, XEnd, YStart, YEnd, NumberOfPoints, currentChunk)
    status = 1;
    if( InitMatlabVariables(XStart, XEnd, YStart, YEnd, NumberOfPoints) == 1)
        return;
    end
    if( InitCondorVariables(NumberOfPoints, currentChunk) == 1)
        return;
    end
    status = 0;
end


function [status] = InitMatlabVariables(XStart, XEnd, YStart, YEnd, NumberOfPoints)
status = 0;
       
try
    % create array of points X and Y
    global XArray;
    global YArray;
    XArray = linspace(XStart, XEnd, NumberOfPoints);
    YArray = linspace(YStart, YEnd, NumberOfPoints);
    % swap elements in XArray so we can see the special points first
    [XArray, YArray] = SwapElements(XArray, YArray);
    % list of jobs that finished and have exact return values.
    global completedJobs;
    completedJobs = zeros(1, NumberOfPoints);
    % list of completed output results
    global ZArray;
    ZArray = zeros(1, NumberOfPoints);
    ZArray(1:NumberOfPoints) = intmin;
    % list of submitted time of the jobs
    global submittedTime;
    submittedTime = zeros(1, NumberOfPoints);
    % list of finish time of the jobs
    global finishTime;
    finishTime = zeros(1, NumberOfPoints);
    % the array that contains old Z elements from previous step
    global oldZ;
    oldZ =[];
    % the current Z values which are used to draw graph
    global ZToDraw;
    ZToDraw = [];
    % the current X values which are used to draw graph
    global XToDraw;
    XToDraw = [];
    global YToDraw;
    YToDraw = [];
    % total values used to log, we will write data to log file when this
    % variable is greater than 100
    global PointsToLog;
    PointsToLog = 0;
    
    % total points that have been drawn on the graph
    global completedPoints;
    completedPoints = 0;
    
    global totalPoints;
    totalPoints = 0;
       
    % progress bar
    global progressBar;
    
catch err
    disp('ERROR: Cannot initialize Matlab variables.');
    status = 1;
end
end


function [status] = InitCondorVariables(NumberOfPoints, currentChunk)
status = 0;
try
    % list of jobs that finished but don't have exact return values.
    global failedJobs;
    failedJobs = zeros(1, NumberOfPoints);
   
    % the array that contains failed jobs from previous step
    global oldFailedJobs;
    oldFailedJobs = [];
    
    % prepare Condor input data
    % write points of X to files, 1 point in 1 input file:n inp.1,
    % inp.2, inp.3....
    global XArray;
    global YArray;
    status = WriteData(XArray, YArray);
    if(status == 1)
        return;
    end
    
    % Condor submit file name
    global submitFileName;
    submitFileName = 'CoverMe.sub';
    
    % Condor cluster number
    global clusterNumber;
    
    % application log file name
    global applicationLogFile;
    applicationLogFile = ['CondorMatlab_AppLog', num2str(currentChunk), '.xml'];
    
    % jobs to log
    global CompletedXToLog;
    CompletedXToLog = [];
    global CompletedYToLog;
    CompletedYToLog = [];
    global CompletedZToLog;
    CompletedZToLog = [];
    global finishTimeToLog;
    global submittedTimeToLog;
    finishTimeToLog = [];
    submittedTimeToLog = [];
    
    global XFailedJobsToLog;
    XFailedJobsToLog = [];
    global YFailedJobsToLog;
    YFailedJobsToLog = [];
    
    global XUnprocessedJobsToLog;
    XUnprocessedJobsToLog = [];
    global YUnprocessedJobsToLog;
    YUnprocessedJobsToLog = [];
catch err
    disp('ERROR: Cannot initialize Condor variables.');
    status = 1;
    msg = getReport(err);
    disp(msg);
end
end


function [status] = SubmitDataToCondor(cusFunction, NumberOfPoints)
status = 0;
global submitFileName;
% generate Condor submit file with custom function (cusFunction)
% and number of Condor jobs (length(XArray))
status = GenerateCondorSubmitFile(cusFunction, NumberOfPoints);
if(status == 1)
    return;
end

% submit file CoverMe.sub to Condor
command = sprintf('condor_submit %s',submitFileName);
% store status and return values of command 'system' in 2 params
[cmdStatus, cmdResult] = system(command);
% if the action fails
if(cmdStatus ~= 0)
    % display error message
   disp(cmdResult);
   status =1;
    % exit application
   return;
end
% so now condor_submit command is submitted successfully
% display information to screen
disp('Submitted Condor jobs successfully! Please wait for return values...');
% get the cluster number from the result of condor_submit command
global clusterNumber;

[clusterNumber, status] = GetClusterNumber(cmdResult);
if(status == 1)
    return;
end

end


function [status] = DrawCurrentData(NumberOfPoints, currentChunk, MAXPOINT, TOTALPOINTS)
status = 0;
try
    global oldFailedJobs;
    global failedJobs;
    global completedJobs;
    global completedPoints;
    global XArray;
    global YArray;
    global oldZ;
    global ZArray;
    global totalPoints;
   
    % create progress bar
    global progressBar;
    progressBar = waitbar(0, 'Please wait...', 'CreateCancelBtn', 'setappdata(gcf, ''abort'',1);', 'WindowStyle', 'modal');
        % set bar always on top
            %frames = java.awt.Frame.getFrames();
            %frames(end).setAlwaysOnTop(1);
        % set initial value for param 'abort'
        setappdata(progressBar,'abort',0);
        
    % repeat the loop until all log files completed
    while true
        oldFailedJobs = failedJobs;
        % get the list of completed log files
        [completedJobs, failedJobs] = CheckLogFile(NumberOfPoints, completedJobs, failedJobs);
        % if all the jobs finished but don't have correct values (they are
        % all uncompleted jobs)
        if(sum(failedJobs) == NumberOfPoints)
                status = 1;
                % remove progressbar
                if(ishandle(progressBar) == 1)
                     delete(progressBar);
                end
                disp('ERROR: Jobs terminated. Could not find output values.');
            return;
        end
        % if we haven't got any completed job
        if(sum(completedJobs) == 0)
            % wait 30 seconds
            pause(30);
            % then return to the beginning of while loop to check values
            % again
            continue;
        end
        % store the values of completed Z elements to previous Z array
        oldZ = ZArray;
        % get the current values of completed Z elements
        ZArray = GetOutputs(completedJobs,ZArray);
        % write data to log file AppLog.xml
        resultLog = WriteLog(XArray, YArray, ZArray, oldZ, completedJobs, failedJobs, oldFailedJobs);
        % display info if the task fails
        if(resultLog == 1)
            disp('ERROR: Cannot write data to log file.');
        end
                
        % display graph
        [completedPoints, status] = PlotXY(XArray, YArray, ZArray, oldZ, completedPoints, currentChunk, MAXPOINT, TOTALPOINTS);
        hold on;
        if(status == 1)
            return;
        end
        
        
        % total processed points so far, remember to add the total points
        % from last chunk
        
        totalPoints = completedPoints + sum(failedJobs);
        TotalPointsWithLast = totalPoints + MAXPOINT * currentChunk;
           
        % display progress bar
        waitbar(TotalPointsWithLast/TOTALPOINTS, progressBar, [sprintf('Completed: %.2f', TotalPointsWithLast*100/TOTALPOINTS), '%']);
        % when click on Cancel button, stop here
        if(getappdata(progressBar, 'abort') == 1)
            % clean application
            CleanApplication(NumberOfPoints);
            % display statistic information
            DisplayInfo();
             % remove progress bar
            delete(progressBar);
            % draw histogram
            status = DrawHistogram(NumberOfPoints, currentChunk);
            if(status == 1)
                return;
            end
            status = 1;
            return;
        end;
        if(totalPoints == NumberOfPoints)
            break;
        end
        % delay 30s
        % pause(30);
    end
    % remove progress bar
    delete(progressBar);
catch err
    % remove progressbar
    if(ishandle(progressBar) == 1)
         delete(progressBar);
     end
    disp('ERROR: Cannot draw current data.');
    % display message when we have exceptions
    msg = getReport(err);
    disp(msg);
    status = 1;
    
end
end

% clear all temprorary data
function [status] = CleanApplication(NumberOfPoints)
status = 0;
try
    global totalPoints;
    global completedJobs;
    global failedJobs;
    global clusterNumber;
    % remove uncompleted Condor jobs
    
    if(totalPoints < NumberOfPoints)
      status =  RemoveJobs(completedJobs, failedJobs, clusterNumber);
      if (status == 1)
          return;
      end
    end
    
     % delete temporary files
     status = DeleteFiles();
     if (status == 1)
         return;
     end
catch err
    disp('ERROR: Cannot clean application temporary data.');
     % display message when we have exceptions
     msg = getReport(err);
     disp(msg);
    status = 1;
end
end


function DisplayInfo()
global completedPoints;
global failedJobs;

mess1 = sprintf('Total completed jobs with output values: %d jobs.', completedPoints);
disp(mess1);
mess2 = sprintf('Total failed jobs without output values: %d jobs.', sum(failedJobs));
disp(mess2);
end

% Draw graph with the points from xArray and yArray.
% INPUT:
% - xArray: array that has current input elements
% - yArray: array that has current output elements (current step)
% - oldY: array that has old output elements (previous step)
% - total: total elements that are represented so far.
% OUTPUT:
% - result: total points that are being represented by the graph.CreateCancelBtn
function [result, status] = PlotXY(XArray, YArray, ZArray, oldZ, total, currentChunk, MAXPOINT, TOTALPOINTS)
result = 0;
status = 0;
try    

global XToDraw;
global YToDraw;
global ZToDraw;
global failedJobs;
global completedJobs;
% only draw the points if zArray has much more 100 elements than the old one.
    % get the number of intmin in old Z array
    oldNumberOfZeros = sum(oldZ(:) == intmin);
    % get the number of intmin in current Z array
    curNumberOfZeros = sum(ZArray(:) == intmin);
    
    % if we don't have any new Y values
    if(curNumberOfZeros == oldNumberOfZeros)
        % result doesn't change
        result = total;
        % exit this function
        return;
    end
    
    LENGTH = length(ZArray);
    listX = zeros(1, LENGTH);
    listY = zeros(1, LENGTH);
    listZ = zeros(1, LENGTH);
    count = 1;

    for i = 1:LENGTH
        if(ZArray(i) ~=intmin)
            if(oldZ(i) == intmin)
                listX(count) = XArray(i);             
                listY(count) = YArray(i);
                listZ(count) = ZArray(i);
                count = count + 1;
            end
        end
    end
    
    result = total + count - 1;
    listX = listX(1:count-1);
    listY = listY(1:count-1);
    listZ = listZ(1:count-1);
    
    %jFrame = get(handle(gcf),'JavaFrame');
    %jFrame.setMaximized(true);
    XToDraw = [XToDraw listX];
    YToDraw = [YToDraw listY];
    ZToDraw = [ZToDraw listZ];
    % if we have less than 100 new points
    if(length(XToDraw) < 100 && sum(completedJobs) + sum(failedJobs) < LENGTH)
        % exit function
        return
    end
    
    
    strComplete = [sprintf('Completed: %.2f', (result + MAXPOINT * currentChunk) *100/TOTALPOINTS), '%'];
        
    plot3(XToDraw, YToDraw, ZToDraw, '.-r','LineStyle','none', 'MarkerFaceColor','r');
    grid on;
    view([45 30]);
    title(strComplete);
    xlabel('X value');
    ylabel('Y value');
    zlabel('Z value');
    % clear global X, Y, Z arrays
    XToDraw = [];
    YToDraw = [];
    ZToDraw = [];
    
catch err
    disp('ERROR: Cannot draw new points.');
     msg = getReport(err);
     disp(msg);
    status = 1;
end
end


% write currenWriteLog data into file AppLog.xml
% OUTPUT:
% result:
%       0: success
%       1: fail

function [result] = WriteLog(XArray, YArray, ZArray, oldZ, completedJobs, failedJobs, oldFailedJobs)
    global PointsToLog;
    
    % calculate new points we have so far
    % only log the  jobs if yArray has much more 100 elements than the old one.
    % get the number of intmin in old Y array
    oldNumberOfZeros = sum(oldZ(:) == intmin);
    % get the number of zeros in current Y array
    curNumberOfZeros = sum(ZArray(:) == intmin);
    
    PointsToLog = PointsToLog + oldNumberOfZeros - curNumberOfZeros;
    
    % if we have less than 100 new points, then we do not write the current
    % new points into log file
    NeedLog = 1;
    if(PointsToLog < 100 && sum(completedJobs) + sum(failedJobs) < length(XArray))
        % we dont need to log current new points
        NeedLog = 0;
    end
        % write completed jobs
        success = LogCompletedJobs(XArray, YArray, ZArray, oldZ, NeedLog);
            % exit function if fail
            if(success == 1)
                result = 1;
                return;
            end
        % write failed jobs
        success = LogFailedJobs(XArray, YArray, failedJobs, oldFailedJobs, NeedLog);
            % exit function if fail
            if(success == 1)
                result = 1;
                return;
            end
        % write unprocessed jobs
        success = LogUnprocessedJobs(XArray, YArray, completedJobs, failedJobs, NeedLog);
            % exit function if fail
                if(success == 1)
                    result = 1;
                    return;
                end
                
        % remove extra white spaces in log file AppLog.xml
        global applicationLogFile;
        success = RemoveSpace(applicationLogFile);
            % exit function if fail
                if(success == 1)
                    result = 1;
                    return;
                end
   % clear value after logging the new points
   if(NeedLog == 1)
       PointsToLog = 0;
   end
   
   result = 0;
 
end

% create file AppLog.xml
% OUTPUT:
% success:
%       0: success
%       1: fail
function [status] = CreateLogFile(totalJobs, functionName)
global clusterNumber;   
    try
     
     docNode = com.mathworks.xml.XMLUtils.createDocument('LogInfo');
     docRootNode = docNode.getDocumentElement;
     % create attribute TotalPoints = 1000
     docRootNode.setAttribute('TotalJobs', num2str(totalJobs));
     % create attribute ClusterNumber = 300
     docRootNode.setAttribute('ClusterNumber', clusterNumber);
     % create attribute Function= 'a.out'
     docRootNode.setAttribute('Function', functionName);
     
     thisElement = docNode.createElement('CompletedJobs');
     docRootNode.appendChild(thisElement); 
     
     thisElement = docNode.createElement('FailedJobs');
     docRootNode.appendChild(thisElement);
     
     thisElement = docNode.createElement('UnprocessedJobs');
     docRootNode.appendChild(thisElement);
     
     % write data to file [AppLog + currentChunk].xml
     global applicationLogFile;
     xmlwrite(applicationLogFile, docNode);
     
    catch err
        status = 1;
        disp('ERROR: Cannot create log file!');
        return;
    end
    status = 0;
end

%The extra whitespace is introduced by the Saxon XML processor, 
% which is the library that MATLAB uses for the XML processing.
function [status] = LogCompletedJobs(XArray, YArray, ZArray, oldZ, NeedLog)
    status = 0;
    global finishTimeToLog;
    global submittedTimeToLog;
    global CompletedXToLog;
    global CompletedYToLog;
    global CompletedZToLog;
    global submittedTime;
    global finishTime;
    LENGTH = length(ZArray);
    listSubmittedTime = zeros(1, LENGTH);
    listFinishTime = zeros(1, LENGTH);
    listX = zeros(1, LENGTH);
    listY = zeros(1, LENGTH);
    listZ = zeros(1, LENGTH);
    count = 1;

try
    % get the new points
    for i = 1:LENGTH
        if(ZArray(i) ~=intmin)
            if(oldZ(i) == intmin)
                listX(count) = XArray(i);             
                listY(count) = YArray(i);
                listZ(count) = ZArray(i);
                listSubmittedTime(count) = submittedTime(i);
                listFinishTime(count) = finishTime(i);
                count = count + 1;
            end
        end
    end
    listX = listX(1:count-1);
    listY = listY(1:count-1);
    listZ = listZ(1:count-1);
    listSubmittedTime = listSubmittedTime(1:count-1);
    listFinishTime = listFinishTime(1:count-1);
    
    CompletedXToLog = [CompletedXToLog listX];
    CompletedYToLog = [CompletedYToLog listY];
    CompletedZToLog = [CompletedZToLog listZ];
    submittedTimeToLog = [submittedTimeToLog listSubmittedTime];
    finishTimeToLog = [finishTimeToLog listFinishTime];
    
    % if we dont need to log the data, then exit the function
    if(NeedLog == 0)
        return;
    end

    
    
    % write the completed jobs to AppLog.xml
    global applicationLogFile;
    xDoc = xmlread(applicationLogFile);
    % get the 'LogInfo' node
    xRoot = xDoc.getDocumentElement;
    % get the 'CompletedJobs' node
    completedJobNode = xRoot.getChildNodes.item(1);
    % append child to completeJob node
    for i=1:length(CompletedXToLog);
        % create Job node
        jobNode = xDoc.createElement('CompletedJob');
            % create X node
            xNode = xDoc.createElement('X');
            % add value to X node
            xNode.appendChild(xDoc.createTextNode(num2str(CompletedXToLog(i))));
            jobNode.appendChild(xNode);
            % create Y node
            yNode = xDoc.createElement('Y');
            % add value to Y node
            yNode.appendChild(xDoc.createTextNode(num2str(CompletedYToLog(i))));
            jobNode.appendChild(yNode);
            % create Z node
            zNode = xDoc.createElement('Z');
            % add value to Y node
            zNode.appendChild(xDoc.createTextNode(num2str(CompletedZToLog(i))));
            jobNode.appendChild(zNode);
              
            % create SubmittedTime node
            sNode = xDoc.createElement('SubmittedTime');
            % add value to SubmittedTime node
            sNode.appendChild(xDoc.createTextNode(num2str(submittedTimeToLog(i))));
            jobNode.appendChild(sNode);
            % create FinishTime node
            fNode = xDoc.createElement('FinishTime');
            % add value to FinishTime node
            fNode.appendChild(xDoc.createTextNode(num2str(finishTimeToLog(i))));
            jobNode.appendChild(fNode);
        % append Job node to CompletedJob node
        completedJobNode.appendChild(jobNode);      
    end
    
    % write data to file AppLog.xml
    
    xmlwrite(applicationLogFile,xDoc);
    % clear data
    CompletedXToLog = [];
    CompletedYToLog = [];
    CompletedZToLog = [];
    submittedTimeToLog = [];
    finishTimeToLog = [];
catch err
    status = 1;
    disp('ERROR: Cannot write completed jobs to application log file.');
    % display message when we have exceptions
     msg = getReport(err);
     disp(msg);
end
end        % store the values of completed Y elements to previous Y array


function [status] = LogFailedJobs(XArray, YArray, failedJobs, oldFailedJobs, NeedLog)
status = 0;
global XFailedJobsToLog;
global YFailedJobsToLog;
try    
    
    % if we don't have any failed job
    if(sum(failedJobs) == 0)
        % do nothing
        return;
    end
    
% get only X values of the new failedJobs
LENGTH = length(XArray);
XnewJobs = zeros(1, LENGTH);
YnewJobs = zeros(1, LENGTH);
count = 1;
    % get the new points
    for i = 1:LENGTH
        if(failedJobs(i) ~=0)
            if(oldFailedJobs(i) == 0)
                XnewJobs(count) = XArray(i);
                YnewJobs(count) = YArray(i);
                count = count + 1;
            end
        end
    end
XnewJobs = XnewJobs(1:count-1);
YnewJobs = YnewJobs(1:count-1);
% if have no jobs
if (isempty(XnewJobs))
    % do nothing
    return;
end

XFailedJobsToLog = [XFailedJobsToLog XnewJobs];
YFailedJobsToLog = [YFailedJobsToLog YnewJobs];
% if we dont need to log new points, exit the function
if(NeedLog == 0)
    return;
end


    % write the failed jobs to AppLog.xml
    global applicationLogFile;
    xDoc = xmlread(applicationLogFile);
    % get the 'LogInfo' node
    xRoot = xDoc.getDocumentElement;
    % get the 'FailedJob' node
    failedJob = xRoot.getChildNodes.item(3);
    % append child to FailedJob node
    for i = 1: length(XFailedJobsToLog)
            % create Job node
            jobNode = xDoc.createElement('FailedJob');
            % create X node
            xNode = xDoc.createElement('X');
            % add value to X node
            xNode.appendChild(xDoc.createTextNode(num2str(XFailedJobsToLog(i))));
            jobNode.appendChild(xNode);
            % create Y node
            yNode = xDoc.createElement('Y');
            % add value to Y node
            xNode.appendChild(xDoc.createTextNode(num2str(YFailedJobsToLog(i))));
            jobNode.appendChild(yNode);
        % append Job node to FailedJob node
        failedJob.appendChild(jobNode);
    end
    % write data to file AppLog.xml
    
    xmlwrite(applicationLogFile, xDoc);
    % clear data
    XFailedJobsToLog = [];
    YFailedJobsToLog = [];
    
catch err
    status = 1;
    disp('ERROR: Cannot write failed jobs to AppLog.xml');
    % display message when we have exceptions
     msg = getReport(err);
     disp(msg);
end
end

function [status] = LogUnprocessedJobs(XArray, YArray, completedJobs, failedJobs, NeedLog)
     status = 0;
     if(NeedLog == 0)
         return;
     end
try
    % write the unprocessed jobs to AppLog.xml
    global applicationLogFile;
    xDoc = xmlread(applicationLogFile);
    % get the node 'LogInfo'
    xRoot = xDoc.getDocumentElement;
    % get the node 'UnprocessedJobs'
    unprocessedJobNode = xRoot.getChildNodes.item(5);
    % remove node UnprocessedJobs and all its children
    xRoot.removeChild(unprocessedJobNode);
    % create new node UnprocessedJobs
    unprocessedJobNode = xDoc.createElement('UnprocessedJobs');
    % append child to node UnprocessedJobs
    for i = 1:length(XArray)
        % if we find an unprocessed job
        if((completedJobs(i) == 0) && failedJobs(i) == 0)
            % create UnprocessedJob node
            jobNode = xDoc.createElement('UnprocessedJob');
            % create node JobID
            idNode = xDoc.createElement('JobID');
            % add process id to node JobID
            idNode.appendChild(xDoc.createTextNode(num2str(i-1)));
            jobNode.appendChild(idNode);
            % create X node
            xNode = xDoc.createElement('X');
            % add value to X node
            xNode.appendChild(xDoc.createTextNode(num2str(XArray(i))));
            jobNode.appendChild(xNode);
            % create Y node
            yNode = xDoc.createElement('Y');
            % add value to Y node
            yNode.appendChild(xDoc.createTextNode(num2str(YArray(i))));
            jobNode.appendChild(yNode);
        % append Job node to FailedJob node
        unprocessedJobNode.appendChild(jobNode);
        end
    end
    % append new node UnprocessedJobs to document
    xRoot.appendChild(unprocessedJobNode);
    % write data to file AppLog.xml
    xmlwrite(applicationLogFile, xDoc);
    
catch err
    status = 1;
    disp('ERROR: Cannot write unprocessed jobs to AppLog.xml');
    % display message when we have exceptions
     msg = getReport(err);
     disp(msg);
end
end

function [status] = RemoveSpace(filePath)
    status = 0;
    try
        % Read file.
        fId = fopen(filePath, 'r');
        fileContents = fread(fId, '*char')';
        fclose(fId);

        % Write new file.
        fId = fopen(filePath, 'w');
        % Remove extra lines.
        fwrite(fId, regexprep(fileContents, '\n\s*\n', '\n'));
        fclose(fId);
    catch err
        msg = sprintf('ERROR: Error when remove white spaces in the file %s', filePath);
        disp(msg);
        status = 1;
    end
end



% Delete all the temporary files: input files, output files, log files, error files.
% INPUT:
% - n: the maximum index of the files. (indexes from 0 to n-1)
function [status] = DeleteFiles()
status = 0;
try
cover = 'CoverMe';

% delete CoverMe files
 delete(['*',cover,'*']);
 % delete log file
 if(exist('DrawGraph.log', 'file') ~= 0)
    delete('DrawGraph.log');
 end
% delete error file
 if(exist('DrawGraph.err', 'file') ~= 0)
     delete('DrawGraph.err');
 end

% delete input files
if(exist('inp', 'file') == 7)
     rmdir('inp','s');
end

% delete output files
if(exist('out', 'file') == 7)
     rmdir('out','s');
end
 
catch err
    disp('ERROR: Cannot delete temporary files- DrawGraphOneChunk3D.');
    msg = getReport(err);
    disp(msg);
    status = 1;
end
end

 
% Stores elements of input array into files. Each file contains 1 element in real value. 
% For example: xArray = [1 2 3], the output will be: file inp.0 has number 1, 
% file inp.1 has number 2, file inp.2 has number 3.
% INPUT:
% - xArray: input array which has real-type elements.
% OUTPUT:
% Boolean type. True if all the processes have completed, else return false. 
function [status] = WriteData(XArray, YArray)
    status = 0; % true
        
try
    % create input and output folders
    mkdir('inp');
    mkdir('out');
    fname = 'inp/inp';
    
    for i = 1: length(XArray)
        fid = fopen([fname,'.',int2str(i-1)], 'w');
        fprintf(fid, '%f %f ', XArray(i), YArray(i));
        fclose(fid);
    end
catch err
    status = 1; % false
end

end

 
% Check the log files to detect the completed Condor jobs.
% search the line: (1) Normal termination
% INPUT:
% - n : number of log files. With n, we will have n log files from �log.0� to �log.(n-1)�.
% - doneList: the completed log files so far.
% OUTPUT: 
% - outCompletedJobs: This is the current completedJobs which mark all the completed jobs.
% - outUncompletedJobs: This is the current uncompletedJobs which mark all
% the completed jobs but don't have correct output values.
function [outCompletedJobs, outUncompletedJobs] = CheckLogFile(n, completedJobs, uncompletedJobs)
global submittedTime;
global finishTime;

try
    fileName = 'DrawGraph.log';
    lastLine = '';
    % open file
    fid = fopen(fileName, 'r');
    % read a line in the file
    curLine = fgetl(fid);
    
    while ischar(curLine)
        % if the line contains 'Job submitted from host', store the
        % submitted time of that jobs
        submittedLine = strfind(curLine, 'Job submitted from host');
        if(isempty(submittedLine) == 0)
             % get the process code
            [status, processNumber] = GetProcess(curLine);
             % if didn't log the submitted time
             if(submittedTime(processNumber) == 0)
                % get the submitted time
                [status, time] = GetTime(curLine);
                submittedTime(processNumber) = time;
             end
        else
              
            % if the line contains '(1) Normal termination (return value 0)'
            completedLine = strfind(curLine, '(1) Normal termination (return value 0)');
            if(isempty(completedLine) == 0)
                     % get the process code
                    [status, processNumber] = GetProcess(lastLine);
                    % add the process number to the output list
                    completedJobs(processNumber) = 1;
                    uncompletedJobs(processNumber) = 0;
                    % get the finish time of the job
                    if(finishTime(processNumber) == 0)
                        [status, time] = GetTime(lastLine);
                        finishTime(processNumber) = time;
                    end
                    
            else
               % check if the job finished but didn't have correct output value
               % if job was aborted
               aborted = strfind(lastLine, 'Job was aborted by the user.');% can kiem tra lai dieu kien nay
               % if the job finished but didn't have value
               error = strfind(lastLine, 'Job terminated.');
               if(isempty(aborted) == 0 || isempty(error) == 0)
                    % get the process code
                    [status, processNumber] = GetProcess(lastLine);
                    % add the process number to the uncompleted list
                    uncompletedJobs(processNumber) = 1;
               end

            end
        end
        % read the next line
        lastLine = curLine;
        curLine = fgetl(fid);
    end
    fclose(fid);
catch  err
    % display message when we have exceptions
    msg = getReport(err);
    disp(msg);
end

outCompletedJobs = completedJobs;
outUncompletedJobs = uncompletedJobs;
end

% extract the process number in a line
function [status, number] = GetProcess(line)
status = 0;
number = -1;
try
% extract the process number
    % find the character .
    dotPositions = strfind(line, '.');
    % get the process code
    processCode = line(dotPositions(1)+1: dotPositions(2)-1);
    % convert process code to number
    number = str2double(processCode) + 1;
catch err
    status = 1;
end
end

% extract the date time in a line
function [status, time] = GetTime(line)
status = 0;
time = 0;
try
% extract the date time value
    % find the character /
    slash = strfind(line, '/');
    % get the process code
    timeStr = line(slash-2: slash+11);
    % set time format
    fmt = 'mm/dd HH:MM:SS';
    time = datenum(timeStr, fmt)*86400;
catch err
    status = 1;
end
end


% Get the values from output files that are returned back from Condor (out.0 to out.(n-1))
% INPUT:
% - outDone: current list of completed output files
% - outValue: current values of output
% OUTPUT:
% out: an array. This is the current outValue array which has all the output values.
function [out] = GetOutputs(completedJobs, outValue)
    
for i = 1: length(completedJobs)
    
    if(outValue(i) ~= intmin)
        continue;
    end
    
    
    if(completedJobs(i) ~= 0) % if i-th log file done, then read the outputfile
        try
            count = 0;
            % open output file to readinp
            fid = fopen(['out/out.',int2str(i-1)], 'r');
            tline = fgetl(fid);
            % read value line-by-line
            while ischar(tline)
                count = count + 1;
                z = strread(tline, '%f');
                outValue(i) = z;
                tline = fgetl(fid);
            end
            fclose(fid);
        % catch exceptions when reading the file
        catch err
            msg = getReport(err);
            disp(msg);
        end
    end
    
end

out = outValue;
end



% Swap the elements of an array, so we can get the special elements first. 
% For example: 
%  input array: [1 2 3 4 5 6 7 8]
%  output array: [1 4 8 2 6 3 5 7]
% INPUT:
% - inList: input array
% OUTPUT:2
% - outList: an array. This is the new array that contains all the elements but in a new order.
function [XOutList, YOutList] = SwapElements(XInList, YInList)
    n = length(XInList);
    checkedList = zeros(1,n);
    count = 2;
    XOutList = zeros(1, n);
    XOutList(1) = XInList(1);
    XOutList(2) = XInList(n);
    YOutList = zeros(1, n);
    YOutList(1) = YInList(1);
    YOutList(2) = YInList(n);
    INDEX = 3;
    checkedList(1) = 1;
    checkedList(n) = 1;
    
    while(sum(checkedList) < n)
       part = int32(n/count);
       if(part == 0)
           break;
       end
       range = int32(n/count);
       while(part <= n)
           if(checkedList(part) == 0)
               XOutList(INDEX) = XInList(part);
               YOutList(INDEX) = YInList(part);
               INDEX = INDEX + 1;
               checkedList(part) = 1;
           end
           part = range + part;
       end
       count = count * 2;
                
    end
    % do these lines neccessary?
    XOutList = XOutList(1:INDEX-1);
    YOutList = YOutList(1:INDEX-1);
end

% remove all the uncompleted Condor jobs
% INPUT:
% - doneList: the list has information about completed jobs, and
% uncompleted jobs. Ex: doneList(3) = 1, then 3rd job is complete.
% doneList(4) = 0, then 4th job is uncomplete.
function [status] = RemoveJobs(completedJobs, uncompletedJobs, clusterNumber)
status = 0;

try
    % n is total jobs that are submitted to Condor
    n = length(completedJobs);
    % get the list of current completed jobs
    [completedJobs, uncompletedJobs] = CheckLogFile(n, completedJobs, uncompletedJobs);
    % check if all the jobs completed
    totalProcessedJobs = sum(completedJobs) + sum(uncompletedJobs);
    if(totalProcessedJobs == n)
        % then exit
        return;
    end
     
    % remove all uncompleted processes
        for i = 1:n
             % remove only unprocessed jobs
             if((completedJobs(i) == 0) && (uncompletedJobs(i) ==0))
                 % current output file name. Ex: out.1, out.2
                 outputFileName = ['out/out.', int2str(i-1)];
                 % we need to be sure that the output files dont have
                 % values
                 if (CheckData(outputFileName) == 1)
                     % now try to remove cluster.process in Condor queue
                     process = [clusterNumber, int2str(i-1)];
                     command = sprintf('condor_rm %s', process);
                     [cmdStatus, cmdResult] = system(command);
                     % if the action fails
                     % if(cmdStatus ~= 0)
                         % display error message
                         % disp(cmdResult);
                     % end
                 end
             end
        end
  
catch err
     msg = getReport(err);
     disp(msg);
     status = 1;
end

end


% check the value in output file
% INPUT: file name
% OUTPUT: result 0: has value
%                1: no value

function [status] = CheckData(file)
    status = 1;
    % if file not found, return 1
    if(exist(file, 'file') == 0)
        return;
    end
    % if file size > 0 bytes
    fileInfo = dir(file);
    if(fileInfo.bytes > 0)
        status = 0;
    end
end


% Validate the input data of function DrawGraph
function [status] = CheckInput(XStart, XEnd, YStart, YEnd, NumberOfPoints, exeFile)
    status = 0;
 try
    if(XStart > XEnd || YStart > YEnd)

        disp('Start point must be less than or equal end point.');
        status = 1;
        return;

    end
     % check the lower bound and upper bound for NumberOfPoints
    if(NumberOfPoints <=0 || NumberOfPoints > 3000000)

        disp('Number of points must be a positive integer from 1 to 3,000,000');
        status = 1;
        return;

    end
    

    if(mod(NumberOfPoints,1) ~=0)

        disp('Number of points must be a positive integer.');
        status = 1;
        return;
    end

    if(exist(exeFile) == 0)
        disp('ERROR: Can not find the executable file.');
        status = 1;
        return;

    end
    
 catch err
    disp('ERROR: invalid input data.');
    status = 1;
 end
end

% generate Condor submit file
% INPUT:
% - exeFile: executable file in Condor submit file
% - num: number of Condor jobs
function [status] = GenerateCondorSubmitFile(exeFile, num)
status = 0;
try
    % Condor input file name
    inp = 'inp/inp';
    % Condor error file name
    errorFileName = 'DrawGraph.err';
    % Condor output file
    outputFileName = 'out/out';
    % Condor log file name
    logFileName = 'DrawGraph.log'; 
    % write necessary data into Condor submit file (CoverMe.sub)
    fid = fopen('CoverMe.sub', 'w');
    % config executable file info. Ex: executable=a.out
    fprintf(fid, 'executable=%s\r\n', exeFile);
    % config universe info. Ex: universe=vanilla
    fprintf(fid, 'universe=%s\r\n', 'vanilla');
    % config arguments with inputs. Ex: arguments=inp.0
    fprintf(fid, 'arguments=%s.$(Process)\r\n', 'inp');
    % config environment. Ex: getenv=true
    fprintf(fid, 'getenv=%s\r\n', 'true');
    % config transfer files. Ex: when_to_transfer_output=ON_EXIT
    fprintf(fid, 'when_to_transfer_output=%s\r\n', 'ON_EXIT');
    % should_transfer_files=yes
    fprintf(fid, 'should_transfer_files=yes\r\n');
    % transfer_input_files=a.out,inp.0
    fprintf(fid, 'transfer_input_files=%s,%s.$(Process)\r\n',exeFile, inp);
    % config output file name. output=out.0, out.1.... 
    fprintf(fid, 'output=%s.$(Process)\r\n',outputFileName);
    % config error file name. error=err.0, err.1.....
    fprintf(fid, 'error=%s\r\n',errorFileName);
    % config log file. log=log.0, log.1
    fprintf(fid, 'log=%s\r\n', logFileName);
    % disable sending email function. notification=never
    fprintf(fid, 'notification=%s\r\n', 'never');
    % config queue. Ex: queue 1000
    fprintf(fid, 'queue %d', num);
fclose(fid);
catch err
    disp('ERROR: Cannot create Condor submit file.');
    status = 1;
end
    
end

% get cluster number from the result of condor_submit command
function [clusterNum, status] =  GetClusterNumber(cmdResult)
status = 0;
try
    % get the start location of string 'cluster '
    startLoc = strfind(cmdResult, 'cluster ');
    % get the cluster number, need to remove the last space character
    clusterNum = cmdResult(startLoc + 8: length(cmdResult)-1);
catch err
    disp('ERROR: Cannot get cluster number.');
    status = 1;
end
end


% draw histogram about running time of all the jobs
function [status] = DrawHistogram(NumberOfPoints, currentChunk)
status = 0;
try
    timeList = zeros(1, NumberOfPoints);
    total = 1;
    
    % get all the times in all chunks
    for i=0:currentChunk
           [status, timeList, total] = GetTimeInChunk(i, timeList, total);
           if(status == 1)
               return;
           end
           
    end
    % truncate the array
    timeList = timeList(1:total-1);   
    % find min, max elements, then draw histogram
    minValue = min(timeList);
    maxValue = max(timeList);
    % divide the time from min to max into every seconds
    group = maxValue - minValue;
    DISTANCE = group + 1;
    if(DISTANCE > 100)
        DISTANCE = 100;
    end
    ranges = linspace(minValue, maxValue, DISTANCE);
    % draw histogram
    figure('name','Histogram');
    hist(timeList, ranges);
    % set title and x-label, y-label, and color
    title('Run-time distribution of the jobs');
    xlabel('Run-time(s)');
    ylabel('Number of jobs');
    % set color for histogram
    h = findobj(gca, 'Type', 'patch');
    set(h, 'FaceColor','g','EdgeColor','w');
        
catch err
     msg = getReport(err);
     disp(msg);   
end

end


% get time of complete jobs in a specific chunk
function [status, timeListOut, totalOut ] = GetTimeInChunk(currentChunk, timeListIn, totalIn)
status = 0;
timeListOut = timeListIn;
totalOut = totalIn;
logfileName = 'CondorMatlab_AppLog';
try
        logFile = [logfileName, num2str(currentChunk), '.xml'];
        if(exist(logFile, 'file') == 0)
            return;
        end
        xDoc = xmlread(logFile);
        % root Node
        xRoot = xDoc.getDocumentElement;
        % get node CompletedJobs
        CompletedJobsNode = xRoot.getChildNodes.item(1);
        % get all submitted time
        sNodes = CompletedJobsNode.getElementsByTagName('SubmittedTime');
        % get all finish Time
        fNodes = CompletedJobsNode.getElementsByTagName('FinishTime');
        
        n = sNodes.getLength;
        if(n == 0)
            return;
        end
        
        % read all the node SubmittedTime, FinishTime and store the
        % runningTime in array timeList
        for i = 0:n-1
            % get current value of node X
            s = str2double(sNodes.item(i).getTextContent);
            % get current value of node Y
            f = str2double(fNodes.item(i).getTextContent);
            % add x and y values to xArray and yArray
            timeListIn(totalIn) = f - s;
            totalIn = totalIn + 1;
        end
    timeListOut = timeListIn;
    totalOut = totalIn;
catch err
    msg = getReport(err);
    disp(msg);
end


end


