function planC = sumDose(doseNums,wtfactor,lqParamS,assocScan,newDoseName,planC)
%function planC = sumDose(doseNumsV,weightsV,assocScan)
%
%This function creates a new dose distribution by adding up doseNums
%according to factors wtfactor. assocScan is the scan number to associate
%new dose with. newDoseName is the name of new dose.
%
%APA, 12/23/2009
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

wtfactorTmp = wtfactor;
wtfactor = ones(1,max(doseNums));
wtfactor(doseNums) = wtfactorTmp;

indexS = planC{end};
if assocScan > 0
    assocScanUID = planC{indexS.scan}(assocScan).scanUID;
    %Get associated transM
    assocTransM = planC{indexS.scan}(assocScan).transM;
    if isempty(assocTransM)
        assocTransM = eye(4);
    end
else %No Association
    assocScanUID = '';
    assocTransM = eye(4);
end

% doseNums = checkedDoses;

%Get the x,y,z grid for new dose
for i = 1:length(doseNums)
    doseNum = doseNums(i);
    %Get x,y,z values for doseNum
    [xV, yV, zV] = getDoseXYZVals(planC{indexS.dose}(doseNum));

    %Get the corners of the original dataset.
    [xCorn, yCorn, zCorn] = meshgrid([min(xV) max(xV)], [min(yV) max(yV)], [min(zV) max(zV)]);

    %Add ones to the corners so we can apply a transformation matrix.
    corners = [xCorn(:) yCorn(:) zCorn(:) ones(prod(size(xCorn)), 1)];

    %Apply transform to corners, so we know boundary of the slice.
    transM = getTransM(planC{indexS.dose}(doseNum),planC);
    if isempty(transM) || isequal(transM,eye(4))
        [xV,yV,zV] = getDoseXYZVals(planC{indexS.dose}(doseNum));
        xGrid{doseNum} = xV(:)';
        yGrid{doseNum} = yV(:)';
        zGrid{doseNum} = zV(:)';
    else
        newCorners = inv(assocTransM) * transM * corners';
        xGrid{doseNum} = linspace(min(newCorners(1,:)), max(newCorners(1,:)), length(xV));
        yGrid{doseNum} = linspace(max(newCorners(2,:)), min(newCorners(2,:)), length(yV));
        zGrid{doseNum} = linspace(min(newCorners(3,:)), max(newCorners(3,:)), length(zV));
    end
    xRes{doseNum} = length(xV);
    yRes{doseNum} = length(yV);
    zRes{doseNum} = length(zV);

    %Get associated scan
    % assocScanV{doseNum} = getAssociatedScan(planC{indexS.dose}(doseNum).assocScanUID);
    assocScanV{doseNum} = getDoseAssociatedScan(doseNum,planC);
end
newXgrid = linspace(min(cell2mat(xGrid)),max(cell2mat(xGrid)),max(cell2mat(xRes)));
newYgrid = linspace(max(cell2mat(yGrid)),min(cell2mat(yGrid)),max(cell2mat(yRes)));
newZgrid = linspace(min(cell2mat(zGrid)),max(cell2mat(zGrid)),max(cell2mat(zRes)));

%Obtain doses with same grid
if isempty(assocTransM)
    assocTransM = eye(4);
end
doseIndC = {};
doseNumsTmp = doseNums;
for iSortAll = 1:length(doseNums)
    iSort = doseNums(iSortAll);
    indRemaining = doseNums;
    indRemaining(iSortAll) = [];
    doseSortM = [iSort];
    if ~ismember(iSort,[doseIndC{:}])
        for jSortAll = 1:length(indRemaining)
            jSort = indRemaining(jSortAll);
            if ~isempty(getTransM('dose',iSort,planC))
                doseITM = inv(assocTransM) * getTransM('dose',iSort,planC);
            else
                doseITM = inv(assocTransM);
            end
            if ~isempty(getTransM('dose',jSort,planC))
                doseJTM = inv(assocTransM) * getTransM('dose',jSort,planC);
            else
                doseJTM = inv(assocTransM);
            end
            if isequal([xRes{iSort},yRes{iSort},zRes{iSort}],[xRes{jSort},yRes{jSort},zRes{jSort}]) && isequal(doseITM,doseJTM)
                doseSortM(end+1) = jSort;
                indJsort = find(doseNumsTmp == jSort);
                doseNumsTmp(indJsort) = [];
            end
        end
    end
    doseIndC{iSort} = doseSortM;
end

doseNums = doseNumsTmp;

%Loop over doses and add over new grid
hWait = waitbar(0,'Summing Dose distributions');
doseSumM = zeros([length(newYgrid),length(newXgrid),length(newZgrid)],'single');
doseEmptyM = zeros([length(newYgrid),length(newXgrid)],'single');

%Assume dose units are same as that of 1st dose
doseUnits = getDoseUnitsStr(doseNums(1),planC);
for i = 1:length(doseNums)

    doseNum = doseNums(i);

    %Check for transM
    if ~isempty(assocTransM) && ~isequal(assocTransM,eye(4))
        doseTtransM = getTransM('dose',doseNum,planC);
        if isempty(doseTtransM)
            doseTtransM = eye(4);
        end
        inputTM = inv(assocTransM) * doseTtransM;
    else
        inputTM = getTransM('dose',doseNum,planC);
        if isempty(inputTM)
            inputTM = eye(4);
        end
    end

    SOPInstanceUIDv = {planC{indexS.beams}.SOPInstanceUID};
    if ~isempty(lqParamS)
        paramS.Tk.val = inf;         %Kick-off time of repopulation (days)
        paramS.Tp.val = NaN;        %Potential tumor doubling time (days)
        paramS.alpha.val = NaN;
        paramS.abRatio.val = lqParamS.abRatio; %10;  %alpha/beta
        paramS.stdFractionSize = lqParamS.stdFractionSize; % 2;
    end

    %Get the summation for this grid
    doseCombinedM = [];
    for iDoseAll = 1:length(doseIndC{doseNum})
        iDose = doseIndC{doseNum}(iDoseAll);
        doseUnits2 = getDoseUnitsStr(iDose,planC);
        if strcmpi(doseUnits, 'Gy') && strcmpi(doseUnits2, 'cGy')
            multFact = 0.01;
        elseif strcmpi(doseUnits, 'cGy') && strcmpi(doseUnits2, 'Gy')
            multFact = 100;
        else
            multFact = 1;
        end
        doseOffset = planC{indexS.dose}(iDose).doseOffset;
        if isempty(doseOffset)
            doseOffset = 0;
        end
        doseArray = single(getDoseArray(planC{indexS.dose}(iDose)) - doseOffset);

        % Apply BED/EQD2 correction
        if ~isempty(lqParamS)
            ReferencedSOPInstanceUID = planC{indexS.dose}(iDose)...
                .DICOMHeaders.ReferencedRTPlanSequence.Item_1.ReferencedSOPInstanceUID;
            planNum = find(strcmpi(ReferencedSOPInstanceUID,SOPInstanceUIDv));
            paramS.numFractions.val = planC{indexS.beams}(planNum(1)).FractionGroupSequence...
                .Item_1.NumberOfFractionsPlanned;
            paramS.numFractions.val = double(paramS.numFractions.val);
            paramS.frxSize.val = doseArray / paramS.numFractions.val;
            doseArray = calc_BED(paramS) / (1+paramS.stdFractionSize/paramS.abRatio.val);
        end

        if ~isempty(doseCombinedM)
            doseCombinedM = doseCombinedM + multFact * wtfactor(iDose) * doseArray;
        else
            doseCombinedM = multFact * wtfactor(iDose) * doseArray;
        end
    end

    %Check if this dose is on same grid as the new one
    %if isequal(xGrid{doseNum},newXgrid) && isequal(yGrid{doseNum},newYgrid) && isequal(zGrid{doseNum},newZgrid)
    chkLength = length(xGrid{doseNum})==length(newXgrid) && length(yGrid{doseNum})==length(newYgrid) && length(zGrid{doseNum})==length(newZgrid);
    if sum(sum((inputTM-eye(4)).^2)) < 1e-3 && chkLength && max(abs(xGrid{doseNum}-newXgrid)) < 1e-3 && max(abs(yGrid{doseNum}-newYgrid)) < 1e-3 && max(abs(zGrid{doseNum}-newZgrid)) < 1e-3

        doseSumM = doseSumM + doseCombinedM;
        waitbar((i-1)/length(doseNums),hWait,['Calculating contribution from Dose ', num2str(doseNum)])

    else %interpolation required

        %Transform this dose
        doseTmpM = [];

        for slcNum=1:length(newZgrid)
            [xV, yV, zV] = getDoseXYZVals(planC{indexS.dose}(doseNum));
            doseTmp = slice3DVol(doseCombinedM, xV, yV, zV, newZgrid(slcNum), 3, 'linear', inputTM, [], newXgrid, newYgrid);
            if isempty(doseTmp)
                doseTmpM(:,:,slcNum) = doseEmptyM;
            else
                doseTmpM(:,:,slcNum) = doseTmp;
            end
            waitbar((i-1)/length(doseNums) + (slcNum-1)/length(newZgrid)/length(doseNums) ,hWait,['Calculating contribution from Dose ', num2str(doseNum)])
        end

        doseSumM = doseSumM + doseTmpM;

    end

end

delete(hWait)

%Create new dose distribution
newDoseNum = length(planC{indexS.dose}) + 1;
planC{indexS.dose}(newDoseNum).doseArray = doseSumM;
clear doseSumM
planC{indexS.dose}(newDoseNum).doseUID = createUID('dose');
planC{indexS.dose}(newDoseNum).assocScanUID = assocScanUID;

%Find minimum value in 3d array, use its negative as the offset
maxDose = max(max(max(planC{indexS.dose}(newDoseNum).doseArray)));
offset = -min(min(min(planC{indexS.dose}(newDoseNum).doseArray)));
if offset > 0
    planC{indexS.dose}(newDoseNum).doseOffset = offset;
    planC{indexS.dose}(newDoseNum).doseArray = planC{indexS.dose}(newDoseNum).doseArray + offset;
end

%set labels on new dose, overwriting some of the copied labels **Check for more labels that need to be replaced
planC{indexS.dose}(newDoseNum).doseNumber = newDoseNum;
planC{indexS.dose}(newDoseNum).fractionGroupID = newDoseName;

%Remove old caching info.
planC{indexS.dose}(newDoseNum).cachedMask = [];
planC{indexS.dose}(newDoseNum).cachedColor = [];
planC{indexS.dose}(newDoseNum).cachedTime = [];

%Set coordinates.
planC{indexS.dose}(newDoseNum).sizeOfDimension1 = length(newXgrid);
planC{indexS.dose}(newDoseNum).sizeOfDimension2 = length(newYgrid);
planC{indexS.dose}(newDoseNum).sizeOfDimension3 = length(newZgrid);
planC{indexS.dose}(newDoseNum).horizontalGridInterval = newXgrid(2)-newXgrid(1);
planC{indexS.dose}(newDoseNum).verticalGridInterval = newYgrid(2)-newYgrid(1);
planC{indexS.dose}(newDoseNum).depthGridInterval = newZgrid(2)-newZgrid(1);
planC{indexS.dose}(newDoseNum).coord1OFFirstPoint = newXgrid(1);
planC{indexS.dose}(newDoseNum).coord2OFFirstPoint = newYgrid(1);
planC{indexS.dose}(newDoseNum).coord3OfFirstPoint = newZgrid(1);
planC{indexS.dose}(newDoseNum).zValues = newZgrid;
planC{indexS.dose}(newDoseNum).doseUnits = doseUnits;


%% Load the data into MATLAB from a binary log file
% Usage: >> [datapoints, numpoints] = readdata('datafile.log')
% Header information format:
%           String "MWLOGV##"
%           Time/Date 4 bytes (time())
%           Number of Signals per record Logged 1 bytes (256 max)
%           Data Type of Signals Logged  1 bytes (1-10)
%           Number of bytes per record 2 (65535 max)
% Plot Data Example: plot([1:numpoints], datapoints(1,:), [1:numpoints], datapoints(2,:))
% MathWorks Pilot Engineering 2015
% Steve Kuznicki
function [datapts, numpts] = readdata(dataFile)
%%
datapts = 0;
numpts = 0;

