function SlabAnalysis(mCatalog,vCoastlines,vFaults,vVolcano)
% % Example: SlabAnalysis(a,coastline,faults,vo)
% -------------------------------------------------------------------------------------------------------------
% Creates a cell-array with subcatalogs for every grid node defined by mPolygon. These subcatalogs
%   contain only indices to the earthquake "rows" in mCatalog.
%
% Input parameters:
%   params.mCatalog             Earthquake catalog
%   params.vCoastlines          Coastlines
%   params.vFaults              Faults
%   params.vVolcano             Volcano
%
% Output parameters:
%   none
%
% Thomas van Stiphout
% February 28, 2006
% -------------------------------------------------------------------------------------------------------------
%

if ~strcmp(get(gcf,'name'),'3DResult plot')
% Launch GUI
hMenuFig = gui_slab;
handles = guidata(hMenuFig);

% Analyze Output
if ~ishandle(hMenuFig)
    %     handles.answer = 0;
elseif (handles.answer==0)
    disp('Canceled SlabAnalysis - Goodbye');
elseif (handles.answer==1)
    % define equidistance btw gridpoints
    params.nDx=str2double(get(handles.nDx,'String'));
    params.nDy=params.nDx;
    % Define the percentile of the depth values
    params.vPercentiles=str2double(get(handles.vPerc,'String'));
    % Radius for Map-Gridding for Columns
    params.fColRadius=str2double(get(handles.fRadius,'String'));
    % Minimum number of events that have to be in the column
    params.nColMinEvents=str2double(get(handles.nMinColEvents,'String'));
    % Volume sampling is cylindrical
    params.bCylSmp=get(handles.chkCylSmp,'Value');
    params.bCylSmpModeN=get(handles.radCylSmpModeN,'Value');
    params.bCylSmpModeR=get(handles.radCylSmpModeR,'Value');
    params.fCylSmpValue=str2double(get(handles.fCylSmpValue,'String'));
    params.fCylSmpBnd=str2double(get(handles.fCylSmpBnd,'String'));
    if ((params.bCylSmpModeN==1) && (params.bCylSmpModeR==0))
        params.nGriddingMode=1; % constant numbers
        params.nNumberEvents=params.fCylSmpValue;
    elseif ((params.bCylSmpModeN==0) && (params.bCylSmpModeR==1))
        params.nGriddingMode=0; % constant radius
    else
        disp('Something is going wrong');
    end
    % Volume sampling is 3D-spherical
    params.b3DSmp=get(handles.chk3DSmp,'Value');
    params.b3DSmpModeN=get(handles.radCylSmpModeN,'Value');
    params.b3DSmpModeR=get(handles.radCylSmpModeR,'Value');
    params.f3DSmpValue=str2double(get(handles.fCylSmpValue,'String'));
    params.f3DSmpBnd=str2double(get(handles.fCylSmpBnd,'String'));
    if ((params.bCylSmpModeN==1) && (params.bCylSmpModeR==0))
        params.n3DGriddingMode=3; % constant numbers
        params.nNumberEvents=params.fCylSmpValue;
    elseif ((params.bCylSmpModeN==0) && (params.bCylSmpModeR==1))
        params.n3DGriddingMode=4; % constant radius
    else
        disp('Something is going wrong');
    end
    %     close(get(handles.figure1,'Name'));

    if (params.bCylSmp==1)
        if ~exist('Name')
            Name=cellstr('Cylindrical Volume Sampling');
        else
            Name(size(Name,1)+1,1)=cellstr('Cylindrical Volume Sampling');
        end
    end
    if params.b3DSmp
        if ~exist('Name')
            Name=cellstr('3D-Spherical Volume Sampling');
        else
            Name(size(Name,1)+1,1)=cellstr('3D Spherical Volume Sampling');
        end
    end

    params.Name=Name;
    params.mCatalog=mCatalog;
    params.vCoastline=vCoastlines;
    params.vFaults=vFaults;
    params.vVolcanoes=vVolcano;
    params.chkVolcanoes=1;

    clear mCatalog vCoastlines vFaults vVolcano
    % Create grid
    params.bMap=1;
    params.nGriddingMode=1;
    params.fSizeRectHorizontal=nan;
    params.fSizeRectDepth=nan;
    params.fSizeRectHorizontal=nan;
    params.fSizeRectDepth=nan;

    % Create map-gridding
    % by defining equidistance btw gridpoints

    % Short distance conversion
    params.fDLon=km2deg(params.nDx);
    params.fDLat=km2deg(params.nDy);
    %
    hFigure=gcf;

    % Select grid from seismicity map
    [params.mPolygon, params.vX, params.vY, params.vUsedNodes] = ex_selectgrid(hFigure, params.fDLon, params.fDLat, 0);

    % Sampling Radius is set equally to the grid raster; indices for each grid point
    [params.caNodeIndices params.vResolution] = ex_CreateIndexCatalog2(params.mCatalog, params.mPolygon, 1, 1, ...
        [], params.fColRadius, params.fSizeRectHorizontal, params.fSizeRectDepth);

    % preparing the matrix containing the depth-levels according to the
    % percentiles defined
    params.mDepths=ones(length(params.mPolygon),length(params.vPercentiles))*nan;
    params.mValueGrid=ones(length(params.mPolygon),1)*nan;

    % calculation the depths-levels according to the percentiles for each gridpoint
    for i=1:length(params.mPolygon)
        params.mDepths(i,:)=prctile(params.mCatalog(params.caNodeIndices{i},7),params.vPercentiles) ;
        %     params.mValueGrid(i)=length(params.mCatalog(params.caNodeIndices{i}));
    end

    % Take only depth values that are well constrained,
    % i.e. with at least 50 earthquakes per map-node; set others to NaN
    params.mDepths((params.vResolution <= params.nColMinEvents),:)=nan;
    % Create polygon with with slab-surface values x,y,z
end
delete(hMenuFig);

elseif strcmp(get(gcf,'name'),'3DResult plot')
    disp('here we go for a cross section');
    view(0,90);
    vSecLim1 = ginput(1)
    vSecLim2 = ginput(1)
    dist_=deg2km(distance(vSecLim1(2),vSecLim1(1),vSecLim2(2),vSecLim2(1)));
    azimuth_=azimuth(vSecLim1(2),vSecLim1(1),vSecLim2(2),vSecLim2(1));

    view(3);
end
for nLayer=1:length(params.vPercentiles);
    params.mPolygon(:,3)=params.mDepths(:,nLayer);
    % overgive depth values to the result-matrix
    params.mValueGrid(:,1)=params.mDepths(:,nLayer);

    for i=1:length(params.vY)
        params.mX(i,:)=params.vX;
    end
    for i=1:length(params.vX)
        params.mY(:,i)=params.vY;
    end

    params.mValueGrid(isnan(params.mPolygon(:,3)),:)=nan;

    % prepare 3D figure by reshaping
    params.mX=reshape(params.mX,length(params.vUsedNodes),1);
    params.mY=reshape(params.mY,length(params.vUsedNodes),1);
    params.mZ=ones(length(params.vUsedNodes),1)*nan;
    params.mZ(params.vUsedNodes)=params.mValueGrid(:,1);
    params.mX=reshape(params.mX,length(params.vY),length(params.vX));
    params.mY=reshape(params.mY,length(params.vY),length(params.vX));
    params.mZ=reshape(params.mZ,length(params.vY),length(params.vX));

    % transform spherical coordinates to xyz-coordinates
    % by applying distances-command

    % center of data as reference/origin point
    fOriginX_=(max(max(params.mX))+min(min(params.mX)))./2;
    fOriginY_=(max(max(params.mY))+min(min(params.mY)))./2;
    mOriginX_=ones(size(params.mX))*fOriginX_;
    mOriginY_=ones(size(params.mX))*fOriginY_;

    mYkm=deg2km(distance(params.mY,mOriginX_,mOriginY_,mOriginX_)).*sign(params.mY-mOriginY_);
    mXkm=deg2km(distance(mOriginY_,params.mX,mOriginY_,mOriginX_))*sin(deg2rad(fOriginY_)).*sign(params.mX-mOriginX_);

    % mZkm=ones(size(params.mZ))*50
    mZkm=params.mZ;

    % mZkm=ones(size(params.mZ))*50
    % mZkm(isnan(params.mZ))=nan
    % surfnorm(mXkm,mYkm,-mZkm);

    [mNxkm,mNykm,mNzkm]=surfnorm(mXkm,mYkm,-mZkm);

    % make figure
    % figure;
    % surf(params.mX,params.mY,-params.mZ);
    % surf(params.mX,params.mY,-params.mZ);
    % colorbar;
    % set(gca,'CLim',[0.6 1.4]);
    % box on
    % hold on; plot3(params.mPolygon(:,1),params.mPolygon(:,2),-params.mPolygon(:,3),'s','MarkerSize',5);
    [mNx,mNy,mNz]=surfnorm(params.mX,params.mY,-params.mZ);
    [mNxkm,mNykm,mNzkm]=surfnorm(mXkm,mYkm,-mZkm);
    % determin the gradient of the slab at each grid point
    [mGx,mGy]=gradient(params.mZ);
    [mGxkm,mGykm]=gradient(params.mZ);
    mGz=zeros(size(mGx,1),size(mGx,2));
    mGzkm=zeros(size(mGxkm,1),size(mGxkm,2));
    % take only valid gridpoints
    % mGx(isnan(mNx))=nan; mGy(isnan(mNx))=nan; mGz(isnan(mNx))=nan;

    vGx=reshape(mGx,size(mGx,1)*size(mGx,2),1);vGy=reshape(mGy,size(mGy,1)*size(mGy,2),1);vGz=reshape(mGz,size(mGz,1)*size(mGz,2),1);
    vNx=reshape(mNx,size(mNx,1)*size(mNx,2),1);vNy=reshape(mNy,size(mNy,1)*size(mNy,2),1);vNz=reshape(mNz,size(mNz,1)*size(mNz,2),1);
    % same for kmgrid
    vGxkm=reshape(mGxkm,size(mGxkm,1)*size(mGxkm,2),1);vGykm=reshape(mGykm,size(mGykm,1)*size(mGykm,2),1);vGzkm=reshape(mGzkm,size(mGzkm,1)*size(mGzkm,2),1);
    vNxkm=reshape(mNxkm,size(mNxkm,1)*size(mNxkm,2),1);vNykm=reshape(mNykm,size(mNykm,1)*size(mNykm,2),1);vNzkm=reshape(mNzkm,size(mNzkm,1)*size(mNzkm,2),1);

    mask=(~isnan(vGx) .* ~isnan(vGy) .* ~isnan(vGz) .* ~isnan(vNx) .* ~isnan(vNy) .* ~isnan(vNy));
    vGx(~mask)=nan;vGy(~mask)=nan;vGz(~mask)=nan;vNx(~mask)=nan;vNy(~mask)=nan;vNz(~mask)=nan;
    % same for km-grid
    mask=(~isnan(vGxkm) .* ~isnan(vGykm) .* ~isnan(vGzkm) .* ~isnan(vNxkm) .* ~isnan(vNykm) .* ~isnan(vNykm));
    vGxkm(~mask)=nan;vGykm(~mask)=nan;vGzkm(~mask)=nan;vNxkm(~mask)=nan;vNykm(~mask)=nan;vNzkm(~mask)=nan;
    % create a vector perpendicular to the surface define by the normal and the
    % gradient, mT is tangent and represents the axes of the sampling cylinder.
    params.mT=cross([vGx vGy vGz],[vNx vNy vNz]);
    % same for km-grid
    mTkm=cross([vGxkm vGykm vGzkm],[vNxkm vNykm vNzkm]);
%     hold on;quiver3(mXkm(:),mYkm(:),-mZkm(:),mTkm(:,1),mTkm(:,2),mTkm(:,3),2,'r');
    % hold on;quiver3(params.mX(:),params.mY(:),-params.mZ(:),params.mT(:,1),params.mT(:,2),params.mT(:,3),2,'r');
    % params.mT=params.mT(params.vUsedNodes,:)

    % we only want to have horizontal cylinder axis
    params.mT(~isnan(params.mT(:,3)),3)=0;

    for ii=1:length(Name)
        if strcmp(Name(ii),'Cylindrical Volume Sampling')
            disp(Name(ii));
            params.sComment=strcat(Name(ii),' Depth=',num2str(params.vPercentiles(nLayer)));
            [params.caNodeIndices2 params.vResolution2]=ex_CreateIndexCatalogCylinder(params.mCatalog,params.mPolygon,params.mT,...
                params.vUsedNodes,params.bCylSmpModeN,params.fCylSmpValue,params.fCylSmpBnd);
            if ((params.bCylSmpModeN==1) && (params.bCylSmpModeR==0))
                params.vcsGridNames(6) = cellstr(char('Resolution [R]'));
            else
                params.vcsGridNames(6) = cellstr(char('Resolution [N]'));
            end
        elseif strcmp(Name(ii),'3D Spherical Volume Sampling')
            disp(Name(ii));
            params.sComment=strcat(Name(ii),' Depth=',num2str(params.vPercentiles(nLayer)));
            % nGriddingMode = 3 means 3D sphere around polygonpoint
            params.nGriddingMode=3;
            % select
            % Create Indices to catalog and select quakes in time period
            [params.caNodeIndices2 params.vResolution2] = ex_CreateIndexCatalog3D(params.mCatalog, params.mPolygon, '1', params.n3DGriddingMode, ...
                params.f3DSmpValue, params.f3DSmpBnd, params.fSizeRectHorizontal, params.fSizeRectDepth);
            if ((params.b3DSmpModeN==1) && (params.b3DSmpModeR==0))
                params.vcsGridNames(6) = cellstr(char('Resolution [R]'));
            else
                params.vcsGridNames(6) = cellstr(char('Resolution [N]'));
            end
            % clear indices where not enough quakes for depth estimation
            for i=1:length(params.mPolygon)
                if isnan(params.mPolygon(i,3))
                    params.caNodeIndices2{i}=[];
                end
            end
        else
            disp('Something is going wrong');
        end

        % Calculation of bValue
        for i=1:length(params.mPolygon)
            if ~isempty(params.mCatalog(params.caNodeIndices2{i},:))
                % function [fBValue, fStdDev, fAValue, fMeanMag] =  calc_bvalue(mCatalog, fBinning)
                [params.mValueGrid(i,2),params.mValueGrid(i,3),params.mValueGrid(i,4),params.mValueGrid(i,5),] =  calc_bvalue(params.mCatalog(params.caNodeIndices2{i},:));
                params.mValueGrid(i,6)=max(params.vResolution2{i});
            else
                % create NaN's in mValueGrid, where strike is not defined
                params.mValueGrid(i,:)=nan;
            end

        end

        params.vcsGridNames(1:5) = cellstr(char(...
            'Depth Level',...               %   1
            'b-Value',...                   %   2
            'Standard Deviation',...        %   3
            'A-Value',...                   %   4
            'Mean Magnitude'));             %   5

        params.bMap=2;
        params.mValueGrid(isnan(params.mValueGrid(:,1)),:)=nan;
        vResults((nLayer-1)*length(Name)+ii)=params;

    end
