%
%  Context-Aware Correlation Filters
%
%  Written by Matthias Mueller, 2016
%
%  This function takes care of setting up parameters,
%  and interfacing with the online tracking benchmark.

function results=run_DCF_CA(seq, res_path, bSaveImage)

%default settings
kernel.type =  'linear';

padding = 2;  %extra area surrounding the target
lambda1 = 1e-4;     %regularization
lambda2 = 25;
interp_factor = 0.015;  %linear interpolation factor for adaptation
output_sigma_factor = 0.1; %spatial bandwidth (proportional to target)

features.gray = false;
features.hog = true;
features.hog_orientations = 9;

cell_size = 4;

target_sz = seq.init_rect(1,[4,3]);
pos = seq.init_rect(1,[2,1]) + floor(target_sz/2);
img_files = seq.s_frames;
video_path = [];

%call tracker function with all the relevant parameters
[positions, time] = tracker(video_path, img_files, pos, target_sz, ...
    padding, kernel, lambda1, lambda2, output_sigma_factor, interp_factor, ...
    cell_size, features, false);

%return results to benchmark, in a workspace variable
rects = [positions(:,2) - target_sz(2)/2, positions(:,1) - target_sz(1)/2];
rects(:,3) = target_sz(2);
rects(:,4) = target_sz(1);
fps = numel(seq.s_frames) / time;
disp(['fps: ' num2str(fps)])
results.type = 'rect';
results.res = rects;
results.fps = fps;
%assignin('base', 'res', res);

end


function make_eMouseData_drift(fpath, KS2path, chanMapName, useGPU, useParPool)
% this script makes a binary file of simulated eMouse recording
% written by Jennifer Colonell, based on Marius Pachitariu's original eMouse simulator for Kilosort 1
% Adds the ability to simulate simple drift of the tissue relative to the
% probe sites.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% you can play with the parameters just below here to achieve a signal more similar to your own data!!!
norm_amp  = 1.5 * 16.7; % if 0, use amplitudes of input waveforms; if > 0, set all amplitudes to norm_amp*rms_noise
mu_mean   = .75; %0.75; % mean of mean spike amplitudes. Incoming waveforms are in uV; make <1 to make sorting harder
noise_model = 'gauss'; %'gauss' or 'fromData'; 'fromData' requires a noiseModel.mat built by make_noise_model
rms_noise = 10; % rms noise in uV. Will be added to the spike signal. 15-20 uV an OK estimate from real data
t_record  = 1200; % duration in seconds of simulation. longer is better (and slower!) (1000)
fr_bounds = [1 10]; % min and max of firing rates ([1 10])
tsmooth   = 0.5; % gaussian smooth the noise with sig = this many samples (increase to make it harder) (0.5)
chsmooth  = 0.5; % smooth the noise across channels too, with this sig (increase to make it harder) (0.5)
amp_std   = .1; % standard deviation of single spike amplitude variability (increase to make it harder, technically std of gamma random variable of mean 1) (.25)
fs_rec    = 30000; % sample rate for the for the recording. Waveforms must be sampled at a rate w/in .01% of this rate
nt        = 81; % number of timepoints expected. All waveforms must have this time window
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%drift params. See the comments in calcYPos_v2 for details
drift.addDrift = 1;
drift.sType = 'rigid';  %'rigid' or 'point';
drift.tType = 'sine';   %'exp' or 'sine'
drift.y0 = 3800;        %in um, position along probe where motion is largest
                        %y = 0 is the tip of the probe
drift.halfDistance = 1000;   %in um, distance along probe over which the motion decays
drift.amplitude = 5;        %in um for a sine wave
%                             peak variation is 2Xdrift.amplitude
drift.halfLife = 2;     %in seconds
drift.period = 600;      %in seconds
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%waveform source data
% waveform files to use for the simulation
% these come in two types:
%     3A data, used for "large" units that cover > 100 um in Y
%     Data from Kampff ultradense survey, analyzed by Nick Steimetz and
%     Susu Chen, for "small" units that cover < 100 um in Y
% Interpolation of large units uses a linear "scattered interpolant"
% Interpolation of small units uses a grid interpolant.
% currently, the signal at the simulated sites is taken as the interpolated
% field value at the center of the site. This is likely appropriate for
% mapping 3A data onto simulated 3A data (or 3B data). For creating 3A data
% from the Kampff data, averaging over site area might be more realistic.

%fileCopies specifies how many repeats of each file (to make dense data
%sets). The units will be placed at random x and y on the probe, but
%using many copies can lead to too many very similar units.

useDefault = 1;     %use waveforms from eMouse folder in KS2

if useDefault
    %get waveforms from eMouse folder in KS2
     filePath{1} = fullfile(KS2path,'eMouse_drift','kampff_St_unit_waves_allNeg_2X.mat');
     fileCopies(1) = 2;
     filePath{2} = fullfile(KS2path,'eMouse_drift','121817_SU_waves_allNeg_gridEst.mat');
     fileCopies(2) = 2;
else
    %fill in paths to waveform files
    filePath = {};
    filePath{1} = 'C:\Users\labadmin\Documents\emouse_drift\eMouse\121817_single_unit_waves_allNeg.mat';
    fileCopies(1) = 1;
    filePath{2} = 'C:\Users\labadmin\Documents\emouse_drift\UltradenseKS4Jennifer\SpikeMeanWaveforms\kampff_St_unit_waves_allNeg_2X.mat';
    fileCopies(2) = 1;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%other parameters
bPair     = 0; % set to 0 for randomly distributed units, 1 for units in pairs
pairDist  = 50; % distance between paired units
bPlot     = 0; %make diagnostic plots of waveforms