if nargin == 0
    dataFile = 'data.bin';
end

fid = fopen(dataFile, 'r');
% load the header information
hdrToken = fread(fid, 8, 'char');
if strncmp(char(hdrToken),'MWLOGV',6) == true
    logTime = uint32(fread(fid, 1, 'uint32'));
    numflds = double(fread(fid, 1, 'uint8'));
    typefld = uint8(fread(fid, 1, 'uint8'));
    recSize = uint16(fread(fid, 1, 'uint16'));
    fieldTypeStr = get_elem_type(typefld);
    datapts = fread(fid, double([numflds, Inf]), fieldTypeStr);
    fclose(fid);
    numpts = size(datapts,2);
end

end

%% get the element type string
function [dtypeStr] = get_elem_type(dtype)
    switch(dtype)
        case 1
            dtypeStr = 'double';
        case 2
            dtypeStr = 'single';
        case 3
            dtypeStr = 'int32';
        case 4
            dtypeStr = 'uint32';
        case 5
            dtypeStr = 'int16';
        case 6
            dtypeStr = 'uint16';
        case 7
            dtypeStr = 'int8';
        case 8
            dtypeStr = 'uint8';
        case 9
            dtypeStr = 'logical';
        case 10
            dtypeStr = 'embedded.fi';
    end
end


function varargout = unique(varargin)
% - Adds support for 'stable' flag, with all three outputs. If 'stable' or 'sorted'
% are used they must be the last input
% - The first ocurrence is returned by default, as in new Matlab versions. Octave,
% like old Matlab version, returns the last y default.
% - Second and third outputs are column vectors, as in new Matlab versions.
if nargin>=2 && strcmp(varargin{end},'stable')
    if strcmp(varargin{2},'rows')
        [a, ~, x] = builtin('unique', varargin{1}, 'rows', 'first');
    else
        [a, ~, x] = builtin('unique', varargin{1}, 'first');
    end
    if ~isempty(x)
        [~, ind] = max(bsxfun(@eq, x(:), (1:max(x)))); [y, ind2] = sort(ind(:));
        [~, ind3] = sort(ind2); z =  ind3(x);
        for n = 1:numel(x)
            x(n) = x(n) * ~any(x(n)==x(1:n-1));
        end
        x = nonzeros(x).';
    end
    if strcmp(varargin{2},'rows')
        x = a(x,:);
    else
        x = a(x);
    end
    varargout{1} = x;
    if nargout>=2, varargout{2} = y; end
    if nargout>=3, varargout{3} = z; end
else
    if strcmp(varargin{end},'sorted'), varargin(end) = []; end
    varargout = cell(1,nargout);
    if ~any(cellfun(@(x) any(strcmp(x, {'first', 'last'})), varargin(2:end))), varargin{end+1} = 'first'; end
    [varargout{:}] = builtin('unique', varargin{:});
end
if nargout>=2, varargout{2} = varargout{2}(:); end
if nargout>=3, varargout{3} = varargout{3}(:); end
end


function model = loss_pyramid(h_loss_func, pyra, model, fg_box, ...
                              bg_boxes, min_fg_overlap, max_bg_overlap)
% Computes a pyramid of loss function values for each top-level
% rule in the grammar.
%   model = loss_pyramid(h_loss_func, pyra, model, fg_box, ...
%                        bg_boxes, min_fg_overlap, max_bg_overlap)
%
%   These loss values are used for computing the loss adjusted inference:
%     \max_{s \in S(x)} w \dot \psi(x,s) + L_margin(y,s)
%   The set of valid outputs S(x) is enforced by making L(y,s) = -inf for
%   some values of s, which prevents them from being selected in the
%   maximization.
%
% Return value
%   model           Model augmented to store the computed loss pyramids
%
% Arguments
%   h_loss_func     Handle to loss function
%   model           Model
%                   (augmented with DP tables from gdetect_dp.m)
%   pyra            Feature pyramid
%                   (augmented with overlaps from gdetect_pos_prepare.m)
%   fg_box          Selected foreground bounding box index
%   bg_boxes        Indices of non-selected bounding boxes in image
%   min_fg_overlap  Minimum required amount of overlap with fg box
%   max_bg_overlap  Maximum allowed amount of overlap with bg bounding boxes