end
save vResults
gui_result2(vResults);
disp('surf done');

% % plot the gradient on the slab
% hold on;quiver3(params.mX(:),params.mY(:),-params.mZ(:),mGx(:),mGy(:),0,2);
% % plot strike
% hold on;quiver3(params.mX(:),params.mY(:),-params.mZ(:),params.mT(:,1),params.mT(:,2),params.mT(:,3),3,'r');

% prepare Value that will be illustrated on the 3D surface defined above
% params.mC=ones(length(params.vUsedNodes),1)*nan;
% params.mC(params.vUsedNodes)=params.mValueGrid(:,2);
% params.mC(params.mC == 0)=nan;
% params.mC=reshape(params.mC,length(params.vY),length(params.vX));
% params.mZ(isnan(params.mC))=nan;

% make figure
% figure;
% surf(params.mX,params.mY,-params.mZ);
% surf(params.mX,params.mY,-params.mZ,params.mC);
% colorbar;
% set(gca,'CLim',[0.6 1.4]);
% box on
% shading interp


function [ctcell,ct_xmesh,ct_ymesh,ct_zmesh] = dicomrt_loadct(filename,sorting)
% dicomrt_loadct(filename,sorting)
%
% Read dicom CT for a case using MATLAB native function dicomread. Light Version.
%
% filename contains a list of CT slices to import
%
% CT are stored in a single 3D matrix: case_study
% x-y-z coordinates of the center of the ct-pixel are stored in ct_xmesh, ct_ymesh and ct_zmesh.
%
% NOTE:  CT numbers range from -1000 for air to 1000 for bone with that for water set to 0.
% CT numbers normalized in this manner are called Hounfield numbers or units (HU):
%
% HU = ((mu_tissue-mu_water)/mu_water)*1000
%
% Following DICOM RT standard HU = m*(SV)+b where m is RescaleSlope, b RescaleIntercept
% and SV are pixel (stored) values.
%
% Often CT scale is shifted so that HU(water)=1000 (=CToffset) instead of 0.
%
% NOTE: as opposed to dicomrt_loadct ct_xmesh, ct_ymesh and ct_zmesh are vectors and not
% matrices. This allow to run this functions also in "low" memory pcs.
%
% See also dicomrt_loaddose, dicomrt_getPatientPosition
%
% Copyright (C) 2002 Emiliano Spezi (emiliano.spezi@physics.org)

% Sort CT slices first
%
% load ct slices info

% Check number of argument and set-up some parameters and variables
error(nargchk(1,2,nargin))

if exist('sorting')==0
    sorting=1; % perform sorting check
end

% Retrieve the number of images
nlines=dicomrt_nASCIIlines(filename);

if nlines>1
    disp('Loading CT slice information ...');
    [filelist,xlocation,ylocation,zlocation,modifUID,user_request] = dicomrt_loadctlist(filename);
    if user_request==1;
        return
    end
    disp('CT slice information loaded.');

    if sorting==1
        % sort ct slices
        disp('Sorting CT slices ...');
        [modifZ,user_request]=dicomrt_sortct(filelist,xlocation,ylocation,zlocation,filename);
        if user_request==1;
            return
        end
        disp('CT slices sorted.');
    else
        modifZ=0;
        modifUID=0;
    end

    if modifZ~=0 | modifUID~=0 % a modification to the filelist was made by dicomrt_sortct
        filename_sorted=[filename,'.sort.txt'];
    else % no modification have been made
        filename_sorted=filename;
    end
else
    disp('Loading one image. Zmesh will have no sense. Slice position can be loaded using dicominfo.');
    filename_sorted=filename;
end

% Now get CT images and create 3D Volume

% Set parameters
CToffset=1000;
nct=0;

% Define cell array to store info
case_study_info=cell(1);

%loop until the end-of-file is reached and build 3D CT matrix
disp('Loading ct volume ...');

% Progress bar
h = waitbar(0,['Loading progress:']);
set(h,'Name','dicomrt_loadct: loading CT images');

fid=fopen(filename_sorted);

while (feof(fid)~=1);
    clear info_temp temp
    nct=nct+1; % counting
    nctcheck=nct; % check for eof
    ct_file_location{1,nct}=fgetl(fid);
    if isnumeric(ct_file_location{1,nct}), nct=nct-1; break, end %end of line reached

    temp=dicomread(deblank(ct_file_location{1,nct}));

    dictFlg = checkDictUse;
    if dictFlg
        if isdeployed
            info_temp = dicominfo(deblank(ct_file_location{1,nct}),...
                'dictionary', fullfile(getCERRPath,'bin','ES - IPT4.1CompatibleDictionary.mat'));
        else
            info_temp = dicominfo(deblank(ct_file_location{1,nct}),...
                'dictionary', 'ES - IPT4.1CompatibleDictionary.mat');
        end
    else
        info_temp=dicominfo(deblank(ct_file_location{1,nct}));
    end

    zmesh(nct)=info_temp.ImagePositionPatient(3);

    if isfield(info_temp,'RescaleSlope')~=0 | isfield(info_temp,'RescaleIntercept')~=0
        temp=double(temp)*info_temp.RescaleSlope+info_temp.RescaleIntercept+CToffset;
    else
        warning('dicomrt_loadct: no DICOM Rescale data were found. Assuming RescaleSlope = 1, RescaleIntercept = 0 and CToffset = 1000');
        temp=double(temp);
    end

    case_study_info{nct}=info_temp;
    case_study(:,:,nct)=uint16(temp);
    waitbar(nct/nlines,h);
end

% Make zmesh a column vector
zmesh=dicomrt_makevertical(zmesh);

fclose(fid);

if nctcheck~=nct;
    warning('dicomrt_loadct: End of file was prematurely reached. Check the expected dimensions of your data. It may be OK to continue');
end

[PatientPositionCODE]=dicomrt_getPatientPosition(info_temp);

% PatientOrientation     ImageOrientationPatient
%
% HFS                    (1,0,0) (0,1,0)
% FFS                    (1,0,0) (0,1,0)
% HFP                    (-1,0,0) (0,-1,0)
% FFP                    (-1,0,0) (0,-1,0)
%
%
if PatientPositionCODE == 1 | PatientPositionCODE == 2
    min_x=info_temp.ImagePositionPatient(1);
    pixel_spacing_x=info_temp.PixelSpacing(1);

    min_y=info_temp.ImagePositionPatient(2);
    pixel_spacing_y=info_temp.PixelSpacing(2);

    [xmesh] = dicomrt_create1dmesh(min_x,pixel_spacing_x,size(temp,2),0);
    [ymesh] = dicomrt_create1dmesh(min_y,pixel_spacing_y,size(temp,1),0);

else
    max_x=info_temp.ImagePositionPatient(1);
    pixel_spacing_x=info_temp.PixelSpacing(1);

    max_y=info_temp.ImagePositionPatient(2);
    pixel_spacing_y=info_temp.PixelSpacing(2);

    [xmesh] = dicomrt_create1dmesh(max_x,pixel_spacing_x,size(temp,1),1);
    [ymesh] = dicomrt_create1dmesh(max_y,pixel_spacing_y,size(temp,2),1);

end

ct_zmesh=dicomrt_mmdigit(zmesh*0.1,7);
ct_ymesh=dicomrt_mmdigit(ymesh*0.1,7);
ct_xmesh=dicomrt_mmdigit(xmesh*0.1,7);

disp('Loading complete ...');

% 3D CT matrix and mesh matrix imported
%
% Some CT images info have private tags or fragmented info.
% If so, go into manual mode.
%

% Check support for current Patient Position
if PatientPositionCODE == 1 % supported Patient Position: HFS
    disp('dicomrt_loadct: Patient Position is Head First Supine (HFS).');
elseif PatientPositionCODE == 2 % supported Patient Position: FFS
    disp('dicomrt_loadct: Patient Position is Feet First Supine (FFS).');
elseif PatientPositionCODE == 3 % unsupported Patient Position: HFP
    disp('dicomrt_loadct: Patient Position is Head First Prone (HFP).');
elseif PatientPositionCODE == 4 % unsupported Patient Position: FFP
    disp('dicomrt_loadct: Patient Position is Feet First Prone (FFP).');
else
    warning('Unable to determine Patient Position');
    PatientPosition=input('dicomrt_loadct: Please specify Patient Position: HFS(default),FFS,HFP,FFP: ','s');
    if isempty(PatientPosition)==1
        PatientPosition='HFS';
    end

    if strcmpi(PatientPosition, 'HFS')
        PatientPositionCODE = 1;
    elseif strcmpi(PatientPosition, 'FFS')
        PatientPositionCODE = 2;
    elseif strcmpi(PatientPosition, 'HFP')
        PatientPositionCODE = 3;
    elseif strcmpi(PatientPosition, 'FFP')
        PatientPositionCODE = 4;
    end

end

% Create cell array
ctcell=cell(3,1);
ctcell{1,1}=case_study_info;
ctcell{2,1}=case_study;
ctcell{3,1}=[];

% Close progress bar
close(h);
pack


function update_background_parallel(obj, use_parallel)
%% update the background related variables in CNMF framework
% input:
%   use_parallel: boolean, do initialization in patch mode or not.
%       default(true); we recommend you to set it false only when you want to debug the code.

%% Author: Pengcheng Zhou, Columbia University, 2017
%% email: zhoupc1988@gmail.com

%% process parameters

try
    % map data
    mat_data = obj.P.mat_data;

    % folders and files for saving the results
    log_file =  obj.P.log_file;
    flog = fopen(log_file, 'a');
    log_data = matfile(obj.P.log_data, 'Writable', true); %#ok<NASGU>

    % dimension of data
    dims = mat_data.dims;
    d1 = dims(1);
    d2 = dims(2);
    T = dims(3);
    obj.options.d1 = d1;
    obj.options.d2 = d2;

    % parameters for patching information
    patch_pos = mat_data.patch_pos;
    block_pos = mat_data.block_pos;

    % number of patches
    [nr_patch, nc_patch] = size(patch_pos);
catch
    error('No data file selected');
end
fprintf('\n-----------------UPDATE BACKGROUND---------------------------\n');

% frames to be loaded for initialization
frame_range = obj.frame_range;
T = diff(frame_range) + 1;

% threshold for detecting large residuals
thresh_outlier = obj.options.thresh_outlier;

% use parallel or not
if ~exist('use_parallel', 'var')||isempty(use_parallel)
    use_parallel = true; %don't save initialization procedure
end

% options
options = obj.options;
nb = options.nb;
bg_ssub = options.bg_ssub;
bg_model = options.background_model;
with_projection = options.bg_acceleration;

% previous estimation
A = cell(nr_patch, nc_patch);
C = cell(nr_patch, nc_patch);
sn = cell(nr_patch, nc_patch);
W = obj.W;
b0 = obj.b0;
b = obj.b;
f = obj.f;
RSS = cell(nr_patch, nc_patch);