% Noise can either be generated from a gaussian distribution, or modeled on
% noise from real data, matching the frequency spectrum and cross channel
% correlation. The sample noise data is taken from a 3B2 recording performed
% by Susu Chen. Note that the noise data should come from a probe with
% the same geometry as the model probe.
if ( strcmp(noise_model,'fromData') )
    if useDefault
        nmPath = [KS2path,'\eMouse_drift\','SC026_3Bstag_noiseModel.mat'];
    else
        %fill in path to desired noise model.mat file
    end
    noiseFromData = load(nmPath);
end

% Add a SYNC channel to the file
% 16 bit word with a 1 Hz square wave in 7th bit
addSYNC = false;
syncOffset = 0.232; % must be between 0 and 0.5, offset to first on edge
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

rng('default');

% There are 3 seeds for the randome number generator, so some parts of the
% simulation can be fixed while others vary from run to run

% For unit placment, average amplitude, and spike times

unit_seed = 101;

% For individual spike amplitudes, but still based on the same average
% Meant to simulate the same spikes showing up in different streams

amp_seed = 101;

% For noise generation

noise_seed = 101;

% set the seed of the random number generator used for unit definition
rng(unit_seed);

%bitPerUV = 0.42667; %imec 3A or 3B, gain = 500
bitPerUV = 1.3107; %for NP 2.0, fixed gain of 80

%  load channel map file built with make_eMouseChannelMap_3A_short.m

chanMapFile = fullfile(fpath, chanMapName);
load(chanMapFile);

zeroSites = find(connected == 0);

Nchan = numel(chanMap); %physical sites on the probe
%invChanMap(chanMap) = [1:Nchan]; % invert the  channel map here--create the order in which to write output

% range of y positions to place units
minY = min(ycoords(find(connected==1)));
maxY = max(ycoords(find(connected==1)));
minX = min(xcoords(find(connected==1)));
maxX = max(xcoords(find(connected==1)));

%build the complete filePath list for waveforms
nUniqueFiles = numel(filePath);
currCount = nUniqueFiles;
for i = 1:nUniqueFiles
    for j = 1:fileCopies(i)-1
        currCount = currCount + 1;
        filePath{currCount} = filePath{i};
    end
end

nFile = length(filePath);

% for i = 1: nFile
%     fprintf('%s\n',filePath{i});
% end

NN = 0; %number of units included
uF = {}; %cell array for interpolants
uR = []; %array of max/min X and Y for points that can be interpolateed
         %structure with uR.maxX, uR.maxY, uR.minX, uR.minY
origLabel = []; %array of original labels

for fileIndex = 1:nFile
    % generate waveforms for the simulation
    uData = load(filePath{fileIndex});
    fs_diff = 100*(abs(uData.fs - fs_rec)/fs_rec);
    if (fs_diff > 0.01)
        fprintf( 'Waveform file %d has wrong sample rate.\n', fileIndex );
        fprintf( 'Skipping to next file.');
        continue
    end
    if (uData.nt ~= nt)
        fprintf( 'Waveform file %d has wrong time window.\n', fileIndex );
        fprintf( 'Skipping to next file.');
        continue
    end
    if ~( strcmp(uData.dataType, '3A') || strcmp(uData.dataType, 'UD') )
        fprintf( 'Waveform file %d has unknown sample type.\n', fileIndex );
        fprintf( 'Skipping to next file.');
        continue
    end

    nUnit = length(uData.uColl);
    unitRange = NN+1:NN+nUnit;  %for now, using all the units we have

    if strcmp(uData.dataType, '3A')
        uType(unitRange) = 0;
    end
    if strcmp(uData.dataType, 'UD')
        uType(unitRange) = 1;
    end

    %vary intensity for each unit
    if uType(NN+1) == 0
        scaleIntensity = mu_mean*(1 + (rand(nUnit,1) - 0.5));
    end
    if uType(NN+1) == 1     %these are already small, so don't make them smaller.
        scaleIntensity = (1 + (rand(nUnit,1)-0.5));
    end

   if (norm_amp > 0)
        %get min and max intensity, and normalize to norm_amp X the rms noise
        targAmp = rms_noise*norm_amp;
        for i = 1:nUnit
            maxAmp = max(max(uData.uColl(i).waves)) - min(min(uData.uColl(i).waves));
            uData.uColl(i).waves = uData.uColl(i).waves*(targAmp/maxAmp);
        end
    else
        for i = 1:nUnit
            uData.uColl(i).waves = uData.uColl(i).waves*scaleIntensity(i);
        end
   end

    %deprecated
    %allowed use of scattered interpolants (non-grid sampling of the
    %waveforms -- but the calculations are 5-10X slower than gridded
    %interpolants. For non-square sampling of waveforms (e.g. Neuropixels
    %probes) -- make a scattered interpolant and then sample on a gridded
    %interpolant to feed to the simulator.
    %for these units, create an intepolant which will be used to
    %calculate the waveform at arbitrary sites

%     if (uType(unitRange(1)) == 1)
%         [uFcurr, uRcurr] = makeGridInt( uData.uColl, nt );
%     else
%         [uFcurr, uRcurr] = makeScatInt( uData.uColl, nt );
%     end

    % with both types gridded, always make a gridded interpolant
    [uFcurr, uRcurr] = makeGridInt( uData.uColl, nt );

    %append these to the array over all units
    uF = [uF, uFcurr];
    uR = [uR, uRcurr];

    for i = unitRange
        origLabel(i) = uData.uColl(i-NN).label;
    end

    NN = NN + nUnit;
end

% calculate a size for each unit
  uSize = zeros(1,NN);
  for i = 1:NN
      uSize(i) = (uR(i).maxX - uR(i).minX) * (uR(i).maxY - uR(i).minY);
  end

% distribute units along the length the probe, either in pairs separated
% by unitDist um, or fully randomly.
% for now, keep x = the original position (i.e. don't try to recenter)
uX = zeros(1,NN);
uY = zeros(1,NN);
uX = uX + ((maxX - minX)/2 + minX);

yRange = maxY - minY;

unitOrder = randperm(NN);   %really only important for the pairs

if ~bPair
    uX(unitOrder) = (maxX - minX)/2 + minX;
    uY(unitOrder) = minY + yRange * rand(NN,1);
else
    %pairs will be placed at regular spacing (so the spacing is controlled)
    %want to avoid placing units at the ends of the probe
    endBuff = 50; %min distance in um from end of probe
    if (mod(NN,2))
        %odd number of units; pair up the first NN - 1
        %place the first units between (minY + pairDist) and maxY
        nPair = (NN-1)/2;
        % (0:nPair) = nPair + 1 positions; one extra for the loner
        pairPos = minY + endBuff + (pairDist/2) + ...
            (0:nPair)*(yRange - pairDist - 2*endBuff)/(nPair);
        uY(unitOrder(1:2:NN-2)) =  pairPos(1:nPair) + (pairDist/2);
        uY(unitOrder(2:2:NN-1)) = pairPos(1:nPair) - (pairDist/2);
        uY(unitOrder(NN)) = pairPos(nPair + 1);
    else
        %even number of units; pair them all
        nPair = NN/2;
        pairPos = minY + endBuff + (pairDist/2) + ...
            (0:nPair-1)*(yRange - pairDist - 2*endBuff)/(nPair-1);
        uY(unitOrder(1:2:NN-2)) = pairPos(1:nPair) + (pairDist/2);
        uY(unitOrder(2:2:NN-1)) = pairPos(1:nPair) - (pairDist/2);
    end
end

% calculate monitor sites for these units
monSite = zeros(1,NN);
for i = 1:NN
    [currWav, uSites] = intWav( uF{i}, uX(i), uY(i), uR(i), xcoords, ycoords, connected, nt );
%         fprintf( 'site %d: ',i);
%         for uCount = 1:length(uSites)
%             fprintf( '%d,', uSites(uCount));
%         end
%         fprintf( '\n' );
    monSite(i) = pickMonSite( currWav, uSites, connected );
    if( monSite(i) < 0 )
        fprintf( 'All sites nearby unit %d are not connected. Probably an error.\n', i);
        return;
    end
end

if (bPlot)

    %write out units and positions to console
    fprintf( 'label\torig label\ty position\tmonitor site\n' );
    for i = 1:NN
        fprintf( '%d\t%d\t%.1f\t%d\n', i, origLabel(i), uY(i), monSite(i) );
    end

    %for an overview of the units included, plot waves over the whole
    %probe, at the initial position, and shifted by 1*delta, and 2*delta

    deltaUM = 10;

    %set up waves for whole probe
    testwav = zeros(Nchan,nt);

    %for each unit, find the sites with signal, calculate the waveform based on
    %the current probe position (using the interpolant) and add to wav

    for i = 1:NN
        [currWav, uSites] = intWav( uF{i}, uX(i), uY(i), uR(i), xcoords, ycoords, connected, nt );
        testwav(uSites,:) = testwav(uSites,:) + currWav;
    end

    %calculate waveforms with units shifted by deltaUM
    testwav2 = zeros(Nchan,nt);
    for i = 1:NN
        currYPos = uY(i) + deltaUM;
        [currWav, uSites] = intWav( uF{i}, uX(i), currYPos, uR(i), xcoords, ycoords, connected, nt );
        testwav2(uSites,:) = testwav2(uSites,:) + currWav;
    end

    %calculate waveforms with units shifted by 2*deltaUM
    testwav3 = zeros(Nchan,nt);

    for i = 1:NN
        currYPos = uY(i) + 2*deltaUM;
        [currWav, uSites] = intWav( uF{i}, uX(i), currYPos, uR(i), xcoords, ycoords, connected, nt );
        testwav3(uSites,:) = testwav3(uSites,:) + currWav;
    end
    figure(1);
    tRange = 1.1*nt;
    yRange = 1.1*( max(max(testwav)) - min(min(testwav)) );

    %properties of the 3A probe.
    %TODO: add derivation of these from xcoords, ycoords
    xMin = 11;
    yMin = 20;
    xStep = 16;
    yStep = 20;

    tDat = 1:nt;
    %only plotting 1-384 (rather than 1-385) because 385 is unconnected
    %digital channel

    for i = 1:Nchan-1
        currDat = testwav( i, : );
        currDat2 = testwav2(i,:);
        currDat3 = testwav3(i,:);
        xPlotPos = (xcoords(i) - xMin)/xStep;
        xOff = tRange * xPlotPos + nt/3;
        yOff = yRange * (ycoords(i) - yMin)/yStep;
        figure(1);
        %plot(tDat + xOff, currDat + yOff,'b-');
        %plot(tDat + xOff, scatDat + yOff,'b-');
        plot(tDat + xOff, currDat + yOff,'b-', tDat + xOff, currDat3 + yOff, 'r-' );
        if(xPlotPos == 0)
          msgstr=sprintf('ch%d',i);
          text(xOff,yOff+50,msgstr);
        end
        hold on
    end

    %plot signals at monitor sites at initial position.
    figure(2);

    %some plotting params
    colorStr ={};
    colorStr{1} = '-b';
    colorStr{2} = '-r';
    %fprintf('unit\tmaxAmp\n');
    for i = 1:NN
        [currWav, uSites] = intWav( uF{i}, uX(i), uY(i), uR(i), xcoords, ycoords, connected, nt );
        cM = find(uSites == monSite(i));
        currDat = currWav(cM,:);
        %fprintf( '%d\t%.2f\n', i, (max(currDat)-min(currDat)));
        currColor = colorStr{uType(i) + 1};
        plot(tDat,currDat,currColor);
        hold on;
    end

end

bContinue = 1;

% set the sample rate to that specified in the hard coded params in this
% file (independent of fs read in through channel map)
% allows simulation of multiple streams with slightly different clock rates

fs = fs_rec;
fs_std = 30000; %used to generate spike times

%same for range of firing rates (note that we haven't included any info
%about the original firign rates of the units
fr = fr_bounds(1) + (fr_bounds(2)-fr_bounds(1)) * rand(NN,1); % create variability in firing rates

% totfr = sum(fr); % total firing rate

spk_times = [];
clu = [];

if bContinue        %done with setup, now starting the time consuming stuff

for j = 1:length(fr)      %loop over neurons
    %generate a set of time differences bewteen spikes
    %random numbers from a geometric distribution with with probability
    %(sample time)*firing rate = (1/sample rate)*firing rate =
    %(1/(samplerate*firingrate))
    %geometric distribution an appropriate model for the number of trials
    %before a success (neuron firing)
    %second two params for geornd are size of the array, here 2*firing
    %rate*total time of the simulation.

    dspks = int64(geornd(1/(fs_std/fr(j)), ceil(2*fr(j)*t_record),1));
    dspks(dspks<ceil(fs_std * 2/1000)) = [];  % remove ISIs below the refractory period
    res = cumsum(dspks);
    spk_times = cat(1, spk_times, res);
    clu = cat(1, clu, j*ones(numel(res), 1));
end
% convert spike times to the requested rate
spk_times = int64( double(spk_times)*(fs/fs_std));

[spk_times, isort] = sort(spk_times);
clu = clu(isort);
clu       = clu(spk_times<t_record*fs);
spk_times = spk_times(spk_times<t_record*fs);
nspikes = numel(spk_times);

% this generates single spike amplitude with mean = 1 and std deviation
% ~amp_std, while ensuring all values are positive
% re-seed the random number generator to allow variable amplitudes
% while holding positions of units constant
rng(amp_seed);
amps = gamrnd(1/amp_std^2,amp_std^2, nspikes,1);

%
buff = 128;
NT   = 4 * fs + buff; % batch size + buffer

fidW     = fopen(fullfile(fpath, 'sim_binary.imec.ap.bin'), 'w');

t_all    = 0;

if useGPU
    %set up the random number generators for run to run reproducibility
    gpurng('default'); % start with a default set
    gpurng(noise_seed); % set the seed
    %gpurng('shuffle'); uncomment to have the seed set using the clock
end

% reset random number generator for noise
rng(noise_seed);

%record a y position for each spike time
yDriftRec = zeros( length(spk_times), 5, 'double' );
allspks = 0;

% The parpool option can speed up the calculation when using scattered
% interpolants AND running with a large number of workers (>8). With all
% gridded interpolants, the overhead is too large and
if (useParPool)
    %delete any currently running pool
    delete(gcp('nocreate'))
    %create parallel pool
    locCluster = parcluster('local');
    parpool('local',locCluster.NumWorkers);
    %get a handle to it
    p = gcp();
    %add variables to the workers
    p_xcoords = parallel.pool.Constant(xcoords);
    p_ycoords = parallel.pool.Constant(ycoords);
    p_connected = parallel.pool.Constant(connected);
    p_uF = parallel.pool.Constant(uF);
    p_uX = parallel.pool.Constant(uX);
    p_uY = parallel.pool.Constant(uY);
    p_uR = parallel.pool.Constant(uR);
end

while t_all<t_record

    if ( strcmp(noise_model,'gauss') )
        if useGPU
            enoise = gpuArray.randn(NT, Nchan, 'single');
        else
            enoise = randn(NT, Nchan, 'single');
        end
        if t_all>0
            enoise(1:buff, :) = enoise_old(NT-buff + [1:buff], :);
        end

        dat = enoise;
        dat = my_conv2(dat, [tsmooth chsmooth], [1 2]);
        %rescale the smoothed data to make the std = 1;
        dat = zscore(dat, 1, 1);
        dat = gather_try(dat);
        %multiply the final noise calculation by the expected rms in uV
        dat = dat*rms_noise;
        %fprintf( 'Noise mean = %.3f; std = %.3f\n', mean(dat(:,1)), std(dat(:,1)));
    elseif ( strcmp(noise_model,'fromData') )
        enoise = makeNoise( NT, noiseFromData, chanMap, connected, NchanTOT );
        if t_all>0
            enoise(1:buff, :) = enoise_old(NT-buff + [1:buff], :);
        end
        dat = enoise;
    end

    if addSYNC
        sync = zeros(NT,1,'int16');
        hilo = zeros(NT,1,'logical');
        % for each time point, calculate where it is in the 1Hz cycle
        currTime = zeros(NT,1,'single');
        currTime(:,1) = (0:NT-1) + t_all*fs;
        currTime(:,1) = currTime(:,1)/fs - syncOffset;  %time in seconds, relative to offset
        hilo(:,1) = currTime(:,1) - floor(currTime(:,1)) < 0.5;
        sync(hilo,1) = 64;
    end

    if t_all>0
        dat(1:buff/2, :) = dat_old(NT-buff/2 + [1:buff/2], :);
    end

    % now we add spikes all channels; ref channels zeroed out after
    ibatch = (spk_times >= t_all*fs) & (spk_times < t_all*fs+NT-buff);
    ts = spk_times(ibatch) - t_all*fs;
    ids = clu(ibatch);
    am = amps(ibatch);
    tRange = int64(1:nt);
    tic

    if (useParPool)
        %     %first run a parfor loop for the time consuming calculation of the
        %     %interpolated waves

        currWavArray = zeros(length(ts),Nchan,nt,'double'); %actual array much shorter
        currSiteArray = zeros(length(ts),Nchan,'uint16'); %record indices of the sites in currWavArray;
        currNsiteArray = zeros(length(ts),'uint16'); %record number of sites

        parfor i = 1:length(ts)
            cc = ids(i); %current cluster index
            currT = t_all + double(ts(i))/fs;
            currYPos = calcYPos_v2( currT, p_uY.Value(cc), drift );
            [currWav, uSites] = ...
                intWav( p_uF.Value{cc}, p_uX.Value(cc), currYPos, p_uR.Value(cc), p_xcoords.Value, p_ycoords.Value, p_connected.Value, nt );
            nSite = length(uSites);
            currWavArray(i,:,:) = padarray(currWav * am(i),[(Nchan-nSite),0],0,'post');
            currSiteArray(i, :) = padarray(uSites,(Nchan-nSite),0,'post')';
            currNsiteArray(i) = nSite;
        end

    end

    for i = 1:length(ts)
        %for a given time, need to calcuate the wave that correspond to
        %the current position of the probe.
        allspks = allspks + 1;
        currT = t_all + double(ts(i))/fs;
        cc = ids(i); %current cluster index

        %get current position of this unit
        currYPos = calcYPos_v2( currT, uY(cc), drift );

        %currYdrift = calcYPos( currT, ycoords );
        yDriftRec(allspks,1) = currT;
        yDriftRec(allspks,2) = currYPos;
        yDriftRec(allspks,3) = cc; %record the unit label in the drift record

        if (useParPool)
            %get the waves for this unit from the big precalculated array
            uSites = squeeze(currSiteArray(i,1:currNsiteArray(i)));
            tempWav = squeeze(currWavArray(i,1:currNsiteArray(i),:));
            dat(ts(i) + tRange, uSites) = dat(ts(i) + tRange, uSites) + tempWav';
        else
            %calculate the interpolations now
            [tempWav, uSites] = ...
                intWav( uF{cc}, uX(cc), currYPos, uR(cc), xcoords, ycoords, connected, nt );

            dat(ts(i) + tRange, uSites) = dat(ts(i) + tRange, uSites) +...
                am(i) * tempWav(:,:)';
        end

        cM = find(uSites == monSite(cc));
        %if drift has moved the monitor site outside the footprint of the
        %unit, the recorded amplitudes just stay = 0;

        if ( cM )
            yDriftRec(allspks,4) = max(tempWav(cM,:)) - min(tempWav(cM,:));
            yDriftRec(allspks,5) = max(dat(ts(i) + tRange,monSite(cc))) - min(dat(ts(i) + tRange,monSite(cc)));
        end

    end

    %zero out the unconnected channels

    dat(:, zeroSites') = 0; % these are the reference and dig channels

    dat_old    =  dat;
    %convert to 16 bit integers; waveforms are in uV
    dat = int16(bitPerUV * dat);
    if addSYNC
        %add the column of sync data
        dat = horzcat(dat, sync);
    end
    fwrite(fidW, dat(1:(NT-buff),:)', 'int16');

    t_all = t_all + (NT-buff)/fs;
    elapsedTime = toc;
    fprintf( 'created %.2f seconds of data; nSpikes %d; calcTime: %.3f\n ', t_all, length(ts), elapsedTime );
    enoise_old = enoise;
    clear currWavArray;
    clear currNSiteArray;
    clear currSiteArray;
end

fclose(fidW); % all done

gtRes = spk_times + nt/2; % add back the time of the peak for the templates (half the time span of the defined waveforms)
gtClu = clu;

save(fullfile(fpath, 'eMouseGroundTruth'), 'gtRes', 'gtClu')
save(fullfile(fpath, 'eMouseSimRecord'), 'yDriftRec' );

end

end

function [uF, uR] = makeScatInt( uColl, nt )
    %create an interpolating function for each unit in
    % the array uColl
    uF = {};
    % array of maximum radii for each unit. only sites within this radius
    % will get get contributions from this unit
    uR = [];
    for i = 1:length(uColl)
        %reshape waves to a 1D vector
        wave_pts = reshape(uColl(i).waves',[uColl(i).nChan*nt,1]);
        xpts = repelem(uColl(i).xC,nt)';
        ypts = repelem(uColl(i).yC,nt)';
        tpts = (repmat(1:nt,1,uColl(i).nChan))';
        %specify points as y, x to be congruent with row,column of grid
        %interpolant.
        uF{i} = scatteredInterpolant( ypts, xpts, tpts, wave_pts, 'linear', 'nearest' );
        uR(i).minX = min(uColl(i).xC);
        uR(i).maxX = max(uColl(i).xC);
        uR(i).minY = min(uColl(i).yC);
        uR(i).maxY = max(uColl(i).yC);
        %earlier version that used y extend
        %uR(i) = min( max(uColl(i).yC), abs(min(uColl(i).yC)) );
    end

end

function [uF, uR] = makeGridInt( uColl, nt )
    %Reshape the array of [nsite, nt] into [Y, X, nt]
    %Note that this is dependent on how the data were stored:
    %here, assumes the sites are stored in row major order.
    %Could also derive this from the X and Y coordinates for generality

    %create an interpolating function for each unit in
    % the array uColl
    uF = {};
    % array of maximum radii for each unit. only sites within this radius
    % will get get contributions from this unit
    uR = [];

    for i = 1:length(uColl)
        sCol = length(unique(uColl(i).xC));
        sRow = length(unique(uColl(i).yC));
        %reshape waveform array into [sRow x sCol x nt array]
        %the data are stored as (1,1),(1,2),(1,3)...(2,1),(2,3)
        %reshape transforms as "row fast", so need to reshape and permute
        v = permute(reshape(uColl(i).waves,[sCol,sRow,nt]),[2,1,3]);
        xVal = uColl(i).xC(1:sCol);
        %pick off first element of each column to get y values
        colOne = (0:sRow-1)*sCol + 1;
        yVal = uColl(i).yC(colOne);
        tVal = 1:nt;
        %remember: y = rows, x = columns!
        [Y,X,T] = ndgrid(yVal, xVal, tVal);
        uF{i} = griddedInterpolant(Y,X,T,v,'makima');
        uR(i).minX = min(uColl(i).xC);
        uR(i).maxX = max(uColl(i).xC);
        uR(i).minY = min(uColl(i).yC);
        uR(i).maxY = max(uColl(i).yC);
        %earlier version that just used y extent
        %uR(i) = min( max(uColl(i).yC), abs(min(uColl(i).yC)) );
    end

end

function uSites = findSites( xPos, yPos, xcoords, ycoords, connected, uR )

    %find the sites currently in range for this unit at this point in time
    %(i.e. this value of yDrift)
    % xPos, yPos are the coordinates of the com of the unit signal in the
    % coordinates of the probe at the current time
    % xcoords and ycoords are the positions of the sites, assumed constant
    % motion of the probe should be modeled as rigid motion of the units

    %calculate distance from xPos, yPos for each site
    xDist = xcoords - xPos;
    yDist = ycoords - yPos;

    uSites = find( (xDist >= uR.minX) & (xDist <= uR.maxX) & ...
                   (yDist >= uR.minY) & (yDist <= uR.maxY) & ...
           	       (connected==1) );
%     dist_sq = (xPos - xcoords).^2 + (yPos - (ycoords + yDrift)).^2;
%     % want to exclude sites that aren't connected; just add a constant so
%     % they won't pass the distance test
%     dist_sq(find(connected==0)) = dist_sq(find(connected==0)) + 10*maxRad^2;
%     uSites = find( dist_sq < maxRad^2 );

end

function [uWav, uSites] = intWav( currF, xPos, yPos, uR, xcoords, ycoords, connected, nt )

    % figure out for which sites we need to calculate the waveform
    uSites = findSites( xPos, yPos, xcoords, ycoords, connected, uR );

    % given an array of sites on the probe, calculate the waveform using
    % the interpolant determined for this unit
    % xPos and yPos are the positions of the current unit
    % nt = number of timepoints

    currX = xcoords - xPos;
    currY = ycoords - yPos;

    nSites = length(uSites);
    xq = double(repelem(currX(uSites), nt));
    yq = double(repelem(currY(uSites), nt));
    tq = (double(repmat(1:nt, 1, nSites )))';
    %remember, y = rows in the grid, and x = columns in the grid
    %interpolation, and scattered interpolation set to match.
    nVal = numel(currF.Values);
    %tic
    uWav = currF( yq, xq, tq );
    %fprintf( '%d\t%d\t%.3f\n', numel(uSites), nVal, 1000*toc);
    uWav = (reshape(uWav', [nt,nSites]))';

end
function [uWav, uSites] = dumWav( currF, xPos, yPos, uR, xcoords, ycoords, connected, nt )

    % figure out for which sites we need to calculate the waveform
    uSites = findSites( xPos, yPos, xcoords, ycoords, connected, uR );
    uWav = zeros(length(uSites),nt);
end

function monSite = pickMonSite( currWav, uSites, connected )

    % find the connected site with the largest expected signal with no
    % drift. If all uSites are unconnected, returns an error

    %calc amplitudes for each site
    currAmp = max(currWav,[],2) - min(currWav,[],2);

    %sort amplitudes
    [sortAmp, ind] = sort(currAmp,'descend');

    monSite = -1;
    i = 1;

    while ( i < length(uSites) && monSite < 0 )
        if (connected(uSites(ind(i))))
            monSite = uSites(ind(i));
        else
            i = i + 1;
        end
    end

end

function currYPos = calcYPos_v2( t, yPos0, drift  )
%   calculate current position of a unit given the current time and
%   initial position (yPos0)

%   The pattern of tissue motion in space is set by drift.sType:
%       'rigid' -- all the units move together
%       'point' -- motion is largest at a point y0 (furthest from tip) and
%         decreases exponentially for units far from y0. Need to
%         specify y0 and the halfDistance
%
%   The pattern of tissue motion in time is set by drift.tType:
%       'exp' -- initial fast transition followed by exponential
%            decay; specify stepsize (fast transition distance); halfLife (sec)
%            and period (in sec).
%       'sine' -- specify amplitude and period
%
%   Drift parameter values for 20 um p-p, uniform sine motion, with period = 300 s:
%     drift.sType = 'rigid';        %'rigid' or 'point';
%     drift.tType = 'sine';          %'exp' or 'sine'
%
%     drift.y0 = 3800;        %in um, position along probe where motion is largest
%                             %conventially, y = 0 is the bottom of the probe
%     drift.halfDistance = 1000;     %in um
%
%     drift.amplitude = 10;         %in um. For a sine wave, the peak to
%                                    peak variation is 2Xdrift.amplitude
%     drift.halfLife = 10;          %in seconds
%     drift.period = 300;           %in seconds
%
if (drift.addDrift)
    switch drift.tType
        case 'exp'
            timeIntoCycle = t - (floor(t/drift.period))*drift.period;
            delta = drift.amplitude*exp(-timeIntoCycle/drift.halfLife);
        case 'sine'
            delta = drift.amplitude*sin(t*2*pi()/drift.period);
        otherwise
            fprintf( 'unknown parameter in drift calculation \n')
            return;
    end

    switch drift.sType
        case 'rigid'
            %delta is equal for any position
        case 'point'
            %delta falls off exponentially from y0
            delta = delta * exp( -abs(drift.y0 - yPos0)/drift.halfDistance );
        otherwise
            fprintf( 'unknown parameter in drift calculation \n')
            return;
    end
    currYPos = yPos0 + delta;
else
    currYPos = yPos0;
end

end

function eNoise = makeNoise( noiseSamp,noiseModel,chanMap,connected,NchanTOT )

    %if chanMap is a short version of a 3A probe, use the first
    %nChan good channels to generate noise, then copy that array
    %into an NT X NChanTot array

    nChan = numel(chanMap);        %number of noise channels to generate
    goodChan = sum(connected);
    tempNoise = zeros( noiseSamp, goodChan, 'single' );
    nT_fft = noiseModel.nm.nt;         %number of time points in the original time series
    fftSamp = noiseModel.nm.fft;

    noiseBatch = ceil(noiseSamp/nT_fft);
    lastWind = noiseSamp - (noiseBatch-1)*nT_fft; %in samples

    for j = 1:goodChan
            for i = 1:noiseBatch-1
                tStart = (i-1)*nT_fft+1;
                tEnd = i * nT_fft;
                tempNoise(tStart:tEnd,j) = fftnoise(fftSamp(:,j),1);
            end
            %for last batch, call one more time and truncate
            lastBatch = fftnoise(fftSamp(:,j),1);
            tStart = (noiseBatch-1)*nT_fft+1;
            tEnd = noiseSamp;
            tempNoise(tStart:tEnd,j) = lastBatch(1:lastWind);
    end

    %unwhiten this array
    Wrot = noiseModel.nm.Wrot(1:goodChan,1:goodChan);
    tempNoise_unwh = tempNoise/Wrot;

    %scale to uV; will get scaled back to bits at the end
    tempNoise_unwh = tempNoise_unwh/noiseModel.nm.bitPerUV;

    %to get the final noise array, map to an array including all channels
    eNoise = zeros(noiseSamp, NchanTOT, 'single');
    %indicies of the good channels
    goodChanIndex = find(connected);
    eNoise(:,chanMap(goodChanIndex)) = tempNoise_unwh;

end

function noise=fftnoise(f,Nseries)
% Generate noise with a given power spectrum.
% Useful helper function for Monte Carlo null-hypothesis tests and confidence interval estimation.
%
% noise=fftnoise(f[,Nseries])
%
% INPUTS:
% f: the fft of a time series (must be a column vector)
% Nseries: number of noise series to generate. (default=1)
%
% OUTPUT:
% noise: surrogate series with same power spectrum as f. (each column is a surrogate).
%
%   --- Aslak Grinsted (2009)
%
if nargin<2
    Nseries=1;
end
f=f(:);     %ensures f is a column vector
N=length(f);
Np=floor((N-1)/2);
phases=rand(Np,Nseries)*2*pi;
phases=complex(cos(phases),sin(phases)); % this was the fastest alternative in my tests.
f=repmat(f,1,Nseries);
f(2:Np+1,:)=f(2:Np+1,:).*phases;
f(end:-1:end-Np+1,:)=conj(f(2:Np+1,:));
noise=real(ifft(f,[],1));

end


function G = mrdivide(F, a)
%/   DISKFUNV right divide.
%
% F/a divides each component of a DISKFUNV F by the scalar a.
%
% Only allowed to divide by scalars.
%
% See also MLDIVIDE.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Empty check:
if ( ( isempty(F) ) || ( isempty(a) ) )
   G = diskfunv;
   return
end

% Only allow F/a, where a is a scalar:
if ( ~isa(a, 'double') )
    error('CHEBFUN:DISKFUNV:mrdivide:nonScalar', ...
        'Division must be scalar valued.');
end

% Componentwise divide.
G = F;
G.components{1} = mrdivide(F.components{1}, a);
G.components{2} = mrdivide(F.components{2}, a);

end


function [totalz, zscore, mdvs] = compareMultSamp(xglc, model, samps, measuredMetabolites)
% Compare the multiple sets of samples
%
% USAGE:
%
%    [totalz, zscore, mdvs] = compareMultSamp(xglc, model, samps, measuredMetabolites)
%
% INPUTS:
%    xglc:                   sugar distribution, a random sugar distribution is calculated if empty
%    model:                  model structure, expects `model.rxns` to contain a list of rxn names
%    samps:                  samples, expects to have a field named points containing an array of sampled points
%
% OPTIONAL INPUT:
%    measuredMetabolites:    parameter fed to `calcMDVfromSamp.m` which only calculates the MDVs for the metabolites listed in this array
%
% OUTPUTS:
%    totalz:                 sum of all zscores
%    zscore:                 calculated difference for each mdv element distributed across all the points
%    mdvs:                   contains fields:
%
%                              * mdv - the calculated mdv distribution converted from the idv
%                                solved from each point contained in their respective samples `sampX`
%                              * names - the names of the metabolites
%                              * ave - the average of each mdv element across all of the points
%                              * stdev - the standard dev for each mdv element across all points
%
% .. Author: - Wing Choi 2/11/08

if (nargin < 3)
    error '[totalz,zscore,mdvs] = compareMultSamp(xglc,model,samps,measuredMetabolites)';
end

if (nargin < 4)
    measuredMetabolites = [];
end

mdvs.ave = [];
mdvs.stdev = [];

if (isempty(xglc))
    % random glucose
    xglc = rand(64,1);
    xglc = xglc/sum(xglc);
    xglc = idv2cdv(6)*xglc;
end

    % generate the translation index array
    %   can shave time by not regenerating this array on every call.
    xltmdv = zeros(1,4096);
    for i = 1:4096
        xltmdv(i) = length(strrep(dec2base(i-1,2),'0',''));
    end

% calculate mdv for samp1 and samp2
for i = 1:length(samps)
    samp.points = samps(i).points;
    mdv = calcMDVfromSamp(model,xglc,samp,measuredMetabolites);
    l = length(mdv.ave);
    mdvs.ave(1:l,i) = mdv.ave;
    mdvs.stdev(1:l,i) = mdv.stdev;
%[mdv2] = calcMDVfromSamp(model,xglc,samp2,measuredMetabolites);
%[totalz,zscore] = compareTwoMDVs(mdv1,mdv2);
end

totalz = 0;
zscore = 0;
%mdvs = mdv;

return
end

%Here's what the function does.
%Apply slvrXXfast to each point
%for each field in the output, apply iso2mdv to get a much shorter vector.
%store all the mdv's for each point and for each metabolite in both sets.
%
%Compute the mean and standard deviation of each mdv and then get a z-score
% between them (=(mean1-mean2)/(sqrt(sd1^2+sd2^2))).
%Add up all the z-scores (their absolute value) and have this function return
% that value.
%
%Intuitively what we're doing here is comparing the two sets based on
% how different the mdv's appear.
%We're going to see if different glucose mixtures result in different values.
%I'm going to rewrite part of slvrXXfast so it doesn't return every metabolite
% but only those we can actually measure, but for now just make it generic.

function mdv = myidv2mdv (idv,xltmdv)

    % generate the mdv
    len = length(idv);
    %disp(sprintf('idv is %d long',len));
    mdv = zeros(1,xltmdv(len)+1);
    %disp(sprintf('mdv is %d long',length(mdv)));
    for i = 1:len
        idx = xltmdv(i) + 1;
        %disp(sprintf('idx is %d, currently on %d',idx,i));
        mdv(idx) = mdv(idx) + idv(i);
    end

return
end


function [stat, cfg] = ft_statistics_analytic(cfg, dat, design)

% FT_STATISTICS_ANALYTIC performs a parametric statistical test on the data, based on
% a known (i.e. analytic) distribution of the test statistic. This function should
% not be called directly, instead you should call the function that is associated
% with the type of data on which you want to perform the test.
%
% Use as
%   stat = ft_timelockstatistics(cfg, data1, data2, data3, ...)
%   stat = ft_freqstatistics    (cfg, data1, data2, data3, ...)
%   stat = ft_sourcestatistics  (cfg, data1, data2, data3, ...)
%
% where the data is obtained from FT_TIMELOCKANALYSIS, FT_FREQANALYSIS or
% FT_SOURCEANALYSIS respectively, or from FT_TIMELOCKGRANDAVERAGE,
% FT_FREQGRANDAVERAGE or FT_SOURCEGRANDAVERAGE respectively
% and with cfg.method = 'analytic'
%
% The configuration options that can be specified are:
%   cfg.statistic        = string, statistic to compute for each sample or voxel (see below)
%   cfg.correctm         = string, apply multiple-comparison correction, 'no', 'bonferroni', 'holm', 'hochberg', 'fdr' (default = 'no')
%   cfg.alpha            = number, critical value for rejecting the null-hypothesis (default = 0.05)
%   cfg.tail             = number, -1, 1 or 0 (default = 0)
%   cfg.ivar             = number or list with indices, independent variable(s)
%   cfg.uvar             = number or list with indices, unit variable(s)
%   cfg.wvar             = number or list with indices, within-block variable(s)
%
% The parametric statistic that is computed for each sample (and for
% which the analytic probability of the null-hypothesis is computed) is
% specified as
%   cfg.statistic       = 'indepsamplesT'           independent samples T-statistic,
%                         'indepsamplesF'           independent samples F-statistic,
%                         'indepsamplesregrT'       independent samples regression coefficient T-statistic,
%                         'indepsamplesZcoh'        independent samples Z-statistic for coherence,
%                         'depsamplesT'             dependent samples T-statistic,
%                         'depsamplesFmultivariate' dependent samples F-statistic MANOVA,
%                         'depsamplesregrT'         dependent samples regression coefficient T-statistic,
%                         'actvsblT'                activation versus baseline T-statistic.
% or you can specify your own low-level statistical function.
%
% See also FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS, FT_SOURCESTATISTICS
% FT_STATISTICS_MONTECARLO, FT_STATISTICS_STATS, FT_STATISTICS_MVPA,
% FT_STATISTICS_CROSSVALIDATE

% Copyright (C) 2006-2015, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% do a sanity check on the input data
assert(isnumeric(dat),    'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead');
assert(isnumeric(design), 'this function requires numeric data as input, you probably want to use FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS instead');

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'renamedval',  {'correctm', 'bonferoni', 'bonferroni'});
cfg = ft_checkconfig(cfg, 'renamedval',  {'correctm', 'holms', 'holm'});
cfg = ft_checkconfig(cfg, 'renamedval',  {'correctm', 'none', 'no'});
cfg = ft_checkconfig(cfg, 'renamedval',  {'statfun', 'depsamplesF', 'ft_statfun_depsamplesFmultivariate'});
cfg = ft_checkconfig(cfg, 'renamedval',  {'statfun', 'ft_statfun_depsamplesF', 'ft_statfun_depsamplesFmultivariate'});

% set the defaults
cfg.correctm = ft_getopt(cfg, 'correctm', 'no');
cfg.alpha    = ft_getopt(cfg, 'alpha', 0.05);
cfg.tail     = ft_getopt(cfg, 'tail', 0);

% fetch function handle to the low-level statistics function
statfun = ft_getuserfun(cfg.statistic, 'statfun');
if isempty(statfun)
  ft_error('could not locate the appropriate statistics function');
else
  ft_info('using "%s" for the single-sample statistics\n', func2str(statfun));
end

% tell the low-level statfun to compute the statistic, critical values, and the probability
cfg.computestat    = 'yes';
cfg.computecritval = 'yes';
cfg.computeprob    = 'yes';
% perform the statistical test
if nargout(statfun)>1
  [stat, cfg] = statfun(cfg, dat, design);
else
  [stat] = statfun(cfg, dat, design);
end
% these are only intenced for the low-level statfun, and should not be returned
cfg = rmfield(cfg, 'computestat');
cfg = rmfield(cfg, 'computeprob');
cfg = rmfield(cfg, 'computecritval');

if ~isfield(stat, 'prob')
  ft_warning('probability was not computed');
else
  switch lower(cfg.correctm)
    case 'bonferroni'
      ft_notice('performing Bonferoni correction for multiple comparisons\n');
      ft_notice('the returned probabilities are uncorrected, the thresholded mask is corrected\n');
      stat.mask = stat.prob<=(cfg.alpha ./ numel(stat.prob));
    case 'holm'
      % test the most significatt significance probability against alpha/N, the second largest against alpha/(N-1), etc.
      ft_notice('performing Holm-Bonferroni correction for multiple comparisons\n');
      ft_notice('the returned probabilities are uncorrected, the thresholded mask is corrected\n');
      [pvals, indx] = sort(stat.prob(:));                                   % this sorts the significance probabilities from smallest to largest
      k = find(pvals > (cfg.alpha ./ ((length(pvals):-1:1)')), 1, 'first'); % compare each significance probability against its individual threshold
      mask = (1:length(pvals))'<k;
      stat.mask = zeros(size(stat.prob));
      stat.mask(indx) = mask;
    case 'hochberg'
      % test the most significant significance probability against alpha/N, the second largest against alpha/(N-1), etc.
      ft_notice('performing Hochberg''s correction for multiple comparisons (this is *not* the Benjamini-Hochberg FDR procedure!)\n');
      ft_notice('the returned probabilities are uncorrected, the thresholded mask is corrected\n');
      [pvals, indx] = sort(stat.prob(:));                                   % this sorts the significance probabilities from smallest to largest
      k = find(pvals <= (cfg.alpha ./ ((length(pvals):-1:1)')), 1, 'last'); % compare each significance probability against its individual threshold
      mask = (1:length(pvals))'<=k;
      stat.mask = zeros(size(stat.prob));
      stat.mask(indx) = mask;
    case 'fdr'
      ft_notice('performing FDR correction for multiple comparisons\n');
      ft_notice('the returned probabilities are uncorrected, the thresholded mask is corrected\n');
      stat.mask = fdr(stat.prob, cfg.alpha);
    case 'no'
      ft_notice('not performing a correction for multiple comparisons\n');
      stat.mask = stat.prob<=cfg.alpha;
    otherwise
      ft_error('unsupported option "%s" for cfg.correctm', cfg.correctm);
  end
end


function [vs, output, v0] = C13ConfidenceInterval(v0, expdata, model, max_score, directions, majorIterationLimit)
%
%
% USAGE:
%
%    [vs, output, v0] = C13ConfidenceInterval(v0, expdata, model, max_score, directions, majorIterationLimit)
%
% INPUTS:
%    v0:                     set of flux vectors to be used as initial guesses.  They may be valid or not.
%    expdata:                experimental data
%    model:                  the standard model. Additional field .N (= null(S)) should also
%                            be provided. This is a basis of the flux space.
%    max_score:              maximum allowable data fit error
%
% OPTIONAL INPUTS:
%    directions:             ones and zeros of which reactions to compute (size = `n` x 1) or numbers of reactions to use  aka. [1; 5; 7; 8; 200] or
%                            reaction strings  aka.  {'GPK', 'PGL'}.  Ratios are possible with this input only.  Default = [] meaning  - do FVA with no ratios.
%    majorIterationLimit:    default = 10000
%
% OUTPUT:
%    vs:                     matrix
%    output:                 structure
%    v0:                     as in input

if nargin < 5
    directions = ones(size(v0,1),1);
end
if isempty(directions)
    directions = ones(size(v0,1),1);
end
t_start = clock;
printLevel = 3;
if nargin < 6
    majorIterationLimit = 1000;  %max number of iterations
end
diffInterval = 1e-5;         %gradient step size.
feasibilityTolerance = max_score/20; % how close you need to be to the max score.
logdirectory = strcat('temp', filesep);

% isratio(i):  >0 indicates not a ratio and points to reaction#
%              <0 indicates -numerator of ratio fraction.  In this case,
%              denom(i) stores the denominator
%
denom = zeros(size(directions));
if isnumeric(directions) %cannot be an actual ratio
    if max(directions) == 1
        isratio = find(directions);
    else
        isratio = directions;
    end
else % might be a ratio.  Gotta process strings
    isratio = zeros(size(directions));
    for i = 1:length(directions)
        if findstr(directions{i}, '/')
            [rxn1,rest] = strtok(directions{i}, '/');
            rxnID = findRxnIDs(model,rxn1);
            if rxnID == 0
                error('Unable to process rxn from list\n %s', directions{i});
            else
                isratio(i) = -rxnID;
            end
            rxn2 = rest(2:end);
            rxnID = findRxnIDs(model,rxn2);
            if rxnID == 0
                error('Unable to process rxn from list\n %s', rest(2:end))
            else
                denom(i) = rxnID;
            end
        else
            rxnID = findRxnIDs(model,directions{i});
            if rxnID == 0
                error('Unable to process rxn from list\n %s', directions{i});
            else
                isratio(i) = rxnID;
            end
        end
    end
end

numdirections = length(isratio);
numpoints = size(v0,2);

numiterations = numdirections*numpoints*2; % total number of iterations.

x0 = model.N\v0; % back substitute
scores = zeros(numpoints,1);

tProb.user.expdata = expdata;
tProb.user.model = model;
for i = 1:numpoints
    scores(i) = errorComputation2(x0(:,i),tProb);
end

% fit points if they are not currently feasible
v0(:,scores> max_score) = fitC13Data(v0(:,scores > max_score),expdata,model, majorIterationLimit);

if ~isfield(model, 'N')
   model.N = null(model.S);
end

x0 = model.N\v0; % back substitute

% safety check:
if (max(abs(model.S*v0))> 1e-6)
    display('v0 not quite in null space');
    pause;
end
if(max(abs(model.N*x0 - v0)) > 1e-6)
    display('null basis is weird');
    pause;
end

Name = 't2';
nalpha = size(model.N, 2);

x_L = -1000*ones(nalpha,1);
x_U = 1000*ones(nalpha,1);
[A, b_L, b_U] = defineLinearConstraints(model);

scores = zeros(numpoints,1);
% compute scores for all points.
for i = 1:numpoints
    scores(i) = errorComputation2(x0(:,i),tProb);
end
valid_index = scores < max_score + feasibilityTolerance;
fprintf('found %d valid points\n', sum(valid_index));

x0_valid = x0(:,valid_index);
x0_invalid = x0;
scores_valid = scores(valid_index);

% pre-compute unnecesary directions.
% if checkedbefore(i) ~= 0 then direction i is redundant
% if checkedbefore(i) = j then direction i and j are identical and do not
% need to be recomputed.
% checkedbefore(i) = j < 0 means that direction j is the same as i except
% for a sign switch.

checkedbefore = zeros(length(isratio),1);
for i = 2:length(isratio)
    if(isratio(i) < 0) % meaning it actually IS a ratio and no simplification possible
        continue
    end
    d = objective_coefficient(isratio(i), model);
    for j = 1:i-1
        if(isratio(j) < 0) % meaning it actually IS a ratio and no simplification possible
            continue
        end
        dj = objective_coefficient(isratio(j), model);
        if max(abs(dj - d))< 1e-4
            checkedbefore(i) = j;
            break;
        elseif max(abs(dj + d))< 1e-4
            checkedbefore(i) = -j;
            break;
        end
    end
end

% initialize variables;
outputv = 222*ones(numiterations,1);
outputexitflag = -222*ones(numiterations,1);
outputfinalscore = -222*ones(numiterations,1);
outputstruct = cell(numiterations,1);

csense = '';
for mm = 1:length(b_L),csense(mm,1) = 'L';end
for mm = 1:length(b_L),csense(mm+length(b_L),1) = 'G';end

fLowBnds = zeros(length(isratio), 2); %initialize but fill in later.
for rxn = 1:length(isratio)
    for direction = -1:2:1
        if isratio(rxn) < 0
            ration = objective_coefficient(-isratio(rxn),model);
            ratiod = objective_coefficient(denom(rxn),model);

            % in case RXN is a ratio
            Result = solveCobraNLP(...
                struct('lb', x_L, 'ub', x_U,...
                'name', Name,...
                'A', A,...
                'b_L', b_L, 'b_U', b_U,...
                'objFunction', 'ratioScore', 'g', 'ratioScore_grad',...
                'userParams', struct(...
                     'ration', direction*ration, 'ratiod', ratiod,... % set direction here too.
                     'diff_interval', diffInterval,'useparfor', true)...
                ),...
                'printLevel', 1, ...
                'iterationLimit', 1000);
        else
            d = objective_coefficient(isratio(rxn),model);
            Result = solveCobraLP(...
             struct('A', [A;A],'b',[b_U;b_L],'csense', csense, ...
                    'c', direction*d, ...
                    'lb', x_L,'ub', x_U, ...
                    'osense', 1),...
                    'feasTol',1e-7,'optTol',1e-7);
            if Result.stat ~= 1
                Result
                pause
            end
        end
        fLowBnds(rxn, (direction+3)/2) = direction*Result.obj; % fill in
    end
end

if ~exist (logdirectory, 'dir')
    if ~mkdir(logdirectory)
        error('unable to create logdirectory');
    end
end
clear d
%iterate through directions
parfor itnum = 1:numiterations
    if exist('ttt.txt', 'file') % abort w/o crashing if file 'ttt.txt' found in current directory
        fprintf('quitting due to file found\n');
        continue;
    end
    [rxn, direction, point] = getValues(itnum, numpoints); % translate itnum to rxn, direction and point
    % direction == 1 means minimize.  direction == -1 means maximize
    % (opposite of what you might think.

    if checkedbefore(rxn) ~= 0 %if this reaction maps to a previous reaction, we can skip
        continue;
    end

    fLowBnd = fLowBnds(rxn, (direction+3)/2); %get the absolute bound in the space w/o regards to C13 constraints.
    fprintf('reaction %d of %d, direction %d, lowerbound %f point %d of %d\n', rxn ,length(isratio), direction, fLowBnd, point, numpoints);
     % short circuit if x0 already close to a bound.
    if isratio(rxn) > 0
        di = objective_coefficient(isratio(rxn),model);
        obj1 = di'*x0_valid;
    else
        rationi = objective_coefficient(-isratio(rxn),model);
        ratiodi = objective_coefficient(denom(rxn),model);
        obj1 = (rationi'*x0_valid) ./ (ratiodi'*x0_valid);
    end

    if(any(abs(obj1-fLowBnd)<.0001))
        display('short circuiting');
        [nil, min_index] = min(obj1);
        outputv(itnum,1) = fLowBnd; % multiply by direction to correct sign.
        outputexitflag(itnum,1) = 111;
        outputfinalscore(itnum,1) = scores_valid(min_index);

    else % gotta actually do the computation.
        xinitial = x0_invalid(:,point);
        if isratio(rxn) > 0
            NLPsolution = solveCobraNLP(...
                struct('x0', xinitial, ...
                'lb', x_L, 'ub', x_U,...
                'name', Name,...
                'A', A, 'b_L', b_L, 'b_U', b_U,...
                'd', 'errorComputation2', 'dd', 'errorComputation2_grad',...
                'd_L', 0, 'd_U', max_score,...
                'c', di*direction, ... % direction of optimization
                'userParams', struct(...
                     'expdata', expdata,'model', model,'max_error', max_score,...
                     'diff_interval', diffInterval,'useparfor', true)...
                ),...
                'printLevel', printLevel, ...
                ...%'intTol', 1e-7, ...
                'iterationLimit', majorIterationLimit, ...
                'logFile', strcat(logdirectory, 'ci_', num2str(rxn),'x',num2str(direction),'x', point, '.txt'));
        else
            NLPsolution = solveCobraNLP(...
                struct('x0', xinitial, ...
                'lb', x_L, 'ub', x_U,...
                'name', Name,...
                'A', A, 'b_L', b_L, 'b_U', b_U,...
                'd', 'errorComputation2', 'dd', 'errorComputation2_grad',...
                'd_L', 0, 'd_U', max_score,...
                'objFunction', 'ratioScore', 'g', 'ratioScore_grad',...
                'userParams', struct(...
                     'expdata', expdata,'model', model,'max_error', max_score,...
                     'ration', direction*rationi,...
                     'ratiod', ratiodi,...
                     'diff_interval', diffInterval,'useparfor', false)...
                ),...
                'printLevel', printLevel, ...
                ...%'intTol', 1e-7, ...
                'iterationLimit', majorIterationLimit, ...
                'logFile', strcat(logdirectory, 'ci_', num2str(rxn),'x',num2str(direction),'x', point, '.txt'));
        end

        tscore = errorComputation2(NLPsolution.full, tProb);
        tbest = NLPsolution.obj;

        fprintf('reaction %d (%d), x %d; x=%f (%f); score=%f (%f)\n', rxn, length(isratio),direction, tbest,fLowBnd, tscore, max_score)

        outputv(itnum,1) = direction*tbest; % multiply by direction to correct sign.
        outputexitflag(itnum,1) = NLPsolution.origStat;
        outputfinalscore(itnum,1) = tscore;
        outputstruct{itnum,1} = NLPsolution;
    end
end

for itnum = 1:numiterations
    [rxn, direction, point] = getValues(itnum,numpoints);
    if direction == 1;
        output.minv(rxn,point) = outputv(itnum);
        output.minexitflag(rxn,point) = outputexitflag(itnum);
        output.minfinalscore(rxn,point) = outputfinalscore(itnum);
        output.minstruct(rxn,point) = outputstruct(itnum);
    else
        output.maxv(rxn,point) = outputv(itnum);
        output.maxexitflag(rxn,point) = outputexitflag(itnum);
        output.maxfinalscore(rxn,point) = outputfinalscore(itnum);
        output.maxstruct(rxn,point) = outputstruct(itnum);
    end
end

for i = 1:length(isratio)
    if checkedbefore(i) > 0 %short circuit if seen before.
        output.minv(i) = output.minv(checkedbefore(i));
        output.maxv(i) = output.maxv(checkedbefore(i));
        output.minexitflag(i) = output.minexitflag(checkedbefore(i));
        output.maxexitflag(i) = output.maxexitflag(checkedbefore(i));
        output.minfinalscore(i) = output.minfinalscore(checkedbefore(i));
        output.maxfinalscore(i) = output.maxfinalscore(checkedbefore(i));
        output.minstruct(i) = output.minstruct(checkedbefore(i));
        output.maxstruct(i) = output.maxstruct(checkedbefore(i));
    elseif checkedbefore(i) < 0
        output.minv(i) = -output.maxv(-checkedbefore(i));
        output.maxv(i) = -output.minv(-checkedbefore(i));
        output.minexitflag(i) = output.maxexitflag(-checkedbefore(i));
        output.maxexitflag(i) = output.minexitflag(-checkedbefore(i));
        output.minfinalscore(i) = output.maxfinalscore(-checkedbefore(i));
        output.maxfinalscore(i) = output.minfinalscore(-checkedbefore(i));
        output.minstruct(i) = output.maxstruct(-checkedbefore(i));
        output.maxstruct(i) = output.minstruct(-checkedbefore(i));
    end
end

vs = zeros(length(isratio), 2);
for i = 1:length(isratio)
    validindex = output.minfinalscore(i,:) < max_score + feasibilityTolerance;
    if any(validindex)
        vs(i,1) = min(output.minv(i,validindex));
    else
        vs(i,1) = 222;
    end
    validindex = output.maxfinalscore(i,:) < max_score + feasibilityTolerance;
    if any(validindex)
        vs(i,2) = max(output.maxv(i,validindex));
    else
        vs(i,2) = -222;
    end
end

elapsed_time = etime(clock, t_start)
return;

% function [index] = getIndex(rxn, direction, point, numpoints)
% % point goes from 1 .. NUMPOINTS
% % rxn goes from 1 .. NUMRXNS
% % direction is -1 or 1
% % index goes from 1 to NUMPOINTS*NUMRXNS*2
%
% rxn = rxn - 1;
% point = point -1;
% direction = (direction + 1)/2;
%
%
% index = rxn*numpoints*2 + direction*numpoints + point;
% index = index+1;
%
% return;

function [rxn, direction, point] = getValues(index, numpoints)
% point goes from 1 .. NUMPOINTS
% rxn goes from 1 .. NUMRXNS
% direction is -1 or 1
% index goes from 1 to NUMPOINTS*NUMRXNS*2
index = index - 1;
point = mod(index, numpoints);
index = index - point;
index = index/numpoints;
direction = mod(index,2);
index = index - direction;
index = index/2;
rxn = index;

point = point +1; % remap to 1..NUMPOINTS
direction = direction*2-1; % remap to -1,1
rxn = rxn+1; % remap to 1 .. number of rxns;
return;

% function that returns the proper objective coefficient for each reaction
% takes into account the reversibility of reactinos etc.
function [d] =  objective_coefficient(i, model)
d = zeros(length(model.lb),1);
d(i) = 1;
if (model.match(i))
    d(model.match(i)) = -1;
end
d = (d'*model.N)'; % transform to null space;
return


function so = scaleStream(si,factor)
% SCALESTREAM  Scale a stream (of numerics)
so = {head(si)*factor,delayEval(@scaleStream,{tail(si),factor})};


function plotPlanes(planes, figureName)
%% Plot 3d planes given plane structure
% See also: Calibration.planeStructFromPoints

%% License
% ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY
% Copyright (c) 2018 Bingyao Huang
% All rights reserved.

% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:

% The above copyright notice and this permission notice shall be included in all
% copies or substantial portions of the Software.

% If you publish results obtained using this software, please cite our paper.

% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
% SOFTWARE.

%%

nPlanes = length(planes);

% each plane has a different color
planeColors = hsv(nPlanes);

if(nargin > 1)
    if(isempty(figureName))
        error('Figure name is empty');
    end

    if(~isa(figureName, 'char'))
        error('Figure name should be a string');
    end

    % find figure by name
    figHandle = findobj( 'Type', 'Figure', 'Name', figureName );

    if(length(figHandle) > 1)
        warning(['More than 1 figures named', figureName, 'are found, use the 1st figure']);
    end

    if(length(figHandle) < 1)
        warning(['No figure named', figureName, 'is found, create one instead']);
        figHandle = figure('name', figureName);
    end

    figure(figHandle(1));
end

% plot
for i = 1:nPlanes
    corners = planes(i).corners;
    p = patch(corners(:,1), corners(:,2), corners(:,3), planeColors(i,:));
    p.FaceAlpha = 0.3;
end

end


classdef DehomogenisationPrinter < handle

    properties (Access = private)
        mesh
        dataRes
        alpha
        density
    end

    properties (Access = private)
        fileName
        folderPath
    end

    methods (Access = public)

        function obj = DehomogenisationPrinter(cParams)
            obj.init(cParams);
        end

        function print(obj)
            obj.wrapResMeshData();
            obj.projectAlphaToNodes();
            obj.projectDensityToNodes();
            obj.printDehomogResFile();
            obj.printDehomogMshFile();
        end

    end

    methods (Access = private)

        function init(obj,cParams)
            obj.fileName   = cParams.fileName;
            obj.folderPath = cParams.folderPath;
        end

        function wrapResMeshData(obj)
            s.fileName   = obj.fileName;
            s.folderPath = obj.folderPath;
            w = WrapperMshResFiles(s);
            w.compute();
            obj.dataRes = w.dataRes;
            obj.mesh    = w.mesh;
        end

        function projectAlphaToNodes(obj)
            alphaN = obj.dataRes.AlphaGauss;
            alpha1 = obj.projectInNodesScalarPieceWiseFunction(alphaN(:,1));
            alpha2 = obj.projectInNodesScalarPieceWiseFunction(alphaN(:,2));
            normA = sqrt(alpha1.^2 + alpha2.^2);
            obj.alpha = [alpha1,alpha2]./normA;
        end

        function projectDensityToNodes(obj)
            alphaN = obj.dataRes.DensityGauss;
            dens   = obj.projectInNodesScalarPieceWiseFunction(alphaN(:,1));
            obj.density = dens;
        end

        function alpha = projectInNodesScalarPieceWiseFunction(obj,fValues)
            s.mesh   = obj.mesh;
            s.fValues = fValues;
            f = P0Function(s);
            alpha = f.projectToLinearNodalFunction();
        end

        function printDehomogResFile(obj)
            fD = [obj.fileName,'DehomogRes.txt'];
            fOutName = fullfile(obj.folderPath,fD);
            s.fileName = fOutName;
            s.values(:,1) = obj.dataRes.DesignVar1;
            s.values(:,2) = obj.dataRes.DesignVar2;
            s.values(:,3) = obj.alpha(:,1);
            s.values(:,4) = obj.alpha(:,2);
            s.values(:,5) = obj.dataRes.SuperEllipseExponent;
            s.values(:,6) = obj.density;
            p = DehomogenisationOutputPrinter(s);
            p.print();
        end

        function printDehomogMshFile(obj)
            fD = [obj.fileName,'DehomogMesh.txt'];
            fOutName = fullfile(obj.folderPath,fD);
            s.fileName = fOutName;
            s.mesh     = obj.mesh;
            p = DehomogenisationMeshPrinter(s);
            p.print();
        end

    end

end


function DEMr = resample(DEM,target,method,swapzone,varargin)

%RESAMPLE change spatial resolution of a GRIDobj
%
% Syntax
%
%     DEMr = resample(DEM,cellsize)
%     DEMr = resample(DEM,GRID)
%     DEMr = resample(...,method)
%     DEMr = resample(DEM,GRID,method,swapzone)
%
% Description
%
%     resample changes the cellsize of a grid. The function uses the MATLAB
%     function imtransform. If an instance of GRIDobj is supplied as
%     second argument, resample interpolates values in DEM to match the
%     spatial reference of GRID.
%
% Input arguments
%
%     DEM       grid object (GRIDobj)
%     cellsize  cellsize of resampled grid
%     GRID      other grid object
%     method    'bicubic', 'bilinear', or 'nearest'
%     swapzone  true or false. If true and if DEM and GRID have different
%               projected coordinate systems, the function will attempt to
%               reproject and resample the DEM in one step. Note that this
%               requires the mapping toolbox. In case, the DEM is in a
%               geographic coordinate system, please use the function
%               reproject2utm(DEM,GRID).
%
% Output arguments
%
%     DEMr    grid object (GRIDobj)
%
% Example
%
%     DEM = GRIDobj('srtm_bigtujunga30m_utm11.tif');
%     DEMr = resample(DEM,100);
%     imagesc(DEMr)
%
%
% See also: griddedInterpolant, imtransform
%
% Author:  Wolfgang Schwanghart (w.schwanghart[at]geo.uni-potsdam.de)
% Date: 8. August, 2015

% check input arguments
% narginchk(2,6)
validateattributes(target,{'double' 'GRIDobj'},{'scalar'})
if nargin == 2;
    method = 'bilinear';
    swapzone = false;
elseif nargin == 3;
    method = validatestring(method,{'bicubic', 'bilinear', 'nearest' });
    swapzone = false;
else
    method = validatestring(method,{'bicubic', 'bilinear', 'nearest' });
end

% check underlying class
if islogical(DEM.Z)
    method = 'nearest';
end

if swapzone && isa(target,'GRIDobj');
    if ~isequal(DEM.georef.GeoKeyDirectoryTag.ProjectedCSTypeGeoKey,...
            target.georef.GeoKeyDirectoryTag.ProjectedCSTypeGeoKey)
        warning('UTM zones differ. Will attempt to match.');
        swapzone = true;
    else
        swapzone = false;
    end
end

% get coordinate vectors
[u,v] = getcoordinates(DEM);

% tform
T = maketform('affine',[1 0 0; 0 1 0; 0 0 1]);

% Fillvalues

if isinteger(DEM.Z)
    fillval = 0;
elseif islogical(DEM.Z)
    fillval = 0;
else
    fillval = nan;
end

p = inputParser;
p.FunctionName = 'Paras';
% optional
% default values.
addParameter(p,'fillval',fillval);
parse(p,varargin{:});
fillval = p.Results.fillval;

if isa(target,'GRIDobj')
    % the target is another GRIDobj

    % check whether both grids have the same projection
    if swapzone
        mstructsource = DEM.georef.mstruct;
        mstructtarget = target.georef.mstruct;
        T = maketform('custom', 2, 2, ...
                @FWDTRANS, ...
                @INVTRANS, ...
                []);
    end

    DEMr    = target;
    [xn,yn] = getcoordinates(DEMr);

    DEMr.Z = imtransform(DEM.Z,T,method,...
        'Udata',[u(1) u(end)],'Vdata',[v(1) v(end)],...
        'Xdata',[xn(1) xn(end)],'Ydata',[yn(1) yn(end)],...
        'Size',DEMr.size,...
        'FillValues',fillval);
    DEMr.name = [DEM.name ' (resampled)'];

else
    csnew   = target;
    DEMr    = GRIDobj([]);
    [DEMr.Z,xn,yn] = imtransform(DEM.Z,T,method,...
        'Udata',[u(1) u(end)],'Vdata',[v(1) v(end)],...
        'Xdata',[u(1) u(end)],'Ydata',[v(1) v(end)],...
        'XYscale',[csnew csnew],...
        'FillValues',fillval);

    % new referencing matrix
    DEMr.refmat = [0 -csnew;...
                   csnew 0; ...
                   xn(1)-csnew ...
                   yn(1)+csnew];
    % size of the resampled grid
    DEMr.size    = size(DEMr.Z);
    DEMr.cellsize = csnew;
    DEMr.georef = DEM.georef;
end

DEMr.name    = [DEM.name ' (resampled)'];

if ~isempty(DEMr.georef) && ~isa(target,'GRIDobj');
    DEMr.georef.RefMatrix = DEMr.refmat;
    DEMr.georef.Height = DEMr.size(1);
    DEMr.georef.Width  = DEMr.size(2);

    DEMr.georef.SpatialRef = refmatToMapRasterReference(DEMr.refmat, DEMr.size);

end

%%

    function x = FWDTRANS(u,~)
        % invtrans first
        [lati,long] = minvtran(mstructsource,u(:,1),u(:,2));
        [x,y] = mfwdtran(mstructtarget,lati,long);
        x = [x y];
    end

    function u = INVTRANS(x,~)
        [lati,long] = minvtran(mstructtarget,x(:,1),x(:,2));
        [x,y] = mfwdtran(mstructsource,lati,long);
        u = [x y];

    end
end


function [ temp ] = SAH(temp, vitalslab_hold)

% Matthieu Komorowski - Imperial College London 2017
% will copy a value in the rows below if the missing values are within the
% hold period for this variable (e.g. 48h for weight, 2h for HR...)
% vitalslab_hold = 2x55 cell (with row1 = strings of names ; row 2 = hold time)

h = waitbar(0,'Initializing waitbar...');

hold=table2array(cell2table(vitalslab_hold(2,:)));
nrow=size(temp,1);
ncol=size(temp,2);

lastcharttime=zeros(1,ncol);
lastvalue=zeros(1,ncol);
oldstayid=temp(1,2);

for i=4:ncol
     waitbar(i/ncol,h,i/ncol*100)

    for j=1:nrow

        if oldstayid~=temp(j,2)
            lastcharttime=zeros(1,ncol);
            lastvalue=zeros(1,ncol);
            oldstayid=temp(j,2);
        end

        if isnan(temp(j,i))==0
            lastcharttime(i)=temp(j,3);
            lastvalue(i)=temp(j,i);
        end

        if j>1
        if isnan(temp(j,i)) &&  temp(j,2)==oldstayid && (temp(j,3)-lastcharttime(i))<=hold(i-3)*3600 %note : hold has 53 cols, temp has 55
            temp(j,i)=lastvalue(i);
        end

        end
    end
end

close(h);

end


function h = GB_plotboneVRML_New(bone_model,RT,faceColor,lightsOn,parentName,faceAlpha)

bone_conn1 = bone_model.conn;
bone_pts   = bone_model.pts;

if ~exist('lightsOn','var')
    lightsOn = 1;
end

if ~exist('faceColor','var')
    faceColor =  [0.9882 0.9490 0.9020];%[0.2 0.5 0.2];
end
if ~exist('RT','var')
    RT = eye(4,4);
end
if ~exist('faceAlpha','var')
    faceAlpha = 0.4;
end

new_pts = (RT*[bone_pts ones(length(bone_pts),1)]')';

bone_pts = new_pts;

% % % Check Connections
% % if min(min(bone_conn1(1,1:3))) == 1
% %     bone_conn1(:,1:3) = bone_conn1(:,1:3)-1;
% % end

if ~exist('parentName','var')
    h = trisurf(bone_conn1(:,1:3)+1, bone_pts(:,1),bone_pts(:,2),bone_pts(:,3),...
        'EdgeColor','none','LineStyle','none','FaceLighting','phong','FaceColor',faceColor,'FaceAlpha',faceAlpha); hold on
    if lightsOn ~= 0
        GB_giveMeSomeLights(lightsOn);
    end

else
    hold(parentName,'on');
    h = trisurf(bone_conn1(:,1:3)+1, bone_pts(:,1),bone_pts(:,2),bone_pts(:,3),...
        'Parent', parentName,'EdgeColor','none','LineStyle','none','FaceLighting','phong','FaceColor',faceColor,'FaceAlpha',faceAlpha);
    if lightsOn ~= 0
        GB_giveMeSomeLights(lightsOn,parentName);
    end

end


function [ImTrans,tau,ImTrans_color] = align_c(I2,I1,tau,iter)

c=size(I2,3);
ImTrans = I2;
if nargin<3
  tau = zeros(6,1);
end
if nargin<4
  iter = 5;
end

numLevel = fix(log(size(I2,1)*size(I2,2))/log(2)/2-3)-1;

% I1=mean(Imref,3);
% I2=mean(Im,3);
% I2=luminance_transfer(I1,I2);
[ImTrans,ImTrans_color,tau] = regMGNC_c(I1,I2,tau,numLevel,iter);

end


function CPD = set_fields(CPD, varargin)
% SET_PARAMS Set the parameters (fields) for a gaussian_CPD object
% CPD = set_params(CPD, name/value pairs)
%
% The following optional arguments can be specified in the form of name/value pairs:
%
% mean       - mu(:,i) is the mean given Q=i
% cov        - Sigma(:,:,i) is the covariance given Q=i
% weights    - W(:,:,i) is the regression matrix given Q=i
% cov_type   - if 'diag', Sigma(:,:,i) is diagonal
% tied_cov   - if 1, we constrain Sigma(:,:,i) to be the same for all i
% clamp_mean - if 1, we do not adjust mu(:,i) during learning
% clamp_cov  - if 1, we do not adjust Sigma(:,:,i) during learning
% clamp_weights - if 1, we do not adjust W(:,:,i) during learning
% clamp      - if 1, we do not adjust any params
% cov_prior_weight - weight given to I prior for estimating Sigma
% cov_prior_entropic - if 1, we also use an entropic prior for Sigma [0]
%
% e.g., CPD = set_params(CPD, 'mean', [0;0])

args = varargin;
nargs = length(args);
for i=1:2:nargs
  switch args{i},
   case 'mean',        CPD.mean = args{i+1};
   case 'cov',         CPD.cov = args{i+1};
   case 'weights',     CPD.weights = args{i+1};
   case 'cov_type',    CPD.cov_type = args{i+1};
   %case 'tied_cov',    CPD.tied_cov = strcmp(args{i+1}, 'yes');
   case 'tied_cov',    CPD.tied_cov = args{i+1};
   case 'clamp_mean',  CPD.clamped_mean = args{i+1};
   case 'clamp_cov',   CPD.clamped_cov = args{i+1};
   case 'clamp_weights',  CPD.clamped_weights = args{i+1};
   case 'clamp',  clamp = args{i+1};
    CPD.clamped_mean = clamp;
    CPD.clamped_cov = clamp;
    CPD.clamped_weights = clamp;
   case 'cov_prior_weight',  CPD.cov_prior_weight = args{i+1};
   case 'cov_prior_entropic',  CPD.cov_prior_entropic = args{i+1};
   otherwise,
    error(['invalid argument name ' args{i}]);
  end
end


function out=mp_log2(precision)

if nargin==0
 out=mpLog2(mp(0));
else
 out=mpLog2(mp(precision));
end


function [cum_ret, cumprod_ret, daily_ret, daily_portfolio] ...
    = bcrp(fid, data, varargins, opts)
% This file is an entry for the bcrp strategy.
%
% function [cum_ret, cumprod_ret, daily_ret, daily_portfolio] ...
%           = bcrp_start(fid, data, varargins, opts)
%
% cum_ret: cumulative wealth achived at the end of a period.
% cumprod_ret: cumulative wealth achieved till the end each period.
% daily_ret: daily return achieved by a strategy.
%
% data: market sequence vectors
% fid: handle for write log file
% varargins: variable parameters
% opts: option parameter for behvaioral control
%
% Example: [cum_ret, cumprod_ret, daily_ret, daily_portfolio] ...
%          = bcrp_start(fid, data, {0}, opts);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This file is part of OLPS: http://OLPS.stevenhoi.org/
% Original authors: Bin LI, Steven C.H. Hoi
% Contributors:
% Change log:
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Extract the parameters
tc = varargins{1};      % transaction cost fee rate

% Run the bcrp simulation
[cum_ret, cumprod_ret, daily_ret, daily_portfolio]...
    = bcrp_run(fid, data, tc, opts);

end


function [ ] = display_graph(x_category, y_category, algorithm_list, w_list, info_list, scale, line, width, xlim_range_in, ylim_range_in)
% SHow graphs of optimizations
%
% Inputs:
%       x_category          "numofgrad" or "iter" or "epoch" or "grad_calc_count"
%       y_category          "cost" or "optimality_gap" or "gnorm" or "subgnorm"
%       algorithms_list     algorithms to be evaluated
%       w_list              solution produced by each algorithm
%       info_list           statistics produced by each algorithm
%
%
% This file is part of GDLibrary and SGDLibrary.
%
% Created by H.Kasai on Oct. 23, 2016
% Modified by H.Kasai on Apr. 24, 2017

    if nargin < 6
        scale_type = 'semilogy';
    else
        scale_type = scale;
    end

    if nargin < 7
        line_type = 'line';
    else
        line_type = line;
    end

    if nargin < 8
        linewidth = 2;
    else
        linewidth = width;
    end

    if nargin < 9
        xlim_range = [];
    else
        xlim_range = xlim_range_in;
    end

    if nargin < 10
        ylim_range = [];
    else
        ylim_range = ylim_range_in;
    end

    % for plotting
    if strcmp(line_type, 'line')
        linetype = {'r','b','m','g','c','y','r--','b--','c--','g--','m--','y--','r:','b:','c:','g:','m:','y:','r.','b.','c.','g.','m.','y.'};
    elseif strcmp(line_type, 'line-with-mark')
        linetype = {'ro-','bo-','mo-','go-','co-','yo-','r*-','b*-','m*-','g*-','c*-','y*-','r+--','b+--','m+--','g+--','c+--','y+--','rs:','bs:','ms:','gs:','cs:','ys:','r.','b.','c.','g.','m.','y.'};
    else
    end
    fontsize = 16;
    markersize = 5;

    % initialize
    legend_str = cell(1);
    alg_num = 0;

    % plot
    figure;
    for alg_idx=1:length(algorithm_list)
        if ~isempty(info_list{alg_idx})
            alg_num = alg_num + 1;

            if strcmp(x_category, 'numofgrad')
                x_plot_data = info_list{alg_idx}.grad_calc_count;
            elseif strcmp(x_category, 'iter')
                if isfield(info_list{alg_idx}, 'iter')
                    x_plot_data = info_list{alg_idx}.iter;
                elseif isfield(info_list{alg_idx}, 'total_iter')
                    x_plot_data = info_list{alg_idx}.total_iter;
                end
            elseif strcmp(x_category, 'epoch')
                x_plot_data = info_list{alg_idx}.epoch;
            elseif strcmp(x_category, 'inner_iter')
                x_plot_data = info_list{alg_idx}.subinfos.inner_iter;
            elseif strcmp(x_category, 'grad_calc_count')
                x_plot_data = info_list{alg_idx}.grad_calc_count;
            elseif strcmp(x_category, 'time')
                x_plot_data = info_list{alg_idx}.time;
            elseif strcmp(x_category, 'lambda') || strcmp(x_category, 'l1-norm')
                x_plot_data = w_list;
            elseif strcmp(x_category, 'coeff_pos')
                x_plot_data = [1:length(w_list{alg_idx})];
            else
            end

            if strcmp(y_category, 'cost')
                y_plot_data = info_list{alg_idx}.cost;
            elseif strcmp(y_category, 'best_cost')
                y_plot_data = info_list{alg_idx}.best_cost;
            elseif strcmp(y_category, 'cost_lag')
                y_plot_data = info_list{alg_idx}.cost_lag;
            elseif strcmp(y_category, 'optimality_gap')
                y_plot_data = info_list{alg_idx}.optgap;
            elseif strcmp(y_category, 'abs_optimality_gap')
                y_plot_data = info_list{alg_idx}.absoptgap;
            elseif strcmp(y_category, 'best_optimality_gap')
                y_plot_data = info_list{alg_idx}.best_optgap;
            elseif strcmp(y_category, 'sol_optimality_gap')
                y_plot_data = info_list{alg_idx}.sol_optgap;
            elseif strcmp(y_category, 'dual_gap')
                y_plot_data = info_list{alg_idx}.dual_gap;
            elseif strcmp(y_category, 'const_norm')
                y_plot_data = info_list{alg_idx}.const_norm;
            elseif strcmp(y_category, 'econst_norm')
                y_plot_data = info_list{alg_idx}.econst_norm;
            elseif strcmp(y_category, 'ineconst_norm')
                y_plot_data = info_list{alg_idx}.ineconst_norm;
            elseif strcmp(y_category, 'inv_rho')
                y_plot_data = info_list{alg_idx}.inv_rho;
            elseif strcmp(y_category, 'eta')
                y_plot_data = info_list{alg_idx}.eta;
            elseif strcmp(y_category, 'gradL_norm')
                y_plot_data = info_list{alg_idx}.gradL_norm;
            elseif strcmp(y_category, 'gnorm')
                y_plot_data = info_list{alg_idx}.gnorm;
            elseif strcmp(y_category, 'subgnorm')
                y_plot_data = info_list{alg_idx}.subgnorm;
            elseif strcmp(y_category, 'K')
                y_plot_data = info_list{alg_idx}.K;
            elseif strcmp(y_category, 'reg') || strcmp(y_category, 'l1-norm') || strcmp(y_category, 'trace_norm')
                y_plot_data = info_list{alg_idx}.reg;
            elseif strcmp(y_category, 'coeffs') || strcmp(y_category, 'aprox_err')
                y_plot_data = info_list{1};
            elseif strcmp(y_category, 'coeff_amp') || strcmp(y_category, 'aprox_err')
                y_plot_data = info_list{alg_idx};
            elseif strcmp(y_category, 'dnorm')
                y_plot_data = info_list{alg_idx}.subinfos.dnorm;
            end

            if strcmp(scale_type, 'semilogy')
                semilogy(x_plot_data, y_plot_data, linetype{alg_num}, 'MarkerSize', markersize, 'Linewidth', linewidth); hold on;
            elseif strcmp(scale_type, 'loglog')
                loglog(x_plot_data, y_plot_data, linetype{alg_num}, 'MarkerSize', markersize, 'Linewidth', linewidth); hold on;
            elseif strcmp(scale_type, 'linear')
                if strcmp(y_category, 'coeffs')
                    plot(x_plot_data, y_plot_data, 'Linewidth', linewidth); hold on;
                else
                    plot(x_plot_data, y_plot_data, linetype{alg_num}, 'MarkerSize', markersize, 'Linewidth', linewidth); hold on;
                end
            else
                error('Invalid scale type');
            end

            if ~strcmp(y_category, 'coeff')
                legend_str{alg_num} = algorithm_list{alg_idx};
            end
        else
            %
        end
    end
    hold off;

    % X label
    if strcmp(x_category, 'numofgrad')
        xlabel('Number of gradient evaluations', 'FontSize', fontsize);
    elseif strcmp(x_category, 'iter')
        xlabel('Iteration', 'FontSize', fontsize);
    elseif strcmp(x_category, 'epoch')
        xlabel('Epoch', 'FontSize', fontsize);
    elseif strcmp(x_category, 'grad_calc_count')
        xlabel('Number of gradient calculations', 'FontSize', fontsize);
    elseif strcmp(x_category, 'time')
        xlabel('Time', 'FontSize', fontsize);
    elseif strcmp(x_category, 'lambda')
        xlabel('$$\lambda$$', 'FontSize', fontsize,'Interpreter', 'Latex');
    elseif strcmp(x_category, 'l1-norm')
        xlabel('$$\ell$$-1 norm', 'FontSize', fontsize,'Interpreter', 'Latex');
    elseif strcmp(x_category, 'coeff_pos')
        xlabel('Coefficient position', 'FontSize', fontsize);
    elseif strcmp(x_category, 'inner_iter')
        xlabel('Inner Iteration', 'FontSize', fontsize);
    else
    end

    % Y label
    if strcmp(y_category, 'cost')
        ylabel('Cost', 'FontSize', fontsize);
    elseif strcmp(y_category, 'best_cost')
        ylabel('Best cost', 'FontSize', fontsize);
    elseif strcmp(y_category, 'cost_lag')
        ylabel('Lagrangian cost', 'FontSize', fontsize);
    elseif strcmp(y_category, 'optimality_gap')
        ylabel('Optimality gap', 'FontSize', fontsize);
    elseif strcmp(y_category, 'abs_optimality_gap')
        ylabel('Absolute optimality gap', 'FontSize', fontsize);
    elseif strcmp(y_category, 'best_optimality_gap')
        ylabel('Best optimality gap', 'FontSize', fontsize);
    elseif strcmp(y_category, 'sol_optimality_gap')
        ylabel('Solution optimality gap', 'FontSize', fontsize);
    elseif strcmp(y_category, 'dual_gap')
        ylabel('Dual gap', 'FontSize', fontsize);
    elseif strcmp(y_category, 'const_norm')
        ylabel('Norm of constraints', 'FontSize', fontsize);
    elseif strcmp(y_category, 'econst_norm')
        ylabel('Norm of equality constraints', 'FontSize', fontsize);
    elseif strcmp(y_category, 'ineconst_norm')
        ylabel('Norm of inequality constraints', 'FontSize', fontsize);
    elseif strcmp(y_category, 'gradL_norm')
        ylabel('Norm of gradient of Lagrangian', 'FontSize', fontsize);
    elseif strcmp(y_category, 'inv_rho')
        ylabel('Inverse of \rho', 'FontSize', fontsize);
    elseif strcmp(y_category, 'eta')
        ylabel('\eta', 'FontSize', fontsize);
    elseif strcmp(y_category, 'gnorm')
        ylabel('Norm of gradient', 'FontSize', fontsize);
    elseif strcmp(y_category, 'subgnorm')
        ylabel('Norm of subgradient', 'FontSize', fontsize);
    elseif strcmp(y_category, 'K')
        ylabel('Batch size', 'FontSize', fontsize);
    elseif strcmp(y_category, 'reg')
        ylabel('Regularizer', 'FontSize', fontsize);
    elseif strcmp(y_category, 'trace_norm')
        ylabel('Trace (nuclear) norm', 'FontSize', fontsize);
    elseif strcmp(y_category, 'l1-norm')
        ylabel('$$\ell$$-1 norm', 'FontSize', fontsize, 'Interpreter', 'Latex', 'FontName','Arial');
    elseif strcmp(y_category, 'coeffs')
        ylabel('Coefficient', 'FontSize', fontsize);
    elseif strcmp(y_category, 'aprox_err')
        ylabel('Approximation error', 'FontSize', fontsize);
    elseif strcmp(y_category, 'coeff_amp')
        ylabel('Coefficient amplitude', 'FontSize', fontsize);
    elseif strcmp(y_category, 'dnorm')
        ylabel('Norm of direction', 'FontSize', fontsize);
    end

    % range
    if ~isempty(xlim_range)
        xlim([xlim_range])
    end

    if ~isempty(ylim_range)
        ylim([ylim_range])
    end

    % legend
    if ~strcmp(y_category, 'coeffs')
        legend(legend_str);
    end

    set(gca, 'FontSize', fontsize);
end


% Copyright (C) 2008  David Bateman
%
% This program is free software; you can redistribute it and/or modify it under
% the terms of the GNU General Public License as published by the Free Software
% Foundation; either version 3 of the License, or (at your option) any later
% version.
%
% This program is distributed in the hope that it will be useful, but WITHOUT
% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
% FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
% details.
%
% You should have received a copy of the GNU General Public License along with
% this program; if not, see <http://www.gnu.org/licenses/>.
%
% -*- texinfo -*-
% @deftypefn {Function File} {@var{w} =} window (@var{f}, @var{n}, @var{opts})
% Create a @var{n}-point windowing from the function @var{f}. The
% function @var{f} can be for example @code{@@blackman}. Any additional
% arguments @var{opt} are passed to the windowing function.
% @end deftypefn

function wout = window (f, n, varargin)
  if (nargin == 0)
    error ('window: UI tool not supported');
  elseif (nargin > 1)
    w = feval (f, n, varargin{:});
    if (nargout > 0)
      wout = w;
    end % if
  else
    help(mfilename);
  end % if
end


function [output] = compareBinsOfFluxes(xglc, model, sammin, sammax, metabolites)
% Takes the overall sammin and sammax samples, bins them into
% separate bin sizes and compares them, then compares the
% results to the largest bin size.
% calls `[totalz, zscore, mdv1, mdv2] = compareTwoSamp(xglc, model, samp1, samp2, measuredMetabolites)`
% sammin and sammax each contain bins of fluxes in `x.samps(r,1).points`
%
% USAGE:
%
%    [output] = compareBinsOfFluxes(xglc, model, sammin, sammax, metabolites)
%
% INPUTS:
%    xglc:           sugar distribution
%    model:          model structure
%    sammin:         samples containing bins of fluxes
%    sammax:         samples containing bins of fluxes
%
% OPTIONAL INPUT:
%    metabolites:    list of metabolites
%
% OUTPUT:
%    output:         result of comparison
%
% .. Author: - Wing Choi 3/7/08

output = 0;

if (nargin < 4)
    disp '[output] = compareBinsOfFluxes(xglc,model,samplo,samphi,metabolites)'
    return;
end

if (nargin < 5)
    metabolites = [];
end

if (isempty(xglc))
    % random glucose
    %xglc = rand(64,1);
    %xglc = xglc/sum(xglc);
    %xglc = idv2cdv(6)*xglc;

    glucose = rand(8,1);
    glucose = glucose/sum(glucose);
    %glc = idv2cdv(6)*glc;

    % glc 1-6 = carbon 1-6
    % glc 7 = carbon 1+2 (really 5 and 6)
    % glc 8 = unlabeled
    % glc 9 = fully labeled
    glc = zeros(64,9);
    glc(1+1,1) = 1;
    glc(2+1,2) = 1;
    glc(4+1,3) = 1;
    glc(8+1,4) = 1;
    glc(16+1,5) = 1;
    glc(32+1,6) = 1;
    glc(32+16+1,7) = 1;
    glc(0+1,8) = 1;
    glc(63+1,9) = 1;

    xGlc = zeros(64,1);
    for i = 1:8
        xGlc = xGlc + glucose(i)*glc(:,i);
    end

    xglc = idv2cdv(6)*xGlc;

end

nbins = size(sammin.samps,1);
npoints = size(sammin.samps(1,1).points,2);

disp (sprintf('found %d samples in input',npoints));
disp (sprintf('numbins  : %d',nbins));

for bin = 1:nbins
    samp1.points = sammin.samps(bin,1).points;
    samp2.points = sammax.samps(bin,1).points;
    [totalz,zscore,mdv1,mdv2] = compareTwoSamp(xglc,model,samp1,samp2,metabolites);
    output.totalz(bin,1) = totalz;
end

return
end


%%*******************************************************************
%% Prod2: compute the block diagonal matrix A*B
%%
%%    C = Prod2(blk,A,B,options);
%%
%% INPUT:  blk = a cell array describing the block structure of A and B
%%         A,B = square matrices or column vectors.
%%
%%   options = 0  if no special structure
%%             1  if C is symmetric
%%
%% SDPT3: version 3.1
%% Copyright (c) 1997 by
%% K.C. Toh, M.J. Todd, R.H. Tutuncu
%% Last Modified: 16 Sep 2004
%%*******************************************************************

function  C = Prod2(blk,A,B,options)

global spdensity

if (nargin == 3); options = 0; end;
iscellA = iscell(A); iscellB = iscell(B);
%%
if (~iscellA && ~iscellB)
    if (size(blk,1) > 1);
        error('Prod2: blk and A,B are not compatible');
    end;
    if strcmp(blk{1},'s')
        numblk = length(blk{2});
        isspA = issparse(A); isspB = issparse(B);
        if (numblk > 1)
            if ~isspA; A=sparse(A); isspA=1; end
            if ~isspB; B=sparse(B); isspB=1; end
        end
        %%use_matlab = (options==0 && ~isspA && ~isspB) || (isspA && isspB);
        use_matlab = (~isspA && ~isspB) || (isspA && isspB);
        if (use_matlab)
            C = A*B;
            if (options==1); C = 0.5*(C+C'); end;
        else
            C = mexProd2(blk,A,B,options);
        end
        checksparse = (numblk==1) && (isspA || isspB);
        if (checksparse)
            n2 = sum(blk{2}.*blk{2});
            if (mexnnz(C) <= spdensity*n2);
                if ~issparse(C); C = sparse(C); end;
            else
                if issparse(C); C = full(C); end;
            end
        end
    elseif (strcmp(blk{1},'q') || strcmp(blk{1},'l') || strcmp(blk{1},'u'))
        C = A.*B;
    end
else
    error('Prod2: A,B must be matrices');
end
%%*******************************************************************


function [K, sK] = indexardKernCompute(kern, x, x2)

% INDEXARDKERNCOMPUTE Compute the INDEXARD kernel given the parameters and X.
% FORMAT
% DESC computes the kernel parameters for the index ard based covariance function
% kernel given inputs associated with rows and columns.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : the input matrix associated with the rows of the kernel.
% ARG x2 : the input matrix associated with the columns of the kernel.
% RETURN k : the kernel matrix computed at the given points.
%
% FORMAT
% DESC computes the kernel matrix for the index ard based covariance function
% kernel given a design matrix of inputs.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : input data matrix in the form of a design matrix.
% RETURN k : the kernel matrix computed at the given points.
%
% SEEALSO : indexardKernParamInit, kernCompute, kernCreate, indexardKernDiagCompute
%
% COPYRIGHT : Neil D. Lawrence, 2011

% KERN
  if size(x, 2)>1
    error('Index kernel requires 1-dimensional input.')
  end

  if nargin<3
    x2 = x;
  end
  K = zeros(size(x, 1), size(x2, 1));
  for i = 1:size(x, 1)
    for j = 1:size(x2, 1)
      if round(x(i)) == round(x2(j))
        ind = find(round(x(i))==kern.indices);
        if isempty(ind)
          error('Unknown index in input');
        end
        K(i, j) = kern.indexScales(ind);
      end
    end
  end
end


%% IMPORTSWARMDB
function obj = importswarmdb(obj, dbname, auth, snum, enum)
    % IMPORTSWARMDB
    % Load a swarm database metrics table into an EventRate object
    % eventrate = importswarmdb(erobj, dbname, auth, snum, enum);
    %
    % INPUT:
    %	dbname		the path of the database (must have a 'metrics' table)
    %	auth		name of the grid to load swarm tracking metrics for
    %	snum,enum	start and end datenumbers (Matlab time format, see 'help datenum')
    %
    % OUTPUT:
    %	obj		an eventrate object
    %
    % Example:
    %	erobj = importswarmdb('/avort/devrun/dbswarm/swarm_metadata', 'RD_lo', datenum(2010, 7, 1), datenum(2010, 7, 14) );

    % Glenn Thompson, 20100714

    % initialize
    obj.dbroot = dbname;
    obj.snum = snum;
    obj.enum = enum;
    obj.auth = auth;

    % check that database exists
    dbtablename = sprintf('%s.metrics',dbname);
    if exist(dbtablename,'file')
        % load the data
        try
            db = dbopen(dbname, 'r');
        catch me
            fprintf('Error: Could not open %s for reading',dbname);
                return;
        end
        db = dblookup_table(db, 'metrics');
        if (dbquery(db, 'dbRECORD_COUNT')==0)
            fprintf('Error: Could not open %s for reading',dbtablename);
            return;
        end
        db = dbsubset(db, sprintf('auth ~= /.*%s.*/',auth));
        numrows = dbquery(db,'dbRECORD_COUNT');
        debug.print_debug(sprintf('Got %d rows after auth subset',numrows),2);
        sepoch = datenum2epoch(snum);
        eepoch = datenum2epoch(enum);
        db = dbsubset(db, sprintf('timewindow_starttime >= %f && timewindow_endtime <= %f',sepoch,eepoch));
        numrows = dbquery(db,'dbRECORD_COUNT');
        debug.print_debug(sprintf('Got %d rows after time subset',numrows),2);

        if numrows > 0
            % Note that metrics are only saved when mean_rate >= 1.
            % Therefore there will be lots of mean_rate==0 timewindows not in
            % database.
            [tempsepoch, tempeepoch, mean_rate, median_rate, mean_mag, cum_mag] = dbgetv(db,'timewindow_starttime', 'timewindow_endtime', 'mean_rate', 'median_rate', 'mean_ml', 'cum_ml');
            obj.binsize = (tempeepoch(1) - tempsepoch(1))/86400;
            obj.stepsize = min(tempsepoch(2:end) - tempsepoch(1:end-1))/86400;
            obj.time = snum+obj.stepsize:obj.stepsize:enum;
            obj.numbins = length(obj.time);
            obj.mean_rate = zeros(obj.numbins, 1);
            obj.counts = zeros(obj.numbins, 1);
            obj.median_rate = zeros(obj.numbins, 1);
            obj.mean_mag = zeros(obj.numbins, 1);
            obj.cum_mag = zeros(obj.numbins, 1);
            for c=1:length(tempeepoch)
                tempenum = epoch2datenum(tempeepoch(c));
                i = find(obj.time == tempenum);
                obj.mean_rate(i) = mean_rate(c);
                obj.counts(i) = mean_rate(c) * (obj.binsize * 24);
                obj.median_rate(i) = median_rate(c);
                obj.mean_mag(i) = mean_mag(c);
                obj.cum_mag(i) = cum_mag(c);
            end
        end
        dbclose(db);

    else
        % error - table does not exist
        fprintf('Error: %s does not exist',dbtablename);
        return;
    end

    obj.total_counts = sum(obj.counts)*obj.stepsize/obj.binsize;

end


function y = exp( x )

%   Disciplined convex programming information:
%       EXP(X) is convex and nondecreasing in X. When used in CVX
%       expressions, X must be real. Typically, X must also be affine
%       or convex; X can also be concave, but this produces a log-concave
%       result with very limited usefulness.

global cvx___
error(nargchk(1,1,nargin));
cvx_expert_check( 'log', x );

%
% Determine the expression types
%

persistent remap
if isempty( remap ),
    remap_1 = cvx_remap( 'real' );
    remap_2 = cvx_remap( 'convex', 'concave' ) & ~remap_1;
    remap = remap_1 + 2 * remap_2;
end
v = remap( cvx_classify( x ) );

%
% Process each type of expression one piece at a time
%

vu = sort( v(:) );
vu = vu([true;diff(vu)~=0]);
nv = length( vu );
sx = x.size_;
if nv ~= 1,
    y = cvx( sx, [] );
end
for k = 1 : nv,

    %
    % Select the category of expression to compute
    %

    vk = vu( k );
    if nv == 1,
        xt = x;
    else
        t = v == vk;
        xt = cvx_subsref( x, t );
    end

    %
    % Perform the computations
    %

    switch vk,
        case 0,
            % Invalid
            error( 'Disciplined convex programming error:\n    Illegal operation: exp( {%s} ).', cvx_class( xt ) );
        case 1,
            % Constant
            xt = cvx( exp( cvx_constant( xt ) ) );
        case 2,
            % Affine, convex, concave
            xt = sparsify( xt, 'exponential' );
            [ rx, cx, vx ] = find( xt.basis_ );
            tt = rx == 1;  rx( tt ) = [];
            cc = cx( tt ); cx( tt ) = [];
            vc = vx( tt ); % vx( tt ) = [];
            exps = cvx___.exponential( rx, 1 );
            tt = exps == 0;
            if any( tt ),
                n1 = unique( rx( tt ) );
                n2 = newvar( cvx___.problems( end ).self, '', length( n1 ) );
                [ n2, dummy ] = find( n2.basis_ ); %#ok
                cvx___.exponential( n1, 1 ) = n2( : );
                cvx___.logarithm( n2, 1 ) = n1( : );
                cvx___.vexity( n2 ) = 1;
                n2 = n2( cvx___.vexity( n1 ) < 0 );
                cvx___.vexity( n2 ) = NaN;
                cvx___.nan_used = true;
                cvx___.canslack( n2 ) = false;
                exps = cvx___.exponential( rx, 1 );
            end
            nb = size( xt.basis_, 2 );
            bx = sparse( exps, cx, 1, max( exps ), nb );
            if ~isempty( cc ),
                bx = bx * diag(exp(sparse(cc,1,vc,nb,1)));
            end
            xt = cvx( xt.size_, bx );
        otherwise,
            error( 'Shouldn''t be here.' );
    end

    %
    % Store the results
    %

    if nv == 1,
        y = xt;
    else
        y = cvx_subsasgn( y, t, xt );
    end

end

% Copyright 2010 Michael C. Grant and Stephen P. Boyd.
% See the file COPYING.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function [loser] = wholoses(plr,box)
    loser=0; % "0" means no loser yet
    % the player having no piece on table will lose
    for x=1:8
        for y=1:8
            if plr==1 && box(x,y)==10   %king1 is present KING=10
                loser=0;
                return;
            elseif plr==2 && box(x,y)==-10 %king2 is present king=-10
                loser=0;
                return;
            else
                loser=plr;
            end
        end
    end
end