% AUTORIGHTS
% -------------------------------------------------------
% Copyright (C) 2011-2012 Ross Girshick
%
% This file is part of the voc-releaseX code
% (http://people.cs.uchicago.edu/~rbg/latent/)
% and is available under the terms of an MIT-like license
% provided in COPYING. Please retain this notice and
% COPYING if you use this file (or a portion of it) in
% your project.
% -------------------------------------------------------

num_bg_boxes = length(bg_boxes);

% For each model component
for comp = 1:length(model.rules{model.start})
  % For each feature pyramid level
  for level = 1:pyra.num_levels
    if pyra.valid_levels(level)
      % Assign loss for root locations based on the selected foreground box
      o = pyra.overlaps(comp).box(fg_box).o{level};
      losses = h_loss_func(o);
      model.rules{model.start}(comp).loss{level} = losses;

      % Require at least some overlap with the foreground bounding box
      % Rationale:
      %  In an image with multiple objects, this constraint encourages a
      %  diverse set of false positives (otherwise, they will tend to come
      %  from the same high-scoring / low-overlapping region of the image
      %  -- i.e. somewhere in the background)
      I = find(o < min_fg_overlap);
      model.rules{model.start}(comp).loss{level}(I) = -inf;

      % Mark root locations that have too much overlap with background boxes
      % as invalid
      % Rationale:
      %   We don't want to select detections of other foreground objects
      %   in the image as false positives (i.e., no true positive should
      %   be allowed to be used as a false positive)
      for b = 1:num_bg_boxes
        o = pyra.overlaps(comp).box(bg_boxes(b)).o{level};
        inds = find(o >= max_bg_overlap);
        model.rules{model.start}(comp).loss{level}(inds) = -inf;
      end
    else
      model.rules{model.start}(comp).loss{level} = 0;
    end
  end
end


function [dose3D, A_z3D, r3D, PB3D] = getInfluence(IM, structNum, PBWeightsV)
%Create 3D dose matrix from PB weigths (PBWeightsV) and
%a list of structures to have dose computed to (structsV).
%JOD, 14 Nov 03.
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

global planC

% maskSingle3D = getUniformStr(structNum);

maskSingle3D = getSurfaceExpand(structNum,0.5,1); %%%Expandewd structure

[rowV, colV, sliceV] = find3d(maskSingle3D);

numPBs = size(IM.beamlets,2);

influenceM = sparse(prod(size(maskSingle3D)),numPBs);

s = size(maskSingle3D);

doseInStruct = zeros(s);

sampleRate = IM.beamlets(structNum,1).sampleRate;

if sampleRate ~= 1
    disp('Inflating downsampled dose distribution...')
  if rem(log2(sampleRate),1) ~= 0
    error('Sample factor must (currently) be a power of 2.')
  end
  maskSample3D = getDown3Mask(maskSingle3D, sampleRate, 1);

  tmp3D = maskSample3D .* maskSingle3D;

  [rowV,colV,sliceV] = find3d(tmp3D);

  clear tmp3D

  toInterp3D = ~maskSample3D .* maskSingle3D;

  [rInterpV,cInterpV,sInterpV] = find3d(toInterp3D);

end

dose3D = zeros(s);

for PBNum = 1 : size(IM.beamlets,2)       %Loop over beamlets

  if ~isempty(IM.beamlets(structNum,PBNum).influence)

    doseV     = double(IM.beamlets(structNum,PBNum).influence);
    indV      = IM.beamlets(structNum,PBNum).indexV;
    maxVal    = IM.beamlets(structNum,PBNum).maxInfluenceVal;
    sizeParam = IM.beamlets(structNum,PBNum).fullLength;

    doseScaledV = PBWeightsV(PBNum) * (doseV * maxVal) /(2^8 -1);

    %inflate:
    doseInflateV = zeros(1,sizeParam);
    doseInflateV(indV) = doseScaledV;

    %Then put into the dose3D matrix:
    ind2V = sub2ind(size(dose3D), rowV, colV, sliceV);

    doseInStruct(ind2V) = doseInStruct(ind2V) + doseInflateV(:);

    if strcmpi(IM.params.debug(1),'y')
      ind0V =IM.beamlets(structNum,PBNum).rIndex;
      AInflate = zeros(1,sizeParam);
      rInflate = zeros(1,sizeParam);
      AInflate(ind0V) = PBWeightsV(PBNum) * double(IM.beamlets(structNum,PBNum).A_zV);
      A_z3D(ind2V) = AInflate(:); %overwrite
      rInflate(ind0V) = PBWeightsV(PBNum) * double(IM.beamlets(structNum,PBNum).r);
      r3D(ind2V) = rInflate(:); %overwrite
      PB3D(ind2V) = PBNum * ones(length(rInflate),1);
    end

  end

end

%If sub-sampled, use 3-D interpolation to fill out dose.

if sampleRate ~= 1

  %Now do 3-D interpolation:
  sizeV = size(dose3D);

  doseInterp = doseInStruct;
  %Get rid of all points which are not sampled
  [r3V,c3V,s3V] = find3d(maskSample3D);
  whereV = sub2ind(sizeV,r3V,c3V,s3V);
  doseInterp = doseInterp(whereV);
  doseInterp = reshape(doseInterp,[sizeV(1)/2^log2(sampleRate),sizeV(2)/2^log2(sampleRate),sizeV(3)]);

  rDownV = (rInterpV + 2^log2(sampleRate) - 1)/2^log2(sampleRate);
  cDownV = (cInterpV + 2^log2(sampleRate) - 1)/2^log2(sampleRate);

  fillsV = matInterp3(rDownV,cDownV,sInterpV,doseInterp);

  ind3V = sub2ind(sizeV,rInterpV,cInterpV,sInterpV);  %index for interpolated values
  doseInStruct(ind3V) = fillsV;
  disp('Finished inflating.')
end

dose3D = dose3D + doseInStruct .* [dose3D==0];  %voxel over-adding avoided


function X=ctranspose(X)
%CTRANSPOSE (overloaded)

if isa(X,'blkvar')
    X = sdpvar(X);
end

n = X.dim(1);
m = X.dim(2);
ind = reshape(reshape(1:n*m,n,m)',n*m,1);
if isreal(X.basis)
    X.basis = X.basis(ind,:);
else
   X.basis = conj(X.basis(ind,:));
end
X.dim(1) = m;
X.dim(2) = n;
% Reset info about conic terms
X.conicinfo = [0 0];

% Flip noncommuting terms
% Get all the tables, and expand them so that they correspond to the same
% number of variables globally (nonCommutingTable is not up to date after a
% new commuting variables has been defined, to save flops)
nonCommutingTable         = yalmip('nonCommutingTable');
[monomtable,variabletype] = yalmip('monomtable');
if size(monomtable,1)>size(nonCommutingTable,1)
    nonCommutingTable((1+size(nonCommutingTable,1)):(size(monomtable,1)),1) = (1+size(nonCommutingTable,1)):(size(monomtable,1));
end
% Cast commutative variables as nc temporarily by adding them to the table
commuting = find(~any(nonCommutingTable,2));
nonCommutingTable(commuting,1) = commuting;

for i = 1:length(X.lmi_variables)
    if nonCommutingTable(X.lmi_variables(i),2)
        monoms = nonCommutingTable(X.lmi_variables(i),2:end);
        monoms(find(monoms)) = fliplr( monoms(find(monoms)));
        monoms = [nonCommutingTable(X.lmi_variables(i),1) monoms];
        old = findrows(nonCommutingTable,monoms);
        old = findrows(nonCommutingTable(:,2:end),monoms(2:end));
        if ~isempty(old)
            old = old(find((monoms(1) == nonCommutingTable(old,1)) | isnan(monoms(1)) & isnan(nonCommutingTable(old,1))));
        end
        if isempty(old)
            % Create a new monomial
            monomtable(end+1,end+1) = 0;
            variabletype(end+1) = variabletype(X.lmi_variables(i));
            nonCommutingTable = [nonCommutingTable;monoms];
            X.lmi_variables(i) = size(nonCommutingTable,1);
        else
            X.lmi_variables(i) = old;
        end
    end
end
% Fucked up order (lmi_variables should be sorted and unique)
if any(diff(X.lmi_variables)<0)
    [i,j]=sort(X.lmi_variables);
    X.basis = [X.basis(:,1) X.basis(:,j+1)];
    X.lmi_variables = X.lmi_variables(j);
end
[un_Z_vars2] = uniquestripped(X.lmi_variables);
if length(un_Z_vars2) < length(X.lmi_variables)
    [un_Z_vars,hh,jj] = unique(X.lmi_variables);
    if length(X.lmi_variables) ~=length(un_X_vars)
        X.basis = Z.basis*sparse([1 1+jj],[1 1+(1:length(jj))],ones(1,1+length(jj)))';
        X.lmi_variables = un_Z_vars;
    end
end
if size(monomtable,2) < size(monomtable,1)
    monomtable(size(monomtable,1),size(monomtable,1)) = 0;
end
yalmip('nonCommutingTable',nonCommutingTable);
yalmip('setmonomtable',monomtable,variabletype);


function [f,k,s0,gof] = relaxFitMtDist(data,delta,t1,s0,tr,flipAngle,brainMask,fitMethod,outMontage)
%
% [f,k,s0,gof] = relaxFitMtDist(data,delta,t1,s0,tr,flipAngle,brainMask,fitMethod,outMontage)
%
% Computes a nonlinear fit of the bound-pool (f) map.
%
% Returns:
%
% SEE ALSO:
%
% relaxFitT1.m to fit the t1 and pd maps used by this function.
%
% HISTORY:
% 2008.02.26 RFD: wrote it.

if(~exist('fitMethod','var')||isempty(fitMethod))
    fitMethod = 'fmin';
end
if(lower(fitMethod(1))=='p')
    fitMethod = fitMethod(2:end);
    useParfor = true;
else
    useParfor = false;
end
fitMethod = lower(fitMethod(1));

if(~exist('outMontage','var')||isempty(outMontage))
    outMontage = 'f.png';
end

flipAngle = flipAngle*pi/180;
tr = tr/1000;
sz = size(data);

% disp('smoothing s0 map...');
% deltaT = 1/44;
% kappa = 200;
% s0 = dtiSmoothAnisoPM(s0, 4, deltaT, kappa, 1, [1 1 1]);
%
%disp('smoothing MT measurments...');
%kappa = 50;
%for(ii=1:sz(4))
%    data(:,:,:,ii) = dtiSmoothAnisoPM(data(:,:,:,ii), 2, deltaT, kappa, 1, [1 1 1]);
%end

brainInds = find(brainMask);
numVoxelsPerUpdate = 16000;
nVoxAll = length(brainInds);
for(ii=1:size(data,4))
  tmpVol = data(:,:,:,ii);
  tmpMT(ii,:) = tmpVol(brainInds);
end
clear tmpVol;
data = tmpMT;
clear tmpMT;
r1 = 1./t1(brainInds);
s0 = s0(brainInds);

f = zeros(1,nVoxAll);
k = zeros(1,nVoxAll);
gof = zeros(1,nVoxAll);
totalSecs = 0;

tmpImg = zeros(sz(1:3));
tmpName = tempname
nSteps = ceil(nVoxAll/numVoxelsPerUpdate);
totalTime = 0;
tic;
for(ii=1:nSteps)
    if(useParfor), matlabpool; end
    curInd = (ii-1)*numVoxelsPerUpdate+1;
    endInd = min(curInd+numVoxelsPerUpdate,nVoxAll);
    [tf,tk,tgof] = relaxFitMt(data(:,curInd:endInd),delta,r1(curInd:endInd),s0(curInd:endInd),tr,flipAngle,fitMethod,useParfor);
    prevSecs = toc;
    totalTime = totalTime+prevSecs;
    f(curInd:endInd) = tf;
    k(curInd:endInd) = tk;
    gof(curInd:endInd) = tgof;
    secsPerVox = prevSecs/(endInd-curInd+1);
    estTime = secsPerVox*(nVoxAll-endInd);
    if(estTime>5400) estTime=estTime./3600; estTimeUnits='hours';
    elseif(estTime>90) estTime=estTime./60; estTimeUnits='minutes';
    else estTimeUnits='seconds';
    end
    fprintf('Processed %d of %d voxels- %0.1f %s remaining (%0.3f secs per vox)...\n',endInd,nVoxAll,estTime,estTimeUnits,secsPerVox);
    tmpImg(brainInds) = f;
    m = makeMontage(tmpImg);
    if(max(f)>0), m = m./max(f); end
    m = uint8(round(m.*255));
    imwrite(m,outMontage);
    save(tmpName,'k','f','gof','brainMask');
    tic;
    if(useParfor), matlabpool close; end
end
fprintf('Processed %d voxels in %0.2f hours.\n',totalTime/3600);

% f = vertcat(tf);
% k = vertcat(tk);
% gof = vertcat(tgof);

im=zeros(size(brainMask)); im(brainInds) = f; f = im;
im=zeros(size(brainMask)); im(brainInds) = k; k = im;
im=zeros(size(brainMask)); im(brainInds) = gof; gof = im;
im=zeros(size(brainMask)); im(brainInds) = s0; s0 = im;

return;


function output = callipopt(model)

options = [];
try
    options.ipopt = optiRemoveDefaults(model.options.ipopt,ipoptset());
catch
    options.ipopt = model.options.ipopt;
end
options.ipopt.print_level = 2+model.options.verbose;

% Standard NONLINEAR setup
tempF = model.F_struc;
tempK = model.K;
tempx0 = model.x0;
model = yalmip2nonlinearsolver(model);
% % Try to propagate initial values now that nonlinear evaluation logic is
% % set up. Do this if some of the intials are nan
if any(isnan(model.x0)) & ~all(isnan(model.x0))
    [~,~,xevaledout] = fmincon_fun(model.x0,model);
    startNan = nnz(isnan(xevaledout));
    goon = 1;
    while goon
        temp = propagatex0(xevaledout,tempF,tempK);
        model.x0 = temp(model.linearindicies);
        [~,~,xevaledout] = fmincon_fun(model.x0,model);
        goon =  nnz(isnan(xevaledout)) < startNan;
        startNan = nnz(isnan(xevaledout));
    end
end
model.x0(isnan(model.x0))=0;

if ~model.derivative_available
    disp('Derivate-free call to ipopt not yet implemented')
    error('Derivate-free call to ipopt not yet implemented')
end
if model.options.savedebug
    save ipoptdebug model
end
showprogress('Calling IPOPT',model.options.showprogress);

% Figure out which variables are artificially introduced to normalize
% arguments in callback operators to simplify chain rules etc. We can do
% our function evaluations and gradient computations in our lifted world,
% but only expose the model in the original variables to the nonlinear
% solver.
% model = compressLifted(model);

Fupp = [ repmat(0,length(model.bnonlinineq)+length(model.K.q)*(model.K.q(1)>0),1);
    repmat(0,length(model.bnonlineq),1);
    repmat(0,length(model.b),1);
    repmat(0,length(model.beq),1)];

Flow = [ repmat(-inf,length(model.bnonlinineq)+length(model.K.q)*(model.K.q(1)>0),1);
    repmat(0,length(model.bnonlineq),1);
    repmat(-inf,length(model.b),1);
    repmat(0,length(model.beq),1)];

if isempty(Flow)
    Flow = [];
    Fupp = [];
end

% Since ipopt react strangely on lb>ub, we should bail if that is detected
% (ipopt creates an exception)
if ~isempty(model.lb)
    if any(model.lb>model.ub)
        problem = 1;
        solverinput = [];
        solveroutput = [];
        output = createoutput(model.c*0,[],[],problem,'IPOPT',solverinput,solveroutput,0);
        return
    end
end

% These are needed to avoid recomputation due to ipopts double call to get
% f and df, and g and dg
global latest_x_f
global latest_x_g
global latest_df
global latest_f
global latest_G
global latest_g
global latest_xevaled
global latest_x_xevaled
latest_G = [];
latest_g = [];
latest_x_f = [];
latest_x_g = [];
latest_xevaled = [];
latest_x_xevaled = [];

funcs.objective = @(x)ipopt_callback_f(x,model);
funcs.gradient = @(x)ipopt_callback_df(x,model);
if ~isempty(Fupp)
    funcs.constraints = @(x)ipopt_callback_g(x,model);
    funcs.jacobian  = @(x)ipopt_callback_dg(x,model);
end

options.lb = model.lb(:)';
options.ub = model.ub(:)';
if ~isempty(Fupp)
    options.cl = Flow;
    options.cu = Fupp;
end

if ~isempty(Fupp)
    Z = jacobiansparsityfromnonlinear(model);
    funcs.jacobianstructure = @() Z;
end

if ~model.options.usex0
    model.x0 = (options.lb+options.ub)/2;
    model.x0(isinf(options.ub)) = options.lb(isinf(options.ub))+1;
    model.x0(isinf(options.lb)) = options.ub(isinf(options.lb))-1;
    model.x0(isinf(model.x0)) = 0;
    if any(model.variabletype == 4)
        problematic = find(any(model.monomtable(:,model.linearindicies) < 0 ,1));
        if ~isempty(problematic)
            problematic = problematic(find(model.x0(problematic)==0));
            Oneisfeas = problematic(find(model.ub(problematic) > 1));
            model.x0(Oneisfeas) = 1;
        end
    end
    model.x0(find(model.lb==model.ub)) = model.lb(find(model.lb==model.ub));
end

% If quadratic objective and no nonlinear constraints, we can supply an
% Hessian of the Lagrangian
usedinObjective = find(model.c | any(model.Q,2));
if ~any(model.variabletype(usedinObjective)) & any(model.Q)
    if  length(model.bnonlinineq)==0 & length(model.bnonlineq)==0
        H = model.Q(:,model.linearindicies);
        H = H(model.linearindicies,:);
        funcs.hessian = @(x,s,l) tril(2*H);
        funcs.hessianstructure = @()tril(sparse(double(H | H)));
        options.ipopt.hessian_approximation = 'exact';
    end
end

solvertime = tic;
[xout,info] = ipopt(model.x0,funcs,options);
solvertime = toc(solvertime);

% Duals currently not supported
lambda = [];

if ~isempty(xout) && ~isempty(model.lift);
    x = zeros(length(model.linearindicies),1);
    x(model.lift.linearIndex) = xout(:);
    x(model.lift.liftedIndex) = model.lift.T*xout(:) + model.lift.d;
    x = RecoverNonlinearSolverSolution(model,x);
else
    x = RecoverNonlinearSolverSolution(model,xout);
end

switch info.status
    case {0,1}
        problem = 0;
    case {2}
        problem = 1;
    case {-1}
        problem = 3;
    case {3,4,-2,-3}
        problem = 4;
    case {-11,-12,-13}
        problem = 7;
    case {-10,-100,-101,-102,-199}
        problem = 11;
    otherwise
        problem = -1;
end

% Internal format for duals
D_struc = [];

% Save all data sent to solver?
if model.options.savesolverinput
    solverinput.model = model;
else
    solverinput = [];
end

% Save all data from the solver?
if model.options.savesolveroutput
    solveroutput.x = xout;
    solveroutput.info = info;
else
    solveroutput = [];
end

% Standard interface
output = createoutput(x,D_struc,[],problem,'IPOPT',solverinput,solveroutput,solvertime);


function shim = cvx_glpk( shim )

% CVX_SOLVER_SHIM	GLPK interface for CVX.
%   This procedure returns a 'shim': a structure containing the necessary
%   information CVX needs to use this solver in its modeling framework.

if ~isempty( shim.solve ),
    return
end
if isempty( shim.name ),
    fname = 'glpk.m';
    ps = pathsep;
    shim.name = 'GLPK';
    shim.dualize = true;
    flen = length(fname);
    fpaths = which( fname, '-all' );
    if ~iscell(fpaths),
      fpaths = { fpaths };
    end
    old_dir = pwd;
    oshim = shim;
    shim = [];
    for k = 1 : length(fpaths),
        fpath = fpaths{k};
        if ~exist( fpath, 'file' ) || any( strcmp( fpath, fpaths(1:k-1) ) ),
            continue
        end
        new_dir = fpath(1:end-flen-1);
        cd( new_dir );
        tshim = oshim;
        tshim.fullpath = fpath;
        tshim.version = 'unknown';
        tshim.location = new_dir;
        if isempty( tshim.error ),
            tshim.check = @check;
            tshim.solve = @solve;
            tshim.eargs = {};
            if k ~= 1,
                tshim.path = [ new_dir, ps ];
            end
        end
        shim = [ shim, tshim ]; %#ok
    end
    cd( old_dir );
    if isempty( shim ),
        shim = oshim;
        shim.error = 'Could not find a GLPK installation.';
    end
else
    shim.check = @check;
    shim.solve = @solve;
end

function found_bad = check( nonls ) %#ok
found_bad = false;

function [ x, status, tol, iters, y, z ] = solve( At, b, c, nonls, quiet, prec, settings )

n  = length( c );
m  = length( b );
lb = -Inf(n,1);
ub = +Inf(n,1);
vtype = 'C';
vtype = vtype(ones(n,1));
ctype = 'S';
ctype = ctype(ones(m,1));
rr = zeros(0,1);
cc = rr;
vv = rr;
zinv = rr;
is_ip = false;
for k = 1 : length( nonls ),
    temp = nonls( k ).indices;
    nn = size( temp, 1 );
    nv = size( temp, 2 );
    tt = nonls( k ).type;
    if strncmp( tt, 'i_', 2 ),
      is_ip = true;
      vartype(temp) = 'I';
      if strcmp(tt,'i_binary'),
        lb(temp) = 0;
        ub(temp) = 1;
      end
    elseif nn == 1 || isequal( tt, 'nonnegative' ),
        lb(temp) = 0;
    elseif isequal( tt, 'lorentz' ),
        if nn == 2,
            rr2  = [ temp ; temp ];
            cc2  = reshape( floor( 1 : 0.5 : 2 * nv + 0.5 ), 4, nv );
            vv2  = [1;1;-1;1]; vv = vv(:,ones(1,nv));
            rr   = [ rr ; rr(:) ];
            cc   = [ cc ; cc(:) ];
            vv   = [ vv ; vv(:) ];
            zinv = [ zinv ; temp(:) ];
        else
            error('GLPK does not support nonlinear constraints.' );
        end
    else
      error('GLPK does not support nonlinear constraints.' );
    end
end
if ~isempty(rr),
  znorm = [1:n]';
  znorm(zinv) = [];
  rr = [ rr ; znorm ];
  cc = [ cc ; znorm ];
  vv = [ vv ; ones(size(znorm)) ];
  reord = sparse( rr, cc, vv, n, n );
  At = reord' * At;
  c  = reord' * c;
end
if quiet,
  param.msglev = 0;
else
  param.msglev = 2;
end
param.scale = 128;
param.tolbnd = prec(1);
param.toldj = prec(1);
param.tolobj = prec(1);
[ xx, fmin, errnum, extra ] = cvx_run_solver( @glpk, c, At', b, lb, ub, ctype, vtype, 1, param, 'xx', 'fmin', 'errnum', 'extra', settings, 9 );
tol   = [];
iters = [];
x = full( xx );
y = full( extra.lambda );
z = full( extra.redcosts );
if ~isempty( rr ),
  x = reord * x;
  z = reord * z;
  z(zinv) = z(zinv) * 0.5;
end
status = 'Failed';
switch errnum,
case 0,
  switch extra.status,
  case 2,
    if is_ip,
      status = 'Suboptimal';
    elseif errnumn == 0,
      status = 'Solved';
    else
      status = 'Inaccurate/Solved';
    end
  case {3,4},
    status = 'Infeasible';
  case 5,
    status = 'Solved';
  case 6,
    status = 'Unbounded';
  end
case 10,
  status = 'Infeasible';
case 11,
  status = 'Unbounded';
case {5,17,15,19}
  status = 'Failed';
case {6,7,8,9,13,14},
  switch extra.status,
  case {2,5}
    if is_ip,
      status = 'Suboptimal';
    else
      status = 'Inaccurate/Solved';
    end
  case { 3,4 },
    status = 'Inaccurate/Infeasible';
  case 6,
    status = 'Inaccurate/Unbounded';
  end
end
if strcmp(status,'Failed'),
  tol = Inf;
elseif strncmp(status,'Inaccurate/',11),
  tol = prec(3);
else
  tol = prec(2);
end

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function varargout = spm_bms_display(BMS,action)
% Display results from BMS Maps
% FORMAT spm_bms_display(BMS,action)
%
% Input:
% BMS    - BMS containing details of excursion set
% action - 'Init' (Initialise)
%          'do_plot' (plot voxel results)
%          'save' (save results as NIfTI image)
%          'overlays' (options overlays menu)
%__________________________________________________________________________
% Copyright (C) 2009-2019 Wellcome Trust Centre for Neuroimaging

% Maria Joao Rosa
% $Id: spm_bms_display.m 7577 2019-04-24 08:59:56Z guillaume $

% Main options (action)
% =========================================================================
switch action

    % Inititalise - action: 'Init'
    % =====================================================================
    case 'Init'

    % Initialise variables
    % ---------------------------------------------------------------------
    xSPM  = BMS.xSPM;
    M     = xSPM.xVol.M;
    iM    = xSPM.iM;
    DIM   = xSPM.xVol.DIM;

    try
        if strcmp(spm('CheckModality'),'EEG')
            datatype = {...
                'Volumetric (2D/3D)',...
                'Scalp-Time',...
                'Scalp-Frequency',...
                'Time-Frequency',...
                'Frequency-Frequency'};
            selected = spm_input('Data Type: ','+1','m',datatype);
            datatype = datatype{selected};
        else
            datatype = 'Volumetric (2D/3D)';
        end
    catch
        datatype     = 'Volumetric (2D/3D)';
    end

    switch datatype
        case 'Volumetric (2D/3D)'
            units    = {'mm' 'mm' 'mm'};
        case 'Scalp-Time'
            units    = {'mm' 'mm' 'ms'};
        case 'Scalp-Frequency'
            units    = {'mm' 'mm' 'Hz'};
        case 'Time-Frequency'
            units    = {'Hz' 'ms' ''};
        case 'Frequency-Frequency'
            units    = {'Hz' 'Hz' ''};
        otherwise
            error('Unknown data type.');
    end

    title = 'Bayesian Model Selection';
    str   = xSPM.str;

    % Initialise figures
    % ---------------------------------------------------------------------
    [Finter,Fgraph,CmdLine] = spm('FnUIsetup','BMS: Results');
    FS = spm('FontSizes');
    WS = spm('WinScale');
    PF = spm_platform('fonts');

    % Clear satellite figure if it exists
    % ---------------------------------------------------------------------
    hSat = findobj('tag','Satellite');
    spm_figure('clear',hSat);

    % Setup Finter (interactive window)
    % ---------------------------------------------------------------------
    spm_figure('Clear',Finter);
    spm('FigName','BMS results',Finter,CmdLine);
    Finter  = spm_figure('GetWin',Finter);

    hReg    = uicontrol(Finter,'Style','Frame','Position',...
        [001 001 400 190].*WS,'BackgroundColor',spm('Colour'));

    hFResUi = uicontrol(Finter,'Style','Frame','Position',...
        [008 007 387 178].*WS);

    [hReg,xyz] = spm_XYZreg('InitReg',hReg,M,DIM,[0;0;0]);

    % Draw MIP
    % ---------------------------------------------------------------------
    hMIPax = axes('Parent',Fgraph,'Position',...
        [0.125 0.5450 0.59 0.40],'Visible','off');
    hMIPax = spm_mip_ui(xSPM.Z,xSPM.XYZmm,M,DIM,hMIPax,units);
    spm_XYZreg('XReg',hReg,hMIPax,'spm_mip_ui');
    hTitAx = axes('Parent',Fgraph,'Position',[0.02 0.95 0.86 0.02],...
        'Visible','off');
    text(0.5,0,title,'Parent',hTitAx,'HorizontalAlignment','center',...
        'VerticalAlignment','baseline','FontWeight','Bold','FontSize',FS(14))

    text(240,260,str,'Interpreter','TeX','FontSize',FS(14),...
        'Fontweight','Bold','Parent',hMIPax)

    % Print BMSresults: Results directory & thresholding info
    %----------------------------------------------------------------------
    hResAx = axes('Parent',Fgraph,...
        'Position',[0.160 0.510 0.45 0.05],...
        'DefaultTextVerticalAlignment','baseline',...
        'DefaultTextFontSize',FS(9),...
        'DefaultTextColor',[1,1,1]*.7,...
        'Units','points',...
        'Visible','off');
    AxPos = get(hResAx,'Position'); set(hResAx,'YLim',[0,AxPos(4)])
    h     = text(0,24,'BMSresults:','Parent',hResAx,...
        'FontWeight','Bold','FontSize',FS(14));
    text(get(h,'Extent')*[0;0;1;0],24,spm_file(pwd,'short30'),'Parent',hResAx)
    text(0,12,sprintf('Threshold: %0.2d',BMS.xSPM.thres),'Parent',hResAx)

    % Store handles of results section Graphics window objects
    %----------------------------------------------------------------------
    H  = get(Fgraph,'Children');
    H  = findobj(H,'flat','HandleVisibility','on');
    H  = findobj(H);
    Hv = get(H,'Visible');
    set(hResAx,'Tag','PermRes','UserData',struct('H',H,'Hv',{Hv}))

    % Draw buttons
    %----------------------------------------------------------------------
    Finter = spm_figure('FindWin','Interactive');
    xyz    = [0;0;0];
    xyz    = spm_XYZreg('RoundCoords',xyz,M,DIM);

    % Create XYZ control objects
    % ---------------------------------------------------------------------
    hFxyz = uicontrol(Finter,'Style','Text',...
            'Position',[010 010 265 030].*WS);
    uicontrol(Finter,'Style','Text','String','co-ordinates',...
            'Position',[020 033 078 016].*WS,...
            'FontAngle','Italic',...
            'FontSize',FS(10),...
            'HorizontalAlignment','Left',...
            'ForegroundColor','w')

    uicontrol(Finter,'Style','Text','String','x =',...
            'Position',[020 015 024 018].*WS,...
            'FontName',PF.times,'FontSize',FS(10),'FontAngle','Italic',...
            'HorizontalAlignment','Center');
    hX   = uicontrol(Finter,'Style','Edit','String',...
            sprintf('%.2f',xyz(1)),...
            'ToolTipString','enter x-coordinate',...
            'Position',[044 015 056 020].*WS,...
            'FontSize',FS(10),'BackGroundColor',[.8,.8,1],...
            'HorizontalAlignment','Right',...
            'Tag','hX',...
            'Callback','spm_bms_display('''',''plot_xyz'')');

    uicontrol(Finter,'Style','Text','String','y =',...
            'Position',[105 015 024 018].*WS,...
            'FontName',PF.times,'FontSize',FS(10),'FontAngle','Italic',...
            'HorizontalAlignment','Center')
    hY   = uicontrol(Finter,'Style','Edit','String',...
            sprintf('%.2f',xyz(2)),...
            'ToolTipString','enter y-coordinate',...
            'Position',[129 015 056 020].*WS,...
            'FontSize',FS(10),'BackGroundColor',[.8,.8,1],...
            'HorizontalAlignment','Right',...
            'Tag','hY',...
            'Callback','spm_bms_display('''',''plot_xyz'')');

    uicontrol(Finter,'Style','Text','String','z =',...
            'Position',[190 015 024 018].*WS,...
            'FontName',PF.times,'FontSize',FS(10),'FontAngle','Italic',...
            'HorizontalAlignment','Center')
    hZ   = uicontrol(Finter,'Style','Edit','String',...
            sprintf('%.2f',xyz(3)),...
            'ToolTipString','enter z-coordinate',...
            'Position',[214 015 056 020].*WS,...
            'FontSize',FS(10),'BackGroundColor',[.8,.8,1],...
            'HorizontalAlignment','Right',...
            'Tag','hZ',...
            'Callback','spm_bms_display('''',''plot_xyz'')');

    % Voxel value reporting pane
    % ---------------------------------------------------------------------
    hFconB = uicontrol(Finter,'Style','Text',...
            'Position',[280 010 110 030].*WS);
    uicontrol(Finter,'Style','Text','String','voxel value',...
            'Position',[285 035 085 016].*WS,...
            'FontAngle','Italic',...
            'FontSize',FS(10),...
            'HorizontalAlignment','Left',...
            'ForegroundColor','w')
    hSPM = uicontrol(Finter,'Style','Text','String','',...
            'Position',[285 012 100 020].*WS,...
            'FontSize',FS(10),...
            'HorizontalAlignment','Center');

    % Store UserData
    % ---------------------------------------------------------------------
    set(hFxyz,'Tag','hFxyz','UserData',struct(...
              'hReg',   [],...
              'M',      M,...
              'iM',     iM,...
              'DIM',    DIM,...
              'XYZ',    xSPM.XYZmm,...
              'Z',      xSPM.Z,...
              'hX',     hX,...
              'hY',     hY,...
              'hZ',     hZ,...
              'hSPM',   hSPM,...
              'xSPM',   xSPM,...
              'fhFxyz', hFxyz,...
              'hMIPax', hMIPax,...
              'xyz',    xyz,...
              'thres',  BMS.xSPM.thres,...
              'scale',  BMS.xSPM.scale,...
              'vols',   BMS.xSPM.vols,...
              'k',      BMS.xSPM.k,...
              'BMS',    BMS));

    set([hX,hY,hZ],'UserData',hFxyz);

    % Register with hReg
    % ---------------------------------------------------------------------
    spm_XYZreg('XReg',hReg,hFxyz,'spm_results_ui');

    % Model partition
    % ---------------------------------------------------------------------
    uicontrol(Finter,'Style','PushButton','String','compare subsets',...
                'FontSize',FS(10),...
                'ToolTipString','Create and compare subsets of models',...
                'Callback','spm_bms_display('''',''partition'');',...
                'Interruptible','on','Enable','on',...
                'Position',[130 055 140 020].*WS,...
                'ForegroundColor','k');

    % Compare groups
    % ---------------------------------------------------------------------
    uicontrol(Finter,'Style','PushButton','String','compare groups',...
                'FontSize',FS(10),...
                'ToolTipString','Compare two groups. E.g. controls vs patients.',...
                'Callback','spm_bms_display('''',''groups'');',...
                'Interruptible','on','Enable','on',...
                'Position',[015 055 100 020].*WS,...
                'ForegroundColor','k');

    % Draw Save, Clear and Exit
    % ---------------------------------------------------------------------
    hClear = uicontrol(Finter,'Style','PushButton','String','clear',...
            'ToolTipString','clears results subpane',...
            'FontSize',FS(9),'ForegroundColor','b',...
            'Callback',['spm_results_ui(''Clear''); ',...
            'spm_input(''!DeleteInputObj''),',...
            'spm_clf(''Satellite'')'],...
            'Interruptible','on','Enable','on',...
            'DeleteFcn','spm_clf(''Graphics'')',...
            'Position',[285 055 035 020].*WS);

    hExit  = uicontrol(Finter,'Style','PushButton','String','exit',...
            'ToolTipString','exit the results section',...
            'FontSize',FS(9),'ForegroundColor','r',...
            'Callback',['spm_clf(''Interactive''),spm_clf(''Graphics''),'...
            'close(spm_figure(''FindWin'',''Satellite'')),'...
            'clear'],...
            'Interruptible','on','Enable','on',...
            'Position',[325 055 035 020].*WS);

    hHelp  = uicontrol(Finter,'Style','PushButton','String','',...
            'ToolTipString','',...
            'FontSize',FS(9),'ForegroundColor','g',...
            'Callback','',...
            'Interruptible','on','Enable','off',...
            'Position',[365 055 020 020].*WS);

    % Change options
    % ---------------------------------------------------------------------
    uicontrol(Finter,'Style','Text',...
            'Position',[125 090 150 085].*WS)
    uicontrol(Finter,'Style','Text','String','options',...
            'Position',[135 168 60 015].*WS,...
            'FontAngle','Italic',...
            'FontSize',FS(10),...
            'HorizontalAlignment','Left',...
            'ForegroundColor','w')
    uicontrol(Finter,'Style','PushButton','String','results',...
            'Position',[130 145 140 020].*WS,...
            'ToolTipString',...
            'BMS Maps (Results)',...
            'Callback','spm_run_bms_vis',...
            'Interruptible','on','Enable','on',...
            'FontSize',FS(10),'ForegroundColor','k')
    uicontrol(Finter,'Style','PushButton','String','change model',...
            'Position',[130 120 140 020].*WS,...
            'ToolTipString',...
            'Change model/data',...
            'Callback','spm_bms_display('''',''change_data'')',...
            'Interruptible','on','Enable','on',...
            'FontSize',FS(10),'ForegroundColor','k')
    uicontrol(Finter,'Style','PushButton','String','threshold',...
            'Position',[130 95 68 020].*WS,...
            'ToolTipString',...
            'Change threshold (same data)',...
            'Callback','spm_bms_display('''',''change_thres'')',...
            'Interruptible','on','Enable','on',...
            'FontSize',FS(8),'ForegroundColor','k')
    uicontrol(Finter,'Style','PushButton','String','scale',...
            'Position',[202 95 68 020].*WS,...
            'ToolTipString',...
            'Change scale (same data)',...
            'Callback','spm_bms_display('''',''change_scale'')',...
            'Interruptible','on','Enable','on',...
            'FontSize',FS(8),'ForegroundColor','k')

    %-p-values
    %------------------------------------------------------------------
    uicontrol(Finter,'Style','Text','String','p-values',...
            'Position',[020 168 050 015].*WS,...
            'FontAngle','Italic',...
            'FontSize',FS(10),...
            'HorizontalAlignment','Left',...
            'ForegroundColor','w')
    uicontrol(Finter,'Style','PushButton','String','whole brain','FontSize',FS(10),...
            'ToolTipString',...
            'tabulate summary of local maxima, p-values & statistics',...
            'Callback','spm_bms_display('''',''list'');',...
            'Interruptible','on','Enable','on',...
            'Position',[015 145 100 020].*WS)
    uicontrol(Finter,'Style','PushButton','String','current cluster','FontSize',FS(10),...
            'ToolTipString',...
            'tabulate p-values & statistics for local maxima of nearest cluster',...
            'Callback','spm_bms_display('''',''listCluster'');',...
            'Interruptible','on','Enable','on',...
            'Position',[015 120 100 020].*WS)
    uicontrol(Finter,'Style','PushButton','String','ROI','FontSize',FS(10),...
            'ToolTipString',...
            'plot probabilities for selected ROI',...
            'Callback','spm_bms_display('''',''plotROI'');',...
            'Position',[015 095 100 020].*WS)

    uicontrol(Finter,'Style','PushButton','String','small volume','FontSize',FS(10),...
            'ToolTipString',['Small Volume Correction - probability values ',...
            'for a small search region'],...
            'Callback','spm_bms_display('''',''listVOI'');',...
            'Interruptible','on','Enable','on',...
            'Position',[015 095 100 020].*WS)

    % Draw Options
    % ---------------------------------------------------------------------
uicontrol(Finter,'Style','Text',...
            'Position',[280 090 110 085].*WS)
    uicontrol(Finter,'Style','Text','String','display',...
            'Position',[290 168 065 015].*WS,...
            'FontAngle','Italic',...
            'FontSize',FS(10),...
            'HorizontalAlignment','Left',...
            'ForegroundColor','w')

    strp  = { 'plot...','current voxel','ROI'};
    tstrp = { 'plot results from comparisons at: ',...
        'current voxel / ','region of interest /'};
    tmpp  = { 'spm_bms_display('''',''do_plot'')',...
            'spm_bms_display('''',''do_ROI'')'};

    uicontrol(Finter,'Style','PopUp','String',strp,'FontSize',FS(10),...
            'ToolTipString',cat(2,tstrp{:}),...
            'UserData',tmpp,...
            'Callback','spm_bms_display('''',''overlays'')',...
            'Interruptible','on','Enable','on',...
            'Position',[285 145 100 020].*WS)
    str  = { 'overlays...','slices','sections','render','previous sections'};
    tstr = { 'overlay results on another image: ',...
        '3 slices / ''ortho sections / ','render /','previous ortho sections'};
    tmp  = { 'spm_transverse(''set'',xSPM,hReg)',...
            'spm_sections(xSPM,hReg);global st;st.vols{1}.blobs{1}.min=xSPM.u;spm_orthviews(''redraw'');',...
            ['spm_render(   struct( ''XYZ'',    xSPM.XYZ,',...
            '''t'',     xSPM.Z'',',...
            '''mat'',   xSPM.M,',...
            '''dim'',   xSPM.DIM))'],...
            ['global prevsect;','spm_sections(xSPM,hReg,prevsect)'],...
            ['global prevrend;','if ~isstruct(prevrend)',...
            'prevrend = struct(''rendfile'','''',''brt'',[],''col'',[]); end;',...
            'spm_render(    struct( ''XYZ'',    xSPM.XYZ,',...
            '''t'',     xSPM.Z'',',...
            '''mat'',   xSPM.M,',...
            '''dim'',   xSPM.DIM),prevrend.brt,prevrend.rendfile)']};
    uicontrol(Finter,'Style','PopUp','String',str,'FontSize',FS(10),...
            'ToolTipString',cat(2,tstr{:}),...
            'Callback','spm_bms_display('''',''overlays'')',...
            'UserData',tmp,...
            'Interruptible','on','Enable','on',...
            'Position',[285 120 100 020].*WS)
    uicontrol(Finter,'Style','PushButton','String','save','FontSize',...
            FS(10),'ToolTipString','save thresholded BMS as image',...
            'Callback','spm_bms_display('''',''save'')',...
            'Interruptible','on','Enable','on',...
            'Position',[285 095 100 020].*WS)
    user_data = get(hFxyz,'UserData');
    set(Finter,'UserData',user_data,...
            'HandleVisibility','callback')

    varargout = { hReg };

    % Do plot - action: 'do_plot'
    % =====================================================================
    case 'do_plot'

        fig       = gcf;
        user_data = get(fig,'UserData');
        iM        = user_data.iM;
        BMS       = user_data.BMS;
        user_data = get(user_data.hMIPax,'UserData');
        user_data = get(user_data.hMIPxyz,'UserData');
        xyz_vx    = iM*[user_data; 1];
        spm_bms_display_vox(BMS,xyz_vx(1:3));

    % Do ROI - action: 'do_ROI'
    % =====================================================================
    case 'do_ROI'

        fig       = gcf;
        user_data = get(fig,'UserData');
        iM        = user_data.iM;
        BMS       = user_data.BMS;
        user_data = get(user_data.hMIPax,'UserData');
        user_data = get(user_data.hMIPxyz,'UserData');
        xyz_vx    = iM*[user_data; 1];
        spm_bms_display_ROI(BMS);

    % Do overlays - action: 'overlays'
    % =====================================================================
    case 'overlays'

        h   = gcbo;
        v   = get(h,'Value');
        if v==1, return, end
        set(h,'Value',1)
        CBs       = get(h,'UserData');
        fig       = gcf;
        user_data = get(fig,'UserData');
        hReg      = user_data.hReg;
        xSPM      = user_data.xSPM;
        eval(CBs{v-1})

    % Save - action: 'save'
    % =====================================================================
    case 'save'

        fig       = gcf;
        user_data = get(fig,'UserData');
        xSPM      = user_data.xSPM;
        spm_write_filtered(xSPM.Z,xSPM.XYZ,xSPM.DIM,xSPM.M,'Results saved');

    % Plot xyz BMS values - action: 'plot_xyz'
    % =====================================================================
    case 'plot_xyz'

        hC    = gcbo;
        d     = find(strcmp(get(hC,'Tag'),{'hX','hY','hZ'}));
        hFxyz = get(hC,'UserData');
        UD    = get(hFxyz,'UserData');
        xyz   = UD.xyz;
        nxyz  = xyz;

        o = evalin('base',['[',get(hC,'String'),']'],'sprintf(''error'')');
        if ischar(o) || length(o)>1
            warning('%s: Error evaluating ordinate:\n\t%s',...
                mfilename,lasterr)
        else
            nxyz(d) = o;
            nxyz = spm_XYZreg('RoundCoords',nxyz,UD.M,UD.DIM);
        end

        if abs(xyz(d)-nxyz(d))>0
            UD.xyz = nxyz; set(hFxyz,'UserData',UD)
            if ~isempty(UD.hReg), spm_XYZreg('SetCoords',nxyz,UD.hReg,hFxyz); end
            set(hC,'String',sprintf('%.3f',nxyz(d)))
            i  = spm_XYZreg('FindXYZ',UD.xyz,UD.XYZ);
            if isempty(i), str = ''; else str = sprintf('%6.2f',UD.Z(i)); end
               set(UD.hSPM,'String',str);
        end

        fig = gcf;
        set(fig,'UserData',UD)

    % Change model
    % =====================================================================
    case 'change_data'

        fig         = gcf;
        user_data   = get(fig,'UserData');
        job.img{1}  = '';
        job.file{1} = user_data.BMS.fname;
        job.thres   = user_data.thres;
        job.scale   = user_data.scale;
        job.k       = user_data.k;
        spm_run_bms_vis(job);

    % Change threshold
    % =====================================================================
    case 'change_thres'

        fig         = gcf;
        user_data   = get(fig,'UserData');
        job.img{1}  = user_data.vols;
        job.thres   = [];
        job.file{1} = user_data.BMS.fname;
        job.scale   = user_data.scale;
        job.k       = user_data.k;
        spm_run_bms_vis(job);

    % Change scale
    % =====================================================================
    case 'change_scale'

        fig         = gcf;
        user_data   = get(fig,'UserData');
        job.img{1}  = user_data.vols;
        job.thres   = user_data.thres;
        job.scale   = [];
        job.k       = user_data.k;
        job.file{1} = user_data.BMS.fname;
        spm_run_bms_vis(job);

    % List p-values
    % =====================================================================
    case 'list'

        fig       = gcf;
        user_data = get(fig,'UserData');
        xSPM      = user_data.xSPM;
        hReg      = user_data.hReg;
        xSPM.STAT = 'P';
        xSPM.Z    = xSPM.z_ps;
        spm_list('List',xSPM,hReg);

    % List cluster
    % =====================================================================
    case 'listCluster'

        fig       = gcf;
        user_data = get(fig,'UserData');
        xSPM      = user_data.xSPM;
        hReg      = user_data.hReg;
        xSPM.STAT = 'P';
        xSPM.Z    = xSPM.z_ps;
        spm_list('ListCluster',xSPM,hReg);

    % Small volume
    % =====================================================================
    case 'listVOI'

        fig       = gcf;
        user_data = get(fig,'UserData');
        xSPM      = user_data.xSPM;
        hReg      = user_data.hReg;
        xSPM.STAT = 'P';
        spm_VOI(xSPM.SPM,xSPM,hReg);

    % Model partitioning
    % =====================================================================
    case 'partition'
        fig       = gcf;
        user_data = get(fig,'UserData');
        BMS       = user_data.BMS;
        spm_bms_partition(BMS);

    % Model partitioning
    % =====================================================================
    case 'groups'

        fig         = gcf;
        user_data   = get(fig,'UserData');
        BMS         = user_data.BMS;

        con_image   = spm_bms_compare_groups;

        job.img{1}  = con_image;
        job.file{1} = user_data.BMS.fname;
        job.thres   = [];
        job.scale   = [];
        job.k       = [];
        spm_run_bms_vis(job);

end  % End switch

end  % End function


function [params] = sv_calcMcNodes_2methods(params,nNodeStart, nNodeEnd)
% function [params] = sv_calcMcNodes_2methods(params,nNodeStart, nNodeEnd);
% ----------------------------------------------------------------
% Calculation of magnitude of completeness specifying the nodes to be calculated for distributing on different CPUs
% Same as sv_calcMcNodes but just using McEMR and MAXC
%
% Input parameters:
%   params.mCatalog           Earthquake catalog
%   params.mPolygon           Polygon (defined by ex_selectgrid)
%   params.vX                 X-vector (defined by ex_selectgrid)
%   params.vY                 Y-vector (defined by ex_selectgrid)
%   params.vUsedNodes         Used nodes vX * vY defining the mPolygon (defined by ex_selectgrid)
%   params.bRandom            Perform random simulation (=1) or real calculation (=0)
%   params.nCalculation       Number of random simulations
%   params.bMap               Calculate a map (=1) or a cross-section (=0)
%   params.bNumber            Use constant number (=1) or constant radius (=0)
%   params.nNumberEvents      Number of earthquakes if bNumber == 1
%   params.fMaxRadius         Maximum Radius using a constant number of events; works only with bNumber == 1
%   params.fRadius            Radius of gridnode if bNumber == 0
%   params.nMinimumNumber     Minimum number of earthquakes per node for determining a b-value
%   params.fMinMag            Lower limit of magnitude range for testing
%   params.fMaxMag            Upper limit of magnitude range for testing
%   params.bTimePeriod        Calculate seismicity difference for 2 periods (0) until start and end of catalog or
%                             a specific time period before and after fSplitTime (1)
%   params.fTimePeriod        Length of time periods
%   params.bTstart            Check for starting time of temporal mapping
%   params.fTstart            Starting time for temporal mapping
%   params.bBstnum            Check for boostrap sampling
%   params.fBstnum            Number of bootstrap samples
%   params.fBinning           Bin size for magnitude binning
%   params.sComment           Comment on calculation

% Output parameters:
%   Same as input parameters including
%   params.mValueGrid         Matrix of calculated values
%   params.vcsGridNames       Names of parameters calculated
%   Check sv_NodeCalcMc.m for a list of variables!!
%
% J. Woessner; j.woessner@sed.ethz.ch
% last update: 27.07.04

global bDebug;
if bDebug
    report_this_filefun(mfilename('fullpath'));
end

% Check calculation for splitting of nodes
if nargin < 2
    nNodeStart = 1;
    nNodeEnd = length(params.mPolygon(:,1));
end

% Initialize
vResults = [];
params.sComment = [];
if isempty(params.fBinning)
    params.fBinning = 0.1;
end

% Determine time period of catalog
params.fTminCat = min(params.mCatalog(:,3));
params.fTmaxCat = max(params.mCatalog(:,3));
% Adjust to decimal years
fTimePeriod =params.fTimePeriod/365;

% Init result matrix
mValueGrid_ = [];

% Temporary saving the original catalog
mCatalog = params.mCatalog;

% Force saving all 500 nodes
fDivide = length(params.mPolygon(:,1))/500
fForceSave = length(params.mPolygon(:,1))/fDivide;
% Check for bootstrapping or not
% ------------------------------
% Case of calculations with bootstrapping
if (params.bBstnum == 1)
    % Loop over time
    fTstart = params.fTstart;
    while fTstart < params.fTmaxCat
        mValueGrid_ = [];
        params.mCatalog = mCatalog;
        % Create Indices to catalog and select quakes in time period
        vSel = (fTstart <= params.mCatalog(:,3) & params.mCatalog(:,3) < fTstart+fTimePeriod);
        params.mCatalog = params.mCatalog(vSel,:);
        [params.caNodeIndices] = ex_CreateIndexCatalog(params.mCatalog, params.mPolygon, params.bMap, params.nGriddingMode, ...
         params.nNumberEvents, params.fRadius, params.fSizeRectHorizontal, params.fSizeRectDepth);
        % Loop over all grid nodes
        hWaitbar1 = waitbar(0,'Calculating nodes...');
        set(hWaitbar1,'Numbertitle','off','Name','Node percentage');
        for nNode_ = nNodeStart:nNodeEnd
            % Create node catalog
            mNodeCatalog_ = params.mCatalog(params.caNodeIndices{nNode_}, :);
            % Check for constant number of events calculations
            if (params.nGriddingMode == 0)
                [mNodeCatalog_] = ex_CheckMaxRadius(mNodeCatalog_, params.mPolygon, nNode_, params.caNodeIndices, params.fMaxRadius, params.nNumberEvents, params.bMap);
            end
            [nX,nY] = size(mNodeCatalog_);
            if (nX < params.nMinimumNumber)
                mValueGrid_= [mValueGrid_; NaN NaN NaN NaN NaN NaN NaN NaN nX NaN NaN NaN NaN];
            else
                [rCalcNodeResult_] = sv_NodeCalcMc_2method(params,mNodeCatalog_);
                % Store the results
                mValueGrid_= [mValueGrid_; rCalcNodeResult_.fMc_max rCalcNodeResult_.fMc_EMR rCalcNodeResult_.fMc_Bst...
                        rCalcNodeResult_.fStd_Mc rCalcNodeResult_.fBvalue_Bst rCalcNodeResult_.fStd_B...
                        rCalcNodeResult_.fAvalue_Bst rCalcNodeResult_.fStd_A nX...
                        rCalcNodeResult_.bH_EMR rCalcNodeResult_.fPval rCalcNodeResult_.bH_Bst rCalcNodeResult_.fPval_Bst];
            end; % End of if on nNode_
            if rem(nNode_,floor(fForceSave)) == 0
                waitbar(nNode_/length(params.mPolygon(:,1)))
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                % Temporary saving
                params.vcsGridNames = cellstr(char('Mc max. curvature', 'Mc EMR-method', 'Mc(Bst-mean)', 'Mc(Bst-2nd-moment)',...
                    'Mc(Bst-b)', 'Mc(b_2nd-moment)','Mc(Bst-a)', 'Mc(a_2nd-moment)','Number of events',...
                    'H(KST)','P(KST)','H(KST_Bst)','P(KST_Bst)'));
                params.mValueGrid = mValueGrid_;
                % Add parameter to params.sComment
                if  params.nGriddingMode == 0;   % Constant number
                    params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                            ' Time period ' num2str(params.fTimePeriod) ' d, Constant number: ' num2str(params.nNumberEvents) ', MaxRadius: '...
                            num2str(params.fMaxRadius) ' km'];
                    vResults = params;
                    save(['tmp_result_Time' num2str(fTstart) '_Constnum_' num2str(params.nNumberEvents) '_MaxRad_' num2str(params.fMaxRadius)...
                            '_Nmin_' num2str(params.nMinimumNumber) '.mat'], 'vResults');
                elseif params.nGriddingMode == 1;   % Constant radius
                    params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                            ' Time period ' num2str(params.fTimePeriod) ' d, Radius: ' num2str(params.fRadius) ' km, Nmin: ' num2str(params.nMinimumNumber)];
                    vResults = params;
                    save(['tmp_result_Time' num2str(fTstart) '_Rad_' num2str(params.fRadius) '_Nmin_' num2str(params.nMinimumNumber) '.mat'], 'vResults');
                else  % Rectangle mode
                    params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                            ' Time period ' num2str(params.fTimePeriod) ' d, Rect. X: ' num2str(params.fSizeRectHorizontal) ' km, Rect. Y: ' num2str(params.fSizeRectDepth)...
                            ' km, Nmin: ' num2str(params.nMinimumNumber)];
                    vResults = params;
                    save(['tmp_result_Time' num2str(fTstart) '_RectX_' num2str(params.fSizeRectHorizontal) '_RectY_' num2str(params.fSizeRectDepth)...
                            '_Nmin_' num2str(params.nMinimumNumber) '.mat'], 'vResults');
                end; % END of params.nGriddingmode
                vResults =[];
            end; % End updating waitbar
        end; % for nNode
        close(hWaitbar1);
        % Parameter description
        params.vcsGridNames = cellstr(char('Mc max. curvature', 'Mc EMR-method', 'Mc(Bst-mean)', 'Mc(Bst-2nd-moment)',...
            'Mc(Bst-b)', 'Mc(b_2nd-moment)','Mc(Bst-a)', 'Mc(a_2nd-moment)','Number of events',...
                    'H(KST)','P(KST)','H(KST_Bst)','P(KST_Bst)'));
        params.mValueGrid = mValueGrid_;
        % Add parameter to params.sComment
        if  params.nGriddingMode == 0;   % Constant number
            params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                    ' Time period ' num2str(params.fTimePeriod) ' d, Constant number: ' num2str(params.nNumberEvents) ', MaxRadius: '...
                    num2str(params.fMaxRadius) ' km'];
            vResults = params;
            save(['result_Time' num2str(fTstart) '_Constnum_' num2str(params.nNumberEvents) '_MaxRad_' num2str(params.fMaxRadius)...
                    '_Nmin_' num2str(params.nMinimumNumber) '_Nodes_' num2str(nNodeStart) '.mat'], 'vResults');
        elseif params.nGriddingMode == 1;   % Constant radius
            params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                    ' Time period ' num2str(params.fTimePeriod) ' d, Radius: ' num2str(params.fRadius) ' km, Nmin: ' num2str(params.nMinimumNumber)];
            vResults = params;
            save(['result_Time' num2str(fTstart) '_Rad_' num2str(params.fRadius) '_Nmin_' num2str(params.nMinimumNumber)...
                '_Nodes_' num2str(nNodeStart) '.mat'], 'vResults');
        else  % Rectangle mode
            params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                    ' Time period ' num2str(params.fTimePeriod) ' d, Rect. X: ' num2str(params.fSizeRectHorizontal) ' km, Rect. Y: ' num2str(params.fSizeRectDepth)...
                    ' km, Nmin: ' num2str(params.nMinimumNumber)];
            vResults = params;
            save(['result_Time' num2str(fTstart) '_RectX_' num2str(params.fSizeRectHorizontal) '_RectY_' num2str(params.fSizeRectDepth)...
                    '_Nmin_' num2str(params.nMinimumNumber) '_Nodes_' num2str(nNodeStart) '.mat'], 'vResults');
        end
        vResults =[];
        fTstart = fTstart+fTimePeriod;
    end; % End of while fTstart

    % Case of no bootstrapping