%% check whether the bg_ssub was changed
if strcmpi(bg_model, 'ring')
    tmp_block = block_pos{1};    % block position
    nr_block = diff(tmp_block(1:2))+1;
    nc_block = diff(tmp_block(3:4))+1;
    [~, temp] = size(W{1});
    [d1s, d2s] = size(imresize(zeros(nr_block, nc_block), 1/bg_ssub));
    if temp~=d1s*d2s
        rr = ceil(obj.options.ring_radius/bg_ssub);    % radius of the ring
        [r_shift, c_shift] = get_nhood(rr, obj.options.num_neighbors);    % shifts used for acquiring the neighboring pixels on the ring
        parfor mpatch=1:(nr_patch*nc_patch)
            tmp_patch = patch_pos{mpatch};    % patch position
            tmp_block = block_pos{mpatch};    % block position
            nr = diff(tmp_patch(1:2)) + 1;
            nc = diff(tmp_patch(3:4)) + 1;
            nr_block = diff(tmp_block(1:2))+1;
            nc_block = diff(tmp_block(3:4))+1;
            b0{mpatch} = zeros(nr*nc, 1);

            if bg_ssub==1
                [csub, rsub] = meshgrid(tmp_patch(3):tmp_patch(4), tmp_patch(1):tmp_patch(2));
                csub = reshape(csub, [], 1);
                rsub = reshape(rsub, [], 1);
                ii = repmat((1:numel(csub))', [1, length(r_shift)]);
                csub = bsxfun(@plus, csub, c_shift);
                rsub = bsxfun(@plus, rsub, r_shift);
                ind = and(and(csub>=1, csub<=d2), and(rsub>=1, rsub<=d1));
                jj = (csub-tmp_block(3)) * (diff(tmp_block(1:2))+1) + (rsub-tmp_block(1)+1);

                temp = sparse(ii(ind), jj(ind), 1, nr*nc, nr_block*nc_block);
                W{mpatch} = bsxfun(@times, temp, 1./sum(temp, 2));
            else
                d1s = ceil(nr_block/bg_ssub);
                d2s = ceil(nc_block/bg_ssub);

                [csub, rsub] = meshgrid(1:d2s, 1:d1s);
                csub = reshape(csub, [], 1);
                rsub = reshape(rsub, [], 1);
                ii = repmat((1:numel(csub))', [1, length(r_shift)]);
                csub = bsxfun(@plus, csub, c_shift);
                rsub = bsxfun(@plus, rsub, r_shift);
                jj = (csub-1) * d1s + rsub;
                % remove neighbors that are out of boundary
                ind = and(and(csub>=1, csub<=d2s), and(rsub>=1, rsub<=d1s));
                temp = sparse(ii(ind), jj(ind), 1, d1s*d2s, d1s*d2s);
                W{mpatch} = bsxfun(@times, temp, 1./sum(temp, 2));
            end
        end
    end
end

%% start updating the background
for mpatch=1:(nr_patch*nc_patch)
    tmp_block = block_pos{mpatch};

    % find the neurons that are within the block
    mask = zeros(d1, d2);
    mask(tmp_block(1):tmp_block(2), tmp_block(3):tmp_block(4)) = 1;

    ind = (reshape(mask(:), 1, [])* obj.A>0);
    A{mpatch}= obj.A(logical(mask), ind);
    C{mpatch} = obj.C(ind, :);
    temp = obj.P.sn(logical(mask));
    if bg_ssub==1
        sn{mpatch} = temp;
    else
        nr_block = diff(tmp_block(1:2))+1;
        nc_block = diff(tmp_block(3:4))+1;
        sn{mpatch} = imresize(reshape(temp, nr_block, nc_block), 1/bg_ssub, 'nearest')*bg_ssub;
    end
end

% check whether this is the first run of updating background components
if strcmpi(bg_model, 'ring')
    flag_first = (length(unique(W{1}(1, :)))==2);
else
    flag_first = (mean2(b{1})==0);
end

if use_parallel
    % load data before running parfor
    %     data_patch = cell(nr_patch, nc_patch);
    %     flag_ignore = cell(nr_patch, nc_patch);
    %     for mpatch=1:(nr_patch*nc_patch)
    %         tmp_patch = patch_pos{mpatch};
    %         A_block = A{mpatch};
    %
    %         % stop updating B because A&C doesn't change in this area
    %         if isempty(A_block) && (~flag_first)
    %             flag_ignore{mpatch} = true;
    %             [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
    %
    %             % keep the current results. this step looks rediculous, but it
    %             % is needed for some computer/matlab. very weird.
    %             W{mpatch} = W{mpatch};
    %             b0{mpatch} = b0{mpatch};
    %             b{mpatch} = b{mpatch};
    %             f{mpatch} = f{mpatch};
    %             fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
    %             continue;
    %         else
    %             flag_ignore{mpatch} = false;
    %             % pull data
    %             data_patch{mpatch} = get_patch_data(mat_data, tmp_patch, frame_range, true);
    %         end
    %     end
    % do the actual computation
    parfor mpatch=1:(nr_patch*nc_patch)
        %         if flag_ignore{mpatch}
        %             continue;
        %         end
        tmp_patch = patch_pos{mpatch};
        tmp_block = block_pos{mpatch};

        A_block = A{mpatch};
        sn_block = sn{mpatch};
        C_block = C{mpatch};

        % stop updating B because A&C doesn't change in this area
        if isempty(A_block) && (~flag_first)
            [r, c] = ind2sub([nr_patch, nc_patch], mpatch);

            % keep the current results. this step looks rediculous, but it
            % is needed for some computer/matlab. very weird.
            W{mpatch} = W{mpatch};
            b0{mpatch} = b0{mpatch};
            b{mpatch} = b{mpatch};
            f{mpatch} = f{mpatch};
            fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
            continue;
        end

        % use ind_patch to indicate pixels within the patch and only
        % update (W, b0) corresponding to these pixels
        ind_patch = false(diff(tmp_block(1:2))+1, diff(tmp_block(3:4))+1);
        ind_patch((tmp_patch(1):tmp_patch(2))-tmp_block(1)+1, (tmp_patch(3):tmp_patch(4))-tmp_block(3)+1) = true;

        % pull data
        %         Ypatch = data_patch{mpatch};
        Ypatch = get_patch_data(mat_data, tmp_patch, frame_range, true);
        [nr_block, nc_block, T_block] = size(Ypatch);
        if strcmpi(bg_model, 'ring')
            % get the previous estimation
            W_old = W{mpatch};
            Ypatch = reshape(Ypatch, [], T);

            % run regression to get A, C, and W, b0
            if bg_ssub==1
                sn_patch = sn_block(ind_patch);
                [W{mpatch}, b0{mpatch}] = fit_ring_model(Ypatch, A_block, C_block, W_old, thresh_outlier, sn_patch, ind_patch, with_projection);
            else
                % downsapmle data first
                temp = reshape(double(Ypatch)-A_block*C_block, nr_block, nc_block, T_block);
                tmp_b0 = mean(temp, 3);
                b0{mpatch} = tmp_b0(ind_patch);
                Ypatch = imresize(temp, 1./bg_ssub, 'nearest');
                Ypatch = reshape(Ypatch, [], T_block);

                [W{mpatch}, ~] = fit_ring_model(Ypatch, [], [], W_old, thresh_outlier, sn_block(:), [],  with_projection);
                %                 tmp_b0 = imresize(reshape(tmp_b0, size(sn_block)), [nr_block, nc_block]);
                %                 b0{mpatch} = tmp_b0(ind_patch(:));
            end
        elseif strcmpi(bg_model, 'nmf')
            b_old = b{mpatch};
            f_old = f{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            sn_patch = sn_block(ind_patch);
            [b{mpatch}, f{mpatch}] = fit_nmf_model(Ypatch, nb, A_block, C_block, b_old, f_old, thresh_outlier, sn_patch, ind_patch);
        else
            b_old = b{mpatch};
            f_old = f{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            sn_patch = sn_block(ind_patch);
            [b{mpatch}, f{mpatch}, b0{mpatch}] = fit_svd_model(Ypatch, nb, A_block, C_block, b_old, f_old, thresh_outlier, sn_patch, ind_patch);
        end
        [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
        fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);

    end
else
    for mpatch=1:(nr_patch*nc_patch)
        tmp_patch = patch_pos{mpatch};
        tmp_block = block_pos{mpatch};

        A_block = A{mpatch};
        sn_block = sn{mpatch};
        C_block = C{mpatch};

        % stop the updating B because A&C doesn't change in this area
        if isempty(A_block) && (~flag_first)
            [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
            fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
            continue;
        end

        % use ind_patch to indicate pixels within the patch and only
        % update (W, b0) corresponding to these pixels
        ind_patch = false(diff(tmp_block(1:2))+1, diff(tmp_block(3:4))+1);
        ind_patch((tmp_patch(1):tmp_patch(2))-tmp_block(1)+1, (tmp_patch(3):tmp_patch(4))-tmp_block(3)+1) = true;

        % pull data
        Ypatch = get_patch_data(mat_data, tmp_patch, frame_range, true);
        [nr_block, nc_block, T_block] = size(Ypatch);
        if strcmpi(bg_model, 'ring')
            % get the previous estimation
            W_old = W{mpatch};
            Ypatch = reshape(Ypatch, [], T_block);

            % run regression to get A, C, and W, b0
            if bg_ssub==1
                sn_patch = sn_block(ind_patch);
                [W{mpatch}, b0{mpatch}] = fit_ring_model(Ypatch, A_block, C_block, W_old, thresh_outlier, sn_patch, ind_patch, with_projection);
            else
                % downsapmle data first
                temp = reshape(double(Ypatch)-A_block*C_block, nr_block, nc_block, T_block);
                tmp_b0 = mean(temp, 3);
                b0{mpatch} = tmp_b0(ind_patch);
                Ypatch = imresize(temp, 1./bg_ssub, 'nearest');
                Ypatch = reshape(Ypatch, [], T_block);

                [W{mpatch}, ~] = fit_ring_model(Ypatch, [], [], W_old, thresh_outlier, sn_block(:), [],  with_projection);
                %                 tmp_b0 = imresize(reshape(tmp_b0, size(sn_block)), [nr_block, nc_block]);
                %                 b0{mpatch} = tmp_b0(ind_patch(:));
            end
        elseif strcmpi(bg_model, 'nmf')
            b_old = b{mpatch};
            f_old = f{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            sn_patch = sn_block(ind_patch);
            [b{mpatch}, f{mpatch}] = fit_nmf_model(Ypatch, nb, A_block, C_block, b_old, f_old, thresh_outlier, sn_patch, ind_patch);
        else
            b_old = b{mpatch};
            f_old = f{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            sn_patch = sn_block(ind_patch);
            [b{mpatch}, f{mpatch}, b0{mpatch}] = fit_svd_model(Ypatch, nb, A_block, C_block, b_old, f_old, thresh_outlier, sn_patch, ind_patch);
        end
        [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
        fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);

    end
end
obj.b = b;
obj.f = f;
obj.b0 = b0;
obj.W = W;
obj.b0_new = obj.reconstruct_b0();
obj.A_prev = obj.A;
obj.C_prev = obj.C;

%% save the results to log
fprintf('Finished updating background using %s model.\n', bg_model);

fprintf(flog, '[%s]\b', get_minute());
fprintf(flog, 'Finished updating background using %s model.\n', bg_model);
if obj.options.save_intermediate
    bg.b = obj.b;
    bg.f = obj.f;
    bg.b0 = obj.b0;
    bg.W = obj.W;
    tmp_str = get_date();
    tmp_str=strrep(tmp_str, '-', '_');
    eval(sprintf('log_data.bg_%s = bg;', tmp_str));
    fprintf(flog, '\tThe results were saved as intermediate_results.bg_%s\n\n', tmp_str);
end
fclose(flog);


function [sos,info] = sossolve(sos,solver_options)
% SOSSOLVE --- Solve a sum of squares program.
%
% SOSP = sossolve(SOSP)
%
% SOSP is the SOS program to be solved.
%
% Alternatively, SOSP = sossolve(SOSP,SOLVER_OPT) also defines the solver
% and/or the solver-specific options respectively by fields
%
% SOLVER_OPT.solver (name of the solver)
% SOLVER_OPT.params (a structure containing solver-specific parameters)
%
% The default values for solvers is 'SeDuMi' with parameter ALG = 2, which
% uses the xz-linearization in the corrector and parameter tol =1e-9. See
% SeDuMi help files or user manual for more detail.
%
% Using a second output argument such as [SOSP,INFO] = sossolve(SOSP) will
% return in INFO numerous information concerning feasibility  and CPU time
% that is generated by the SDP solver.
%

% This file is part of SOSTOOLS - Sum of Squares Toolbox ver 3.00.
%
% Copyright (C)2002, 2004, 2013  A. Papachristodoulou (1), J. Anderson (1),
%                                G. Valmorbida (1), S. Prajna (2),
%                                P. Seiler (3), P. A. Parrilo (4)
% (1) Department of Engineering Science, University of Oxford, Oxford, U.K.
% (2) Control and Dynamical Systems - California Institute of Technology,
%     Pasadena, CA 91125, USA.
% (3) Aerospace and Engineering Mechanics Department, University of
%     Minnesota, Minneapolis, MN 55455-0153, USA.
% (4) Laboratory for Information and Decision Systems, M.I.T.,
%     Massachusetts, MA 02139-4307
%
% Send bug reports and feedback to: sostools@cds.caltech.edu
%
% 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 2 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/>.
%

% 12/25/01 - SP
% 01/05/02 - SP - primal
% 01/07/02 - SP - objective
% aug/13 - JA,GV - CDSP,SDPNAL,SDPA solvers and SOS matrix decomposition

if (nargin==1)
   %Default options from old sossolve
   solver_options.solver = 'sdpt3';
   solver_options.params.tol = 1e-9;
   solver_options.params.alg = 2;
elseif ((nargin==2) & ~isnumeric('solver_options') )%2 arguments given,
    if ~isfield(solver_options,'solver')
       solver_options.solver = 'sdpt3';
    end
    if ~isfield(solver_options,'params')
        solver_options.params.tol = 1e-9;%default values for SeDuMi
        solver_options.params.alg = 2;
    end
end

%whenever nargin>=2 options are overwritten
if (nargin==3)
    error('Current SOSTOOLS version does not support call to sossolve with 3 arguments, see manual.');
end;

if ~isempty(sos.solinfo.x)
    error('The SOS program is already solved.');
end;

% Adding slack variables to inequalities
sos.extravar.idx{1} = sos.var.idx{sos.var.num+1};
% SOS variables
I = [find(strcmp(sos.expr.type,'ineq')), find(strcmp(sos.expr.type,'sparse')), find(strcmp(sos.expr.type,'sparsemultipartite'))];
if ~isempty(I)
    sos = addextrasosvar(sos,I);
end;
% SOS variables type II (restricted on interval)
I = find(strcmp(sos.expr.type,'posint'));
if ~isempty(I)
    sos = addextrasosvar2(sos,I);
end;

% Processing all expressions

Atf = []; bf = [];
for i = 1:sos.expr.num
    Atf = [Atf, sos.expr.At{i}];
    bf = [bf; sos.expr.b{i}];
end;

% Processing all variables
[At,b,K,RR] = processvars(sos,Atf,bf);

% Objective function
c = sparse(size(At,1),1);

%% Added by PAP, for compatibility with MATLAB 6.5
if isempty(sos.objective);
    sos.objective = zeros(size(c(1:sos.var.idx{end}-1)));
end
%% End added stuff

c(1:sos.var.idx{end}-1) = c(1:sos.var.idx{end}-1) + sos.objective;       % 01/07/02
c = RR'*c;

pars = solver_options.params;

if strcmp(lower(solver_options.solver),'sedumi')
    % SeDuMi in action
    disp(['Size: ' num2str(size(At))]);
    disp([' ']);
    [x,y,info] = sedumi(At,b,c,K,pars);

elseif strcmp(lower(solver_options.solver),'sdpt3')
    % SDPT3 in action
    smallblkdim = 50;
    save sostoolsdata_forSDPT3 At b c K smallblkdim;
    [blk,At2,C2,b2] = read_sedumi('sostoolsdata_forSDPT3.mat');
    delete sostoolsdata_forSDPT3.mat;
    [obj,X,y,Z,infoSDPT] = sqlp(blk,At2,C2,b2,pars);
    x = zeros(length(c),1);
    cellidx = 1;
    if K.f ~= 0
        x(1:K.f) = X{1}(:);
        cellidx = 2;
    end;
    if K.s(1) ~= 0
        idxX = 1;
        idx = K.f+1;
        smblkdim = 100;
        deblkidx = find(K.s > smblkdim);
        spblkidx = find(K.s <= smblkdim);
        blknnz = [0 cumsum(K.s.*K.s)];
        for i = deblkidx
            dummy = X{cellidx};
            x(idx+blknnz(i):idx+blknnz(i+1)-1) = dummy(:);
            cellidx = cellidx+1;
        end;
        for i = spblkidx
            dummy = X{cellidx}(idxX:idxX+K.s(i)-1,idxX:idxX+K.s(i)-1);
            x(idx+blknnz(i):idx+blknnz(i+1)-1) = dummy(:);
            idxX = idxX+K.s(i);
        end;
    end;
	info.cpusec = infoSDPT.cputime;
	info.iter = infoSDPT.iter;
	if infoSDPT.termcode == 1
		info.pinf = 1;
	else
		info.pinf = (infoSDPT.pinfeas>0.1);
	end;
	if infoSDPT.termcode == 2
		info.dinf = 1;
	else
		info.dinf = (infoSDPT.dinfeas>0.1);
	end;
	if infoSDPT.termcode<= 0
		info.numerr = infoSDPT.termcode;
	else
		info.numerr = 0;
	end;

elseif strcmp(lower(solver_options.solver),'csdp') %6/6/13 JA CSDP interface
    %CSDP in action
    if exist('solver_options.params')
        pars = solver_options.params;
    else
        pars.objtol = 1e-9;
        pars.printlevel = 1;
    end
    if (isfield(K,'f')) %Convert free vars to non-negative LP vars
            n_free = K.f;
            [A,b,c,K] = convertf(At,b,c,K); %K.f set to zero
            At = A';
    end
    c = full(c);
    [x,y,z,info] = csdp(At,b,c,K,pars);
    c=sparse(c);
    % 7/6/13 JA Remove extra entries from x corresponding to LP vars
    if (isfield(K,'f')) %Convert free vars to non-negative LP vars
           index = [n_free+1:2*n_free];
           x(1:n_free) = x(1:n_free)-x(index);
           x(index) = [];
           At(index,:)=[];
           c(index) = [];
    end
    if info~=0
       info.dinf=1;
       info.pinf=1;
    else
       info.dinf=0;
       info.pinf=0;
    end;

elseif strcmp(lower(solver_options.solver),'sdpnal') %6/11/13 JA SDPNAL interface
    % SDPNAL in action
    smallblkdim = 50;
    save sostoolsdata_forSDPNAL At b c K smallblkdim;
    [blk,At2,C2,b2] = read_sedumi('sostoolsdata_forSDPNAL.mat');
    delete sostoolsdata_forSDPNAL.mat;
    try
        [obj,X,y,Z,info] = sdpnal(blk,At2,C2,b2,pars); %run history not returned;
    catch
        [obj,X,y,Z] = sdpnal(blk,At2,C2,b2,pars); %run history not returned;
        info = [];
    end

    x = zeros(length(c),1);
    cellidx = 1;
    if K.f ~= 0
        x(1:K.f) = X{1}(:);
        cellidx = 2;
    end;
    if K.s(1) ~= 0
        idxX = 1;
        idx = K.f+1;
        smblkdim = 100;
        deblkidx = find(K.s > smblkdim);
        spblkidx = find(K.s <= smblkdim);
        blknnz = [0 cumsum(K.s.*K.s)];
        for i = deblkidx
            dummy = X{cellidx};
            x(idx+blknnz(i):idx+blknnz(i+1)-1) = dummy(:);
            cellidx = cellidx+1;
        end;
        for i = spblkidx
            dummy = X{cellidx}(idxX:idxX+K.s(i)-1,idxX:idxX+K.s(i)-1);
            x(idx+blknnz(i):idx+blknnz(i+1)-1) = dummy(:);
            idxX = idxX+K.s(i);
        end;
    end;

    if ~isempty(info)
        if info.pinfeas>1e-6||info.pinfeas>1e-6
            info.dinf=1;
            info.pinf=1;
        else
            info.dinf=0;
            info.pinf=0;
        end;
    else
        info.dinf=1;
        info.pinf=1;
    end

    elseif strcmp(lower(solver_options.solver),'sdpa')
    % SDPA in action

    disp(['Size: ' num2str(size(At))]);
    disp([' ']);
    [x,y,info]=sedumiwrap(At',b,c,K,[],pars);

    info.primalObj
    info.primalError

    info.dualObj
    info.dualError

    if info~=0
       info.dinf=1;
       info.pinf=1;
    else
       info.dinf=0;
       info.pinf=0;
    end;

end;
disp([' ']);
disp(['Residual norm: ' num2str(norm(At'*x-b))]);
disp([' ']);
sos.solinfo.x = x;
sos.solinfo.y = y;
sos.solinfo.RRx = RR*x;
sos.solinfo.RRy = RR*(c-At*y);    % inv(RR') = RR
sos.solinfo.info = info;
sos.solinfo.solverOptions = solver_options;
disp(info)

%return;

% Constructing the (primal and dual) solution vectors and matrices
% If you want to have them, comment/delete the return command above.
% In the future version, these primal and dual solutions will be computed only
% when they are needed. We don't want to store redundant info.

for i = 1:sos.var.num
    switch sos.var.type{i}
    case 'poly'
        sos.solinfo.var.primal{i} = sos.solinfo.RRx(sos.var.idx{i}:sos.var.idx{i+1}-1);
        sos.solinfo.var.dual{i} = sos.solinfo.RRy(sos.var.idx{i}:sos.var.idx{i+1}-1);
    case 'sos'
        primaltemp = sos.solinfo.RRx(sos.var.idx{i}:sos.var.idx{i+1}-1);
        dualtemp = sos.solinfo.RRy(sos.var.idx{i}:sos.var.idx{i+1}-1);
        sos.solinfo.var.primal{i} = reshape(primaltemp,sqrt(length(primaltemp)),sqrt(length(primaltemp)));
        sos.solinfo.var.dual{i} = reshape(dualtemp,sqrt(length(dualtemp)),sqrt(length(dualtemp)));
    end;
end;

for i = 1:sos.extravar.num
    primaltemp = sos.solinfo.RRx(sos.extravar.idx{i}:sos.extravar.idx{i+1}-1);
    dualtemp = sos.solinfo.RRy(sos.extravar.idx{i}:sos.extravar.idx{i+1}-1);
    sos.solinfo.extravar.primal{i} = reshape(primaltemp,sqrt(length(primaltemp)),sqrt(length(primaltemp)));
    sos.solinfo.extravar.dual{i} = reshape(dualtemp,sqrt(length(dualtemp)),sqrt(length(dualtemp)));
end;

sos.solinfo.decvar.primal = sos.solinfo.RRx(1:sos.var.idx{1}-1);
sos.solinfo.decvar.dual = sos.solinfo.RRy(1:sos.var.idx{1}-1);

% ====================================================================================
function sos = addextrasosvar(sos,I)
% Adding slack SOS variables to inequalities

for i = I

    numstates = size(sos.expr.Z{i},2);%GV&JA 6/12/2013

    % Creating extra variable
    maxdeg = full(max(sum(sos.expr.Z{i},2)));
    mindeg = full(min(sum(sos.expr.Z{i},2)));
    Z = monomials(numstates,[floor(mindeg/2):ceil(maxdeg/2)]);
    %disp(['Original : ',num2str(size(Z,1))]);

    % Discarding unnecessary monomials
    maxdegree = max(sos.expr.Z{i},[],1)/2;
    mindegree = min(sos.expr.Z{i},[],1)/2;
    j = 1;
    while (j <= size(Z,1))
        Zdummy1 = maxdegree-Z(j,:);
        Zdummy2 = Z(j,:)-mindegree;
        idx = find([Zdummy1, Zdummy2]<0);
        if ~isempty(idx)
            Z = [Z(1:j-1,:); Z(j+1:end,:)];
        else
            j = j+1;
        end;
    end;
    %disp(['Optimized : ',num2str(size(Z,1))]);

    % Convex hull algorithm
    if strcmp(sos.expr.type{i},'sparse')
        Z2 = sos.expr.Z{i}/2;
        Z = inconvhull(full(Z),full(Z2));
        Z = makesparse(Z);
        %disp(['Optimized again : ',num2str(size(Z,1))]);
    end;

    if strcmp(sos.expr.type{i},'sparsemultipartite')
        Z2 = sos.expr.Z{i}/2;
        info2 = sos.expr.multipart{i};%the vectors of independent variables
        sizeinfo2m = length(info2);
        vecindex = [];
        for indm = 1:sizeinfo2m%for each set of independent variables
            sizeinfo2n(indm) = length(info2{indm});
            for indn = 1:sizeinfo2n(indm)
                if isfield(sos,'symvartable')%
                    varcheckindex = find(info2{indm}(indn)==sos.symvartable);
                    if ~isempty(varcheckindex)
                        vecindex{indm}(indn) = varcheckindex;
                    else
                        vecindex{indm}(indn) = length(info2{1})+find(info2{indm}(indn)==sos.varmat.symvartable);%GV&JA 6/12/2013
                    end

                else
                    % PJS 9/12/13: Update code to handle polynomial objects
                    var = info2{indm}(indn);
                    cvartable = char(sos.varmat.vartable);

                    if ispvar(var)
                        % Convert to string representation
                        var = var.varname;
                    end
                    varcheckindex = find(strcmp(var,sos.vartable));
                    if ~isempty(varcheckindex)
                        vecindex{indm}(indn) = varcheckindex;
                    else
                        vecindex{indm}(indn) = length(info2{1}) + find(strcmp(var,cvartable));
                    end

                    % PJS 9/12/13: Original Code to handle polynomial objects
                    %vecindex{indm}(indn) = find(strcmp(info2{indm}(indn).varname,sos.vartable));
                end;
            end
        end
        Z = sparsemultipart(full(Z),full(Z2),vecindex);
        Z = makesparse(Z);
    end;

    dimp = size(sos.expr.b{i},2);

    % Adding slack variables
    sos.extravar.num = sos.extravar.num + 1;
    var = sos.extravar.num;
    sos.extravar.Z{var} = makesparse(Z);
    [T,ZZ] = getconstraint(Z);
    sos.extravar.ZZ{var} = ZZ;
    sos.extravar.T{var} = T';
    %sos.extravar.idx{var+1} = sos.extravar.idx{var}+size(Z,1)^2;%GVcomment the next slack variable starts in the column i+dim(Z)^2 - the elements of the vectorized square matrix

    sos.extravar.idx{var+1} = sos.extravar.idx{var}+(size(Z,1)*dimp)^2;
    for j = 1:sos.expr.num
        sos.expr.At{j} = [sos.expr.At{j}; ...
                sparse(size(sos.extravar.T{var},1)*dimp^2,size(sos.expr.At{j},2))];
    end

    ZZ = flipud(ZZ);
    T = flipud(T);

    Zcheck = sos.expr.Z{i};
    %this is for the matrix case

    if dimp==1
        % JFS 6/3/2003: Ensure correct size:
        pc.Z = sos.extravar.ZZ{var};
        pc.F = -speye(size(pc.Z,1));
        [R1,R2,newZ] = findcommonZ(sos.expr.Z{i},pc.Z);
        % JFS 6/3/2003: Ensure correct size:

        if isempty(sos.expr.At{i})
            sos.expr.At{i} = sparse(size(sos.expr.At{i},1),size(R1,1));
        end
        %------------
        sos.expr.At{i} = sos.expr.At{i}*R1;
        lidx = sos.extravar.idx{var};
        uidx = sos.extravar.idx{var+1}-1;
        sos.expr.At{i}(lidx:uidx,:) = sos.expr.At{i}(lidx:uidx,:) - sos.extravar.T{var}*pc.F*R2;
        sos.expr.b{i} = R1'*sos.expr.b{i};
        sos.expr.Z{i} = newZ;

    else

        [R1,R2,Znew] = findcommonZ(Zcheck,ZZ);

        R1 = fliplr(R1);
        R2 = fliplr(R2);
        Znew = flipud(Znew);

        R1sum = sum(R1,1);
        T = R2'*T;

        ii = 1;
        sig_ZZ = size(ZZ,1);
        sig_Z = size(Z,1);
        sig_Znew = size(Znew,1);

        Tf = sparse(dimp^2*sig_Znew,(dimp*sig_Z)^2);
        Sv = sparse(sig_Znew*dimp^2,1);
        for j = 1:sig_Znew
            Mt0 = sparse(dimp,dimp*sig_Z^2);
            for k = 1:sig_Z
                Mt0(:, (dimp*sig_Z)*(k-1)+1:(dimp*sig_Z)*k) = kron(eye(dimp),T(j,(sig_Z)*(k-1)+1:(sig_Z)*k));
            end
            Tf((j-1)*dimp^2+1:j*dimp^2,:) = kron(eye(dimp),Mt0);

            if R1sum(j)==1
                Sv((j-1)*dimp^2+1:j*dimp^2)= reshape(sos.expr.b{i}( dimp*(ii-1)+1:dimp*ii,:)',dimp^2,1);
                if ii<size(Zcheck,1)
                    ii = ii+1;
                end
            else
                sos.expr.At{i} = [ sos.expr.At{i}(:,1:(j-1)*dimp^2) sparse(size(sos.expr.At{i},1),dimp^2) sos.expr.At{i}(:,(j-1)*dimp^2+1:end)];
            end
        end

        lidx = sos.extravar.idx{var};
        uidx = sos.extravar.idx{var+1}-1;
        sos.expr.At{i}(lidx:uidx,:) =  Tf';
        sos.expr.b{i} =  Sv;

    end
end;

% ====================================================================================
function sos = addextrasosvar2(sos,I)
% Adding slack SOS variables type II

numstates = size(sos.expr.Z{1},2);
for i = I
    % Creating extra variable
    maxdeg = full(max(sum(sos.expr.Z{i},2)));
    mindeg = full(min(sum(sos.expr.Z{i},2)));
    Z = monomials(numstates,[floor(mindeg/2):ceil(maxdeg/2)]);

    % Discarding unnecessary monomials
    maxdegree = max(sos.expr.Z{i},[],1)/2;
    mindegree = min(sos.expr.Z{i},[],1)/2;
    j = 1;
    while (j <= size(Z,1))
        Zdummy1 = maxdegree-Z(j,:);
        Zdummy2 = Z(j,:)-mindegree;
        idx = find([Zdummy1, Zdummy2]<0);
        if ~isempty(idx)
            Z = [Z(1:j-1,:); Z(j+1:end,:)];
        else
            j = j+1;
        end;
    end;

    % Add the variables

    for k = 0:1

    sos.extravar.num = sos.extravar.num + 1;
    var = sos.extravar.num;
    sos.extravar.Z{sos.extravar.num} = makesparse(Z);
    [T,ZZ] = getconstraint(Z);
    sos.extravar.ZZ{var} = ZZ;
    sos.extravar.T{var} = T';
    sos.extravar.idx{var+1} = sos.extravar.idx{var}+size(Z,1)^2;
    for j = 1:sos.expr.num
        sos.expr.At{j} = [sos.expr.At{j}; ...
                sparse(size(sos.extravar.T{var},1),size(sos.expr.At{j},2))];
    end;

    % Modifying expression
    degoffset = [k sparse(1,numstates-1)];
    pc.Z = sos.extravar.ZZ{var} + degoffset(ones(size(sos.extravar.ZZ{var},1),1),:);
    pc.F = -speye(size(pc.Z,1));
    [R1,R2,newZ] = findcommonZ(sos.expr.Z{i},pc.Z);
    % JFS 6/3/2003: Ensure correct size:
    if isempty(sos.expr.At{i})
       sos.expr.At{i} = sparse(size(sos.expr.At{i},1),size(R1,1));
    end
    %------------
    sos.expr.At{i} = sos.expr.At{i}*R1;
    lidx = sos.extravar.idx{var};
    uidx = sos.extravar.idx{var+1}-1;
    sos.expr.At{i}(lidx:uidx,:) = sos.expr.At{i}(lidx:uidx,:) - sos.extravar.T{var}*pc.F*R2;
    sos.expr.b{i} = R1'*sos.expr.b{i};
    sos.expr.Z{i} = newZ;

    Z = Z(find(Z<maxdegree));    % Discard the unnecessary monomial for the second variable

    end;

end;

% ====================================================================================
function [At,b,K,RR] = processvars(sos,Atf,bf)
% Processing all variables

% Decision variables
K.s = [];
K.f = sos.var.idx{1}-1;
RR = speye(K.f);

% Polynomial and SOS variables
for i = 1:sos.var.num
    switch sos.var.type{i}
    case 'poly'
        sizeX = sos.var.idx{i+1}-sos.var.idx{i};
        startidx = sos.var.idx{i};
        RR = spantiblkdiag(RR,speye(sizeX));
        K.f = sizeX+K.f;
    case 'sos'
        sizeX = sqrt(sos.var.idx{i+1}-sos.var.idx{i});
        startidx = sos.var.idx{i};
        RR = spblkdiag(RR,speye(sizeX^2));
        K.s = [K.s sizeX];
    end;
end;

for i = 1:sos.extravar.num
    sizeX = sqrt(sos.extravar.idx{i+1}-sos.extravar.idx{i});
    startidx = sos.extravar.idx{i};
    RR = spblkdiag(RR,speye(sizeX^2));
    K.s = [K.s sizeX];
end;

At = RR'*Atf;

b = bf;


function [OutputSurfaces, OutputMris] = bst_warp(destPts, srcPts, SurfaceFiles, MriFiles, OutputTag, OutputDir, isSurfaceOnly)
%BST_WARP:  Deform an anatomy (MRI+surfaces) to fit a set of landmarks.
%
% USAGE:  bst_warp(destPts, srcPts, SurfaceFiles, MriFiles, OutputTag, OutputDir, isSurfaceOnly=0)
%
% INPUTS:
%     - destPts      : landmarks in real head coordinates, i.e. from a Polhemus or
%     - srcPts       : landmarks in intial anatomy coordinates
%     - SurfaceFiles : Cell array of the surface files to be warped (full path)
%     - MriFiles     : Cell array of the MRI files to be warped (full path)
%     - OutputTag    : Tag to add at the end of the filenames of the wrapped files
%     - OutputDir    : Output directory
%     - isSurfaceOnly: If 1, do not warp the MRI

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Felix Darvas, 2005
%          Louis Hovasse, 2009
%          Francois Tadel, 2010-2022

if (nargin < 7) || isempty(isSurfaceOnly)
    isSurfaceOnly = 0;
end
if ischar(MriFiles)
    MriFiles = {MriFiles};
end
OutputSurfaces = {};
OutputMris = {};

%% ===== COMPUTE WARP PARAMETER =====
% Compute warp transform parameters
[W,A] = warp_transform(srcPts, destPts);

%% ===== WARP SURFACES =====
% Progress bar
bst_progress('start', 'Warp anatomy', 'Warping surfaces...', 0, length(SurfaceFiles));
% Loop on all surface files
for i = 1:length(SurfaceFiles)
    bst_progress('inc', 1);
    % Read input
    sSurf = load(file_fullpath(SurfaceFiles{i}));
    % Warp surface
    switch file_gettype(SurfaceFiles{i})
        case 'fibers'
            nRows = size(sSurf.Points, 1);
            Points = permute(reshape(sSurf.Points, [], 1, 3), [1 3 2]);
            Points = warp_lm(Points, A, W, srcPts) + Points;
            Points = reshape(permute(Points, [1 3 2]), nRows, [], 3);
            sSurfNew.Points = Points;
            sSurfNew.Header = sSurf.Header;
            sSurfNew.Colors = sSurf.Colors;
            sSurfNew.Scouts = sSurf.Scouts;
        case 'fem'
            sSurfNew.Elements     = sSurf.Elements;
            sSurfNew.Tissue       = sSurf.Tissue;
            sSurfNew.TissueLabels = sSurf.TissueLabels;
            sSurfNew.Tensors      = sSurf.Tensors;
            sSurfNew.Vertices     = warp_lm(sSurf.Vertices, A, W, srcPts) + sSurf.Vertices;
        otherwise
            sSurfNew.Faces    = sSurf.Faces;
            sSurfNew.Vertices = warp_lm(sSurf.Vertices, A, W, srcPts) + sSurf.Vertices;
    end
    % Add tag to comment
    sSurfNew.Comment  = [sSurf.Comment ' warped'];
    % Copy previous field
    if isfield(sSurf, 'Atlas') && ~isempty(sSurf.Atlas)
        sSurfNew.Atlas = sSurf.Atlas;
    end
    if isfield(sSurf, 'Reg') && ~isempty(sSurf.Reg)
        sSurfNew.Reg = sSurf.Reg;
    end
    if isfield(sSurf, 'History') && ~isempty(sSurf.History)
        sSurfNew.History = sSurf.History;
    end
    % History: Warp
    sSurfNew = bst_history('add', sSurfNew, 'warp', 'Surface deformed to match the head points from channel file.');
    % Output filename
    [tmp__, fileBase, fileExt] = bst_fileparts(SurfaceFiles{i});
    OutputSurfaces{end+1} = bst_fullfile(OutputDir, [fileBase, OutputTag, fileExt]);
    % Save new file
    bst_save(OutputSurfaces{end}, sSurfNew, 'v7');
end

%% ====== COMPUTE TRANSFORMATION ========
bst_progress('start', 'Warp anatomy', 'Preparing MRI...');
% Load reference MRI (first in the list)
sMriSrc = in_mri_bst(MriFiles{1});
% Transform landmarks into MRI coordinates (meters)
srcPts_mr  = cs_convert(sMriSrc, 'scs', 'voxel', srcPts);
destPts_mr = cs_convert(sMriSrc, 'scs', 'voxel', destPts);
% Compute warp transform in MR coordinates
[Wmr,Amr] = warp_transform(srcPts_mr, destPts_mr);
% Compute "inverse" warp transform in MR coordinates
[Wmr_inv,Amr_inv] = warp_transform(destPts_mr, srcPts_mr);

%% ===== WRAP FIDUCIALS =====
% % SCS transformation
% destSCS.NAS = warp_lm(sMriSrc.SCS.NAS, Amr, Wmr, srcPts_mr) + sMriSrc.SCS.NAS;
% destSCS.LPA = warp_lm(sMriSrc.SCS.LPA, Amr, Wmr, srcPts_mr) + sMriSrc.SCS.LPA;
% destSCS.RPA = warp_lm(sMriSrc.SCS.RPA, Amr, Wmr, srcPts_mr) + sMriSrc.SCS.RPA;
% NCS transformation
if isfield(sMriSrc, 'NCS') && isfield(sMriSrc.NCS, 'AC') && ~isempty(sMriSrc.NCS.AC) && ~isempty(sMriSrc.NCS.PC) && ~isempty(sMriSrc.NCS.IH)
    destNCS.AC = warp_lm(sMriSrc.NCS.AC, Amr, Wmr, srcPts_mr) + sMriSrc.NCS.AC;
    destNCS.PC = warp_lm(sMriSrc.NCS.PC, Amr, Wmr, srcPts_mr) + sMriSrc.NCS.PC;
    destNCS.IH = warp_lm(sMriSrc.NCS.IH, Amr, Wmr, srcPts_mr) + sMriSrc.NCS.IH;
    destNCS.R = [];
    destNCS.T = [];
    destNCS.Origin = [];
else
    destNCS = [];
end

%% ===== WARP MRI =====
bst_progress('start', 'Warp anatomy', 'Warping MRI...', 0, 100*length(MriFiles));
for iMri = 1:length(MriFiles)
    bst_progress('text', sprintf('Warping MRI...   [%d/%d]', iMri, length(MriFiles)));
    % Load MRI (the first one is already loaded above)
    if (iMri >= 2)
        sMriSrc = in_mri_bst(MriFiles{iMri});
    end
    % If warping of the MRI volumes is enabled
    if ~isSurfaceOnly
        % Process coordinates by blocks: Doing all at once costs too much memory, doing only 1 at a time costs too much time
        sizeMri = size(sMriSrc.Cube);
        if (length(sizeMri) > 3)
            error('No support for 4D volumes. Ask on the Brainstorm for help.');
        end
        newCube = ones(sizeMri);
        nVoxels = numel(newCube);
        BLOCK_SIZE = 10000;
        nBlocks = ceil(nVoxels / BLOCK_SIZE);
        ix0 = 1;
        for i = 1:nBlocks
            % Increment progress bar
            if (mod(i, round(nBlocks/100)) == 0)
                bst_progress('inc', 1);
            end
            % Get indices in dest volume
            ix1 = min(ix0 - 1 + BLOCK_SIZE, nVoxels);
            [xv,yv,zv] = ind2sub(sizeMri, ix0:ix1);
            rv = [xv;yv;zv]';
            % Unwarp MRI coordinates
            rv_inv = warp_lm(rv, Amr_inv, Wmr_inv, destPts_mr) + rv;
            % Round coordinates (nearest neighor interpolation)
            rv_inv = round(rv_inv);
            % Remove values that are outside the volume
            iOutside = find(sum((rv_inv < 1) | (rv_inv > repmat(sizeMri,size(rv_inv,1),1)),2) > 0);
            rv_inv(iOutside,:) = 1;
            % Get indices from xyz coordinates
            ix_inv = sub2ind(sizeMri, rv_inv(:,1), rv_inv(:,2), rv_inv(:,3));
            % Get values in initial volume
            newCube(ix0:ix1) = sMriSrc.Cube(ix_inv);
            % Set values outside of the volume to zero
            newCube(iOutside) = 0;
            % Go to next block
            ix0 = ix1 + 1;
        end
        newComment = [sMriSrc.Comment, ' warped'];
    else
        newCube = sMriSrc.Cube;
        newComment = sMriSrc.Comment;
    end

    % === SAVE NEW MRI ===
    % Create new structure
    sMriDest = sMriSrc;
    sMriDest.Cube    = newCube;
    sMriDest.Comment = newComment;
    sMriDest.NCS     = destNCS;
    % History: Copy previous field
    if isfield(sMriSrc, 'History') && ~isempty(sMriSrc.History)
        sMriDest.History = sMriSrc.History;
    end
    % History: Warp
    sMriDest = bst_history('add', sMriDest, 'warp', 'MRI deformed to match the head points from channel file.');
    % Output filename
    [tmp__, fileBase, fileExt] = bst_fileparts(MriFiles{iMri});
    OutputMris{iMri} = bst_fullfile(OutputDir, [fileBase, OutputTag, fileExt]);
    % Save new MRI
    bst_save(OutputMris{iMri}, sMriDest, 'v7');
end

bst_progress('stop');

end

%% =====================================================================================
%  ===== HELPER FUNCTIONS ==============================================================
%  =====================================================================================

%% ===== WARP TRANSFORM =====
% Calculates nonlinear transformation coefficents (see Ermer's Thesis)
% INPUT:
%    - p : Landmarks in system 1
%    - q : Landmarks in system 2
% OUTPUT:
%    - e : Warp energy
function [W,A,e] = warp_transform(p, q)
    N = size(p,1);
    px = repmat(p(:,1), 1, N);
    py = repmat(p(:,2), 1, N);
    pz = repmat(p(:,3), 1, N);
    K = sqrt((px - px').^2 + (py - py').^2 + (pz - pz').^2);

    P = [p, ones(N,1)];
    L = [K P; P' zeros(4,4)];
    D = [q - p; zeros(4,3)];
    warning off
    H = L \ D;
    warning on
    if any(isnan(H))
        H = pinv(L) * D;
    end
    W = H(1:N,:);
    A = H(N+1:end, :);
    e = sum(diag(W' * K * W));
end

%% ===== WARP LANDMARKS: VECTORIZED =====
% Performs warp transformation with linear 3D RFB (see Ermer's Thesis)
function rw = warp_lm(r, A, W, p)
    rw = r * A(1:3,1:3);
    rw = bst_bsxfun(@plus, rw, A(4,:));
    np = size(p,1);
    U = sqrt(bst_bsxfun(@minus, repmat(r(:,1),1,np), p(:,1)') .^ 2 + ...
             bst_bsxfun(@minus, repmat(r(:,2),1,np), p(:,2)') .^ 2 + ...
             bst_bsxfun(@minus, repmat(r(:,3),1,np), p(:,3)') .^ 2);
    rw = rw + U * W;
end

%% ===== WARP LANDMARKS: LOOP VERSION =====
% This function is twice slower
% function rw = warp_lm(r, A, W, p)
%     rw = r * A(1:3,1:3) + repmat(A(4,:), size(r,1), 1);
%     for i = 1:size(p,1)
%         U = sqrt(sum((r - repmat(p(i,:), size(r,1), 1)) .^ 2, 2));
%         rw = rw + U * W(i,:);
%     end
% end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%                                                                  %%%%%
%%%%    IEEE PES Power Grid Library - Optimal Power Flow - v21.07     %%%%%
%%%%          (https://github.com/power-grid-lib/pglib-opf)           %%%%%
%%%%             Benchmark Group - Active Power Increase              %%%%%
%%%%                         29 - July - 2021                         %%%%%
%%%%                                                                  %%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function mpc = pglib_opf_case39_epri__api
mpc.version = '2';
mpc.baseMVA = 100.0;

%% bus data
%	bus_i	type	Pd	Qd	Gs	Bs	area	Vm	Va	baseKV	zone	Vmax	Vmin
mpc.bus = [
	1	 1	 160.91	 44.20	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	2	 1	 0.0	 0.0	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	3	 1	 530.88	 2.40	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	4	 1	 824.35	 184.00	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	5	 1	 0.0	 0.0	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	6	 1	 0.0	 0.0	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	7	 1	 385.47	 84.00	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	8	 1	 860.62	 176.60	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	9	 1	 6.50	 -66.60	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	10	 1	 0.0	 0.0	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	11	 1	 0.0	 0.0	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	12	 1	 14.06	 88.00	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	13	 1	 0.0	 0.0	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	14	 1	 0.0	 0.0	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	15	 1	 527.58	 153.00	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	16	 1	 542.42	 32.30	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	17	 1	 0.0	 0.0	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	18	 1	 260.49	 30.00	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	19	 1	 0.0	 0.0	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	20	 1	 1121.12	 103.00	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	21	 1	 451.74	 115.00	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	22	 1	 0.0	 0.0	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	23	 1	 408.05	 84.60	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	24	 1	 308.60	 -92.20	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	25	 1	 369.31	 47.20	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	26	 1	 229.17	 17.00	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	27	 1	 463.28	 75.50	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	28	 1	 339.63	 27.60	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	29	 1	 467.41	 26.90	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	30	 2	 0.0	 0.0	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	31	 3	 15.17	 4.60	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	32	 2	 0.0	 0.0	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	33	 2	 0.0	 0.0	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	34	 2	 0.0	 0.0	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	35	 2	 0.0	 0.0	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	36	 2	 0.0	 0.0	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	37	 2	 0.0	 0.0	 0.0	 0.0	 2	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	38	 2	 0.0	 0.0	 0.0	 0.0	 3	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
	39	 2	 1820.16	 250.00	 0.0	 0.0	 1	    1.00000	    0.00000	 345.0	 1	    1.06000	    0.94000;
];

%% generator data
%	bus	Pg	Qg	Qmax	Qmin	Vg	mBase	status	Pmax	Pmin
mpc.gen = [
	30	 159.0	 59.0	 400.0	 -282.0	 1.0	 100.0	 1	 318	 0.0; % COW
	31	 789.0	 0.0	 854.4	 -854.4	 1.0	 100.0	 1	 1578	 0.0; % COW
	32	 1277.5	 0.0	 1278.0	 -1278.0	 1.0	 100.0	 1	 2555	 0.0; % COW
	33	 432.5	 0.0	 433.0	 -433.0	 1.0	 100.0	 1	 865	 0.0; % COW
	34	 438.5	 0.0	 439.0	 -439.0	 1.0	 100.0	 1	 877	 0.0; % COW
	35	 560.0	 0.0	 560.0	 -560.0	 1.0	 100.0	 1	 1120	 0.0; % COW
	36	 461.0	 0.0	 461.0	 -461.0	 1.0	 100.0	 1	 922	 0.0; % COW
	37	 759.0	 0.0	 759.0	 -759.0	 1.0	 100.0	 1	 1518	 0.0; % COW
	38	 594.5	 0.0	 595.0	 -595.0	 1.0	 100.0	 1	 1189	 0.0; % COW
	39	 1261.5	 0.0	 1262.0	 -1262.0	 1.0	 100.0	 1	 2523	 0.0; % COW
];

%% generator cost data
%	2	startup	shutdown	n	c(n-1)	...	c0
mpc.gencost = [
	2	 0.0	 0.0	 3	   0.000000	   6.724778	   0.000000; % COW
	2	 0.0	 0.0	 3	   0.000000	  14.707625	   0.000000; % COW
	2	 0.0	 0.0	 3	   0.000000	  24.804734	   0.000000; % COW
	2	 0.0	 0.0	 3	   0.000000	  34.844643	   0.000000; % COW
	2	 0.0	 0.0	 3	   0.000000	  24.652994	   0.000000; % COW
	2	 0.0	 0.0	 3	   0.000000	  32.306483	   0.000000; % COW
	2	 0.0	 0.0	 3	   0.000000	  18.157477	   0.000000; % COW
	2	 0.0	 0.0	 3	   0.000000	  31.550181	   0.000000; % COW
	2	 0.0	 0.0	 3	   0.000000	  22.503168	   0.000000; % COW
	2	 0.0	 0.0	 3	   0.000000	  27.434444	   0.000000; % COW
];

%% branch data
%	fbus	tbus	r	x	b	rateA	rateB	rateC	ratio	angle	status	angmin	angmax
mpc.branch = [
	1	 2	 0.0035	 0.0411	 0.6987	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	1	 39	 0.001	 0.025	 0.75	 1000.0	 1000.0	 1000.0	 0.0	 0.0	 1	 -30.0	 30.0;
	2	 3	 0.0013	 0.0151	 0.2572	 500.0	 500.0	 500.0	 0.0	 0.0	 1	 -30.0	 30.0;
	2	 25	 0.007	 0.0086	 0.146	 500.0	 500.0	 500.0	 0.0	 0.0	 1	 -30.0	 30.0;
	2	 30	 0.0	 0.0181	 0.0	 900.0	 900.0	 2500.0	 1.025	 0.0	 1	 -30.0	 30.0;
	3	 4	 0.0013	 0.0213	 0.2214	 500.0	 500.0	 500.0	 0.0	 0.0	 1	 -30.0	 30.0;
	3	 18	 0.0011	 0.0133	 0.2138	 500.0	 500.0	 500.0	 0.0	 0.0	 1	 -30.0	 30.0;
	4	 5	 0.0008	 0.0128	 0.1342	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	4	 14	 0.0008	 0.0129	 0.1382	 500.0	 500.0	 500.0	 0.0	 0.0	 1	 -30.0	 30.0;
	5	 6	 0.0002	 0.0026	 0.0434	 1200.0	 1200.0	 1200.0	 0.0	 0.0	 1	 -30.0	 30.0;
	5	 8	 0.0008	 0.0112	 0.1476	 900.0	 900.0	 900.0	 0.0	 0.0	 1	 -30.0	 30.0;
	6	 7	 0.0006	 0.0092	 0.113	 900.0	 900.0	 900.0	 0.0	 0.0	 1	 -30.0	 30.0;
	6	 11	 0.0007	 0.0082	 0.1389	 480.0	 480.0	 480.0	 0.0	 0.0	 1	 -30.0	 30.0;
	6	 31	 0.0	 0.025	 0.0	 1800.0	 1800.0	 1800.0	 1.07	 0.0	 1	 -30.0	 30.0;
	7	 8	 0.0004	 0.0046	 0.078	 900.0	 900.0	 900.0	 0.0	 0.0	 1	 -30.0	 30.0;
	8	 9	 0.0023	 0.0363	 0.3804	 900.0	 900.0	 900.0	 0.0	 0.0	 1	 -30.0	 30.0;
	9	 39	 0.001	 0.025	 1.2	 900.0	 900.0	 900.0	 0.0	 0.0	 1	 -30.0	 30.0;
	10	 11	 0.0004	 0.0043	 0.0729	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	10	 13	 0.0004	 0.0043	 0.0729	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	10	 32	 0.0	 0.02	 0.0	 900.0	 900.0	 2500.0	 1.07	 0.0	 1	 -30.0	 30.0;
	12	 11	 0.0016	 0.0435	 0.0	 500.0	 500.0	 500.0	 1.006	 0.0	 1	 -30.0	 30.0;
	12	 13	 0.0016	 0.0435	 0.0	 500.0	 500.0	 500.0	 1.006	 0.0	 1	 -30.0	 30.0;
	13	 14	 0.0009	 0.0101	 0.1723	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	14	 15	 0.0018	 0.0217	 0.366	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	15	 16	 0.0009	 0.0094	 0.171	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	16	 17	 0.0007	 0.0089	 0.1342	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	16	 19	 0.0016	 0.0195	 0.304	 600.0	 600.0	 2500.0	 0.0	 0.0	 1	 -30.0	 30.0;
	16	 21	 0.0008	 0.0135	 0.2548	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	16	 24	 0.0003	 0.0059	 0.068	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	17	 18	 0.0007	 0.0082	 0.1319	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	17	 27	 0.0013	 0.0173	 0.3216	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	19	 20	 0.0007	 0.0138	 0.0	 900.0	 900.0	 2500.0	 1.06	 0.0	 1	 -30.0	 30.0;
	19	 33	 0.0007	 0.0142	 0.0	 900.0	 900.0	 2500.0	 1.07	 0.0	 1	 -30.0	 30.0;
	20	 34	 0.0009	 0.018	 0.0	 900.0	 900.0	 2500.0	 1.009	 0.0	 1	 -30.0	 30.0;
	21	 22	 0.0008	 0.014	 0.2565	 900.0	 900.0	 900.0	 0.0	 0.0	 1	 -30.0	 30.0;
	22	 23	 0.0006	 0.0096	 0.1846	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	22	 35	 0.0	 0.0143	 0.0	 900.0	 900.0	 2500.0	 1.025	 0.0	 1	 -30.0	 30.0;
	23	 24	 0.0022	 0.035	 0.361	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	23	 36	 0.0005	 0.0272	 0.0	 900.0	 900.0	 2500.0	 0.0	 0.0	 1	 -30.0	 30.0;
	25	 26	 0.0032	 0.0323	 0.531	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	25	 37	 0.0006	 0.0232	 0.0	 900.0	 900.0	 2500.0	 1.025	 0.0	 1	 -30.0	 30.0;
	26	 27	 0.0014	 0.0147	 0.2396	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	26	 28	 0.0043	 0.0474	 0.7802	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	26	 29	 0.0057	 0.0625	 1.029	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	28	 29	 0.0014	 0.0151	 0.249	 600.0	 600.0	 600.0	 0.0	 0.0	 1	 -30.0	 30.0;
	29	 38	 0.0008	 0.0156	 0.0	 1200.0	 1200.0	 2500.0	 1.025	 0.0	 1	 -30.0	 30.0;
];

% INFO    : === Translation Options ===
% INFO    : Load Model:                  from file ./pglib_opf_case39_epri.m.api.sol
% INFO    : Gen Active Capacity Model:   stat
% INFO    : Gen Reactive Capacity Model: al50ag
% INFO    : Gen Active Cost Model:       stat
% INFO    :
% INFO    : === Load Replacement Notes ===
% INFO    : Bus 1	: Pd=97.6, Qd=44.2 -> Pd=160.91, Qd=44.20
% INFO    : Bus 3	: Pd=322.0, Qd=2.4 -> Pd=530.88, Qd=2.40
% INFO    : Bus 4	: Pd=500.0, Qd=184.0 -> Pd=824.35, Qd=184.00
% INFO    : Bus 7	: Pd=233.8, Qd=84.0 -> Pd=385.47, Qd=84.00
% INFO    : Bus 8	: Pd=522.0, Qd=176.6 -> Pd=860.62, Qd=176.60
% INFO    : Bus 9	: Pd=6.5, Qd=-66.6 -> Pd=6.50, Qd=-66.60
% INFO    : Bus 12	: Pd=8.53, Qd=88.0 -> Pd=14.06, Qd=88.00
% INFO    : Bus 15	: Pd=320.0, Qd=153.0 -> Pd=527.58, Qd=153.00
% INFO    : Bus 16	: Pd=329.0, Qd=32.3 -> Pd=542.42, Qd=32.30
% INFO    : Bus 18	: Pd=158.0, Qd=30.0 -> Pd=260.49, Qd=30.00
% INFO    : Bus 20	: Pd=680.0, Qd=103.0 -> Pd=1121.12, Qd=103.00
% INFO    : Bus 21	: Pd=274.0, Qd=115.0 -> Pd=451.74, Qd=115.00
% INFO    : Bus 23	: Pd=247.5, Qd=84.6 -> Pd=408.05, Qd=84.60
% INFO    : Bus 24	: Pd=308.6, Qd=-92.2 -> Pd=308.60, Qd=-92.20
% INFO    : Bus 25	: Pd=224.0, Qd=47.2 -> Pd=369.31, Qd=47.20
% INFO    : Bus 26	: Pd=139.0, Qd=17.0 -> Pd=229.17, Qd=17.00
% INFO    : Bus 27	: Pd=281.0, Qd=75.5 -> Pd=463.28, Qd=75.50
% INFO    : Bus 28	: Pd=206.0, Qd=27.6 -> Pd=339.63, Qd=27.60
% INFO    : Bus 29	: Pd=283.5, Qd=26.9 -> Pd=467.41, Qd=26.90
% INFO    : Bus 31	: Pd=9.2, Qd=4.6 -> Pd=15.17, Qd=4.60
% INFO    : Bus 39	: Pd=1104.0, Qd=250.0 -> Pd=1820.16, Qd=250.00
% INFO    :
% INFO    : === Generator Setpoint Replacement Notes ===
% INFO    : Gen at bus 30	: Pg=520.0, Qg=270.0 -> Pg=90.0, Qg=235.0
% INFO    : Gen at bus 31	: Pg=323.0, Qg=100.0 -> Pg=1455.0, Qg=712.0
% INFO    : Gen at bus 32	: Pg=362.5, Qg=225.0 -> Pg=805.0, Qg=402.0
% INFO    : Gen at bus 33	: Pg=326.0, Qg=125.0 -> Pg=858.0, Qg=253.0
% INFO    : Gen at bus 34	: Pg=254.0, Qg=83.5 -> Pg=859.0, Qg=251.0
% INFO    : Gen at bus 35	: Pg=343.5, Qg=100.0 -> Pg=864.0, Qg=250.0
% INFO    : Gen at bus 36	: Pg=290.0, Qg=120.0 -> Pg=874.0, Qg=215.0
% INFO    : Gen at bus 37	: Pg=282.0, Qg=125.0 -> Pg=887.0, Qg=3.0
% INFO    : Gen at bus 38	: Pg=432.5, Qg=75.0 -> Pg=1185.0, Qg=188.0
% INFO    : Gen at bus 39	: Pg=550.0, Qg=100.0 -> Pg=2326.0, Qg=143.0
% INFO    :
% INFO    : === Generator Reactive Capacity Atleast Setpoint Value Notes ===
% INFO    : Gen at bus 30	: Qg 235.0, Qmin 140.0, Qmax 400.0 -> Qmin -282.0, Qmax 400.0
% INFO    : Gen at bus 31	: Qg 712.0, Qmin -100.0, Qmax 300.0 -> Qmin -854.4, Qmax 854.4
% INFO    : Gen at bus 32	: Qg 402.0, Qmin 150.0, Qmax 300.0 -> Qmin -482.4, Qmax 482.4
% INFO    : Gen at bus 33	: Qg 253.0, Qmin 0.0, Qmax 250.0 -> Qmin -303.6, Qmax 303.6
% INFO    : Gen at bus 34	: Qg 251.0, Qmin 0.0, Qmax 167.0 -> Qmin -301.2, Qmax 301.2
% INFO    : Gen at bus 35	: Qg 250.0, Qmin -100.0, Qmax 300.0 -> Qmin -300.0, Qmax 300.0
% INFO    : Gen at bus 36	: Qg 215.0, Qmin 0.0, Qmax 240.0 -> Qmin -258.0, Qmax 240.0
% INFO    : Gen at bus 37	: Qg 3.0, Qmin 0.0, Qmax 250.0 -> Qmin -3.6, Qmax 250.0
% INFO    : Gen at bus 38	: Qg 188.0, Qmin -150.0, Qmax 300.0 -> Qmin -225.6, Qmax 300.0
% INFO    : Gen at bus 39	: Qg 143.0, Qmin -100.0, Qmax 300.0 -> Qmin -171.6, Qmax 300.0
% INFO    :
% INFO    : === Generator Classification Notes ===
% INFO    : COW    10  -   100.00
% INFO    :
% INFO    : === Generator Active Capacity Stat Model Notes ===
% INFO    : Gen at bus 30 - COW	: Pg=90.0, Pmax=1040.0 -> Pmax=318   samples: 2
% WARNING : Failed to find a generator capacity within (1455.0-7275.0) after 100 samples, using percent increase model
% INFO    : Gen at bus 31 - COW	: Pg=1455.0, Pmax=646.0 -> Pmax=1578   samples: 100
% INFO    : Gen at bus 32 - COW	: Pg=805.0, Pmax=725.0 -> Pmax=2555   samples: 26
% INFO    : Gen at bus 33 - COW	: Pg=858.0, Pmax=652.0 -> Pmax=865   samples: 44
% INFO    : Gen at bus 34 - COW	: Pg=859.0, Pmax=508.0 -> Pmax=877   samples: 33
% INFO    : Gen at bus 35 - COW	: Pg=864.0, Pmax=687.0 -> Pmax=1120   samples: 15
% INFO    : Gen at bus 36 - COW	: Pg=874.0, Pmax=580.0 -> Pmax=922   samples: 12
% INFO    : Gen at bus 37 - COW	: Pg=887.0, Pmax=564.0 -> Pmax=1518   samples: 39
% INFO    : Gen at bus 38 - COW	: Pg=1185.0, Pmax=865.0 -> Pmax=1189   samples: 57
% WARNING : Failed to find a generator capacity within (2326.0-11630.0) after 100 samples, using percent increase model
% INFO    : Gen at bus 39 - COW	: Pg=2326.0, Pmax=1100.0 -> Pmax=2523   samples: 100
% INFO    :
% INFO    : === Generator Active Capacity LB Model Notes ===
% INFO    :
% INFO    : === Generator Reactive Capacity Atleast Max 50 Percent Active Model Notes ===
% INFO    : Gen at bus 32 - COW	: Pmax 2555.0, Qmin -482.4, Qmax 482.4 -> Qmin -1278.0, Qmax 1278.0
% INFO    : Gen at bus 33 - COW	: Pmax 865.0, Qmin -303.6, Qmax 303.6 -> Qmin -433.0, Qmax 433.0
% INFO    : Gen at bus 34 - COW	: Pmax 877.0, Qmin -301.2, Qmax 301.2 -> Qmin -439.0, Qmax 439.0
% INFO    : Gen at bus 35 - COW	: Pmax 1120.0, Qmin -300.0, Qmax 300.0 -> Qmin -560.0, Qmax 560.0
% INFO    : Gen at bus 36 - COW	: Pmax 922.0, Qmin -258.0, Qmax 240.0 -> Qmin -461.0, Qmax 461.0
% INFO    : Gen at bus 37 - COW	: Pmax 1518.0, Qmin -3.6, Qmax 250.0 -> Qmin -759.0, Qmax 759.0
% INFO    : Gen at bus 38 - COW	: Pmax 1189.0, Qmin -225.6, Qmax 300.0 -> Qmin -595.0, Qmax 595.0
% INFO    : Gen at bus 39 - COW	: Pmax 2523.0, Qmin -171.6, Qmax 300.0 -> Qmin -1262.0, Qmax 1262.0
% INFO    :
% INFO    : === Generator Setpoint Replacement Notes ===
% INFO    : Gen at bus 30	: Pg=90.0, Qg=235.0 -> Pg=159.0, Qg=59.0
% INFO    : Gen at bus 30	: Vg=1.0 -> Vg=1.0
% INFO    : Gen at bus 31	: Pg=1455.0, Qg=712.0 -> Pg=789.0, Qg=0.0
% INFO    : Gen at bus 31	: Vg=1.0 -> Vg=1.0
% INFO    : Gen at bus 32	: Pg=805.0, Qg=402.0 -> Pg=1277.5, Qg=0.0
% INFO    : Gen at bus 32	: Vg=1.0 -> Vg=1.0
% INFO    : Gen at bus 33	: Pg=858.0, Qg=253.0 -> Pg=432.5, Qg=0.0
% INFO    : Gen at bus 33	: Vg=1.0 -> Vg=1.0
% INFO    : Gen at bus 34	: Pg=859.0, Qg=251.0 -> Pg=438.5, Qg=0.0
% INFO    : Gen at bus 34	: Vg=1.0 -> Vg=1.0
% INFO    : Gen at bus 35	: Pg=864.0, Qg=250.0 -> Pg=560.0, Qg=0.0
% INFO    : Gen at bus 35	: Vg=1.0 -> Vg=1.0
% INFO    : Gen at bus 36	: Pg=874.0, Qg=215.0 -> Pg=461.0, Qg=0.0
% INFO    : Gen at bus 36	: Vg=1.0 -> Vg=1.0
% INFO    : Gen at bus 37	: Pg=887.0, Qg=3.0 -> Pg=759.0, Qg=0.0
% INFO    : Gen at bus 37	: Vg=1.0 -> Vg=1.0
% INFO    : Gen at bus 38	: Pg=1185.0, Qg=188.0 -> Pg=594.5, Qg=0.0
% INFO    : Gen at bus 38	: Vg=1.0 -> Vg=1.0
% INFO    : Gen at bus 39	: Pg=2326.0, Qg=143.0 -> Pg=1261.5, Qg=0.0
% INFO    : Gen at bus 39	: Vg=1.0 -> Vg=1.0
% INFO    :
% INFO    : === Writing Matpower Case File Notes ===


function sos = sosprogram(vartable,decvartable)
% SOSPROGRAM --- Initialize a new sum of squares program.
%
% SOSP = sosprogram(VARTABLE,DECVARTABLE)
%
% SOSP is a new sum of squares program.
% VARTABLE is a vector of independent variables.
% DECVARTABLE is a vector of decision variables (optional).
%
% Both VARTABLE and DECVARTABLE are either symbolic or polynomial objects.
%

% This file is part of SOSTOOLS - Sum of Squares Toolbox ver 3.00.
%
% Copyright (C)2002, 2004, 2013  A. Papachristodoulou (1), J. Anderson (1),
%                                G. Valmorbida (1), S. Prajna (2),
%                                P. Seiler (3), P. A. Parrilo (4)
% (1) Department of Engineering Science, University of Oxford, Oxford, U.K.
% (2) Control and Dynamical Systems - California Institute of Technology,
%     Pasadena, CA 91125, USA.
% (3) Aerospace and Engineering Mechanics Department, University of
%     Minnesota, Minneapolis, MN 55455-0153, USA.
% (4) Laboratory for Information and Decision Systems, M.I.T.,
%     Massachusetts, MA 02139-4307
%
% Send bug reports and feedback to: sostools@cds.caltech.edu
%
% 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 2 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/>.
%

% 12/24/01 - SP
% 01/07/02 - SP
% 02/21/02 - SP -- Symbolic polynomial
% 10/10/02 - SP -- Path checking

if ~exist('sedumi') & ~exist('sqlp') & ~exist('csdp') & ~exist('sdpnal')
    error('No SDP solvers found.');
end;

if ~exist('getpolysym')
    dd = which('sosprogram');
    dd = strrep(dd,'/sosprogram.m','/');
    dd = strrep(dd,'\sosprogram.m','\');
    pp = genpath(dd);
    addpath(pp);
end;

if isa(vartable,'sym')

	sos.var.num = 0;
	sos.var.type = {};
	sos.var.Z = {};
	sos.var.ZZ = {};
	sos.var.T = {};

	sos.expr.num = 0;
	sos.expr.type = {};
	sos.expr.At = {};
	sos.expr.b = {};
	sos.expr.Z = {};

	sos.extravar.num = 0;
	sos.extravar.Z = {};
	sos.extravar.ZZ = {};
	sos.extravar.T = {};
	sos.extravar.idx = {};

	sos.objective = [];        % 01/07/02

	sos.solinfo.x = [];
	sos.solinfo.y = [];
	sos.solinfo.RRx = [];
	sos.solinfo.RRy = [];
	sos.solinfo.info = [];

	sos.vartable = sym2chartable(vartable);    % 02/21/02
	if size(vartable,2) > 1
        vartable = vartable.';
	end;
	sos.symvartable = vartable;

    sos.varmat.vartable = '[]';                 %JA&GV 06/06/13
    sos.varmat.symvartable = [];
    sos.varmat.count = 0;

	if (nargin == 2 & ~isempty(decvartable))
        sos.objective = sparse(length(decvartable),1);
        sos.decvartable = sym2chartable(decvartable);     % 03/01/02
        setofCommaBrackets = [1 strfind(sos.decvartable,',') strfind(sos.decvartable,']')];%26/04/13

        if size(decvartable,2) > 1
            decvartable = decvartable.';%the transpose of a matrix of decision varibles
                                        %it is put under this form in
                                        %sos.symdecvartable (next line)
        end;
        sos.symdecvartable = decvartable;
        sos.var.idx{1} = length(find(sos.decvartable==','))+2;

	else
        sos.decvartable = '[]';
        sos.symdecvartable = [];
        sos.var.idx{1} = 1;

	end;

else

	sos.var.num = 0;
	sos.var.type = {};
	sos.var.Z = {};
	sos.var.ZZ = {};
	sos.var.T = {};

	sos.expr.num = 0;
	sos.expr.type = {};
	sos.expr.At = {};
	sos.expr.b = {};
	sos.expr.Z = {};

	sos.extravar.num = 0;
	sos.extravar.Z = {};
	sos.extravar.ZZ = {};
	sos.extravar.T = {};
	sos.extravar.idx = {};

	sos.objective = [];        % 01/07/02

	sos.solinfo.x = [];
	sos.solinfo.y = [];
	sos.solinfo.RRx = [];
	sos.solinfo.RRy = [];
	sos.solinfo.info = [];

    sos.vartable = sort(vartable.varname);

    sos.varmat.vartable = [];                 % PJS 9/9/2013
    sos.varmat.symvartable = [];
    sos.varmat.count = 0;

	if (nargin == 2 & ~isempty(decvartable))
        sos.objective = sparse(length(decvartable),1);
        sos.decvartable = sort(decvartable.varname);

        sos.var.idx{1} = length(decvartable)+1;
	else
        sos.decvartable = {};
        sos.var.idx{1} = 1;
	end;

end;


function [TWARes] = TWA_MMA(Param, freq, ecg, q,r,s, stlen)
%OVERVIEW, This function checks for arrhythmia in the current analysis window across all
% leads. If no arrhythmia is detected it then calls sub functions which first align the beats in the analysis
% window and find correlations of each beat with a template, to determine if the ecg data is clean enough for analysis.
% If alignment is successful, TWAs are evaluated using the MMA method
% coupled with a surrogate non parametric reshuffling method used for
% reducing false positive TWA detections.
%
% INPUTS        MANDATORY           DESCRIPTION
%               Param               Structure contains parameters used for
%                                   computing TWAs.
%
%               freq                Sampling frequency of the ecg data,
%                                   needs to be 1000 Hz.
%
%               ecg                 N by M array of ecg data. Contains M
%                                   channels of ECG with N datapoints each.
%
%               q                   array of q point locations
%
%               r                   array of r point locations
%
%               s                   array of s point locations
%
%               stlen               scalar, estimate of s-t segment length
%
% OUTPUT
%
%               TWARes              Structure contains the result for TWA
%                                   computation. Contains the following fields,
%
%               .VAlt               TWA amplitude estimates for the analysis
%                                   window.
%
%               .VAlt_Sig           TWA amplitude estimates which are statistically
%                                   significant compared to the noise threshold
%                                   estimate.
%
%               .noise_median       median of the gamma distribution used to model the noise.
%
%               .noise_95           95th percentile of the gamma distribution
%                                   used to model the noise.
%
%               .VAltPt             The location of the point with maximum
%                                   difference between the average even and odd beats.
%
%   REPO:
%       https://github.com/cliffordlab/PhysioNet-Cardiovascular-Signal-Toolbox
%   ORIGINAL SOURCE AND AUTHORS:
%       Written by Shamim Nemati,
%       editted by Ismail Sadiq on 10/26/2019.
%	COPYRIGHT (C) 2019
%   LICENSE:
%       This software is offered freely and without warranty under
%       the GNU (v3 or later) public license. See license file for
%       more information. The license may be found in
%       the Documents folder of the Physionet-Cardiovascular-Signal-Toolbox.

TWARes.successfull = false;

WIN_SIZE = Param.Interval; %e.g., 60beats
OVERLAP_SIZE = floor(WIN_SIZE/2);
TWARes=[];
Num_Ch = size(ecg, 2);

Align.fidBase = []; Align.fid = []; Align.valid = [];

if (length(r) < WIN_SIZE) % need at least one window of beats
    disp('TWAbyMMAOnAFile: Not enough beats...')
    TWARes.HR=NaN;
    TWARes.VAlt=NaN*ones(1,Num_Ch);TWARes.VAlt_Sig=NaN*ones(1,Num_Ch);
    TWARes.VAltPt=NaN*ones(1,Num_Ch);
    TWARes.Noise_Median= NaN*ones(1,Num_Ch);TWARes.Noise_95 = NaN*ones(1,Num_Ch);
    return;
end

i = 0;
for j = 1:(WIN_SIZE-OVERLAP_SIZE):length(r)-WIN_SIZE+1
    i=i+1;
    disp(['Analysis window index = ' num2str(i)])
    beats = j:j+WIN_SIZE-1;

    % Check for arrhythmia

    cur_r = r(beats); % Check current window of beats for arrhythmia
    features = AF_features(diff(cur_r),freq);
    Index_test = SVM_AFdetection_withoutTrainingModel(features,1);
    if (Index_test)
        disp('Arrhythmia detected...')
        TWARes.HR(i)=NaN;
        TWARes.VAlt(i, :)=NaN*ones(1,Num_Ch);TWARes.VAlt_Sig(i, :)=NaN*ones(1,Num_Ch);
        TWARes.VAltPt(i,:)=NaN*ones(1,Num_Ch);
        TWARes.Noise_Median(i, :) = NaN*ones(1,Num_Ch);TWARes.Noise_95(i, :) = NaN*ones(1,Num_Ch);
        % If arrhythmia detected continue to next window
        continue;
    end

    try
        % Align beats and check validity for analysis before computing TWAs
        [Align hrt_rate] = Align_Beats(beats, q(beats), r(beats), s(beats), stlen, ecg, 1000, Param);
    catch
        % If failed to align due to catch continue to next window.
        disp('TWAbyMMAOnAFile: Catch says alignment failed...')
        TWARes.HR(i)=NaN;
        TWARes.VAlt(i, :)=NaN*ones(1,Num_Ch);TWARes.VAlt_Sig(i, :)=NaN*ones(1,Num_Ch);
        TWARes.VAltPt(i,:)=NaN*ones(1,Num_Ch);
        TWARes.Noise_Median(i, :) = NaN*ones(1,Num_Ch);TWARes.Noise_95(i, :) = NaN*ones(1,Num_Ch);
        continue;
    end
    if (isempty(Align.fid))
        disp('TWAbyMMAOnAFile: alignment failed ...');
        TWARes.HR(i)=NaN;
        TWARes.VAlt(i, :)=NaN*ones(1,Num_Ch); TWARes.VAlt_Sig(i, :)=NaN*ones(1,Num_Ch);
        TWARes.VAltPt(i,:)=NaN*ones(1,Num_Ch);
        TWARes.Noise_Median(i, :)=NaN*ones(1,Num_Ch);TWARes.Noise_95(i, :) = NaN*ones(1,Num_Ch);
        continue;
    else
        disp('TWAbyMMAOnAFile: alignment succeeded ...');
    end

    disp('TWAbyMMAOnAFile: looking for alternans ...');
    TWARes.HR(i) = hrt_rate;
    try
        [TWARes.VAlt(i, :), TWARes.VAlt_Sig(i, :), TWARes.Noise_Median(i,:),TWARes.Noise_95(i,:),...
            TWARes.VAltPt(i,:)]= TWA_by_MMA(Param, TWARes, ecg, Align); % *********************** Estimates T-wave alternans from the beat Matrix
    catch
        disp('TWAbyMMAOnAFile: search for alternans failed ')
        TWARes.VAlt(i, :)=NaN*ones(1,Num_Ch); TWARes.VAlt_Sig(i, :)=NaN*ones(1,Num_Ch);
        TWARes.VAltPt(i,:)=NaN*ones(1,Num_Ch);
        TWARes.Noise_Median(i, :)=NaN*ones(1,Num_Ch);TWARes.Noise_95(i, :) = NaN*ones(1,Num_Ch);
        continue;
    end
end

TWARes.successfull = true;

return;


classdef NDK
    % NDK works with NDK files can convert it to a ZmapCatalog
    %
    % from : https://www.ldeo.columbia.edu/~gcmt/projects/CMT/catalog/allorder.ndk_explained
    %
    % CMT files for download at: http://www.globalcmt.org/CMTfiles.html
    %
    % CMT reference:
    %   Dziewonski, A. M., T.-A. Chou and J. H. Woodhouse, Determination of earthquake source parameters from waveform data for studies of global and regional seismicity, J. Geophys. Res., 86, 2825-2852, 1981. doi:10.1029/JB086iB04p02825
    %   Ekström, G., M. Nettles, and A. M. Dziewonski, The global CMT project 2004-2010: Centroid-moment tensors for 13,017 earthquakes, Phys. Earth Planet. Inter., 200-201, 1-9, 2012. doi:10.1016/j.pepi.2012.04.002
    properties
        allNDKs table

        % TypeOfSourceInvertedFor is
        %"CMT: 0" - general moment tensor;
        %"CMT: 1" - moment tensor with constraint of zero trace (standard);
        %"CMT: 2" - double-couple source.

        % from line 3 of NDK format : CMT info (2)
        % Centroid_Time is  wrt reference time

        % CentroidDepthType is one of FREE, FIX, BODY

        % from line 4 of NDK format : CMT info (3)
        % ExponentForAllMomentValues most all moment measurements need to be multipleid by exponent
        %Mrr % r is up
        %Mtt % t is south
        %Mpp % p is east
    end
    properties(Constant)
        base_directory = 'https://www.ldeo.columbia.edu/~gcmt/projects/CMT/catalog/';
        % different dates are stored in differing locations.
        filelocations = {... [startdate>=, <enddate],location relative to base.
            datetime([1962 1976], 1, 1), {'PRE1976/deep_1962-1976.ndk','PRE1976/intdep_1962-1976.ndk'};
            datetime([1976 2017], 1, 1), 'jan76_dec17.ndk';...
            datetime([2017 year(datetime)], 1, 1), 'NEW_MONTHLY/{yyyy}';...
            [datetime(year(datetime), 1, 1), datetime], 'NEW_QUICK/E{yyyymmddHHMM}.ndk'...
            }

    end

    methods
        function obj = NDK(ndktext)
            %
            %
            % parsing based on documentation found at :
            % https://www.ldeo.columbia.edu/~gcmt/projects/CMT/catalog/allorder.ndk_explained

            if nargin==0 || isempty(ndktext)
                return
            end
            if ischar(ndktext) && isvector(ndktext)
                ndktext=splitlines(strip(ndktext));
            end
            obj=obj.fillLine1(strjoin(ndktext(1:5:end),newline));
            obj=obj.fillLine2(strjoin(ndktext(2:5:end),newline));
            obj=obj.fillLine3(strjoin(ndktext(3:5:end),newline));
            obj=obj.fillLine4(strjoin(ndktext(4:5:end),newline));
            obj=obj.fillLine5(strjoin(ndktext(5:5:end),newline));

        end
        function obj = fillLine1(obj,s)
            % make sure reference catalog is not empty, otherwise textscan will choke
            noCat = strfind(s,[newline '    ']);
            s(noCat+1)='-';
            s(noCat+2)='-';
            s(noCat+3)='-';

            % make sure location is not empyt, otherwise textscan will choke
            sidexes = 57:81:numel(s);
            no_location= s(sidexes)==' ';
            s(sidexes(no_location))='-';
            % NDK's exist with 60 seconds. datetime chokes on this. Fix it.
            sidexes = 23:81:numel(s);
            add_a_minute = s(sidexes)=='6';
            s(sidexes(add_a_minute))='0';

            [c,POS]=textscan(s,'%4C %{uuuu/MM/DD}D %{HH:mm:ss.s}D %f %f %f %f %f %24c');
            obj.allNDKs.HypocenterReferenceCatalog=c{1};
            obj.allNDKs.ReferenceEventDateTime =c{2} + timeofday(c{3}) + minutes(add_a_minute(:));
            obj.allNDKs.Latitude = c{4};
            obj.allNDKs.Longitude=c{5};
            obj.allNDKs.Depth=c{6};
            obj.allNDKs.ReportedMagnitudes = [c{7},c{8}];
            obj.allNDKs.GeographicalLocation=categorical(string(c{9}));
        end

        function obj = fillLine2(obj,s)

            c = textscan(s, '%16s B:%d %d %d S:%d %d %d M:%d %d %d CMT:%C %5C:%f');

            % c is now a 1x13 cell, each cell is a field, and contains an array with one item per NDK
            obj.allNDKs.CMTEventName=c{1};
            obj.allNDKs.LPBodyWaves_NumStationsUsed=c{2};
            obj.allNDKs.LPBodyWaves_NumComponentsUsed=c{3};
            obj.allNDKs.LPBodyWaves_ShortestPeriodUsed=c{4};
            obj.allNDKs.IntermedPSurfaceWaves_NumStationsUsed=c{5};
            obj.allNDKs.IntermedPSurfaceWaves__NumComponentsUsed=c{6};
            obj.allNDKs.IntermedPSurfaceWaves_ShortestPeriodUsed=c{7};
            obj.allNDKs.LPMantleWaves_NumStationsUsed=c{8};
            obj.allNDKs.LPMantleWaves__NumComponentsUsed=c{9};
            obj.allNDKs.LPMantleWaves_ShortestPeriodUsed=c{10};
            obj.allNDKs.TypeOfSourceInvertedFor=c{11};
            obj.allNDKs.MomentRateFunctionType=c{12};
            obj.allNDKs.MomentRateFunctionDuration=c{13};
        end

        function obj = fillLine3(obj,s)

            [c, POS] = textscan(s, 'CENTROID: %f %f %f %f %f %f %f %f %C %c-%14c');
            obj.allNDKs.Centroid_Time =c{1};% wrt reference time
            obj.allNDKs.Centroid_TimeStderr = c{2};
            obj.allNDKs.Centroid_Lat = c{3};
            obj.allNDKs.Centroid_LatStderr = c{4};
            obj.allNDKs.Centroid_Lon = c{5};
            obj.allNDKs.Centroid_LonStderr = c{6};
            obj.allNDKs.Centroid_Depth = c{7};
            obj.allNDKs.Centroid_DepthStderr = c{8};
            obj.allNDKs.CentroidDepthType = c{9}; % one of FREE, FIX, BODY
            obj.allNDKs.TimeStampPrefix = c{10};
            obj.allNDKs.TimeStamp = c{11};
        end

        %from line 3 of NDK format : CMT info (2)

        function obj = fillLine4(obj,s)

            [c, POS] = textscan(s, '%d %f %f %f %f %f %f %f %f %f %f %f %f');
            % from line 4 of NDK format : CMT info (3)
            obj.allNDKs.ExponentForAllMomentValues=c{1};
            obj.allNDKs.Mrr=c{2}; % r is up
            obj.allNDKs.MrrStderr=c{3};
            obj.allNDKs.Mtt=c{4}; % t is south
            obj.allNDKs.MttStderr=c{5};
            obj.allNDKs.Mpp=c{6}; % p is east
            obj.allNDKs.MppStderr=c{7};
            obj.allNDKs.Mrt=c{8};
            obj.allNDKs.MrtStderr=c{9};
            obj.allNDKs.Mrp=c{10};
            obj.allNDKs.MrpStderr=c{11};
            obj.allNDKs.Mtp=c{12};
            obj.allNDKs.MtpStderr=c{13};
        end

        function obj = fillLine5(obj,s)
            [c,POS] = textscan(s, '%3C %f %d %d %f %d %d %f %d %d %f %d %d %d %d %d %d');

            obj.allNDKs.VersionCode=c{1};
            obj.allNDKs.MTEigenvalue=[c{2} c{3} c{4}];
            obj.allNDKs.MTPlunge=[c{5} c{6} c{7}];
            obj.allNDKs.MTAzimuth=[c{8} c{9} c{10}];
            obj.allNDKs.ScalarMoment=c{11}; % needs to be multiplied by exponent
            obj.allNDKs.Strike_NodalPlane1=c{12};
            obj.allNDKs.Dip_NodalPlane1=c{13};
            obj.allNDKs.Rake_NodalPlane1=c{14};
            obj.allNDKs.Strike_NodalPlane2=c{15};
            obj.allNDKs.Dip_NodalPlane2=c{16};
            obj.allNDKs.Rake_NodalPlane2=c{17};
        end

        function c = toZmapCatalog(obj)
            c = ZmapCatalog;
            c.Name          = 'ndk';
            c.Longitude     = obj.allNDKs.Longitude;
            c.Latitude      = obj.allNDKs.Latitude;
            c.Depth         = obj.allNDKs.Depth;
            c.Date          = obj.allNDKs.ReferenceEventDateTime;
            c.Magnitude     = obj.allNDKs.ReportedMagnitudes(:,1);
            c.MagnitudeType = repmat(categorical({''}),size(c.Magnitude));
            mt      = obj.allNDKs(:,{'Mrr','Mtt','Mpp','Mrt','Mrp','Mtp'});
            mt{:,:} = mt{:,:}.* 10.^double(obj.allNDKs.ExponentForAllMomentValues);
            mt.Properties.VariableNames = {'mrr', 'mtt', 'mff', 'mrt', 'mrf', 'mtf'};
            mtAddon                 = MomentTensorAddon(mt);
            mtAddon.Dip             = double(obj.allNDKs.Dip_NodalPlane1);
            mtAddon.DipDirection    = mod(double(obj.allNDKs.Strike_NodalPlane1)+ 90, 360 );
            mtAddon.Rake            = double(obj.allNDKs.Rake_NodalPlane1);
            c.setAddon(mtAddon);
        end
    end

    methods(Static)

        function NDKS = read(f)
            tx=fileread(f);
            NDKS = NDK(splitlines(strip(tx)));
        end

        function NDKS = download(varargin)
            p=inputParser();
            p.addRequired('starttime');
            p.addRequired('endtime');
            p.addParameter('minmagnitude',-inf);
            p.addParameter('maxmagnitude',inf);
            p.addParameter('minlatitude',-90);
            p.addParameter('maxlatitude',90);
            p.addParameter('minlongitude',-180);
            p.addParameter('maxlongitude',180);
            p.addParameter('mindepth',inf);
            p.addParameter('maxdepth',inf);
            % TODO finish this

        end
        function tb = getFileLocations(starttime, endtime)
            % unpack theoretical file locations into real ones.
            floc_cells = NDK.filelocations;
            fls = NDK.filelocations{:,1}(1);
            fle = NDK.filelocations{:,1}(2);
            ignoreMe =   starttime > fle || endtime <= fls ;
            floc_cells(ignoreMe,:) = [];
            tb = table('Size', [1,3],...
                'VariableTypes', {'datetime',   'datetime', 'string'},...
                'VariableNames', {'starttime',  'endtime',  'url'});
            % TODO finish this

        end

    end
end