function [mpc, warns] = psse_convert(warns, data, verbose)
%PSSE_CONVERT  Converts data read from PSS/E RAW file to MATPOWER case.
%   [MPC, WARNINGS] = PSSE_CONVERT(WARNINGS, DATA)
%   [MPC, WARNINGS] = PSSE_CONVERT(WARNINGS, DATA, VERBOSE)
%
%   Converts data read from a version RAW data file into a
%   MATPOWER case struct.
%
%   Input:
%       WARNINGS :  cell array of strings containing accumulated
%                   warning messages
%       DATA : struct read by PSSE_READ (see PSSE_READ for details).
%       VERBOSE :   1 to display progress info, 0 (default) otherwise
%
%   Output:
%       MPC : a MATPOWER case struct created from the PSS/E data
%       WARNINGS :  cell array of strings containing updated accumulated
%                   warning messages
%
%   See also PSSE_READ.

%   MATPOWER
%   Copyright (c) 2014-2016, Power Systems Engineering Research Center (PSERC)
%   by Yujia Zhu, PSERC ASU
%   and Ray Zimmerman, PSERC Cornell
%   Based on mpraw2mp.m, written by: Yujia Zhu, Jan 2014, yzhu54@asu.edu.
%
%   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.

%% define named indices into bus, gen, branch matrices
[PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
    VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
[F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
    TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
    ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;

%% options
sort_buses = 1;

%% defaults
if nargin < 3
    verbose = 0;
end
haveVlims = 0;
Vmin = 0.9;
Vmax = 1.1;

%%-----  case identification data  -----
baseMVA = data.id.SBASE;
rev = data.id.REV;

%%-----  bus data  -----
numbus = data.bus.num;
[nb, ncols] = size(numbus); %% number of buses, number of cols
bus = zeros(nb, VMIN);      %% initialize bus matrix
if rev < 24
    bus_name_col = 10;
else
    bus_name_col = 2;
end
if sort_buses
    [numbus, i] = sortrows(numbus, 1);
    bus_name = data.bus.txt(i, bus_name_col);
else
    bus_name = data.bus.txt(:, bus_name_col);
end
if rev < 24     %% includes loads
    bus(:, [BUS_I BUS_TYPE PD QD GS BS BUS_AREA VM VA BASE_KV ZONE]) = ...
        numbus(:, [1:9 11:12]);
elseif rev < 31     %% includes GL, BL
    bus(:, [BUS_I BASE_KV BUS_TYPE GS BS BUS_AREA ZONE VM VA]) = ...
        numbus(:, [1 3 4 5 6 7 8 9 10]);
else                %% fixed shunts and loads are in their own tables
    bus(:, [BUS_I BASE_KV BUS_TYPE BUS_AREA ZONE VM VA]) = ...
        numbus(:, [1 3 4 5 6 8 9]);
    if ncols >= 11 && all(all(~isnan(numbus(:, [10 11]))))
        haveVlims = 1;
        bus(:, [VMAX VMIN]) = numbus(:, [10 11]);
    end
end
if ~haveVlims  %% add default voltage magnitude limits if not provided
    warns{end+1} = sprintf('Using default voltage magnitude limits: VMIN = %g p.u., VMAX = %g p.u.', Vmin, Vmax);
    if verbose
        fprintf('WARNING: No bus voltage magnitude limits provided.\n         Using defaults: VMIN = %g p.u., VMAX = %g p.u.\n', Vmin, Vmax);
    end
    bus(:, VMIN) = Vmin;
    bus(:, VMAX) = Vmax;
end

%% create map of external bus numbers to bus indices
i2e = bus(:, BUS_I);
e2i = sparse(i2e, ones(nb, 1), 1:nb, max(i2e), 1);

%%-----  load data  -----
if rev >= 24
    nld = size(data.load.num, 1);
    loadbus = e2i(data.load.num(:,1));
    %% PSS/E loads are divided into:
    %%  1. constant MVA, (I=1)
    %%  2. constant current (I=I)
    %%  3. constant reactance/resistance (I = I^2)
    %% NOTE: reactive power component of constant admittance load is negative
    %%       quantity for inductive load and positive for capacitive load
    Pd = data.load.num(:,6) + data.load.num(:,8) .* bus(loadbus, VM) ...
            + data.load.num(:,10) .* bus(loadbus, VM).^2;
    Qd = data.load.num(:,7) + data.load.num(:,9) .* bus(loadbus, VM) ...
            - data.load.num(:,11) .* bus(loadbus, VM).^2;
    Cld = sparse(1:nld, loadbus, data.load.num(:,3), nld, nb);    %% only in-service-loads
    bus(:, [PD QD]) = Cld' * [Pd Qd];
end

%%-----  fixed shunt data  -----
if isfield(data, 'shunt')   %% rev > 30
    nsh = size(data.shunt.num, 1);
    shuntbus = e2i(data.shunt.num(:,1));
    Csh = sparse(1:nsh, shuntbus, data.shunt.num(:,3), nsh, nb);  %% only in-service shunts
    bus(:, [GS BS]) = Csh' * data.shunt.num(:, 4:5);
end

%%-----  switched shunt data  -----
nswsh = size(data.swshunt.num, 1);
swshuntbus = e2i(data.swshunt.num(:,1));
Cswsh = sparse(1:nswsh, swshuntbus, 1, nswsh, nb);
if rev <= 27
    bus(:, BS) = bus(:, BS) + Cswsh' * data.swshunt.num(:, 6);
elseif rev <= 29
    bus(:, BS) = bus(:, BS) + Cswsh' * data.swshunt.num(:, 7);
elseif rev < 32
    bus(:, BS) = bus(:, BS) + Cswsh' * data.swshunt.num(:, 8);
else
    bus(:, BS) = bus(:, BS) + Cswsh' * data.swshunt.num(:, 10);
end

%%-----  branch data  -----
nbr = size(data.branch.num, 1);
branch = zeros(nbr, ANGMAX);
branch(:, ANGMIN) = -360;
branch(:, ANGMAX) = 360;
branch(:, [F_BUS BR_R BR_X BR_B RATE_A RATE_B RATE_C]) = ...
    data.branch.num(:, [1 4 5 6 7 8 9]);
branch(:, T_BUS) = abs(data.branch.num(:, 2));  %% can be negative to indicate metered end
if rev <= 27        %% includes transformer ratio, angle
    branch(:, BR_STATUS) = data.branch.num(:, 16);
    branch(~isnan(data.branch.num(:, 10)), TAP) = ...
        data.branch.num(~isnan(data.branch.num(:, 10)), 10);
    branch(~isnan(data.branch.num(:, 11)), SHIFT) = ...
        data.branch.num(~isnan(data.branch.num(:, 11)), 11);
else
    branch(:, BR_STATUS)    = data.branch.num(:, 14);
end
%% integrate branch shunts (explicit shunts, not line-charging)
ibr = (1:nbr)';
fbus = e2i(branch(:, F_BUS));
tbus = e2i(branch(:, T_BUS));
nzf = find(fbus);               %% ignore branches with bad bus numbers
nzt = find(tbus);
if length(nzf) < nbr
    warns{end+1} = sprintf('%d branches have bad ''from'' bus numbers', nbr-length(nzf));
    if verbose
        fprintf('WARNING: %d branches have bad ''from'' bus numbers\n', nbr-length(nzf));
    end
end
if length(nzt) < nbr
    warns{end+1} = sprintf('%d branches have bad ''to'' bus numbers', nbr-length(nzt));
    if verbose
        fprintf('WARNING: %d branches have bad ''to'' bus numbers\n', nbr-length(nzt));
    end
end
Cf = sparse(ibr(nzf), fbus(nzf), branch(nzf, BR_STATUS), nbr, nb);  %% only in-service branches
Ct = sparse(ibr(nzt), tbus(nzt), branch(nzt, BR_STATUS), nbr, nb);  %% only in-service branches
if rev <= 27
    bus(:, [GS BS]) = bus(:, [GS BS]) + ...
        Cf' * data.branch.num(:, 12:13)*baseMVA + ...
        Ct' * data.branch.num(:, 14:15)*baseMVA;
else
    bus(:, [GS BS]) = bus(:, [GS BS]) + ...
        Cf' * data.branch.num(:, 10:11)*baseMVA + ...
        Ct' * data.branch.num(:, 12:13)*baseMVA;
end

%%-----  generator data  -----
ng = size(data.gen.num, 1);
genbus = e2i(data.gen.num(:,1));
gen = zeros(ng, APF);
gen(:, [GEN_BUS PG QG QMAX QMIN VG MBASE GEN_STATUS PMAX PMIN]) = ...
    data.gen.num(:, [1 3 4 5 6 7 9 15 17 18]);

%%-----  transformer data  -----
if rev > 27
    [transformer, bus, warns, bus_name] = psse_convert_xfmr(warns, data.trans2.num, data.trans3.num, verbose, baseMVA, bus, bus_name);
    branch = [branch; transformer];
end

%%-----  two-terminal DC transmission line data  -----
dcline = psse_convert_hvdc(data.twodc.num, bus);

%% assemble MPC
mpc = struct( ...
    'baseMVA',  baseMVA, ...
    'bus', bus, ...
    'bus_name', {bus_name}, ...
    'branch', branch, ...
    'gen', gen ...
);
if ~isempty(dcline)
    mpc.dcline = dcline;
    mpc = toggle_dcline(mpc, 'on');
end


%% initializeWecSim
% Initialize WEC-Sim run, called from wecSim.m
%
%% Following Classes are required to be defined in the WEC-Sim input file:
%%
%% simu = simulationClass();                                               - To Create the Simulation Variable
%%
%% waves = waveClass('<wave type');                                       - To create the Wave Variable and Specify Type
%%
%% body(<body number>) = bodyClass('<hydrodynamics data file name>.h5');   - To initialize bodyClass:
%%
%% constraint(<constraint number>) = constraintClass('<Constraint name>'); - To initialize constraintClass
%%
%% pto(<PTO number>) = ptoClass('<PTO name>');                             - To initialize ptoClass
%%
%% mooring(<mooring number>) = mooringClass('<Mooring name>');             - To initialize mooringClass (only needed when mooring blocks are used)
%%
%%

%% Start WEC-Sim log
% Clear old input, plots, log file and start new log file.
diary off
clear body waves simu output pto constraint ptoSim mooring values names InParam
try delete('*.log'); end
diary('simulation.log')

%% Add 'temp' directory
% Store root directory of this *.m file
projectRootDir = pwd;
if exist('pctDir')
    projectRootDir = [projectRootDir filesep pctDir];
end

% Create 'temp' directory if it doesn't exist and add to 'temp' path
warning('off','MATLAB:MKDIR:DirectoryExists')
if exist('pctDir')
    mkdir ([pctDir filesep 'temp'])
else
    if mkdir('temp') == 0
        mkdir 'temp'
    end
end
addpath(fullfile(projectRootDir,'temp'),'-end');

% Save Simulink-generated helper files to 'temp' directory
Simulink.fileGenControl('set',...
    'CacheFolder',fullfile(projectRootDir,'temp'))

%% Read input file
tic

% Set input parameters based on how the simulation is called
if exist('runWecSimCML','var') && runWecSimCML==1
    % wecSim input from wecSimInputFile.m of case directory in the standard manner
    fprintf('\nWEC-Sim Input From Standard wecSimInputFile.m Of Case Directory... \n');
    bdclose('all');
    run('wecSimInputFile');
else
    % Get global reference frame parameters
    values = get_param([bdroot,'/Global Reference Frame'],'MaskValues');    % Cell array containing all Masked Parameter values
    names = get_param([bdroot,'/Global Reference Frame'],'MaskNames');      % Cell array containing all Masked Parameter names
    j = find(strcmp(names,'InputMethod'));
    if strcmp(values{j},'Input File')
        % wecSim input from input file selected in Simulink block
        fprintf('\nWEC-Sim Input From File Selected In Simulink... \n');
        i = find(strcmp(names,'InputFile'));
        run(values{i});
    elseif strcmp(values{j},'Custom Parameters')
        % wecSim input from custom parameters in Simulink block
        fprintf('\nWEC-Sim Input From Custom Parameters In Simulink... \n');
        inputFile = 'wecSimInputFile_simulinkCustomParameters';
        writeInputFromBlocks(inputFile);
        run(inputFile);
    end
end
clear values names i j;

% Read Inputs for Multiple Conditions Run
try fprintf('wecSimMCR Case %g\n',imcr); end

if exist('mcr','var') == 1
    for n=1:length(mcr.cases(1,:))
        if strcmp(mcr.header{n},'LoadFile')                                 % added this flag for system ID efforts!
            load(mcr.cases{imcr,n});
        elseif iscell(mcr.cases)
            eval([mcr.header{n} '= mcr.cases{imcr,n};']);
        else
            eval([mcr.header{n} '= mcr.cases(imcr,n);']);
        end
    end; clear n combine;
    try
        waves.spectrumFile = ['..' filesep pctDir filesep '..' filesep waves.spectrumFile];
        waves.elevationFile = ['..' filesep pctDir filesep '..' filesep waves.elevationFile];
    end
end

% Waves and Simu: check inputs
waves.checkInputs();
simu.checkInputs();

% Constraints: count & set orientation
if exist('constraint','var') == 1
    simu.numConstraints = length(constraint(1,:));
    for ii = 1:simu.numConstraints
        constraint(ii).setNumber(ii);
        constraint(ii).setOrientation();
    end; clear ii
end

% PTOs: count & set orientation & set pretension
if exist('pto','var') == 1
    simu.numPtos = length(pto(1,:));
    for ii = 1:simu.numPtos
        pto(ii).setNumber(ii);
        pto(ii).setOrientation();
        pto(ii).setPretension();
    end; clear ii
end

% Mooring Configuration: count
if exist('mooring','var') == 1
    simu.numMoorings = length(mooring(1,:));
    for ii = 1:simu.numMoorings
        mooring(ii).setLoc();
        mooring(ii).setNumber(ii);
    end; clear ii
end

% Bodies: count, check inputs, read hdf5 file, and check inputs
numHydroBodies = 0;
numNonHydroBodies = 0;
numDragBodies = 0;
hydroBodLogic = zeros(length(body(1,:)),1);
nonHydroBodLogic = zeros(length(body(1,:)),1);
dragBodLogic = zeros(length(body(1,:)),1);
for ii = 1:length(body(1,:))
    body(ii).setNumber(ii);
    body(ii).checkInputs(simu.explorer);
    if body(ii).nonHydro==0
        if numNonHydroBodies > 0 || numDragBodies > 0
            error('All hydro bodies must be specified before any drag or non-hydro bodies.')
        end
        numHydroBodies = numHydroBodies + 1;
        hydroBodLogic(ii) = 1;
    elseif body(ii).nonHydro==1
        numNonHydroBodies = numNonHydroBodies + 1;
        nonHydroBodLogic(ii) = 1;
    elseif body(ii).nonHydro==2
        numDragBodies = numDragBodies + 1;
        dragBodLogic(ii) = 1;
    end
end
simu.numHydroBodies = numHydroBodies; clear numHydroBodies
simu.numDragBodies = numDragBodies; clear numDragBodies
for ii = 1:simu.numHydroBodies
    body(ii).setDOF(simu.numHydroBodies,simu.b2b);

    % Determine if hydro data needs to be reloaded from h5 file, or if hydroData
    % was stored in memory from a previous run.
    if exist('totalNumOfWorkers','var') ==0 && exist('mcr','var') == 1 && simu.reloadH5Data == 0 && imcr > 1
        body(ii).loadHydroData(hydroData(ii));
    else
        % check for correct h5 file
        h5Info = dir(body(ii).h5File);
        h5Info.bytes;
        if h5Info.bytes < 1000
            error(['This is not the correct *.h5 file. Please install git-lfs to access the correct *.h5 file, or run \hydroData\bemio.m to generate a new *.h5 file'])
        end
        clearvars h5Info
        % Read hydro data from BEMIO and load into the bodyClass
        tmp_hydroData = readBEMIOH5(body(ii).h5File, body(ii).number, body(ii).meanDrift);
        body(ii).loadHydroData(tmp_hydroData);
        clear tmp_hydroData
    end
end; clear ii

% Cable Configuration: count, set Cg/Cb, PTO loc, L0 and initialize bodies
if exist('cable','var')==1
    simu.numCables = length(cable(1,:));
    for ii = 1:simu.numCables
        cable(ii).setNumber(ii);
        cable(ii).setCg();
        cable(ii).setCb();
        cable(ii).setTransPTOLoc();
        cable(ii).setLength();
        cable(ii).dragForcePre(simu.rho);
        cable(ii).setVolume(simu.rho);
        cable(ii).setOrientation();
        cable(ii).linearDampingMatrix();
    end
end

% PTO-Sim: read input, count
if exist('ptoSim','var') == 1
    simu.numPtoSim = length(ptoSim(1,:));
    for ii = 1:simu.numPtoSim
        ptoSim(ii).number = ii;
    end; clear ii
end

toc

%% Pre-processing start
tic
fprintf('\nWEC-Sim Pre-processing ...   \n');
if exist('pctDir')
    cd(pctDir);
end

%% HydroForce Pre-Processing: Wave Setup & HydroForcePre.
% simulation setup
simu.setup();

% wave setup
if any(hydroBodLogic == 1)
    % When hydro bodies (and an .h5) are present, define the wave using those
    % parameters.
    waves.setup(body(1).hydroData.simulation_parameters.w, body(1).hydroData.simulation_parameters.waterDepth, simu.rampTime, simu.dt, simu.maxIt, simu.time, simu.gravity, simu.rho);
    % Check that direction and freq are within range of hydro data
    if  min(waves.direction) <  min(body(1).hydroData.simulation_parameters.direction) || max(waves.direction) >  max(body(1).hydroData.simulation_parameters.direction)
        error('waves.direction outside of range of available hydro data')
    end
    if strcmp(waves.type,'elevationImport')~=1 && strcmp(waves.type,'noWave')~=1 && strcmp(waves.type,'noWaveCIC')~=1
        if  min(waves.omega) <  min(body(1).hydroData.simulation_parameters.w) || max(waves.omega) >  max(body(1).hydroData.simulation_parameters.w)
            error('waves.omega outside of range of available hydro data')
        end
    end
else
    % When no hydro bodies (and no .h5) are present, define the wave using
    % input file parameters
    waves.setup([], [], simu.rampTime, simu.dt, simu.maxIt, simu.time, simu.gravity, simu.rho);
end

% Nonlinear hydro
for kk = 1:length(body(1,:))
    if (body(kk).nonlinearHydro > 0) || (simu.paraview.option == 1)
        body(kk).importBodyGeometry(simu.domainSize)
    end
end; clear kk

% hydroForcePre
idx = find(hydroBodLogic==1);
if ~isempty(idx)
    for kk = 1:length(idx)
        it = idx(kk);
        body(it).hydroForcePre(waves.omega,waves.direction,simu.cicTime,waves.bem.count,simu.dt,...
            simu.rho,simu.gravity,waves.type,waves.waveAmpTime,simu.stateSpace,simu.b2b);
    end; clear kk idx
end

% nonHydroPre
idx = find(nonHydroBodLogic==1);
if ~isempty(idx)
    for kk = 1:length(idx)
        it = idx(kk);
        body(it).nonHydroForcePre(simu.rho);
    end; clear kk idx
end

% dragBodyPre
idx = find(dragBodLogic == 1);
if ~isempty(idx)
    for kk = 1:length(idx)
        it = idx(kk);
        body(it).dragForcePre(simu.rho);
    end; clear kk idx
end

% Check cicEndTime
if waves.typeNum~=0 && waves.typeNum~=10
    for iBod = 1:simu.numHydroBodies
        if simu.cicEndTime > max(body(iBod).hydroData.hydro_coeffs.radiation_damping.impulse_response_fun.t)
            error('simu.cicEndTime is larger than the length of the IRF');
        end
    end
end

% Check that the hydro data for each body is given for the same frequencies
for ii = 1:simu.numHydroBodies
    if length(body(1).hydroData.simulation_parameters.w) ~= length(body(ii).hydroData.simulation_parameters.w)
        error('BEM simulations for each body must have the same number of frequencies')
    else
        for jj = 1:length(body(1).hydroData.simulation_parameters.w)
            if body(1).hydroData.simulation_parameters.w(jj) ~= body(ii).hydroData.simulation_parameters.w(jj)
                error('BEM simulations must be run with the same frequencies.')
            end; clear jj;
        end
    end
end; clear ii;

% Check for elevationImport with nonlinearHydro
for ii = 1:simu.numHydroBodies
    if strcmp(waves.type,'elevationImport') && body(ii).nonlinearHydro == 1
        error(['Cannot run WEC-Sim with nonlinear hydro (body(ii).nonlinearHydro) and "elevationImport" wave type'])
    end
end

% Check for elevationImport with morisonElement
for ii = 1:simu.numHydroBodies
    if strcmp(waves.type,'elevationImport') && body(ii).morisonElement.option ~= 0
        error(['Cannot run WEC-Sim with Morison Element (body(ii).morisonElement.option>0) and "elevationImport" wave type'])
    end
end

% Check for morisonElement inputs for body(ii).morisonElement.option == 1 || body(ii).morisonElement.option == 2
for ii = 1:length(body(1,:))
    if body(ii).morisonElement.option == 1
        if body(ii).nonHydro ~=1
            [rgME,~] = size(body(ii).morisonElement.rgME);
            for jj = 1:rgME
                if true(isfinite(body(ii).morisonElement.z(jj,:))) == true
                    warning(['"body.morisonElement.z" is not used for "body.morisonElement.option = 1". Check body ',num2str(ii),' element ',num2str(jj)])
                end
                if length(body(ii).morisonElement.cd(jj,:)) ~= 3 || length(body(ii).morisonElement.ca(jj,:)) ~= 3 || length(body(ii).morisonElement.area(jj,:)) ~= 3
                    error(['cd, ca, and area coefficients for each elelement for "body.morisonElement.option = 1" must be of size [1x3] and all columns of data must be real and finite. Check body ',num2str(ii),' element ',num2str(jj),' coefficients'])
                end
            end; clear jj
        else
            if body(ii).morisonElement.option == 1 || body(ii).morisonElement.option == 2
                warning(['Morison elements are not available for non-hydro bodies. Please check body ',num2str(ii),' inputs.'])
            end
        end
    elseif body(ii).morisonElement.option == 2
        if body(ii).nonHydro ~=1
            [rgME,~] = size(body(ii).morisonElement.rgME);
            for jj = 1:rgME
                if body(ii).morisonElement.cd(jj,3) ~= 0 || body(ii).morisonElement.ca(jj,3) ~= 0 || body(ii).morisonElement.area(jj,3) ~= 0
                    warning(['cd, ca, and area coefficients for "body.morisonElement.option == 2" must be of size [1x2], third column of data is not used. Check body ',num2str(ii),' element ',num2str(jj),' coefficients'])
                end
            end; clear jj
        else
            if body(ii).morisonElement.option ==1 || body(ii).morisonElement.option ==2
                warning(['Morison elements are not available for non-hydro bodies. Please check body ',num2str(ii),' inputs.'])
            end
        end
    end; clear ii
end

%% Set variant subsystems options
for ii=1:length(body(1,:))
    if body(ii).nonHydro==0
        % Nonlinear FK Force Variant Subsystem
        eval(['nonLinearHydro_' num2str(ii) ' = body(ii).nonlinearHydro;']);
        eval(['sv_b' num2str(ii) '_linearHydro = Simulink.Variant(''nonLinearHydro_', num2str(ii), '==0'');']);
        eval(['sv_b' num2str(ii) '_nonlinearHydro=Simulink.Variant(''nonLinearHydro_', num2str(ii), '>0'');']);
        % Nonlinear Wave Elevation Variant Subsystem
        eval(['sv_b' num2str(ii) '_meanFS=Simulink.Variant(''nonLinearHydro_', num2str(ii), '<2'');']);
        eval(['sv_b' num2str(ii) '_instFS=Simulink.Variant(''nonLinearHydro_', num2str(ii), '==2'');']);
    end
end; clear ii;
% Morison Element
for ii=1:length(body(1,:))
    if body(ii).nonHydro ~=1
        eval(['morisonElement_' num2str(ii) ' = body(ii).morisonElement.option;'])
        eval(['sv_b' num2str(ii) '_MEOff = Simulink.Variant(''morisonElement_' num2str(ii) '==0'');'])
        eval(['sv_b' num2str(ii) '_MEOn = Simulink.Variant(''morisonElement_' num2str(ii) '==1 || morisonElement_' num2str(ii) '==2'');'])
    end
end; clear ii;
% Radiation Damping
if waves.typeNum==0 || waves.typeNum==10 %'noWave' & 'regular'
    radiation_option = 1;
elseif simu.stateSpace == 1
    radiation_option = 3;
else
    radiation_option = 2;
end
sv_constantCoeff=Simulink.Variant('radiation_option==1');
sv_convolution=Simulink.Variant('radiation_option==2');
sv_stateSpace=Simulink.Variant('radiation_option==3');
% Wave type
typeNum = waves.typeNum;
sv_noWave=Simulink.Variant('typeNum<10');

% Passive Yaw
for ii=1:length(body(1,:))
    eval(['yaw_' num2str(ii) ' = body(ii).yaw.option;'])
    eval(['sv_regularWaves_b' num2str(ii) '= Simulink.Variant(''typeNum>=10 && typeNum<20 && yaw_', num2str(ii), '==0'');'])
    eval(['sv_regularWavesYaw_b' num2str(ii) '= Simulink.Variant(''typeNum>=10 && typeNum<20 && yaw_' num2str(ii) '==1'');'])
    eval(['sv_irregularWaves_b' num2str(ii) '= Simulink.Variant(''typeNum>=20 && typeNum<30 && yaw_' num2str(ii) '==0'');'])
    eval(['sv_irregularWavesYaw_b' num2str(ii) '= Simulink.Variant(''typeNum>=20 && typeNum<30 && yaw_' num2str(ii) '==1'');'])
end; clear ii

sv_udfWaves=Simulink.Variant('typeNum>=30');
% Body2Body
B2B = simu.b2b;
sv_noB2B=Simulink.Variant('B2B==0');
sv_B2B=Simulink.Variant('B2B==1');
numBody=simu.numHydroBodies;
% nonHydro
for ii=1:length(body(1,:))
    eval(['nhbody_' num2str(ii) ' = body(ii).nonHydro;'])
    eval(['sv_b' num2str(ii) '_hydroBody = Simulink.Variant(''nhbody_' num2str(ii) '==0'');'])
    eval(['sv_b' num2str(ii) '_nonHydroBody = Simulink.Variant(''nhbody_' num2str(ii) '==1'');'])
    eval(['sv_b' num2str(ii) '_dragBody = Simulink.Variant(''nhbody_' num2str(ii) '==2'');'])
end; clear ii

%Efficiency model for hydraulic motor PTO-Sim block
try
    for ii=1:length(ptoSim(1,:))
        eval(['EffModel_' num2str(ii) ' = ptoSim(ii).hydraulicMotor.effModel;']);
        eval(['sv_b' num2str(ii) '_AnalyticalEfficiency = Simulink.Variant(''EffModel_', num2str(ii), '==1'');']);
        eval(['sv_b' num2str(ii) '_TabulatedEfficiency = Simulink.Variant(''EffModel_', num2str(ii), '==2'');']);
    end; clear ii;
end

% Visualization Blocks
if ~isempty(waves.marker.location) && typeNum < 30
    visON = 1;
else
    visON = 0;
end
sv_visualizationON  = Simulink.Variant('visON==1');
sv_visualizationOFF = Simulink.Variant('visON==0');

%% End Pre-Processing and Output All the Simulation and Model Setting
toc
simu.listInfo(waves.typeNum);
waves.listInfo
fprintf('\nList of Body: ');
fprintf('Number of Bodies = %u \n',simu.numHydroBodies)
for i = 1:simu.numHydroBodies
    body(i).listInfo
end;  clear i
fprintf('\nList of PTO(s): ');
if (exist('pto','var') == 0)
    fprintf('No PTO in the system\n')
else
    fprintf('Number of PTOs = %G \n',length(pto(1,:)))
    for i=1:simu.numPtos
        pto(i).listInfo
    end; clear i
end
fprintf('\nList of Constraint(s): ');
if (exist('constraint','var') == 0)
    fprintf('No Constraint in the system\n')
else
    fprintf('Number of Constraints = %G \n',length(constraint(1,:)))
    for i=1:simu.numConstraints
        constraint(i).listInfo
    end; clear i
end
fprintf('\n')

%% Load simMechanics file & Run Simulation
tic
fprintf('\nSimulating the WEC device defined in the SimMechanics model %s...   \n',simu.simMechanicsFile)
% Modify some stuff for simulation
for iBod = 1:simu.numHydroBodies
    body(iBod).adjustMassMatrix(simu.adjMassFactor,simu.b2b);
end; clear iBod
warning('off','Simulink:blocks:TDelayTimeTooSmall');
warning('off','Simulink:blocks:BusSelDupBusCreatorSigNames');
warning('off','MATLAB:loadlibrary:FunctionNotFound');
warning('off','MATLAB:loadlibrary:parsewarnings');
warning('off','MATLAB:printf:BadEscapeSequenceInFormat');
warning('off','Simulink:blocks:DivideByZero');
warning('off','sm:sli:setup:compile:SteadyStateStartNotSupported')
set_param(0, 'ErrorIfLoadNewModel', 'off')
% Load parameters to Simulink model
simu.loadSimMechModel(simu.simMechanicsFile);

toc


classdef MCMC < BetterHandle
    %%MCMC Simple MH and Gibbs moves for the MotorProgram Class
    %%
    %%   This class provides some simple moves for sampling from
    %%   some variables, with fixed dimension. It does not provide
    %%   a sampler for the entire joint space.
    %%
    properties

       debug_full_score = false;

       %% book-keeping to count the number of times
       %% a move is accepted

       %% parameters that don't call the image evaluation
       accept_shape_type = 0;
       n_shape_type = 0;
       accept_scale_type = 0;
       n_scale_type = 0;
       accept_gpos = 0;
       n_gpos = 0;
       accept_seval = 0;
       n_seval = 0;
       accept_seval_token = 0;
       n_seval_token = 0;
       accept_sid = 0;
       n_sid = 0;
       accept_r = 0;
       n_r = 0;

       %% parameters that call the image evaluation function
       accept_shape_token = 0;
       n_shape_token = 0;
       accept_scale_token = 0;
       n_scale_token = 0;
       accept_pos_token = 0;
       n_pos_token = 0;

    end

    methods

        function this = MCMC(debug_full_score)
            if exist('debug_full_score','var')
                this.debug_full_score = debug_full_score;
            end
        end

        %% print the percent of times various moves are accepted
        function acceptance_ratios(this)
           fprintf(1,'Acceptance ratios\n');
           fprintf(1,' shape type: %s%% (of %d)\n',num2str(this.accept_shape_type/this.n_shape_type*100,3),this.n_shape_type);
           fprintf(1,' scale type: %s%% (of %d)\n',num2str(this.accept_scale_type/this.n_shape_type*100,3),this.n_scale_type);
           fprintf(1,' global pos: %s%% (of %d)\n',num2str(this.accept_gpos/this.n_gpos*100,3),this.n_gpos);
           if this.n_seval > 0
               fprintf(1,' seval type: %s%% (of %d)\n',num2str(this.accept_seval/this.n_seval*100,3),this.n_seval);
               fprintf(1,' seval tokn: %s%% (of %d)\n',num2str(this.accept_seval_token/this.n_seval_token*100,3),this.n_seval_token);
           end
           fprintf(1,' sub-ids   : %s%% (of %d)\n',num2str(this.accept_sid/this.n_sid*100,3),this.n_sid);
           fprintf(1,' R         : %s%% (of %d)\n',num2str(this.accept_r/this.n_r*100,3),this.n_r);

           fprintf(1,' scale tokn: %s%% (of %d)\n',num2str(this.accept_scale_token/this.n_scale_token*100,3),this.n_scale_token);
           fprintf(1,' shape tokn: %s%% (of %d)\n',num2str(this.accept_shape_token/this.n_shape_token*100,3),this.n_shape_token);
           fprintf(1,' pos   tokn: %s%% (of %d)\n',num2str(this.accept_pos_token/this.n_pos_token*100,3),this.n_pos_token);
        end

        %% SHAPE TYPE MOVE
        function [val_new,accept] = mh_shape_type(this,sid,bid,M,lib)
            %% sid: stroke index
            %% bid: sub-stroke index

            %% make proposal
            curr_shape_type = M.S{sid}.shapes_type(:,:,bid);
            prop_shape_type = propose_shape_type(curr_shape_type,M.parameters);

            %% score proposal
            curr_score = score_shape_type(M,sid,bid,lib,M.parameters);
            if this.debug_full_score
               curr_score2 = score_shape_type(M,sid,bid,lib,M.parameters,true);
            end
            M.S{sid}.shapes_type(:,:,bid) = prop_shape_type;
            prop_score = score_shape_type(M,sid,bid,lib,M.parameters);

            if this.debug_full_score %% debug to make sure full-score matches
                prop_score2 = score_shape_type(M,sid,bid,lib,M.parameters,true);
                if ~isinf(prop_score)
                    assert(aeq(prop_score-curr_score,prop_score2-curr_score2));
                    fprintf(1,'DEBUG: MH Shape type passed score test\n');
                end
            end

            %% accept or reject
            accept = mh_accept(prop_score,curr_score);
            if ~accept
                M.S{sid}.shapes_type(:,:,bid) = curr_shape_type;
            end
            val_new = M.S{sid}.shapes_type(:,:,bid);

            this.accept_shape_type = this.accept_shape_type + accept;
            this.n_shape_type = this.n_shape_type + 1;

            if isinf(scoreMP(M,lib))
               error('score should not be negative');
            end
        end

        %% SHAPE TOKEN MOVE
        function [val_new,accept] = mh_shape_token(this,sid,bid,M,lib)
            %% sid: stroke index
            %% bid: sub-stroke index

            %% make proposal
            curr_shape_token = M.S{sid}.shapes_token(:,:,bid);
            prop_shape_token = propose_shape_token(curr_shape_token,M.parameters);

            %% score proposal
            curr_score = score_shape_token(M,sid,bid,lib,M.parameters);
            if this.debug_full_score
               curr_score2 = score_shape_token(M,sid,bid,lib,M.parameters,true);
            end
            M.S{sid}.shapes_token(:,:,bid) = prop_shape_token;
            prop_score = score_shape_token(M,sid,bid,lib,M.parameters);

            if this.debug_full_score %% debug to make sure full-score matches
                prop_score2 = score_shape_token(M,sid,bid,lib,M.parameters,true);
                if ~isinf(prop_score)
                    assert(aeq(prop_score-curr_score,prop_score2-curr_score2));
                    fprintf(1,'DEBUG: MH Shape token passed score test\n');
                end
            end

            %% accept or reject
            accept = mh_accept(prop_score,curr_score);
            if ~accept
                M.S{sid}.shapes_token(:,:,bid) = curr_shape_token;
            end
            val_new = M.S{sid}.shapes_token(:,:,bid);

            this.accept_shape_token = this.accept_shape_token + accept;
            this.n_shape_token = this.n_shape_token + 1;

            if isinf(scoreMP(M,lib))
               error('score should not be negative');
            end
        end

        %% SCALE TYPE MOVE
        function [val_new,accept] = mh_scale_type(this,sid,bid,M,lib)
            %% sid: stroke index
            %% bid: sub-stroke index

            %% make proposal
            curr_invscale_type = M.S{sid}.invscales_type(bid);
            prop_invscale_type = propose_scale_type(curr_invscale_type,M.parameters);

            %% score proposal
            curr_score = score_scale_type(M,sid,bid,lib,M.parameters);
            if this.debug_full_score
                curr_score2 = score_scale_type(M,sid,bid,lib,M.parameters,true);
            end
            M.S{sid}.invscales_type(bid) = prop_invscale_type;
            prop_score = score_scale_type(M,sid,bid,lib,M.parameters);

            if this.debug_full_score %% debug to make sure full-score matches
                prop_score2 = score_scale_type(M,sid,bid,lib,M.parameters,true);
                if ~isinf(prop_score)
                    assert(aeq(prop_score-curr_score,prop_score2-curr_score2));
                    fprintf(1,'DEBUG: MH Scale type passed score test\n');
                end
            end

            %% accept or reject
            accept = mh_accept(prop_score,curr_score);
            if ~accept
               M.S{sid}.invscales_type(bid) = curr_invscale_type;
            end
            val_new = M.S{sid}.invscales_type(bid);

            this.accept_scale_type = this.accept_scale_type + accept;
            this.n_scale_type = this.n_scale_type + 1;

        end

        %% SCALE TOKEN MOVE
        function [val_new,accept] = mh_scale_token(this,sid,bid,M,lib)
            %% sid: stroke index
            %% bid: sub-stroke index

            %% make proposal
            curr_invscale_token = M.S{sid}.invscales_token(bid);
            prop_invscale_token = propose_scale_token(curr_invscale_token,M.parameters);

            %% score proposal
            curr_score = score_scale_token(M,sid,bid,lib,M.parameters);
            if this.debug_full_score
                curr_score2 = score_scale_token(M,sid,bid,lib,M.parameters,true);
            end
            M.S{sid}.invscales_token(bid) = prop_invscale_token;
            prop_score = score_scale_token(M,sid,bid,lib,M.parameters);

            if this.debug_full_score %% debug to make sure full-score matches
                prop_score2 = score_scale_token(M,sid,bid,lib,M.parameters,true);
                if ~isinf(prop_score)
                    assert(aeq(prop_score-curr_score,prop_score2-curr_score2));
                    fprintf(1,'DEBUG: MH Scale token passed score test\n');
                end
            end

            %% accept or reject
            accept = mh_accept(prop_score,curr_score);
            if ~accept
               M.S{sid}.invscales_token(bid) = curr_invscale_token;
            end
            val_new = M.S{sid}.invscales_token(bid);

            this.accept_scale_token = this.accept_scale_token + accept;
            this.n_scale_token = this.n_scale_token + 1;

        end

        %% GLOBAL POSITION FOR UNIHIST RELATION
        function [val_new,accept] = mh_gobal_position(this,sid,M,lib)

            %% sid: stroke index
            assert(strcmp(M.S{sid}.R.type,'unihist'));

            %% make proposal
            curr_gpos = M.S{sid}.R.gpos;
            prop_gpos = propose_global_position(curr_gpos,M.parameters);

            %% score proposal
            curr_score = score_global_position(M,sid,lib);
            if this.debug_full_score
               curr_score2 = score_global_position(M,sid,lib,true);
            end
            M.S{sid}.R.gpos = prop_gpos;
            prop_score = score_global_position(M,sid,lib);

            if this.debug_full_score %% debug to make sure full-score matches
                prop_score2 = score_global_position(M,sid,lib,true);
                if ~isinf(prop_score)
                    assert(aeq(prop_score-curr_score,prop_score2-curr_score2));
                    fprintf(1,'DEBUG: MH position passed score test\n');
                end
            end

            %% accept or reject
            accept = mh_accept(prop_score,curr_score);
            if ~accept
                M.S{sid}.R.gpos = curr_gpos;
            end
            val_new = M.S{sid}.R.gpos;

            this.accept_gpos = this.accept_gpos + accept;
            this.n_gpos = this.n_gpos + 1;

        end

        %% POSITION FOR A STROKE
        function [val_new,accept] = mh_token_position(this,sid,M,lib)
            %% sid: stroke index

            %% make proposal
            curr_pos_token = M.S{sid}.pos_token;
            prop_pos_token = propose_local_position(curr_pos_token,M.parameters);

            %% score proposal
            curr_score = score_local_position(M,sid,lib);
            if this.debug_full_score
               curr_score2 = score_local_position(M,sid,lib,true);
            end
            M.S{sid}.pos_token = prop_pos_token;
            prop_score = score_local_position(M,sid,lib);

            if this.debug_full_score %% debug to make sure full-score matches
                prop_score2 = score_local_position(M,sid,lib,true);
                if ~isinf(prop_score)
                    assert(aeq(prop_score-curr_score,prop_score2-curr_score2));
                    fprintf(1,'DEBUG: MH token position passed score test\n');
                end
            end

            %% accept or reject
            accept = mh_accept(prop_score,curr_score);
            if ~accept
                M.S{sid}.pos_token = curr_pos_token;
            end
            val_new = M.S{sid}.pos_token;

            this.accept_pos_token = this.accept_pos_token + accept;
            this.n_pos_token = this.n_pos_token + 1;
        end

        %% EVAL-SPOT TYPE
        function [val_new,accept] = mh_eval_spot_type(this,sid,M,lib)
            %% sid: stroke index
            assert(strcmp(M.S{sid}.R.type,'mid'));

            %% make proposal
            curr_eval_spot_type = M.S{sid}.R.eval_spot_type;
            assert(~isempty(curr_eval_spot_type));
            prop_eval_spot_type = propose_eval_spot(curr_eval_spot_type,M.parameters);

            %% score proposal
            curr_score = score_eval_spot_type(M,sid,lib,M.parameters);
            if this.debug_full_score
                curr_score2 = score_eval_spot_type(M,sid,lib,M.parameters,true);
            end
            M.S{sid}.R.eval_spot_type = prop_eval_spot_type;
            prop_score = score_eval_spot_type(M,sid,lib,M.parameters);

            if this.debug_full_score %% debug to make sure full-score matches
               prop_score2 = score_eval_spot_type(M,sid,lib,M.parameters,true);
               if ~isinf(prop_score)
                    assert(aeq(prop_score-curr_score,prop_score2-curr_score2));
                    fprintf(1,'DEBUG: MH eval spot type passed score test\n');
                end
            end

            %% accept or reject
            accept = mh_accept(prop_score,curr_score);
            if ~accept
                M.S{sid}.R.eval_spot_type = curr_eval_spot_type;
            end
            val_new = M.S{sid}.R.eval_spot_type;

            this.accept_seval = this.accept_seval + accept;
            this.n_seval = this.n_seval + 1;

        end

        function [val_new,accept] = mh_eval_spot_token(this,sid,M,lib)
            %% sid: stroke index
            assert(strcmp(M.S{sid}.R.type,'mid'));

            %% make proposal
            curr_eval_spot_token = M.S{sid}.R.eval_spot_token;
            prop_eval_spot_token = propose_eval_spot(curr_eval_spot_token,M.parameters);

            %% score proposal
            curr_score = score_eval_spot_token(M,sid,lib,M.parameters);
            if this.debug_full_score
                curr_score2 = score_eval_spot_token(M,sid,lib,M.parameters,true);
            end
            M.S{sid}.R.eval_spot_token = prop_eval_spot_token;
            prop_score = score_eval_spot_token(M,sid,lib,M.parameters);

            if this.debug_full_score %% debug to make sure full-score matches
               prop_score2 = score_eval_spot_token(M,sid,lib,M.parameters,true);
               if ~isinf(prop_score)
                    assert(aeq(prop_score-curr_score,prop_score2-curr_score2));
                    fprintf(1,'DEBUG: MH eval spot token passed score test\n');
                end
            end

            %% accept or reject
            accept = mh_accept(prop_score,curr_score);
            if ~accept
                M.S{sid}.R.eval_spot_token = curr_eval_spot_token;
            end
            val_new = M.S{sid}.R.eval_spot_token;

            this.accept_seval_token = this.accept_seval_token + accept;
            this.n_seval_token = this.n_seval_token + 1;

        end

        %% SUB-STROKE IDS
        function [val_new,accept] = gibbs_substroke_id(this,sid,bid,M,lib)
            % sid: stroke index
            % bid: sub-stroke index
            curr_id = M.S{sid}.ids(bid);
            nclust = lib.N;
            score = score_all_subid(M,sid,bid,lib);

            if this.debug_full_score
                score2 = zeros(nclust,1);
                for c=1:nclust
                    M.S{sid}.ids(bid) = c;
                    score2(c) = scoreMP(M,lib);
                end
                v1 = score-logsumexp(score(:));
                v2 = score2-logsumexp(score2(:));
                v1(isinf(v1)) = [];
                v2(isinf(v2)) = [];
                assert( aeq(v1,v2) );
                fprintf(1,'DEBUG: Gibbs sub-stroke ids passed score test\n');
            end

            %% normalize probability distribution
            score = score - logsumexp(score);
            pvec = exp(score);
            assert(aeq(sum(pvec),1));
            pvec = pvec ./ sum(pvec);

            %% sample the new id
            vr = mnrnd(1,pvec);
            assert(~any(isnan(vr)));
            new_id = find(vr);
            M.S{sid}.ids(bid) = new_id;
            accept = new_id ~= curr_id;

            val_new = M.S{sid}.ids(bid);

            this.accept_sid  = this.accept_sid + accept;
            this.n_sid = this.n_sid + 1;

        end

        %% RELATION (WITH TOKEN PARAMETER FOR ATTACHMENT)
        function [val_new,accept] = mh_relation(this,sid,M,lib)

            %% make proposal
            PM = defaultps;
            curr_R = M.S{sid}.R;
            prop_R = propose_relation(M.S{sid}.pos_token,M.S(1:sid-1),lib,PM);

            %% score proposal
            curr_score = score_relation(M,sid,lib);
            g_prop_to_curr = pp_relation(M,sid,lib,PM);
            if this.debug_full_score
                curr_score2 = score_relation(M,sid,lib,true);
            end
            M.S{sid}.R = prop_R;
            prop_score = score_relation(M,sid,lib);

            g_curr_to_prop = pp_relation(M,sid,lib,PM);
            if this.debug_full_score
               prop_score2 = score_relation(M,sid,lib,true);
               if ~isinf(prop_score)
                    assert(aeq(prop_score-curr_score,prop_score2-curr_score2));
                    fprintf(1,'DEBUG: MH relation passed score test\n');
                end
            end

            %% accept or reject
            accept = mh_accept(prop_score,curr_score,g_curr_to_prop,g_prop_to_curr);
            if ~accept
                M.S{sid}.R = curr_R;
            end
            val_new = M.S{sid}.R;

            %% record keeping
            this.accept_r = this.accept_r + accept;
            this.n_r = this.n_r + 1;

        end

    end
end

%% general MH acceptance function
%%
%% Input
%%  prop_score: log-probability of model with proposal
%%  curr_score: log-probability of current model
%%  g_curr_to_prop: log-prob. of transition from current to proposal model
%%  g_prop_to_curr: reverse log-prob
function accept = mh_accept(prop_score,curr_score,g_curr_to_prop,g_prop_to_curr)
    if exist('g_curr_to_prop','var')
        lr = prop_score - curr_score + g_prop_to_curr - g_curr_to_prop;
    else
        lr = prop_score - curr_score;
    end

    if isinf(prop_score)
       accept = false;
       return
    end

    r = min(1,exp(lr));
    assert(~isinf(curr_score));
    accept = rand < r;
end

function R = propose_relation(pos_token,prev_strokes,lib,PM)
    %% Propose new relation by sampling from prior, except the global
    %% position is sampled from near the token position
    R = CPD.sample_relation_type(lib,prev_strokes);
    if strcmp(R.type,'mid')
        R.eval_spot_token = CPD.sample_relation_token(lib,R.eval_spot_type);
    elseif strcmp(R.type,'unihist')
        R.gpos(1) = pos_token(1) + PM.mcmc_prop_relpos_mlty*lib.rel.sigma_x*randn;
        R.gpos(2) = pos_token(2) + PM.mcmc_prop_relpos_mlty*lib.rel.sigma_y*randn;
    end
end

function ll = pp_relation(M,sid,lib,PM)
    %% The probability of proposing relation M.S{sid}.R
    ll = CPD.score_relation_type(lib,M.S{sid}.R);
    if strcmp(M.S{sid}.R.type,'mid')
       ll = ll + CPD.score_relation_token(lib,M.S{sid}.R.eval_spot_token,...
                                              M.S{sid}.R.eval_spot_type);
    elseif strcmp(M.S{sid}.R.type,'unihist')
       gpos = M.S{sid}.R.gpos;
       pos_token = M.S{sid}.pos_token;
       ll = ll - lib.Spatial.score(gpos,sid); %% since this sample was not used
       ll = ll + mvnormpdfln(gpos(1),pos_token(1),PM.mcmc_prop_relpos_mlty*lib.rel.sigma_x);
       ll = ll + mvnormpdfln(gpos(2),pos_token(2),PM.mcmc_prop_relpos_mlty*lib.rel.sigma_y);
    end
end

%% RELATIONS

function ll = score_relation(M,sid,lib,fullscore)
    if ~exist('fullscore','var')
       fullscore = false;
    end
    if fullscore
        ll = scoreMP(M,lib);
        return
    end

    ll = CPD.score_relation_type(lib,M.S{sid}.R);
    if strcmp(M.S{sid}.R.type,'mid')
       ll = ll + CPD.score_relation_token(lib,M.S{sid}.R.eval_spot_token,...
                                              M.S{sid}.R.eval_spot_type);
    end
    pos = M.S{sid}.pos_token;
    ll = ll + CPD.score_position(lib,pos,M.S{sid}.R,M.S(1:sid-1));
end

%% SHAPE

function shape_type = propose_shape_type(shape_type,PM)
    [~,dim,n] = size(shape_type);
    assert(dim==2 && n==1);
    shape_type = shape_type + PM.mcmc.prop_shape_sd .* randn(size(shape_type));
end

function shape_token = propose_shape_token(shape_token,PM)
    shape_token = propose_shape_type(shape_token,PM);
end

function score = score_shape_type(M,sid,bid,lib,~,fullscore)
    if ~exist('fullscore','var')
       fullscore = false;
    end
    if fullscore
        score = scoreMP(M,lib);
        return
    end
    shape_type = M.S{sid}.shapes_type(:,:,bid);
    shape_token = M.S{sid}.shapes_token(:,:,bid);
    id = M.S{sid}.ids(bid);
    lp = CPD.score_shape_type(lib,shape_type,id);
    ll = CPD.score_shape_token(lib,shape_token,shape_type);
    score = lp + ll;
end

function [score,out] = score_shape_token(M,sid,bid,lib,~,fullscore)
    if ~exist('fullscore','var')
       fullscore = false;
    end
    if fullscore
        [score,out] = scoreMP(M,lib);
        return
    end
    shape_type = M.S{sid}.shapes_type(:,:,bid);
    shape_token = M.S{sid}.shapes_token(:,:,bid);

    %% account for future positions, which this parameter may influence
    ll_next_pos = 0;
    for i=sid+1:M.ns
       nextS = M.S{i};
       ll_next_pos = ll_next_pos + CPD.score_position(lib,nextS.pos_token,nextS.R,M.S(1:i-1));
    end

    %% sum up scores
    score = ll_next_pos + CPD.score_image(M.I,M.pimg) + CPD.score_shape_token(lib,shape_token,shape_type);
end

%% SCALE

function invscales_type = propose_scale_type(invscales_type,PM)
    assert(isscalar(invscales_type));
    invscales_type = invscales_type + PM.mcmc.prop_scale_sd .* randn(size(invscales_type));
end

function invscales_token = propose_scale_token(invscales_token,PM)
    invscales_token = propose_scale_type(invscales_token,PM);
end

function score = score_scale_type(M,sid,bid,lib,~,fullscore)
    if ~exist('fullscore','var')
       fullscore = false;
    end
    if fullscore
        score = scoreMP(M,lib);
        return
    end
    scales_type = M.S{sid}.invscales_type(bid);
    scales_token = M.S{sid}.invscales_token(bid);
    id = M.S{sid}.ids(bid);
    lp = CPD.score_invscale_type(lib,scales_type,id);
    ll = CPD.score_invscale_token(lib,scales_token,scales_type);
    score = lp + ll;
end

function score = score_scale_token(M,sid,bid,lib,~,fullscore)
    if ~exist('fullscore','var')
       fullscore = false;
    end
    if fullscore
        score = scoreMP(M,lib);
        return
    end
    scales_type = M.S{sid}.invscales_type(bid);
    scales_token = M.S{sid}.invscales_token(bid);

    %% account for future positions, which this parameter may influence
    ll_next_pos = 0;
    for i=sid+1:M.ns
       nextS = M.S{i};
       ll_next_pos = ll_next_pos + CPD.score_position(lib,nextS.pos_token,nextS.R,M.S(1:i-1));
    end

    score = ll_next_pos + CPD.score_image(M.I,M.pimg) + CPD.score_invscale_token(lib,scales_token,scales_type);
end

%% POSITION

function pos = propose_local_position(pos,PM)
    pos = propose_global_position(pos,PM);
end

function gpos = propose_global_position(gpos,PM)
    gpos = gpos + PM.mcmc.prop_gpos_sd .* randn(size(gpos));
end

function score = score_global_position(M,sid,lib,fullscore)
    if ~exist('fullscore','var')
       fullscore = false;
    end
    if fullscore
        score = scoreMP(M,lib);
        return
    end
    R = M.S{sid}.R;
    lp = CPD.score_relation_type(lib,R);
    ll = CPD.score_position(lib,M.S{sid}.pos_token,R,M.S(1:sid-1));
    score = lp + ll;
end

function score = score_local_position(M,sid,lib,fullscore)
    if ~exist('fullscore','var')
       fullscore = false;
    end
    if fullscore
        score = scoreMP(M,lib);
        return
    end
    R = M.S{sid}.R;
    ll = CPD.score_position(lib,M.S{sid}.pos_token,R,M.S(1:sid-1));

    %% account for future positions, which this parameter may influence
    ll_next_pos = 0;
    for i=sid+1:M.ns
       nextS = M.S{i};
       ll_next_pos = ll_next_pos + CPD.score_position(lib,nextS.pos_token,nextS.R,M.S(1:i-1));
    end

    score = CPD.score_image(M.I,M.pimg) + ll + ll_next_pos;
end

%% EVALUATION SPOT RELATION

function eval_spot_type = propose_eval_spot(eval_spot_type,PM)
    eval_spot_type = eval_spot_type + PM.mcmc.prop_relmid_sd .* randn;
end

function score = score_eval_spot_type(M,sid,lib,~,fullscore)
    if ~exist('fullscore','var')
       fullscore = false;
    end
    if fullscore
        score = scoreMP(M,lib);
        return
    end
    R = M.S{sid}.R;
    lp = CPD.score_relation_type(lib,R);
    ll = CPD.score_relation_token(lib,R.eval_spot_token,R.eval_spot_type);
    score = lp + ll;
end

function score = score_eval_spot_token(M,sid,lib,~,fullscore)
    if ~exist('fullscore','var')
       fullscore = false;
    end
    if fullscore
        score = scoreMP(M,lib);
        return
    end
    R = M.S{sid}.R;
    lp = CPD.score_relation_token(lib,R.eval_spot_token,R.eval_spot_type);
    ll = CPD.score_position(lib,M.S{sid}.pos_token,R,M.S(1:sid-1));
    score = lp + ll;
end


function hplstat(HPL,HPE,HAL,src_name)
% HPL(1xN) - Horizontal protection level
% HPE(Nx1) - Horizontal position error = (easting.^2, northing.^2).^0.5
% HAL - Horizontal alert limit (define how large is the area for Normal operation
%       [the line between white and yellow])
% src_name = label of vertical axis which will go as subscript after 'HPL'

%*     Copyright c 1998 The board of trustees of the Leland Stanford     *
%*                      Junior University. All rights reserved.          *
%*     This script file may be distributed and used freely, provided     *
%*     this copyright notice is always kept with it.                     *
%*                                                                       *
%*     Questions and comments should be directed to Todd Walter at:      *
%*     walter@relgyro.stanford.edu                                       *
%HPLSTAT
%   HPLSTAT(FNAME, HAL, PATH) reads the given file of TMS Horizontal
%   Protection Limit statistics and plots the color coded histogram.
%   In addition the regions of integrity failures and unavailability
%   are shown.  The optional HAL argument is given in meters (default 30m).
%   See also : TMSSTAT, TRSNAMES, VERTSTAT, VPLSTAT
%   Example code for generating the file is included at the bottom of hplstat.m
%
%   NOTE: To plot on a non-color printer type:
%                 colormap('gray')
% % new colormap for colorbar (HPL and HPE plot)
 color_bar =[
     0.6980    0.6980    0.6980;
    0.6905    0.6905    0.6905;
     0.6830    0.6830    0.6830;
     0.6754    0.6754    0.6754;
     0.6679    0.6679    0.6679;
     0.6604    0.6604    0.6604;
     0.6528    0.6528    0.6528;
     0.6453    0.6453    0.6453;
     0.6378    0.6378    0.6378;
     0.6302    0.6302    0.6302;
     0.6227    0.6227    0.6227;
     0.6152    0.6152    0.6152;
     0.6076    0.6076    0.6076;
     0.6001    0.6001    0.6001;
     0.5926    0.5926    0.5926;
     0.5850    0.5850    0.5850;
     0.5775    0.5775    0.5775;
     0.5700    0.5700    0.5700;
     0.5624    0.5624    0.5624;
     0.5549    0.5549    0.5549;
     0.5474    0.5474    0.5474;
     0.5398    0.5398    0.5398;
     0.5323    0.5323    0.5323;
     0.5247    0.5247    0.5247;
     0.5172    0.5172    0.5172;
     0.5097    0.5097    0.5097;
     0.5021    0.5021    0.5021;
     0.4946    0.4946    0.4946;
     0.4871    0.4871    0.4871;
    0.4795    0.4795    0.4795;
     0.4720    0.4720    0.4720;
     0.4645    0.4645    0.4645;
     0.4569    0.4569    0.4569;
     0.4494    0.4494    0.4494;
     0.4419    0.4419    0.4419;
     0.4343    0.4343    0.4343;
     0.4268    0.4268    0.4268;
     0.4193    0.4193    0.4193;
     0.4117    0.4117    0.4117;
     0.4042    0.4042    0.4042;
     0.3967    0.3967    0.3967;
     0.3891    0.3891    0.3891;
     0.3816    0.3816    0.3816;
     0.3741    0.3741    0.3741;
     0.3665    0.3665    0.3665;
     0.3590    0.3590    0.3590;
     0.3515    0.3515    0.3515;
     0.3439    0.3439    0.3439;
     0.3364    0.3364    0.3364;
     0.3289    0.3289    0.3289;
     0.3213    0.3213    0.3213;
     0.2966    0.2966    0.2966;
     0.2719    0.2719    0.2719;
    0.2472    0.2472    0.2472;
     0.2225    0.2225    0.2225;
     0.1977    0.1977    0.1977;
     0.1730    0.1730    0.1730;
     0.1483    0.1483    0.1483;
     0.1236    0.1236    0.1236;
     0.0989    0.0989    0.0989;
     0.0742    0.0742    0.0742;
     0.0494    0.0494    0.0494;
     0.0247    0.0247    0.0247;
          0         0         0];
 colormap(color_bar)

% other colors used in the plot
%color1 = [134/255 134/255 134/255]; % - dark gray
color1 = [1 0.1 0.1];              % - red (original)

%color2 = [174/255 174/255 174/255]; % - middle gray
color2 = [1 0.55 0.55];            % - pink (original)

%color3 = [222/255 222/255 222/255]; % - light gray
color3 = [1 1 0.5];                % - yellow (original)

%color4 = color2;                    % - middle gray
color4 = [1 .55 0.3];              % - orange (original)

set(0,'DefaultTextFontName','Times');
set(0,'DefaultAxesFontName','Times');
set(0,'DefaultTextFontSize',18);
set(0,'DefaultAxesFontSize',18);

if nargin < 2
    error('Must input HPL and HPE!');
end
if nargin < 3
    HAL = 30;
end

% size of VPL, which should be the same for VPE as well
n = size(HPL,2);

% HPE
j = floor(2.0*abs(HPE))+1;
j(find(j>100)) = 100;
% HPL
k = floor(2.0*abs(HPL))+1;
k(find(k>100)) = 100;

% initialize hpl_stat
data     = zeros(100,100);
diagonal = zeros(100,1);
for i = 1:n
    % statistics
    data(k(i),j(i)) = data(k(i),j(i))+1;
    % diagonal
    if((k(i) == j(i)) && (abs(HPE(i,1)) < abs(HPL(1,i))))
        diagonal(k(i),1) = diagonal(k(i),1)+1;
    end
end

c = 1;
colors = 'brygcmw';
clf

err_bin  = 0.25:0.5:49.75;
sig_bin  = 0.25:0.5:49.75;

seconds  = n;
sec_available = n;
diag_cnt = sum(diagonal);

% determine the number of points and axis ranges
n_pts = sum(sum(data));
if sec_available == 1
    epochs = seconds;
else
    epochs = n_pts;
end

d_err_bin = mean(diff(err_bin));
x_lo_bnd  = min(err_bin) - d_err_bin/2;
x_up_bnd  = max(err_bin) + d_err_bin/2;

d_sig_bin = mean(diff(sig_bin));
y_lo_bnd  = min(sig_bin) - d_sig_bin/2;
y_up_bnd  = max(sig_bin) + d_sig_bin/2;

z_lo_bnd  = 1;
z_up_bnd  = max(max(data));

% clear plot
clf;

% plot each data point as a pixel
[i,j]=find(data);
face_mat=[[1 2 6 5]' [2 3 7 6]' [3 4 8 7]' ...
          [4 1 5 8]' [1 2 3 4]' [5 6 7 8]']';
colors=colormap;
for idx = 1:length(i)
  z = log10(data(i(idx),j(idx)));
  vtx_mat = [err_bin(j(idx))+[-0.5 0.5 0.5 -0.5 -0.5 0.5 0.5 -0.5]'*d_err_bin ...
             sig_bin(i(idx))+[-0.5 -0.5 0.5 0.5 -0.5 -0.5 0.5 0.5]'*d_sig_bin ...
             [0 0 0 0 z z z z]'];
  c_idx = ceil(63*(log10(data(i(idx),j(idx)))/log10(z_up_bnd))) + 1;
  patch('Vertices',  vtx_mat, ...
        'Faces',     face_mat, ...
        'FaceColor', colors(c_idx,:), ...
        'EdgeColor', 'none');
end

% determine availability and # of integrity failures
i_diag1 = find(err_bin == HAL);
i_diag2 = find(err_bin < HAL);
i_diag3 = find(err_bin > HAL);

i_fail1 = find(err_bin(j) >= HAL & sig_bin(i) < HAL);
n_fail1 = sum(sum(diag(data(i(i_fail1),j(i_fail1)))))...
         - sum(diagonal(i_diag1));
i_fail2 = find(err_bin(j)./sig_bin(i) >=1.0 & err_bin(j) < HAL);
n_fail2 = sum(sum(diag(data(i(i_fail2),j(i_fail2)))))...
         - sum(diagonal(i_diag2));
i_fail3 = find(err_bin(j)./sig_bin(i) >=1.0 & sig_bin(i) >= HAL);
n_fail3 = sum(sum(diag(data(i(i_fail3),j(i_fail3)))))...
         - sum(diagonal(i_diag3));
i_cont  = find(sig_bin(i) >= HAL);
n_cont  = sum(sum(diag(data(i(i_cont),j(i_cont)))));
%i_avail = find(err_bin(j) < HAL & sig_bin(i) < HAL);
i_avail = find(err_bin(j)./sig_bin(i) < 1.0 & sig_bin(i) < HAL);
n_avail = sum(sum(diag(data(i(i_avail),j(i_avail)))))...
         + sum(diagonal(i_diag2));

% set the axes limits and color values
set(gca,'XLim',[x_lo_bnd x_up_bnd]);
set(gca,'YLim',[y_lo_bnd y_up_bnd]);
set(gca,'CLim',[z_lo_bnd z_up_bnd]);

% show the region of normal operation
HT=text(0.37*(HAL - x_lo_bnd) + x_lo_bnd, ...
     0.93*(HAL - y_lo_bnd) + y_lo_bnd, ...
     0.25*sqrt(z_up_bnd*z_lo_bnd), ...
     '{\fontsize{14pt}Normal Operation}');
pos = get(HT, 'Position');
pos = pos + [.05 -0.05 0];
set(HT, 'Position', pos);
set(HT, 'Color', [1 1 1]); % white
text(0.37*(HAL - x_lo_bnd) + x_lo_bnd, ...
     0.93*(HAL - y_lo_bnd) + y_lo_bnd, ...
     0.25*sqrt(z_up_bnd*z_lo_bnd), ...
     '{\fontsize{14pt}Normal Operation}');
if n_avail/epochs >= .999995
  HT = text(0.33*(HAL - x_lo_bnd) + x_lo_bnd, ...
            0.86*(HAL - y_lo_bnd) + y_lo_bnd, ...
            0.25*sqrt(z_up_bnd*z_lo_bnd), ...
            '> 99.999%');
  pos = get(HT, 'Position');
  pos = pos + [.05 -0.05 0];
  set(HT, 'Position', pos);
  set(HT, 'Color', [1 1 1]); % white
  set(HT, 'FontSize', 14);
  HT = text(0.33*(HAL - x_lo_bnd) + x_lo_bnd, ...
            0.86*(HAL - y_lo_bnd) + y_lo_bnd, ...
            0.25*sqrt(z_up_bnd*z_lo_bnd), ...
            '> 99.999%');
  set(HT, 'FontSize', 14);
else
  HT = text(0.37*(HAL - x_lo_bnd) + x_lo_bnd, ...
            0.86*(HAL - y_lo_bnd) + y_lo_bnd, ...
            0.25*sqrt(z_up_bnd*z_lo_bnd), ...
            [num2str(100.0*n_avail/epochs,'%6.3f'), '%']);
  pos = get(HT, 'Position');
  pos = pos + [.05 -0.05 0];
  set(HT, 'Position', pos);
  set(HT, 'Color', [1 1 1]); % white
  set(HT, 'FontSize', 14);
  HT = text(0.37*(HAL - x_lo_bnd) + x_lo_bnd, ...
            0.86*(HAL - y_lo_bnd) + y_lo_bnd, ...
            0.25*sqrt(z_up_bnd*z_lo_bnd), ...
            [num2str(100.0*n_avail/epochs,'%6.3f'), '%']);
  set(HT, 'FontSize', 14);
end

% outline the region of integrity failures
patch([HAL HAL x_up_bnd x_up_bnd], ...
      [y_lo_bnd HAL HAL y_lo_bnd], ...
      -[0.5 0.5 0.5 0.5], ...
      color1);
HT = text(0.50*(x_up_bnd - HAL) + HAL, ...
          0.55*(HAL - y_lo_bnd) + y_lo_bnd, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          'HMI');
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
pos = get(HT, 'Position');
pos = pos + [.05 -0.05 0];
set(HT, 'Position', pos);
set(HT, 'Color', color1);
HT = text(0.50*(x_up_bnd - HAL) + HAL, ...
          0.55*(HAL - y_lo_bnd) + y_lo_bnd, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          'HMI');
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
HT = text(0.50*(x_up_bnd - HAL) + HAL, ...
          0.45*(HAL - y_lo_bnd) + y_lo_bnd, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          ['epochs: ', int2str(n_fail1)]);
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
pos = get(HT, 'Position');
pos = pos + [.05 -0.05 0];
set(HT, 'Position', pos);
set(HT, 'Color', color1);
HT = text(0.50*(x_up_bnd - HAL) + HAL, ...
          0.45*(HAL - y_lo_bnd) + y_lo_bnd, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          ['epochs: ', int2str(n_fail1)]);
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');

% outline the region of HPL failures
patch([x_lo_bnd HAL HAL], ...
      [y_lo_bnd HAL y_lo_bnd], ...
      -[0.5 0.5 0.5], ...
      color2);
HT = text(0.67*(HAL - x_lo_bnd) + x_lo_bnd, ...
          0.35*(HAL - y_lo_bnd) + y_lo_bnd, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          'MI');
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
pos = get(HT, 'Position');
pos = pos + [.05 -0.05 0];
set(HT, 'Position', pos);
set(HT, 'Color', color2);
HT = text(0.67*(HAL - x_lo_bnd) + x_lo_bnd, ...
          0.35*(HAL - y_lo_bnd) + y_lo_bnd, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          'MI');
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
HT = text(0.67*(HAL - x_lo_bnd) + x_lo_bnd, ...
          0.25*(HAL - y_lo_bnd) + y_lo_bnd, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          ['epochs: ', int2str(n_fail2)]);
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
pos = get(HT, 'Position');
pos = pos + [.05 -0.05 0];
set(HT, 'Position', pos);
set(HT, 'Color', color2);
HT = text(0.67*(HAL - x_lo_bnd) + x_lo_bnd, ...
          0.25*(HAL - y_lo_bnd) + y_lo_bnd, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          ['epochs: ', int2str(n_fail2)]);
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');

% outline the region of unavailability
patch([x_lo_bnd x_up_bnd x_up_bnd x_lo_bnd], ...
      [HAL HAL y_up_bnd y_up_bnd], ...
      -[0.5 0.5 0.5 0.5], ...
      color3);
HT = text(0.50*(x_up_bnd - x_lo_bnd) + x_lo_bnd, ...
          0.70*(y_up_bnd - HAL) + HAL, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          '{\fontsize{14pt}System Unavailable}');
set(HT,'HorizontalAlignment','Center');
pos = get(HT, 'Position');
pos = pos + [.05 -0.05 0];
set(HT, 'Position', pos);
set(HT, 'Color', color3);
HT = text(0.50*(x_up_bnd - x_lo_bnd) + x_lo_bnd, ...
          0.70*(y_up_bnd - HAL) + HAL, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          '{\fontsize{14pt}System Unavailable}');
set(HT,'HorizontalAlignment','Center');
HT = text(0.50*(x_up_bnd - x_lo_bnd) + x_lo_bnd, ...
          0.55*(y_up_bnd - HAL) + HAL, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          ['Alarm Epochs: ', int2str(n_cont)]);
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
pos = get(HT, 'Position');
pos = pos + [.05 -0.05 0];
set(HT, 'Position', pos);
set(HT, 'Color', color3);
HT = text(0.50*(x_up_bnd - x_lo_bnd) + x_lo_bnd, ...
          0.55*(y_up_bnd - HAL) + HAL, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          ['Alarm Epochs: ', int2str(n_cont)]);
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');

% outline the region where integrity failures and unavailability overlap
patch([HAL x_up_bnd x_up_bnd], ...
      [HAL y_up_bnd HAL], ...
      -z_lo_bnd*[0.45 0.45 0.45], ...
      color4);
HT = text(0.70*(x_up_bnd - HAL) + HAL, ...
          0.325*(y_up_bnd - HAL) + HAL, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          'MI');
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
pos = get(HT, 'Position');
pos = pos + [.05 -0.05 0];
set(HT, 'Position', pos);
set(HT, 'Color', color4);
HT = text(0.70*(x_up_bnd - HAL) + HAL, ...
          0.325*(y_up_bnd - HAL) + HAL, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          'MI');
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
HT = text(0.70*(x_up_bnd - HAL) + HAL, ...
          0.175*(y_up_bnd - HAL) + HAL, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          ['epochs: ', int2str(n_fail3)]);
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');
pos = get(HT, 'Position');
pos = pos + [.05 -0.05 0];
set(HT, 'Position', pos);
set(HT, 'Color', color4);
HT = text(0.70*(x_up_bnd - HAL) + HAL, ...
          0.175*(y_up_bnd - HAL) + HAL, ...
          0.25*sqrt(z_up_bnd*z_lo_bnd), ...
          ['epochs: ', int2str(n_fail3)]);
set(HT, 'FontSize', 14);
set(HT,'HorizontalAlignment','Center');

hold on;
grid off;
% make the grid visible over the patch regions (integrity, unavailability)
ytick  = get(gca,'YTick');
xtick  = get(gca,'XTick');
nytick = length(ytick);
nxtick = length(xtick);
for i = 1:nytick
    plot3([x_lo_bnd x_up_bnd], ytick(i)*[1 1], [0.6 0.6], 'k:');
end
for i = 1:nxtick
    plot3(xtick(i)*[1 1], [y_lo_bnd y_up_bnd], [0.6 0.6], 'k:');
end

% label the axes and add a title
xlabel('{\fontsize{16pt}Error [m]}');
set(get(gca,'XLabel'),'Position', ...
    [ 0.50*(x_up_bnd - x_lo_bnd) + x_lo_bnd, ...
     -0.05*(y_up_bnd - y_lo_bnd) + y_lo_bnd, ...
     z_lo_bnd]);

ylabel(['{\fontsize{16pt}HPL_{',src_name,'} [m]}']);
set(get(gca,'YLabel'),'Position', ...
    [-0.06*(x_up_bnd - x_lo_bnd) + x_lo_bnd, ...
      0.50*(y_up_bnd - y_lo_bnd) + y_lo_bnd z_lo_bnd]);

title (['Horizontal Performance [',int2str(seconds),' seconds]']);

set(get(gca,'Title'),'FontSize',14);
set(get(gca,'Title'),'Position', ...
    [0.50*(x_up_bnd - x_lo_bnd) + x_lo_bnd, ...
     1.02*(y_up_bnd - y_lo_bnd) + y_lo_bnd, ...
     z_lo_bnd]);

% put the sigma_H_major scale on the right hand y-axis
% K_h_pa = 6.18;
% for i = ceil(y_lo_bnd/K_h_pa):floor(y_up_bnd/K_h_pa)
%   if abs(i*K_h_pa - (0.5*(y_up_bnd - y_lo_bnd) + y_lo_bnd)) > ...
%      0.05*(y_up_bnd - y_lo_bnd)
%     plot3([.99*(x_up_bnd - x_lo_bnd) + x_lo_bnd x_up_bnd], ...
%           i*K_h_pa*[1 1], ...
%           [0.65 0.65], ...
%           'k');
%     text(1.02*(x_up_bnd - x_lo_bnd) + x_lo_bnd, i*K_h_pa, 0.65, int2str(i));
%   end
% end
% HT = text(1.04*(x_up_bnd - x_lo_bnd) + x_lo_bnd, ...
%           0.50*(y_up_bnd - y_lo_bnd) + y_lo_bnd, ...
%           0.65, ...
%           '{\fontsize{14pt}\sigma}_{H_{major}} (m)');
% set(HT,'HorizontalAlignment','Center');
% set(HT,'Rotation',90);

% put the color scale up on the right hand side
H = colorbar('vert');
set(get(H,'Ylabel'),'String','{\fontsize{14pt}Number of Points per Pixel}');
%set(H,'YScale','log');
set(H,'YLim',[z_lo_bnd z_up_bnd]);
%set(H,'CLim', [z_lo_bnd z_up_bnd]);

if sec_available == 1
    display (['EGNOS differential for ', ...
              num2str(n_pts), ...
              ' out of ', ...
              num2str(seconds), ...
              ' seconds']);
end

set(H, 'linewidth',.8);
hold off
set(gca,'Fontsize',18);

%print -deps env_HPL

%%%%%%%%%%%% end hplstat.m  %%%%%%%%%%%%%%%%%%%%


%{
MIT License

Copyright (c) 2018 ZjxRS

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

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

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
%}
% This is lifted from https://github.com/ZjxRS/ImageProcess/tree/master/Matlab/ENVI_ReadAndWrite

function info = envihdrread(hdrfile)
% ENVIHDRREAD Reads header of ENVI image.
%   INFO = ENVIHDRREAD('HDR_FILE') reads the ASCII ENVI-generated image
%   header file and returns all the information in a structure of
%   parameters.
%
%   Example:
%   >> info = envihdrread('my_envi_image.hdr')
%   info =
%          description: [1x101 char]
%              samples: 658
%                lines: 749
%                bands: 3
%        header_offset: 0
%            file_type: 'ENVI Standard'
%            data_type: 4
%           interleave: 'bsq'
%          sensor_type: 'Unknown'
%           byte_order: 0
%             map_info: [1x1 struct]
%      projection_info: [1x102 char]
%     wavelength_units: 'Unknown'
%           pixel_size: [1x1 struct]
%           band_names: [1x154 char]
%
%   NOTE: This function is used by ENVIREAD to import data.

% Ian M. Howat, Applied Physics Lab, University of Washington
% ihowat@apl.washington.edu
% Version 1: 19-Jul-2007 00:50:57
% Modified by Felix Totir
%

fid = fopen(hdrfile);
while true
    line = fgetl(fid);
    if line == -1
        break
    else
        eqsn = findstr(line,'=');
        if ~isempty(eqsn)
            param = strtrim(line(1:eqsn-1));
            param(findstr(param,' ')) = '_';
            value = strtrim(line(eqsn+1:end));
            if isempty(str2num(value))
                if ~isempty(findstr(value,'{')) && isempty(findstr(value,'}'))
                    while isempty(findstr(value,'}'))
                        line = fgetl(fid);

                        %for polSARpro
                        if line == -1
                            break;
                        end
                        value = [value,strtrim(line)];
                    end
                end
                eval(['info.',param,' = ''',value,''';'])
            else
                eval(['info.',param,' = ',value,';'])
            end
        end
    end
end
fclose(fid);

if isfield(info,'map_info')
    line = info.map_info;
    line(line == '{' | line == '}') = [];

    %originally: line = strtrim(split(line,','));
    %replaced by
    line=textscan(line,'%s', 'Delimiter',','); %behavior is not quite the same if "line" ends in ','
    line=line{:};
    line=strtrim(line);
    %

    info.map_info = [];
    info.map_info.projection = line{1};
    info.map_info.image_coords = [str2num(line{2}),str2num(line{3})];
    info.map_info.mapx = str2num(line{4});
    info.map_info.mapy = str2num(line{5});
    info.map_info.dx  = str2num(line{6});
    info.map_info.dy  = str2num(line{7});
    if length(line) == 9
        info.map_info.datum  = line{8};
        info.map_info.units  = line{9}(7:end);
    elseif length(line) == 11
        info.map_info.zone  = str2num(line{8});
        info.map_info.hemi  = line{9};
        info.map_info.datum  = line{10};
        info.map_info.units  = line{11}(7:end);
    end

    %part below comes form the original enviread
    %% Make geo-location vectors
    %     xi = info.map_info.image_coords(1);
    %     yi = info.map_info.image_coords(2);
    %     xm = info.map_info.mapx;
    %     ym = info.map_info.mapy;
    %     %adjust points to corner (1.5,1.5)
    %     if yi > 1.5
    %         ym =  ym + ((yi*info.map_info.dy)-info.map_info.dy);
    %     end
    %     if xi > 1.5
    %         xm = xm - ((xi*info.map_info.dy)-info.map_info.dx);
    %     end
    %
    %     info.x= xm + ((0:info.samples-1).*info.map_info.dx);
    %     info.y = ym - ((0:info.lines-1).*info.map_info.dy);
end

if isfield(info,'coordinate_system_string')
    line = info.coordinate_system_string;
    line(line == '{' | line == '}' | line == '"' | line == '[' ...
        | line == ']') = [];

    %originally: line = strtrim(split(line,','));
    %replaced by
    line=textscan(line,'%s', 'Delimiter',','); %behavior is not quite the same if "line" ends in ','
    line=line{:};
    line=strtrim(line);
    %

    info.coordinate_system = [];

    if contains(line{1},'UTM')
        info.coordinate_system.PROJCS = extractAfter(line{1},6);

        info.coordinate_system.GEOGCS = extractAfter(line{2},6);
        info.coordinate_system.DATUM = extractAfter(line{3},5);
        info.coordinate_system.SPHEROID = [];
        info.coordinate_system.SPHEROID.datum = extractAfter(line{4},8);
        info.coordinate_system.SPHEROID.a  = line{5};
        info.coordinate_system.SPHEROID.e  = line{6};
        info.coordinate_system.PRIMEM = extractAfter(line{7},6);
        info.coordinate_system.PMlongToGreenwich = line{8};
        info.coordinate_system.UNITGeo = extractAfter(line{9},4);
        info.coordinate_system.UNITGeo_value = line{10};

        info.coordinate_system.PROJECTION = extractAfter(line{11},10);
        info.coordinate_system.PARAMETER1 = extractAfter(line{12},9);
        info.coordinate_system.PARAMETER1_value = line{13};
        info.coordinate_system.PARAMETER2 = extractAfter(line{14},9);
        info.coordinate_system.PARAMETER2_value = line{15};
        info.coordinate_system.PARAMETER3 = extractAfter(line{16},9);
        info.coordinate_system.PARAMETER3_value = line{17};
        info.coordinate_system.PARAMETER4 = extractAfter(line{18},9);
        info.coordinate_system.PARAMETER4_value = line{19};
        info.coordinate_system.PARAMETER5 = extractAfter(line{20},9);
        info.coordinate_system.PARAMETER5_value = line{21};
        info.coordinate_system.UNITPro = extractAfter(line{22},4);
        info.coordinate_system.UNITPro_value = line{23};

    elseif strcmp(info.map_info.projection, 'Geographic Lat/Lon')
        info.coordinate_system.GEOGCS = extractAfter(line{1},6);
        info.coordinate_system.DATUM = extractAfter(line{2},5);
        info.coordinate_system.SPHEROID = [];
        info.coordinate_system.SPHEROID.datum = extractAfter(line{3},8);
        info.coordinate_system.SPHEROID.a  = line{4};
        info.coordinate_system.SPHEROID.e  = line{5};
        info.coordinate_system.PRIMEM = extractAfter(line{6},6);
        info.coordinate_system.PMlongToGreenwich = line{7};
        info.coordinate_system.UNITGeo = extractAfter(line{8},4);
        info.coordinate_system.UNITGeo_value = line{9};
    end

end

if isfield(info,'pixel_size')
    line = info.pixel_size;
    line(line == '{' | line == '}') = [];

    %originally: line = strtrim(split(line,','));
    %replaced by:
    line=textscan(line,'%s','Delimiter',','); %behavior is not quite the same if "line" ends in ','
    line=line{:};
    line=strtrim(line);

    info.pixel_size = [];
    info.pixel_size.x = str2num(line{1});
    info.pixel_size.y = str2num(line{2});
    info.pixel_size.units = line{3}(7:end);
end

% function split is only used when replacements above do not work
% function A = split(s,d)
%This function by Gerald Dalley (dalleyg@mit.edu), 2004
% A = {};
% while (~isempty(s))
%     [t,s] = strtok(s,d);
%     A = {A{:}, t};
% end


%Transcriptional Regulation
%
% @wholeCellModelID Process_TranscriptionalRegulation
% @name             Transcriptional Regulation
% @description
%   Biology
%   ==============
%   The the rate of transcription of each transcription unit is known to be
%   regulated by proteins referred to as transcription factors which modulate
%   the affinity of the RNA polymerase for each promoter. Transcription factors
%   can both positively and negatively modulate RNA polymerase - promoter
%   affinity. Transcription factors can stabilize RNA polymerase - promoter
%   complexes by contributing additional negative free energy to the complex by
%   providing an additional surface for the RNA polymerase binding.
%   Transcription factors can destabilize RNA polymerase - promoter complexes
%   for example by sterically blocking promoters and preventing the RNA
%   polymerase from binding promoters.
%
%   This process simulates the binding of transcription factors to the promoters
%   of each transcription unit (affinity), as well as the affect of the
%   transcription factors on the recruiment of the RNA polymerase to each
%   promoter (activity). The process was built using experimentally observed fold
%   change expression effect of each transcription factor on each promoter.
%
%   Affinity
%   ++++++++++++++
%   Binds enzymes to promoters assuming:
%   1) Transcription factors have high affinity for promoters
%   2) Transcription factors bind promoters rapidly
%   3) Transcription factors bind promoters stably over time
%   4) Only 1 copy of a transcription factor can bind each promoter
%   5) Transcription factors only compete within their species for
%      promoters. That is for each transcription unit we assume each
%      transcription factor species binds a distinct promoters region.
%
%   Consequently, at each time step we simulate that each free
%   transcription factor binds randomly binds unoccupied promoters (no copy
%   of that transcription factor is already bound to the promoter). Random
%   transcription factor-promoter binding is weighted by the affinity of
%   each transcription factor for each promoter.
%
%   Because transcription factor-promoter affinities are generally not
%   experimentally observed, we base them on the transcription factor fold
%   change activities.
%
%   Activity
%   ++++++++++++++
%   The effect of bound transcription factors on the recruitment of RNA
%   polymerase and the expression of transcription units is simulated here
%   and incorporated into the calculation of the RNA polymerase
%   transcription unit promoter binding probabilities in the transcription
%   process. Specifically, the wild-type average RNA polymerase
%   transcription unit promoter binding probabilities are multiplied by the
%   binding probability fold change effects simulated in this process.
%
%   The RNA polymerase binding probability fold change is simulated for
%   each promoter as the product of the observed expression fold change
%   effects of each bound transcription factor. When a promoter is bound by
%   a single transcription factor, the net RNA polymerase binding
%   probability fold change is the observed expression fold change of that
%   transcription factor. When a promoter is bound by multiple
%   transcription units, the net RNA polymerase binding probability fold
%   change is given by the product of the individual fold change effects of
%   the bound transcription factors.
%
%   Knowledge Base
%   ==============
%   The list of transcriptional regulatory relationships is maintained in the
%   the knowledge base. As of 8/10/2010, it contained 31 such relationships
%   between 5 transcription factors and 29 transcription units containing 37
%   genes. The knowledge base was built from a variety of literature sources
%   and databases including:
%   - PUB_0096
%   - PUB_0110
%   - PUB_0112
%   - PUB_0196
%   - PUB_0418-20
%   - PUB_0433-8
%   - PUB_0505
%
%   Initialization
%   =================
%   Because we assume that transcription factors have high affinity for DNA and
%   bind DNA stably, we initialize as many transcription factors as possible to
%   the promoter-bound state. We randomly assign transcription factors to
%   promoters using their relative affinities.
%
%   Simulation
%   ==============
%   For each kind of transcription factor, bind any free transcription factors
%   to promoters that aren't already occupied by this kind of transcription
%   factor. Choose the binding sites randomly, weighted by the transcription
%   factor's affinity to them.
%
%   References
%   ==============
%   1) Lacramioara Bintu and Nicolas E Buchler and Hernan G Garcia and
%      Ulrich Gerland and Terence Hwa, Jane Kondev and Rob Phillips (2005).
%      Transcriptional regulation by the numbers: models. Curr Opin Genet
%      Dev. 15:116-24.
%   2) Lacramioara Bintu and Nicolas E Buchler and Hernan G Garcia and
%      Ulrich Gerland and Terence Hwa, Jane Kondev and Rob Phillips (2005).
%      Transcriptional regulation by the numbers: applications. Curr Opin
%      Genet Dev. 15:125-35.
%
% Author: Jonathan Karr, jkarr@stanford.edu
% Author: Jared Jacobs, jmjacobs@stanford.edu
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 10/19/2010
classdef TranscriptionalRegulation < edu.stanford.covert.cell.sim.Process & edu.stanford.covert.cell.sim.ChromosomeProcessAspect
    %property annotations
    properties (Constant)
        optionNames__              = {}; %names of option properties
        fixedConstantNames__       = {   %names of fixed constant properties
		    'tfIndexs';
            'tuIndexs';
            'tfAffinities';
            'tfActivities';
            'tfPositionStrands';
            'otherActivities';
		    };
        fittedConstantNames__      = {}; %names of fitted constant properties
        localStateNames__          = {}; %names of simulation state properties redundant with timecourses in this or other processes or the simulation
    end

    %IDs, names, and local indices
    properties
        stimuliWholeCellModelIDs = {};
        substrateWholeCellModelIDs = {};

        enzymeWholeCellModelIDs
        enzymeIndexs_fur         %MG_236_MONOMER  ferric uptake repressor
        enzymeIndexs_gntR        %MG_101_MONOMER  Uncharacterized HTH-type transcriptional regulator
        enzymeIndexs_hrcA        %MG_205_DIMER    heat-inducible transcription repressor HrcA, putative
        enzymeIndexs_luxR        %MG_428_DIMER    LuxR bacterial regulatory protein, putative
        enzymeIndexs_spx         %MG_127_MONOMER  Spx subfamily protein

        transcriptionUnitWholeCellModelIDs
    end

    %fixed biological constants
    properties
        tfIndexs           %identifying index of transcription factor that binds each site [sites X 2]
        tuIndexs           %identifying index of transcription unit associated with each TF binding site [sites X 2]
        tfAffinities       %affinity of each transcription factor for each promoter it binds [sites X 2]
        tfActivities       %fold change expression of each transcription unit when its promoter is bound by a particular transcription factor [sites X 2]
        tfPositionStrands  %start position and strand of each transcription factor DNA binding site [2*sites X 2]
        otherActivities    %fold change each non-DNA-binding transcription factor has on each transcription unit's expression [nTFs X nTUs]
    end

    %dependent local state (implemented as dependent property for convenience)
    properties (Dependent, SetAccess = protected)
        tfBoundPromoters       %number of each transcription factor bound to promoter of each transcription unit [transcription factors X promoters X 2]
        boundTFs               %count of how many of each transcription factor are bound [transcription factors X 1]
    end

    %references to cell state
    properties
        rnaPolymerase             %reference to RNA polymerase state
    end

    %constructor
    methods
        function this = TranscriptionalRegulation(wholeCellModelID, name)
            this = this@edu.stanford.covert.cell.sim.Process(wholeCellModelID, name);
        end
    end

    %communication between process/simulation
    methods
        %set references to state objects
        function storeObjectReferences(this, simulation)
            this.storeObjectReferences@edu.stanford.covert.cell.sim.Process(simulation);
            this.storeObjectReferences@edu.stanford.covert.cell.sim.ChromosomeProcessAspect(simulation);

            this.rnaPolymerase = simulation.state('RNAPolymerase');
            this.states = [this.states; {this.rnaPolymerase}];
        end

        %initialize constants
        function initializeConstants(this, knowledgeBase, varargin)
            %build object arrays of transcription factor monomers and complexes
            monomers = [];
            complexs = [];
            for i = 1:knowledgeBase.numTranscriptionUnits
                m = knowledgeBase.transcriptionUnits(i).transcriptionFactorProteinMonomers;
                c = knowledgeBase.transcriptionUnits(i).transcriptionFactorProteinComplexs;
                if ~isempty(m)
                    monomers = [monomers; m]; %#ok<AGROW>
                end
                if ~isempty(c)
                    complexs = [complexs; c]; %#ok<AGROW>
                end
            end

            %whole cell model ids of all transcription factors
            this.enzymeWholeCellModelIDs = unique({
                monomers.wholeCellModelID...
                complexs.wholeCellModelID}');

            %transcription factors indices
            this.enzymeIndexs_fur  = this.enzymeIndexs({'MG_236_MONOMER'});
            this.enzymeIndexs_gntR = this.enzymeIndexs({'MG_101_MONOMER'});
            this.enzymeIndexs_hrcA = this.enzymeIndexs({'MG_205_DIMER'});
            this.enzymeIndexs_luxR = this.enzymeIndexs({'MG_428_DIMER'});
            this.enzymeIndexs_spx  = this.enzymeIndexs({'MG_127_MONOMER'});

            %call super class method to compute mapping between enzymes of
            %this process, and of protein monomers and complexes of the simulation
            this.initializeConstants@edu.stanford.covert.cell.sim.Process(...
                knowledgeBase, varargin{:});
            this.initializeConstants@edu.stanford.covert.cell.sim.ChromosomeProcessAspect(...
                knowledgeBase, varargin{:});

            %whole cell model ids of transcription units
            this.transcriptionUnitWholeCellModelIDs = ...
                {knowledgeBase.transcriptionUnits.wholeCellModelID}';

            %build matrices of transcription factor positions, affinities, activities
            n = length(monomers) + length(complexs);
            this.tuIndexs = zeros(n, 2);
            this.tfIndexs = zeros(n, 2);
            this.tfActivities = zeros(n, 2);
            this.tfPositionStrands = zeros(n*2, 2);
            c = this.chromosome;
            j = 1;
            for i = 1:knowledgeBase.numTranscriptionUnits
                tu = knowledgeBase.transcriptionUnits(i);

                if ~isempty(tu.transcriptionFactorProteinMonomers)
                    [tf, proteinIndexs] = ismember(...
                        [tu.transcriptionFactorProteinMonomers.idx]',...
                        this.enzymeMonomerGlobalIndexs);
                    k = nnz(tf) - 1;
                    this.tuIndexs(j:j+k) = i;

                    proteinIndexs = proteinIndexs(tf);
                    this.tfIndexs(j:j+k) = this.enzymeMonomerLocalIndexs(proteinIndexs);

                    regulationIndexs = ...
                        this.enzymeMonomerCompartmentIndexs(proteinIndexs) == ...
                        [tu.transcriptionFactorProteinMonomerCompartments.idx]';
                    this.tfActivities(j:j+k) = ...
                        tu.transcriptionFactorProteinMonomerActivitys(regulationIndexs);

                    footprints = c.monomerDNAFootprints([tu.transcriptionFactorProteinMonomers.idx]');
                    startCoordinates = tu.transcriptionFactorProteinMonomerBindingSiteStartCoordinates;
                    lengths = tu.transcriptionFactorProteinMonomerBindingSiteLengths;
                    this.tfPositionStrands(j:j+k) = ...
                        mod(ceil(startCoordinates + lengths/2 - footprints/2) - 1, c.sequenceLen) + 1;
                    j = j + 1 + k;
                end

                if ~isempty(tu.transcriptionFactorProteinComplexs)
                    [tf, proteinIndexs] = ismember(...
                        [tu.transcriptionFactorProteinComplexs.idx]',...
                        this.enzymeComplexGlobalIndexs);
                    k = nnz(tf) - 1;
                    this.tuIndexs(j:j+k) = i;

                    proteinIndexs = proteinIndexs(tf);
                    this.tfIndexs(j:j+k) = this.enzymeComplexLocalIndexs(proteinIndexs);

                    regulationIndexs = ...
                        this.enzymeComplexCompartmentIndexs(proteinIndexs) == ...
                        [tu.transcriptionFactorProteinComplexCompartments.idx]';
                    this.tfActivities(j:j+k) = ...
                        tu.transcriptionFactorProteinComplexActivitys(regulationIndexs);

                    footprints = c.complexDNAFootprints([tu.transcriptionFactorProteinComplexs.idx]');
                    startCoordinates = tu.transcriptionFactorProteinComplexBindingSiteStartCoordinates;
                    lengths = tu.transcriptionFactorProteinComplexBindingSiteLengths;
                    this.tfPositionStrands(j:j+k) = ...
                        mod(ceil(startCoordinates + lengths/2 - footprints/2) - 1, c.sequenceLen) + 1;
                    j = j + 1 + k;
                end
            end

            validateattributes(this.tfActivities, {'numeric'}, {'nonnegative'});

            this.tuIndexs(:,2) = this.tuIndexs(:,1);
            this.tfIndexs(:,2) = this.tfIndexs(:,1);
            this.tfActivities(:,2) = this.tfActivities(:,1);

            this.tfPositionStrands(1:n,2) = 1;
            this.tfPositionStrands(n+1:2*n,1) = this.tfPositionStrands(1:n,1);
            this.tfPositionStrands(n+1:2*n,2) = 3;

            this.tfAffinities = this.tfActivities;
            this.tfActivities(this.tfActivities<1 & this.tfAffinities>0) = ...
                this.tfActivities(this.tfActivities<1 & this.tfAffinities>0) .^ -1;

            bind = ~isnan(this.tfPositionStrands(1:n,1));
            this.otherActivities = ones(...
                length(this.transcriptionUnitWholeCellModelIDs), ...
                length(this.enzymeWholeCellModelIDs));
            this.otherActivities(sub2ind(...
                size(this.otherActivities), ...
                this.tuIndexs(~bind), ...
                this.tfIndexs(~bind))) = ...
                this.tfActivities(~bind);
            this.tuIndexs = this.tuIndexs(bind,:);
            this.tfIndexs = this.tfIndexs(bind,:);
            this.tfAffinities = this.tfAffinities(bind,:);
            this.tfActivities = this.tfActivities(bind,:);
            this.tfPositionStrands = this.tfPositionStrands(repmat(bind, [2 1]), :);
        end
    end

    %model
    methods
        %Calculate
        %- contribution to FBA objective
        %- minimum expression consistent with cell cycle length
        function [bmProd, byProd, minEnzExp, maxEnzExp] = calcResourceRequirements_LifeCycle(this, ~, ~)
            %no substrate and byproducts required
            bmProd = zeros(size(this.substrateWholeCellModelIDs));
            byProd = zeros(size(this.substrateWholeCellModelIDs));

            %no enzymes required
            minEnzExp = zeros(size(this.enzymeWholeCellModelIDs));
            maxEnzExp = Inf(size(this.enzymeWholeCellModelIDs));
        end

        %initialization:
        %- Simulation initializeState method initializes 1 undamaged chromosome
        %- Various processes may bind proteins to chromosome
        %- Here we bind transcription factors to the initial chromosome
        function initializeState(this)
            this.evolveState();
        end

        %resource requirements
        function result = calcResourceRequirements_Current(this)
            result = zeros(size(this.substrates));
        end

        %simulation
        function evolveState(this)
            %Bind transcription factors to accessible sites
            tfBoundPromoters = this.bindTranscriptionFactors();

            %Compute new fold changes
            this.rnaPolymerase.transcriptionFactorBindingProbFoldChange = ...
                this.calcBindingProbabilityFoldChange(tfBoundPromoters);
        end

        %Bind transcription factors to accessible sites.
        function tfBoundPromoters = bindTranscriptionFactors(this)
            %Estimate which binding sites exist and are accessible to the
            %transcription unit. (Note chromosome class which ensure that sites
            %are truly accessible, and only allow transcription factors to bind
            %to these sites).
            tfBoundPromoters = this.tfBoundPromoters;
            accessible = ~tfBoundPromoters & reshape(this.chromosome.isRegionPolymerized(this.tfPositionStrands, 1, false), [], 2);

            for i = 1:size(this.enzymes, 1)
                %For each transcription factor:
                %  Stochastically pick transcription unit promoters elements to
                %  bind from among free promoter elements weighted by the
                %  transcription factors' affinities for the promoters. (Note:
                %  each transcription factor is assumed to bind a distinct
                %  promoter region near each transcription unit such that
                %  transcription factors only compete within their species
                %  for binding to promoters.)

                if this.enzymes(i) <= 0
                    continue;
                end

                sites = find((this.tfIndexs == i) & accessible);
                if isempty(sites)
                    continue;
                end

                tf = this.bindProteinToChromosome(...
                    this.tfPositionStrands(sites, :), i, this.enzymes(i), this.tfAffinities(sites), [], false, 1, false, []);

                tfBoundPromoters(sites(tf)) = true;
            end
        end

        function foldChange = calcBindingProbabilityFoldChange(this, tfBoundPromoters)
            %reset fold change
            foldChange = ones(size(this.chromosome.transcriptionUnitStartCoordinates, 1), 2);

            %fold change effect of bound TFs
            for i = 1:size(this.tfIndexs, 1)
                for j = 1:size(this.tfIndexs, 2)
                    if ~tfBoundPromoters(i, j)
                        continue;
                    end

                    foldChange(this.tuIndexs(i, j), j) = foldChange(this.tuIndexs(i, j), j) * this.tfActivities(i, j);
                end
            end

            otherFoldChanges = prod(this.otherActivities .^ (this.enzymes(:, ones(1, size(foldChange, 1)))' > 0), 2);
            foldChange = foldChange .* [otherFoldChanges otherFoldChanges];
        end
    end

    %get methods of dependent local state
    methods
        function result = get.tfBoundPromoters(this)
            result = reshape(this.isDnaBound(this.tfPositionStrands, this.tfIndexs(:)), [], 2);
        end

        function result = get.boundTFs(this)
            result = histc(this.tfIndexs(logical(this.tfBoundPromoters(:,1))), 1:numel(this.enzymes));
        end
    end
end