else
    % Loop over time
    fTstart = params.fTstart;
    while fTstart < params.fTmaxCat
        mValueGrid_ = [];
        params.mCatalog = mCatalog;
        % Create Indices to catalog and select quakes in time period
        vSel = (fTstart <= params.mCatalog(:,3) & params.mCatalog(:,3) < fTstart+fTimePeriod);
        params.mCatalog = params.mCatalog(vSel,:);
        [params.caNodeIndices] = ex_CreateIndexCatalog(params.mCatalog, params.mPolygon, params.bMap, params.nGriddingMode, ...
         params.nNumberEvents, params.fRadius, params.fSizeRectHorizontal, params.fSizeRectDepth);
        % Loop over all grid nodes
        hWaitbar1 = waitbar(0,'Calculating nodes...');
        set(hWaitbar1,'Numbertitle','off','Name','Node percentage');
        for nNode_ = nNodeStart:nNodeEnd
            % Create node catalog
            mNodeCatalog_ = params.mCatalog(params.caNodeIndices{nNode_}, :);
            % Check for constant number of events calculations
            if (params.nGriddingMode == 0)
                [mNodeCatalog_] = ex_CheckMaxRadius(mNodeCatalog_, params.mPolygon, nNode_, params.caNodeIndices, params.fMaxRadius, params.nNumberEvents, params.bMap);
            end
            [nX,nY] = size(mNodeCatalog_);
            if (nX < params.nMinimumNumber)
                mValueGrid_= [mValueGrid_; NaN NaN NaN NaN NaN NaN nX];
            else
                [rCalcNodeResult_] = sv_NodeCalcMc(params,mNodeCatalog_);
                mValueGrid_= [mValueGrid_; rCalcNodeResult_.fMc_max rCalcNodeResult_.fMc_90 rCalcNodeResult_.fMc_95 rCalcNodeResult_.fMc_com...
                        rCalcNodeResult_.fMc_EMR rCalcNodeResult_.fMc_shi nX];
            end; % End of if on nX
            if rem(nNode_,500) == 0
                waitbar(nNode_/length(params.mPolygon(:,1)))
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                % Temporary saving
                params.vcsGridNames = cellstr(char('Mc max. curvature' , 'Mc 90% goodness of fit' , 'Mc 95% goodness of fit',...
                    'Mc best combination', 'Mc EMR-method', 'Mc(Shi-b-uncertainty)','Number of events'));
                params.mValueGrid = mValueGrid_;
                % Add parameter to params.sComment
                if  params.nGriddingMode == 0;   % Constant number
                    params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                            ' Time period ' num2str(params.fTimePeriod) ' d, Constant number: ' num2str(params.nNumberEvents) ', MaxRadius: '...
                            num2str(params.fMaxRadius) ' km'];
                    vResults = params;
                    save(['tmp_result_Time' num2str(fTstart) '_Constnum_' num2str(params.nNumberEvents) '_MaxRad_' num2str(params.fMaxRadius)...
                            '_Nmin_' num2str(params.nMinimumNumber) '_Node' num2str(nNode_) '.mat'], 'vResults');
                elseif params.nGriddingMode == 1;   % Constant radius
                    params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                            ' Time period ' num2str(params.fTimePeriod) ' d, Radius: ' num2str(params.fRadius) ' km, Nmin: ' num2str(params.nMinimumNumber)];
                    vResults = params;
                    save(['tmp_result_Time' num2str(fTstart) '_Rad_' num2str(params.fRadius) '_Nmin_' num2str(params.nMinimumNumber)  '_Node' num2str(nNode_) '.mat'], 'vResults');
                else  % Rectangle mode
                    params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                            ' Time period ' num2str(params.fTimePeriod) ' d, Rect. X: ' num2str(params.fSizeRectHorizontal) ' km, Rect. Y: ' num2str(params.fSizeRectDepth)...
                            ' km, Nmin: ' num2str(params.nMinimumNumber)];
                    vResults = params;
                    save(['tmp_result_Time' num2str(fTstart) '_RectX_' num2str(params.fSizeRectHorizontal) '_RectY_' num2str(params.fSizeRectDepth)...
                            '_Nmin_' num2str(params.nMinimumNumber)  '_Node' num2str(nNode_) '.mat'], 'vResults');
                end; % END of params.nGriddingmode
                vResults =[];
            end; % End updating waitbar
        end; % for nNode
        close(hWaitbar1);
        % Parameter description
        params.vcsGridNames = cellstr(char('Mc max. curvature' , 'Mc 90% goodness of fit' , 'Mc 95% goodness of fit',...
            'Mc best combination', 'Mc EMR-method', 'Mc(Shi-b-uncertainty)'));
        params.mValueGrid = mValueGrid_;
        % Add parameter to params.sComment
        if  params.nGriddingMode == 0;   % Constant number
            params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                    ' Time period ' num2str(params.fTimePeriod) ' d, Constant number: ' num2str(params.nNumberEvents) ', MaxRadius: '...
                    num2str(params.fMaxRadius) ' km'];
            vResults = params;
            save(['result_Time' num2str(fTstart) '_Constnum_' num2str(params.nNumberEvents) '_MaxRad_' num2str(params.fMaxRadius)...
                    '_Nmin_' num2str(params.nMinimumNumber) '_Nodes_' num2str(nNodeStart) '_' num2str(nNodeEnd) '.mat'], 'vResults');
        elseif params.nGriddingMode == 1;   % Constant radius
            params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                    ' Time period ' num2str(params.fTimePeriod) ' d, Radius: ' num2str(params.fRadius) ' km, Nmin: ' num2str(params.nMinimumNumber)];
            vResults = params;
            save(['result_Time' num2str(fTstart) '_Rad_' num2str(params.fRadius) '_Nmin_' num2str(params.nMinimumNumber)...
                '_Nodes_' num2str(nNodeStart) '_' num2str(nNodeEnd) '.mat'], 'vResults');
        else  % Rectangle mode
            params.sComment = ['Starttime ' num2str(fTstart) ' Spacing ' num2str(params.fSpacingHorizontal) ' deg.,'...
                    ' Time period ' num2str(params.fTimePeriod) ' d, Rect. X: ' num2str(params.fSizeRectHorizontal) ' km, Rect. Y: ' num2str(params.fSizeRectDepth)...
                    ' km, Nmin: ' num2str(params.nMinimumNumber)];
            vResults = params;
            save(['result_Time' num2str(fTstart) '_RectX_' num2str(params.fSizeRectHorizontal) '_RectY_' num2str(params.fSizeRectDepth)...
                    '_Nmin_' num2str(params.nMinimumNumber) '_Nodes_' num2str(nNodeStart) '_' num2str(nNodeEnd) '.mat'], 'vResults');
        end
        vResults =[];
        fTstart = fTstart+fTimePeriod;
    end; % End of while fTstart
end; % END of if params.bBst


function script_rfcn_VOC0712_ResNet101_rpn()
% script_rfcn_VOC0712_ResNet101_rpn()
% RFCN training and testing with OHEM using ResNet101 model and RPN
% proposals
% --------------------------------------------------------
% R-FCN implementation
% Modified from MATLAB Faster R-CNN (https://github.com/shaoqingren/faster_rcnn)
% Copyright (c) 2016, Jifeng Dai
% Licensed under The MIT License [see LICENSE for details]
% --------------------------------------------------------

clc;
clear mex;
clear is_valid_handle; % to clear init_key
run(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'startup'));
%% -------------------- CONFIG --------------------
opts.caffe_version          = 'caffe_rfcn';
opts.gpu_id                 = auto_select_gpu;
active_caffe_mex(opts.gpu_id, opts.caffe_version);

% model
model                       = Model.ResNet101_for_RFCN_VOC0712();
% cache name
opts.cache_name             = 'rfcn_VOC0712_ResNet101_rpn_resnet101';
% config
conf                        = rfcn_config_simple('image_means', model.mean_image);
% train/test data
fprintf('Loading dataset...')
dataset                     = [];
dataset                     = Dataset.voc0712_trainval_sp(dataset, 'train', conf.use_flipped, 'resnet101');
dataset                     = Dataset.voc2007_test_sp(dataset, 'test', false, 'resnet101');
fprintf('Done.\n');

% do validation, or not
opts.do_val                 = true;

%% -------------------- TRAINING --------------------

opts.rfcn_model        = rfcn_train(conf, dataset.imdb_train, dataset.roidb_train, ...
                                'do_val',           opts.do_val, ...
                                'imdb_val',         dataset.imdb_test, ...
                                'roidb_val',        dataset.roidb_test, ...
                                'solver_def_file',  model.solver_def_file, ...
                                'net_file',         model.net_file, ...
                                'cache_name',       opts.cache_name, ...
                                'caffe_version',    opts.caffe_version);
assert(exist(opts.rfcn_model, 'file') ~= 0, 'not found trained model');

%% -------------------- TESTING --------------------
                          rfcn_test(conf, dataset.imdb_test, dataset.roidb_test, ...
                                'net_def_file',     model.test_net_def_file, ...
                                'net_file',         opts.rfcn_model, ...
                                'cache_name',       opts.cache_name,...
                                'ignore_cache',     true);

end


%RandomPath Vehicle driver class
%
% Create a "driver" object capable of steering a Vehicle object through random
% waypoints within a rectangular region and at constant speed.
%
% The driver object is connected to a Vehicle object by the latter's
% add_driver() method.  The driver's demand() method is invoked on every
% call to the Vehicle's step() method.
%
% Methods::
%  init       reset the random number generator
%  demand     return speed and steer angle to next waypoint
%  display    display the state and parameters in human readable form
%  char       convert to string
%
% Properties::
%  goal          current goal/waypoint coordinate
%  veh           the Vehicle object being controlled
%  dim           dimensions of the work space (2x1) [m]
%  speed         speed of travel [m/s]
%  closeenough   proximity to waypoint at which next is chosen [m]
%
% Example::
%
%    veh = Vehicle(V);
%    veh.add_driver( RandomPath(20, 2) );
%
% Notes::
% - It is possible in some cases for the vehicle to move outside the desired
%   region, for instance if moving to a waypoint near the edge, the limited
%   turning circle may cause the vehicle to temporarily move outside.
% - The vehicle chooses a new waypoint when it is closer than property
%   closeenough to the current waypoint.
% - Uses its own random number stream so as to not influence the performance
%   of other randomized algorithms such as path planning.
%
% Reference::
%
%   Robotics, Vision & Control, Chap 6,
%   Peter Corke,
%   Springer 2011
%
% See also Vehicle.

% Copyright (C) 1993-2015, by Peter I. Corke
%
% This file is part of The Robotics Toolbox for MATLAB (RTB).
%
% RTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% RTB 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 Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with RTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com

% TODO
%  should be a subclass of VehicleDriver
%  Vehicle should be an abstract superclass

classdef RandomPath < handle
    properties
        goal        % current goal
        h_goal      % graphics handle for goal
        veh         % the vehicle we are driving
        dim
        speed       % speed of travel
        closeenough  % proximity to goal before
        d_prev
        randstream  % random stream just for Sensors
    end

    methods

        function driver = RandomPath(dim, varargin)
        %RandomPath.RandomPath Create a driver object
        %
        % D = RandomPath(DIM, OPTIONS) returns a "driver" object capable of driving
        % a Vehicle object through random waypoints.  The waypoints are positioned
        % inside a rectangular region bounded by +/- DIM in the x- and y-directions.
        %
        % Options::
        % 'speed',S      Speed along path (default 1m/s).
        % 'dthresh',D    Distance from goal at which next goal is chosen.
        %
        % See also Vehicle.

        % TODO options to specify region, maybe accept a Map object?

            driver.dim = dim;

            opt.speed = 1;
            opt.dthresh = 0.05 * dim;
            opt = tb_optparse(opt, varargin);

            driver.speed = opt.speed;
            driver.closeenough = opt.dthresh;
            drive.d_prev = Inf;
            driver.randstream = RandStream.create('mt19937ar');
        end

        function init(driver)
        %RandomPath.init Reset random number generator
        %
        % R.init() resets the random number generator used to create the waypoints.
        % This enables the sequence of random waypoints to be repeated.
        %
        % See also RANDSTREAM.
            driver.goal = [];
            driver.randstream.reset();
        end

        % not used
        function visualize(driver)
            clf
            d = driver.dim;
            axis([-d d -d d]);
            hold on
            xlabel('x');
            ylabel('y');
        end

        % private method, invoked from demand() to compute a new waypoint
        function setgoal(driver)
            r = driver.randstream.rand(2,1);
            driver.goal = 0.8 * driver.dim * (r - 0.5)*2;
            %fprintf('set goal: (%.1f %.1f)\n', driver.goal);
            if isempty(driver.h_goal)
                %driver.h_goal = plot(driver.goal(1), driver.goal(2), '*')
            else
                %set(driver.h_goal, 'Xdata', driver.goal(1), 'Ydata', driver.goal(2))
            end
        end

        function [speed, steer] = demand(driver)
        %RandomPath.demand Compute speed and heading to waypoint
        %
        % [SPEED,STEER] = R.demand() returns the speed and steer angle to
        % drive the vehicle toward the next waypoint.  When the vehicle is
        % within R.closeenough a new waypoint is chosen.
        %
        % See also Vehicle.
            if isempty(driver.goal)
                driver.setgoal()
            end

            speed = driver.speed;

            goal_heading = atan2(driver.goal(2)-driver.veh.x(2), ...
                driver.goal(1)-driver.veh.x(1));
            d_heading = angdiff(goal_heading, driver.veh.x(3));

            steer = d_heading;

            % if nearly at goal point, choose the next one
            d = colnorm(driver.veh.x(1:2) - driver.goal);
            if d < driver.closeenough
                driver.setgoal();
            elseif d > driver.d_prev
                driver.setgoal();
            end
            driver.d_prev = d;
        end

        function display(driver)
        %RandomPath.display Display driver parameters and state
        %
        % R.display() displays driver parameters and state in compact
        % human readable form.
        %
        % See also RandomPath.char.
            loose = strcmp( get(0, 'FormatSpacing'), 'loose');
            if loose
                disp(' ');
            end
            disp([inputname(1), ' = '])
            disp( char(driver) );
        end % display()

        function s = char(driver)
        %RandomPath.char Convert to string
        %
        % s = R.char() is a string showing driver parameters and state in in
        % a compact human readable format.
            s = 'RandomPath driver object';
            s = char(s, sprintf('  current goal=(%g,%g), dimension %.1f', ...
                driver.goal, driver.dim));
        end

    end % methods
end % classdef


function importGen3Model
% Imports Kinova Gen3 Ultra lightweight robot model into a Rigid Body Tree
%
% Copyright 2019 The MathWorks, Inc.

    % Load the URDF file
    %
    % NOTE: This requires you to have the kortex_description folder on
    % your path, which you can download from
    % https://github.com/Kinovarobotics/ros_kortex.git
    %
    % Then, you have to convert the gen3.xacro file to a URDF file using
    % the following commands in a ROS enabled terminal:
    %  $ cd PATH/TO/ros_kortex/kortex_description/robots
    %  $ rosrun xacro xacro --inorder -o gen3.urdf gen3.xacro
    addpath(genpath('kortex_description'))
    gen3 = importrobot('gen3.urdf');

    % Add a "dummy" gripper link
    gripperLength = 0.1; % Gripper length in meters
    gripperBody = rigidBody('Gripper');
    gripperJoint = rigidBodyJoint('GripperLink','fixed');
    T = rotm2tform([0 1 0;0 0 1;1 0 0]) * trvec2tform([gripperLength 0 0]);
    setFixedTransform(gripperJoint,T); % Move and orient the gripper
    gripperBody.Joint = gripperJoint;
    addBody(gen3,gripperBody,'end_effector_link');
    % Add a "dummy" mesh
    addVisual(gen3.Bodies{9},'Mesh','cylinder.stl', ...
              trvec2tform([-0.1 0 0]) * axang2tform([0 1 0 pi/2]));

    % Configure the data format and show the robot in the home position
    gen3.DataFormat = 'row';
    load gen3positions
    show(gen3,jointAnglesHome');

    % Save the Rigid Body Tree to a file
    curDir = pwd;
    saveDir = fileparts(mfilename('fullpath'));
    cd(saveDir)
    save gen3 gen3
    cd(curDir)

end


classdef dme_load < mp.dm_element
%MP.DME_LOAD  MATPOWER data model class for load data

%   MATPOWER
%   Copyright (c) 2020-2022, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

    properties
        bus     %% bus index vector (all loads)
        pd      %% active power demand (p.u.) for constant power loads that are on
        qd      %% reactive power demand (p.u.) for constant power loads that are on
        pd_i    %% active power demand (p.u.) for constant current loads that are on
        qd_i    %% reactive power demand (p.u.) for constant current loads that are on
        pd_z    %% active power demand (p.u.) for constant impedance loads that are on
        qd_z    %% reactive power demand (p.u.) for constant impedance loads that are on
    end     %% properties

    methods
        function name = name(obj)
            name = 'load';
        end

        function label = label(obj)
            label = 'Load';
        end

        function label = labels(obj)
            label = 'Loads';
        end

        function name = cxn_type(obj)
            name = 'bus';
        end

        function name = cxn_idx_prop(obj)
            name = 'bus';
        end

        function names = main_table_var_names(obj)
            names = horzcat( main_table_var_names@mp.dm_element(obj), ...
                {'bus', 'pd', 'qd', 'pd_i', 'qd_i', 'pd_z', 'qd_z', ...
                'p', 'q'});
        end

        function vars = export_vars(obj)
            vars = {};
        end

        function nr = count(obj, dm)
            nr = count@mp.dm_element(obj, dm);
            if nr
                obj.bus = obj.tab.source_uid;
            end
        end

        function obj = update_status(obj, dm)
            %% get bus status info
            bs = dm.elements.bus.tab.status;    %% bus status

            %% update status of loads at isolated/offline buses
            obj.tab.status = obj.tab.status & bs(obj.bus);

            %% call parent to fill in on/off
            update_status@mp.dm_element(obj, dm);
        end

        function obj = build_params(obj, dm)
            obj.pd   = obj.tab.pd(obj.on) / dm.base_mva;
            obj.qd   = obj.tab.qd(obj.on) / dm.base_mva;
            obj.pd_i = obj.tab.pd_i(obj.on) / dm.base_mva;
            obj.qd_i = obj.tab.qd_i(obj.on) / dm.base_mva;
            obj.pd_z = obj.tab.pd_z(obj.on) / dm.base_mva;
            obj.qd_z = obj.tab.qd_z(obj.on) / dm.base_mva;
        end

        function TorF = pp_have_section_sum(obj, mpopt, pp_args)
            TorF = true;
        end

        function obj = pp_data_sum(obj, dm, rows, out_e, mpopt, fd, pp_args)
            %% call parent
            pp_data_sum@mp.dm_element(obj, dm, rows, out_e, mpopt, fd, pp_args);

            %% print load summary
            fprintf(fd, '  %-29s %12.1f MW', 'Total load', ...
                                            sum(obj.tab.p));
            if mpopt.model(1) ~= 'D'    %% AC model
                fprintf(fd, ' %12.1f MVAr', sum(obj.tab.q));
            end
            fprintf(fd, '\n');
            if obj.n ~= obj.nr
                fprintf(fd, '  %-29s %12.1f MW', '  online', ...
                                                sum(obj.tab.p(obj.on)));
                if mpopt.model(1) ~= 'D'    %% AC model
                    fprintf(fd, ' %12.1f MVAr', sum(obj.tab.q(obj.on)));
                end
                fprintf(fd, '\n');
            end
        end

        function TorF = pp_have_section_det(obj, mpopt, pp_args)
            TorF = true;
        end

        function h = pp_get_headers_det(obj, dm, out_e, mpopt, pp_args)
            h = [ pp_get_headers_det@mp.dm_element(obj, dm, out_e, mpopt, pp_args) ...
                {   '                             Power Consumption', ...
                    'Load ID    Bus ID   Status   P (MW)   Q (MVAr)', ...
                    '--------  --------  ------  --------  --------' } ];
            %%       1234567 123456789 -----1 12345678.0 1234567.9
        end

        function f = pp_get_footers_det(obj, dm, out_e, mpopt, pp_args)
            f = {'                            --------  --------',
                sprintf('%18s Total:%10.1f %9.1f', ...
                    '', sum(obj.tab.p(obj.on)), sum(obj.tab.q(obj.on)))};
        end

        function str = pp_data_row_det(obj, dm, k, out_e, mpopt, fd, pp_args)
            if obj.tab.status(k) && abs(obj.tab.p(k)) > 1e-5
                p = sprintf('%10.1f', obj.tab.p(k));
            else
                p = '       -  ';
            end
            if obj.tab.status(k) && abs(obj.tab.q(k)) > 1e-5
                q = sprintf('%9.1f', obj.tab.q(k));
            else
                q = '      -  ';
            end
            str = sprintf('%7d %9d %6d %10s %9s', ...
                obj.tab.uid(k), obj.tab.bus(k), obj.tab.status(k), p, q);
        end
    end     %% methods
end         %% classdef


% Altitute chart containing relative temperature, pressure and density.
h=(0:100:80000)';
chart=h;
for i=2:4,
chart(:,i)=0;
end