%tRNA Aminoacylation
%
% @wholeCellModelID Process_tRNAAminoacylation
% @name             tRNA Aminoacylation
% @description
%   Biology
%   ===============
%   In biological systems tRNAs serve as mediators between the ribosome and
%   the amino acids which forms peptide polymers. In this process we
%   simulate the conjugation of amino acids to the tRNAs which deliver them
%   to the ribosome. Additionally we simulate the aminoacylation of the
%   tmRNA which similarly delivers the amino acid alanine to stalled
%   ribosomes.
%
%   Knowledge Base
%   ===============
%   As of 8/11/2010 the M. genitalium knowledge base contains 39 tRNA
%   aminoacylation reactions involving
%   - 37 aminoacylation reactions, each assuming a cost of 1 ATP per
%     aminoacylation
%   - 2 transfer reactions
%   - 36 tRNAs and 1 tmRNA
%   - 21 enzymes
%   - 20 amino acid substrates and 10 additional substrates
%
%   The reaction kinetics stored in the knowledge base were compiled
%   several sources including SABIO-RK [PUB_0100].
%
%   Note: Unlike knowledge base representation, for transfer reactions
%   reactionStoichiometryMatrix doesn't include the already conjugated
%   amino acid on the left-hand-side or the the ultimate conjugated amino
%   acid on the right-hand-side.
%
%   Representation
%   ===============
%   substrates represents the counts of free metabolites available for tRNA
%   aminoacylation. enzymes represents the counts of proteins available to
%   catalyze tRNA aminoacylation reactions. freeRNAs and aminoacylatedRNAs
%   represent the counts of free, unaminoacylated and aminocylated RNAs. We do
%   not represent intermediate states in the aminoacylation of tRNAs.  The
%   molecular weights of the unaminoacylated and aminocylated tRNAs are computed
%   by the knowledge base RNA classes.
%
%   reactionStoichiometryMatrix, reactionModificationMatrix,
%   reactionCatalysisMatrix, and enzymeBounds represent the tRNA aminoacylation
%   reactions. reactionStoichiometryMatrix represents the free metabolites
%   required for  each reaction. reactionModificationMatrix represents the tRNA
%   aminoacylated by each reaction. reactionCatalysis represents the enzyme
%   required to aminoacylate each tRNA. enzymeBounds represents the kcat of the
%   catalyzing enzyme of each reaction. Note: reactionStoichiometryMatrix here
%   is slightly modified from that computed by the super class: amino acids
%   conjugated to tRNAs before and after transfer reactions have been removed
%   from the left- and righ-hand-side of reactionStoichiometryMatrix.
%
%   Initialization
%   ===============
%   tRNAs are all initialized to the aminoacylated state.
%
%   Simulation
%   ===============
%   Uses greedy algorithm to simulate tRNA aminacylation (complexation of
%   tRNAs with the specific amino acids that aminoacylate them).
%   1. Deterministically activate tRNAs up to the minimum of free tRNAs and
%      amino acids, proportional to free tRNAs
%   2. Stoichastically activate residual tRNAs using residual amino acids
%      with probabilities proportional to remaining free tRNAs
%
%      While(true)
%        1. Calculate numbers of protein monomers that can be modified based on
%           substrate, enzyme, and unmodified protein monomer availability and
%           kinetics.
%        2. Randomly select protein monomer to modify weighted by limits
%           calculated in step (1).
%        3. Update substrates, enzymes, unmodified protein monomers
%        4. Repeat until insufficient resources to further modify protein
%           monomers
%      End
%
% Author: Markus Covert, mcovert@stanford.edu
% Author: Jayodita Sanghvi, jayodita@stanfod.edu
% Author: Jonathan Karr, jkarr@stanford.edu
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 8/11/2010

classdef tRNAAminoacylation < edu.stanford.covert.cell.sim.ReactionProcess
    %property annotations
    properties (Constant)
        optionNames__              = {}; %names of option properties
		fixedConstantNames__       = {   %names of fixed constant properties
			'monomerTRNACounts';
			};
        fittedConstantNames__      = {}; %names of fitted constant properties
        localStateNames__          = {   %names of simulation state properties redundant with timecourses in this or other processes or the simulation
            'freeRNAs';
            'aminoacylatedRNAs'};
    end

    %IDs, names, and local indices
    properties
        substrateIndexs_aminoAcids                   %indices within substrates of amino acids
        substrateIndexs_glutamate                    %index within substrates of glutamate
        substrateIndexs_glutamine                    %index within substrates of glutamine
        substrateIndexs_methionine                   %index within substrates of methionine
        substrateIndexs_fmethionine                  %index within substrates of formylmethionine
        substrateIndexs_atp                          %index within substrates of ATP
        substrateIndexs_amp                          %index within substrates of AMP
        substrateIndexs_adp                          %index within substrates of ADP
        substrateIndexs_diphosphate                  %index within substrates of diphosphate
        substrateIndexs_phosphate                    %index within substrates of inorganic phosphate
        substrateIndexs_water                        %index within substrates of water
        substrateIndexs_hydrogen                     %index within substrates of hydrogen
        substrateIndexs_fthf10                       %index within substrates of 10-forymltetrahydrofolate
        substrateIndexs_thf                          %index within substrates of tetrahydrofolate

        enzymeIndexs_tRNASynthetases                 %indices within enzymes of tRNA synthetases
        enzymeIndexs_tRNATransferases                %indices within enzymes of tRNA transferases
        enzymeIndexs_tRNAGlutamylSynthetase          %index within enzymes of tRNA glutamyl synthetase
        enzymeIndexs_tRNAMethionylSynthetase         %index within enzymes of tRNA methionyl synthetase
        enzymeIndexs_tRNAGlutamylAmidotransferase    %index within enzymes of tRNA glutamyl amidotransferase
        enzymeIndexs_tRNAMethionylFormyltransferase  %index within enzymes of tRNA methionyl formyltransferase
        enzymeIndexs_tRNAs                           %index within enzymes of tRNAs
        enzymeIndexs_tmRNA                           %index within enzymes of tmRNA

        reactionIndexs_aminoacylation                %indices within reactions of tRNA aminoacylation reactions
        reactionIndexs_transfer                      %indices within reactions of tRNA transfer reactions
        reactionIndexs_glutamylamidotransfer         %index within reactions of tRNA glutamylamidotransfer reaction
        reactionIndexs_methionylformyltransfer       %index within reactions of tRNA methionylformyltransfer reaction

        compartmentWholeCellModelIDs                 %whole cell model ids of compartments
        compartmentIndexs_cytosol                    %index within compartments of cytosol

        freeRNAWholeCellModelIDs                     %whole cell model ids of free RNAs
        aminoacylatedRNAWholeCellModelIDs            %whole cell model ids of aminoacylated RNAs

        aminoacylatedRNAGlobalIndexs                 %indices of aminoacylated RNAs within simulation.matureRNAIndexs
        aminoacylatedRNAtRNAIndexs                   %indices of aminoacylated tRNAs within aminoacylatedRNAGlobalIndexs

        speciesIndexs_enzymes                        %indexs of enzyme species within speciesReactantByproductMatrix, speciesReactantMatrix
    end

    %fixed biological constants
    properties
        monomerTRNACounts              %numbers of each tRNA species required to translate each protein monomer species

        speciesReactantByproductMatrix %stoichiometry of susbtrates, enzymes, RNA in modifications necessary to mature each modifying RNA: [RNA modifications] X [subtrates; enzymes; free RNAs]
        speciesReactantMatrix          %reactant stoichiometry of susbtrates, enzymes, RNA in modifications necessary to mature each modifying RNA: [RNA modifications] X [subtrates; enzymes; free RNAs]
    end

    %global state (stored locally for convenience)
    properties
        freeRNAs          %counts of free RNAs
        aminoacylatedRNAs %counts of aminoacylated RNAs
    end

    %dependent global state (implemented as dependent property for
    %convenience)
    properties (Dependent)
        tRNASynthetases   %counts of tRNA synthetases
        tRNATransferases  %counts of tRNA transferases
    end

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

    %communication between process/simulation
    methods
        %initialize constants
        function initializeConstants(this, knowledgeBase, simulation, varargin)
            %import classes
            import edu.stanford.covert.util.ComputationUtil;

            %call super class method to get knowledge base reactions
            this.initializeConstants@edu.stanford.covert.cell.sim.ReactionProcess(knowledgeBase, simulation, varargin{:});

            %compartments
            this.compartmentWholeCellModelIDs = this.compartment.wholeCellModelIDs(this.compartment.cytosolIndexs);
            this.compartmentIndexs_cytosol    = 1;

            %create representation for GLN which super class leaves off
            %because it cancels out in the knowledge base
            this.substrateWholeCellModelIDs  = [this.substrateWholeCellModelIDs; 'GLN'];
            this.reactionStoichiometryMatrix = [this.reactionStoichiometryMatrix; zeros(1, size(this.reactionStoichiometryMatrix, 2))];
            this.reactionCoenzymeMatrix      = [this.reactionCoenzymeMatrix zeros(size(this.reactionCoenzymeMatrix, 1), 1)];

            %substrate indices
            this.substrateIndexs_aminoAcids  = this.substrateIndexs({'ALA'; 'ARG'; 'ASN'; 'ASP'; 'CYS'; 'GLN'; 'GLU'; 'GLY'; 'HIS'; 'ILE'; 'LEU'; 'LYS'; 'MET'; 'PHE'; 'PRO'; 'SER'; 'THR'; 'TRP'; 'TYR'; 'VAL'});
            this.substrateIndexs_glutamate   = this.substrateIndexs({'GLU'});
            this.substrateIndexs_glutamine   = this.substrateIndexs({'GLN'});
            this.substrateIndexs_methionine  = this.substrateIndexs({'MET'});
            this.substrateIndexs_fmethionine = this.substrateIndexs({'FMET'});
            this.substrateIndexs_atp         = this.substrateIndexs({'ATP'});
            this.substrateIndexs_amp         = this.substrateIndexs({'AMP'});
            this.substrateIndexs_adp         = this.substrateIndexs({'ADP'});
            this.substrateIndexs_diphosphate = this.substrateIndexs({'PPI'});
            this.substrateIndexs_phosphate   = this.substrateIndexs({'PI'});
            this.substrateIndexs_water       = this.substrateIndexs({'H2O'});
            this.substrateIndexs_hydrogen    = this.substrateIndexs({'H'});
            this.substrateIndexs_fthf10      = this.substrateIndexs({'FTHF10'});
            this.substrateIndexs_thf         = this.substrateIndexs({'THF'});

            %include tRNAs as enzymes
            this.enzymeWholeCellModelIDs = [
                this.enzymeWholeCellModelIDs;
                this.rna.wholeCellModelIDs(this.rna.matureIndexs(this.rna.matureTRNAIndexs));
                'MG_0004'];

            %enzyme indices
            this.enzymeIndexs_tRNASynthetases = this.enzymeIndexs({
                'MG_292_TETRAMER';       %alanyl-tRNA synthetase
                'MG_378_MONOMER';        %arginyl-tRNA synthetase
                'MG_036_DIMER';          %aspartyl-tRNA synthetase
                'MG_113_DIMER';          %asparaginyl-tRNA synthetase
                'MG_253_MONOMER';        %cysteinyl-tRNA synthetase
                'MG_462_MONOMER';        %glutamyl-tRNA synthetase
                'MG_251_DIMER';          %glycyl-tRNA synthetase
                'MG_035_DIMER';          %histidyl-tRNA synthetase
                'MG_345_MONOMER';        %isoleucyl-tRNA synthetase
                'MG_266_MONOMER';        %leucyl-tRNA synthetase
                'MG_136_DIMER';          %lysyl-tRNA synthetase
                'MG_021_DIMER';          %methionyl-tRNA synthetase
                'MG_194_195_TETRAMER';   %phenylalanyl-tRNA synthetase
                'MG_283_DIMER';          %prolyl-tRNA synthetase
                'MG_005_DIMER';          %seryl-tRNA synthetase
                'MG_375_DIMER';          %threonyl-tRNA synthetase
                'MG_126_DIMER';          %tryptophanyl-tRNA synthetase
                'MG_455_DIMER';          %tyrosyl-tRNA synthetase
                'MG_334_MONOMER'});      %valyl-tRNA synthetase
            this.enzymeIndexs_tRNATransferases = this.enzymeIndexs({
                'MG_098_099_100_TRIMER'; %glutamyl-tRNA(Gln) and/or aspartyl-tRNA(Asn) amidotransferase
                'MG_365_MONOMER'});      %methionyl-tRNA formyltransferase
            this.enzymeIndexs_tRNAGlutamylSynthetase         = this.enzymeIndexs({'MG_462_MONOMER'});        %glutamyl-tRNA synthetase
            this.enzymeIndexs_tRNAMethionylSynthetase        = this.enzymeIndexs({'MG_021_DIMER'});          %methionyl-tRNA synthetase
            this.enzymeIndexs_tRNAGlutamylAmidotransferase   = this.enzymeIndexs({'MG_098_099_100_TRIMER'}); %glutamyl-tRNA(Gln) and/or aspartyl-tRNA(Asn) amidotransferase
            this.enzymeIndexs_tRNAMethionylFormyltransferase = this.enzymeIndexs({'MG_365_MONOMER'});        %methionyl-tRNA formyltransferase
            this.enzymeIndexs_tRNAs                          = this.enzymeIndexs(this.rna.wholeCellModelIDs(this.rna.matureIndexs(this.rna.matureTRNAIndexs)));
            this.enzymeIndexs_tmRNA                          = this.enzymeIndexs({'MG_0004'});

            %RNAs
            this.reactionModificationMatrix(isnan(this.reactionModificationMatrix)) = 1;

            aaGenes = any(this.reactionModificationMatrix, 1)';
            aaRNAs  = ComputationUtil.invertCompositionMatrix(this.rna.matureRNAGeneComposition) * aaGenes;
            this.aminoacylatedRNAGlobalIndexs  = find(aaRNAs);
            this.aminoacylatedRNAtRNAIndexs    = intersect(this.aminoacylatedRNAGlobalIndexs, this.rna.matureTRNAIndexs);

            this.freeRNAWholeCellModelIDs          = this.rna.wholeCellModelIDs(this.rna.matureIndexs(this.aminoacylatedRNAGlobalIndexs));
            this.aminoacylatedRNAWholeCellModelIDs = this.rna.wholeCellModelIDs(this.rna.aminoacylatedIndexs(this.aminoacylatedRNAGlobalIndexs));

            this.reactionModificationMatrix = this.reactionModificationMatrix(:, aaGenes);

            %reaction indices
            this.reactionIndexs_aminoacylation          = find(strcmp(this.reactionTypes, 'aminoacylation'));
            this.reactionIndexs_transfer                = find(strcmp(this.reactionTypes, 'transfer'));
            this.reactionIndexs_glutamylamidotransfer   = this.reactionIndexs({'MG502_Amidotransferase'});
            this.reactionIndexs_methionylformyltransfer = this.reactionIndexs({'MG488_Formyltransferase'});

            %for transfer reactions unlike knowledge base representation,
            %formulate reactionStoichiometryMatrix so that left-hand-side
            %doesn't include the already conjugated amino acid, and the
            %right-hand-side doesn't include the ultimate conjugated amino
            %acid
            this.reactionStoichiometryMatrix(this.substrateIndexs_glutamine,   this.reactionIndexs_glutamylamidotransfer)   = -1;
            this.reactionStoichiometryMatrix(this.substrateIndexs_glutamate,   this.reactionIndexs_glutamylamidotransfer)   =  1;
            this.reactionStoichiometryMatrix(this.substrateIndexs_methionine,  this.reactionIndexs_methionylformyltransfer) =  0;
            this.reactionStoichiometryMatrix(this.substrateIndexs_fmethionine, this.reactionIndexs_methionylformyltransfer) =  0;

            %re-run super class method to update substrate and enzyme
            %mappings to include GLN, the tRNAs, and the tmRNA
            this.initializeConstants@edu.stanford.covert.cell.sim.Process(knowledgeBase, simulation, varargin{:});

            this.initializeSpeciesNetwork();

            %protein monomers -- needed for fitting
            this.monomerTRNACounts = knowledgeBase.proteinMonomerTRNACounts;
        end

        %initialize reaction stoichiomety to be used in evolveState:
        %  [tRNA aminoacylations and transfers] X [subtrates; enzymes; free tRNAs]
        function initializeSpeciesNetwork(this)
            % formulate network: [tRNA aminoacylations and transfers] X [subtrates; enzymes; free tRNAs]
            numMetabolites = size(this.reactionStoichiometryMatrix, 1);
            numEnzymes     = size(this.reactionCatalysisMatrix, 2);
            numRNAs        = size(this.reactionModificationMatrix, 2);

            this.speciesReactantByproductMatrix = [this.reactionModificationMatrix' * [ ...
                -this.reactionStoichiometryMatrix' ...
                this.reactionCatalysisMatrix ./ repmat(this.enzymeBounds(:, 2) * this.stepSizeSec, 1, numEnzymes)] ...
                eye(numRNAs)];
            this.speciesReactantMatrix = [this.reactionModificationMatrix' * max(0, [ ...
                -this.reactionStoichiometryMatrix' ...
                this.reactionCatalysisMatrix ./ repmat(this.enzymeBounds(:, 2) * this.stepSizeSec, 1, numEnzymes)]) ...
                eye(numRNAs)];

            this.speciesIndexs_enzymes = (1:numEnzymes)' + numMetabolites;
        end

        %retrieve state from simulation
        function copyFromState(this)
            this.copyFromState@edu.stanford.covert.cell.sim.ReactionProcess();

            this.freeRNAs          = this.rna.counts(this.rna.matureIndexs(       this.aminoacylatedRNAGlobalIndexs), this.compartment.cytosolIndexs, :);
            this.aminoacylatedRNAs = this.rna.counts(this.rna.aminoacylatedIndexs(this.aminoacylatedRNAGlobalIndexs), this.compartment.cytosolIndexs, :);
        end

        %send state to simulation
        function copyToState(this)
            this.copyToState@edu.stanford.covert.cell.sim.ReactionProcess();

            this.rna.counts(this.rna.matureIndexs(       this.aminoacylatedRNAGlobalIndexs), this.compartment.cytosolIndexs, :) = this.freeRNAs;
            this.rna.counts(this.rna.aminoacylatedIndexs(this.aminoacylatedRNAGlobalIndexs), this.compartment.cytosolIndexs, :) = this.aminoacylatedRNAs;
        end
    end

    %allocate memory for state
    methods
        function allocateMemoryForState(this, numTimePoints)
            this.allocateMemoryForState@edu.stanford.covert.cell.sim.ReactionProcess(numTimePoints);

            this.freeRNAs          = zeros(length(this.aminoacylatedRNAGlobalIndexs), 1, numTimePoints);
            this.aminoacylatedRNAs = zeros(length(this.aminoacylatedRNAGlobalIndexs), 1, numTimePoints);
        end
    end

    %model
    methods
        %Calculate
        %- contribution to FBA objective
        %- minimum expression consistent with cell cycle length
        function [bmProd, byProd, minEnzExp, maxEnzExp] = calcResourceRequirements_LifeCycle(this, ~, states)
            %% substrate and byproducts
            %aminoacylations of each t(m)RNA
            [~, idxs] = ismember(this.aminoacylatedRNAtRNAIndexs, this.aminoacylatedRNAGlobalIndexs);
            aminoacylations = zeros(size(this.aminoacylatedRNAGlobalIndexs));
            aminoacylations(idxs) = aminoacylations(idxs) + ...
                this.monomerTRNACounts' * states.monomerProductions;

            %t(m)RNA aminoacylation reactions
            reactions = this.reactionModificationMatrix * aminoacylations;

            %metabolic load of t(m)RNA aminoacylation reactions
            bmProd = max(0, -this.reactionStoichiometryMatrix) * reactions;
            byProd = max(0,  this.reactionStoichiometryMatrix) * reactions;

            %% enzymes
            %initialize
            minEnzExp = zeros(size(this.enzymeWholeCellModelIDs));
            maxEnzExp = Inf(size(this.enzymeWholeCellModelIDs));

            %tRNAs
            minEnzExp(this.enzymeIndexs_tRNAs) = 1.95 * this.monomerTRNACounts' * states.monomerProductions0;

            %tmRNA
            minEnzExp(this.enzymeIndexs_tmRNA) = 0;

            %tRNA synthetases, transferases
            [~, idxs] = ismember(this.aminoacylatedRNAtRNAIndexs, this.aminoacylatedRNAGlobalIndexs);
            aminoacylations = states.rnaProductions0(this.aminoacylatedRNAGlobalIndexs);
            aminoacylations(idxs)                = max(aminoacylations(idxs),                minEnzExp(this.enzymeIndexs_tRNAs));
            aminoacylations(setdiff(1:end,idxs)) = max(aminoacylations(setdiff(1:end,idxs)), minEnzExp(this.enzymeIndexs_tmRNA));

            minEnzExp(setdiff(1:end, [this.enzymeIndexs_tRNAs; this.enzymeIndexs_tmRNA])) = ...
                + minEnzExp(setdiff(1:end, [this.enzymeIndexs_tRNAs; this.enzymeIndexs_tmRNA])) ...
                + 3 * this.reactionCatalysisMatrix' * ((this.reactionModificationMatrix * aminoacylations) ./ this.enzymeBounds(:, 2));
        end

        %initialization: RNAs initialized to mature state by simulation
        %initializeState method. This class initializes RNAs to
        %aminoacylated state.
        function initializeState(this)
            totalRNAs = this.aminoacylatedRNAs + this.freeRNAs;
            this.aminoacylatedRNAs = ceil(2 / 3 * totalRNAs);
            this.freeRNAs = totalRNAs - this.aminoacylatedRNAs;
        end

        %resource requirements
        function result = calcResourceRequirements_Current(this)
            result = max(0, -this.reactionStoichiometryMatrix) * min(...
                this.reactionModificationMatrix * (this.freeRNAs + this.aminoacylatedRNAs + 1), ...
                this.reactionCatalysisMatrix * this.enzymes(1:numel(this.speciesIndexs_enzymes)) * this.stepSizeSec);
        end

        %simulation
        function evolveState(this)
            %terminate early if no free RNAs
            if ~any(this.freeRNAs)
                return;
            end

            numRNAs = numel(this.freeRNAs);

            species = [
                this.substrates;
                this.enzymes(1:numel(this.speciesIndexs_enzymes));
                this.freeRNAs]';

            reactionFluxes = zeros(size(this.speciesReactantByproductMatrix, 1), 1);
            anyFlux = false;

            limits = species(ones(numRNAs, 1), :) ./ this.speciesReactantMatrix;
            limits(:, this.substrateIndexs_water) = NaN;
            limits(:, this.substrateIndexs_hydrogen) = NaN;
            reactionLimits = min(limits, [], 2)';
            reactionLimits(isinf(reactionLimits) | isnan(reactionLimits) | reactionLimits < 0) = 0;
            isReactionInactive = reactionLimits <= 0;

            while true
                %compute maximum number of each species that can be modified
                limits = species(ones(numRNAs, 1), :) ./ max(0, this.speciesReactantByproductMatrix);
                limits(:, this.substrateIndexs_water) = NaN;
                limits(:, this.substrateIndexs_hydrogen) = NaN;
                reactionLimits = min(...
                    this.randStream.stochasticRound(min(limits(:, this.speciesIndexs_enzymes), [], 2)), ...
                    min(limits(:, [1:this.speciesIndexs_enzymes(1)-1  this.speciesIndexs_enzymes(end)+1:end]), [], 2))';
                reactionLimits(isReactionInactive | isinf(reactionLimits) | isnan(reactionLimits) | reactionLimits < 1) = 0;

                %stop if no more tRNA can be aminoacylated
                if ~any(reactionLimits)
                    break;
                end
                anyFlux = true;

                edges = min([0 cumsum(reactionLimits(:)' / sum(reactionLimits))], 1);
                nRxns = min(reactionLimits(reactionLimits > 0));
                if nRxns <= 1
                    %pick substrate, reaction, enzymes
                    [~, selectedReaction] = histc(rand(this.randStream, 1, 1), edges);
                    reactionFluxes(selectedReaction) = ...
                        + reactionFluxes(selectedReaction) ...
                        + 1;

                    %decrement metabolites, enzymes, unmodified substrates; increment modified substrates
                    species = species - this.speciesReactantByproductMatrix(selectedReaction, :);
                else
                    %pick substrate, reaction, enzymes
                    edges(end) = 1.1;
                    selectedReactions = histc(rand(this.randStream, nRxns, 1), edges);
                    selectedReactions = selectedReactions(1:end-1);
                    selectedReactions = selectedReactions(:);
                    reactionFluxes = ...
                        + reactionFluxes ...
                        + selectedReactions;

                    %decrement metabolites, enzymes, unmodified substrates; increment modified substrates
                    species = species - selectedReactions' * this.speciesReactantByproductMatrix;
                end
            end

            %stop if no reaction can proceed
            if ~anyFlux
                return;
            end

            % update substrates
            this.substrates = this.substrates + ...
                this.reactionStoichiometryMatrix * this.reactionModificationMatrix * reactionFluxes;

            % update RNAs
            this.freeRNAs          = this.freeRNAs          - reactionFluxes;
            this.aminoacylatedRNAs = this.aminoacylatedRNAs + reactionFluxes;
        end
    end

    %get methods of dependent global state
    methods
        function value = get.tRNASynthetases(this)
            value = this.enzymes(this.enzymeIndexs_tRNASynthetases,:,:);
        end

        function value = get.tRNATransferases(this)
            value = this.enzymes(this.enzymeIndexs_tRNATransferases,:,:);
        end
    end

    %get methods of dependent local state
    methods
        function value = getDryWeight(this)
            if size(this.freeRNAs, 3) == 1
                value = this.getDryWeight@edu.stanford.covert.cell.sim.ReactionProcess() + (...
                    this.rna.molecularWeights(this.rna.matureIndexs(this.aminoacylatedRNAGlobalIndexs))'        * this.freeRNAs         + ...
                    this.rna.molecularWeights(this.rna.aminoacylatedIndexs(this.aminoacylatedRNAGlobalIndexs))' * this.aminoacylatedRNAs) ...
                    / edu.stanford.covert.util.ConstantUtil.nAvogadro;
            else
                value = this.getDryWeight@edu.stanford.covert.cell.sim.ReactionProcess() + (...
                    permute(this.rna.molecularWeights(this.rna.matureIndexs(this.aminoacylatedRNAGlobalIndexs))'        * permute(this.freeRNAs,         [1 3 2]),[1 3 2]) + ...
                    permute(this.rna.molecularWeights(this.rna.aminoacylatedIndexs(this.aminoacylatedRNAGlobalIndexs))' * permute(this.aminoacylatedRNAs,[1 3 2]),[1 3 2])) ...
                    / edu.stanford.covert.util.ConstantUtil.nAvogadro;
            end
        end
    end
end


function out = gtdata2mhaconfig(gtdata)
  % This function writes the gain table data into the correct layout to be fed
  % to the mhaconfig file.
  %
  % Variables:
  % - gtdata
  %
  % Returns:
  % - out

  out = '[';
  for i=1:size(gtdata, 1)
    out = [out '[' sprintf('%.1f ',gtdata(i,:)) '];'];
  end
  out = [out ']'];
end


function status = besa_save2SurfacePoint(file_path, file_name, ...
    channel_labels, channel_coords, fiducial_labels, fiducial_coords, ...
    headsurfacepoint_coords)
% besa_save2SurfacePoint writes the channel and fiducial coordinates into
% the file 'file_name'.
%
% Parameters:
%     [file_path]
%         Full path to the folder where the file should be saved.
%
%     [file_name]
%         The name of the file where the output should be written.
%
%     [channel_labels]
%         A cell (size: number of channels x 1) containing the channel
%         labels for the current data.
%
%     [channel_types]
%         An array (size: number of channels x 3) containing the channel
%         coordinates for the current data.
%
%     [fiducial_labels]
%         A cell (size: number of fiducials x 1) containing the fiducial
%         labels for the current data.
%
%     [fiducial_coords]
%         An array (size: number of fiducials x 3) containing the fiducials
%         coordinates for the current data.
%
%     [headsurfacepoint_coords]
%         Optional. An array (size: number of fiducials x 3) containing
%         additional head surface point coordinates for the current data.
%         Labels for head surface points will be created automatically,
%         starting with Sfh1.
%
% Return:
%     [status]
%         The status of the writing process. Still unused.
%

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

n_FiducialLabels = size(fiducial_labels, 1);
n_FiducialCoords = size(fiducial_coords, 1);
n_ChannelLabels = size(channel_labels, 1);
n_ChannelCoords = size(channel_coords, 1);

% Get number of channels and fiducials
n_NumFiducials = n_FiducialLabels;
if n_FiducialLabels ~= n_FiducialCoords
    n_NumFiducials = min(n_FiducialLabels, n_FiducialCoords);
end;
n_NumChannels = n_ChannelLabels;
if n_ChannelLabels ~= n_ChannelCoords
    n_NumChannels = min(n_ChannelLabels, n_ChannelCoords);
end;

if exist('headsurfacepoint_coords', 'var');
    n_HSPCoords = size(headsurfacepoint_coords, 1);
else
    n_HSPCoords = 0;
end;

% Change to the directory where the data should be saved.
cd(file_path)

% Open file for writing.
fid = fopen(file_name, 'w');

% Write the fiducial labels and the coordinates to the file.
for i=1:n_NumFiducials
    fprintf(fid, '%s\t\t\t%2.6f\t%2.6f\t%2.6f\n', fiducial_labels{i}, ...
        fiducial_coords(i,1), fiducial_coords(i,2), fiducial_coords(i,3));
end;

% Write the channel labels and the coordinates to the file.
for i=1:n_NumChannels
    curChannelLabel = strrep(channel_labels{i}, 'Ele_', '');
    fprintf(fid, '%s\t\t\t%2.6f\t%2.6f\t%2.6f\n', curChannelLabel, ...
        channel_coords(i,1), channel_coords(i,2), channel_coords(i,3));
end

% Write additional head surface point labels and coordinates to the file.
for i=1:n_HSPCoords
    curHSPLabel = strcat('Sfh', num2str(i));
    fprintf(fid, '%s\t\t\t%2.6f\t%2.6f\t%2.6f\n', curHSPLabel, ...
        headsurfacepoint_coords(i,1), headsurfacepoint_coords(i,2), ...
        headsurfacepoint_coords(i,3));
end;

fclose(fid);
status = 1;


function obj1 = plus(obj1,obj2)

for i = 1:length(obj1.u)
  obj1.u{i} = obj1.u{i} + obj2.u{i};
end

end


function cortex = read_nimh_cortex(filename, varargin)

% READ_NIMH_CORTEX
%
% Use as
%  cortex = read_nimh_cortex(filename, ...)
%
% Optional input arguments should come in key-value pairs and may
% include
%   begtrial     = number (default = 1)
%   endtrial     = number (default = inf)
%   epp          = read the EPP data, 'yes' or 'no' (default = 'yes')
%   eog          = read the EOG data, 'yes' or 'no' (default = 'yes')
%   feedback     = display the progress on the screen, 'yes' or 'no' (default = 'no')
%
% The output is a structure array with one structure for every trial that was read.

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

% get the optional input arguments
feedback = keyval('feedback', varargin); if isempty(feedback), feedback = 'no'; end
begtrial = keyval('begtrial', varargin); if isempty(begtrial), begtrial = 1;   end
endtrial = keyval('endtrial', varargin); if isempty(endtrial), endtrial = inf; end
% reading the epp and eog data is optional
epp = keyval('epp', varargin); if isempty(epp), epp = 'yes'; end
eog = keyval('eog', varargin); if isempty(eog), eog = 'yes'; end

skipepp   = strcmp(epp, 'no');
skipeog   = strcmp(eog, 'no');
clear epp eog

% this will hold the result
cortex  = struct;

% trials are counted with zero-offset
trial = 0;

i_fid = fopen(filename, 'rb', 'ieee-le');

% read until the end of file or until the specified trial number
while ( ~feof (i_fid) && trial<endtrial )

  % the header of all trials must be read, otherwise the size of the subsequent trial data is not known
  % the header of uninteresting trials is not added to the output
  % the epp and eog data may be skipped completely
  skiptrial = (trial+1)<begtrial;

  length = fread(i_fid, 1, 'ushort');

  % allocate space for the header details
  hd = zeros(1,13);

  if ~isempty(length)

    % give some feedback on the screen
    if feedback && (trial+1)>=begtrial
      fprintf('reading trial %d\n', trial+1);
    end

    % Number of bytes for each variable.
    hd(1,1:8)= (fread(i_fid, 8, 'ushort'))';

    % Convert bytes to number of float point values (4 bytes apiece in windows and FreeBSD).
    hd(1,5)=hd(1,5)/4;
    % Convert bytes to number of short values (2 bytes apiece in windows and FreeBSD).
    hd(1,6)=hd(1,6)/2;
    hd(1,7)=hd(1,7)/2;
    hd(1,8)=hd(1,8)/2;

    header.cond_no =   hd(1,1);
    header.repeat_no = hd(1,2);
    header.block_no =  hd(1,3);
    header.trial_no =  hd(1,4);
    header.isi_size =  hd(1,5);
    header.code_size = hd(1,6);
    header.eog_size =  hd(1,7);
    header.epp_size =  hd(1,8);

    hd(1,9:10) = (fread(i_fid, 2, 'uchar'))';

    header.kHz_resolution   = hd(1,9);
    header.eye_storage_rate = hd(1,10);

    hd(1,11:13) = (fread(i_fid, 3, 'ushort'))';

    header.expected_response = hd(1,11);
    header.response          = hd(1,12);
    header.response_error    = hd(1,13);

    if skiptrial
      fseek(i_fid,header.isi_size,  'cof');
      fseek (i_fid,header.code_size, 'cof');
    else
      time  = (fread (i_fid,header.isi_size,  'ulong'));
      event = (fread (i_fid,header.code_size, 'ushort'));
    end % if skiptrial

    if skipepp || skiptrial
      epp1 = [];
      epp2 = [];
      fseek (i_fid,header.epp_size * 2, 'cof');

    else
      epp = fread (i_fid,header.epp_size, 'short');
      epp1 = zeros(1,header.epp_size/2);
      epp2 = zeros(1,header.epp_size/2);

      % fprintf(o_fid, '\nepp(x)\tepp(y)\n\n');

      for i = 1:2:header.epp_size
        % Must extract the data from the raw epp values.
        % The epp value is made up of 12-bits of data, and 4-bits (the
        % low-order 4 bits) of the channel number.
        % To extract the data, must right shift the raw data by 4 bits (to
        % get rid of the channel number and put the data value in the proper
        % location).  After this conversion, you must still add or subtract
        % 2048, since otherwise the value is not right.  (I think that this
        % is because of the way that matlab handles negative values during
        % the bitwise operations.)
        % These calculations cause the results of ctx2txt.m to be the same as
        % for cortview.exe for the EOG and EPP values.

        s = (i+1)/2;
        epp1(s) = bitshift(epp(i),   -4);
        epp2(s) = bitshift(epp(i+1), -4);

        if (epp1(s) < 0)
          epp1(s) = epp1(s) + 2047;
        else
          epp1(s) = epp1(s) - 2048;
        end;

        if (epp2(s) < 0)
          epp2(s) = epp2(s) + 2047;
        else
          epp2(s) = epp2(s) - 2048;
        end;
      end;
    end; % if skipepp

    if skipeog || skiptrial
      eog = [];
      fseek (i_fid,header.eog_size * 2, 'cof');
    else
      eog = fread (i_fid,header.eog_size, 'short');
      eog = reshape(eog, 2, header.eog_size/2);
    end % if skipeog

    if ~skiptrial
      % collect the headers of all trials in a structure array
      cortex(trial+1).header = header;
      cortex(trial+1).length = length;
      cortex(trial+1).time   = time;
      cortex(trial+1).event  = event;
      cortex(trial+1).epp    = [epp1; epp2];
      cortex(trial+1).eog    = eog;
    end

    trial = trial+1;
  end; % if ~isempty(length)
end;

fclose(i_fid);

% remove the initial empty trials, i.e. the ones that were skipped
cortex = cortex(begtrial:end);


function [str_is_found] = instr(str, pattern)
    % implementation fo contains function, not presents in all matlab
    %
    % SYNTAX:
    %   str_is_found = instr(str, pattern)
    %

    %--- * --. --- --. .--. ... * ---------------------------------------------
    %               ___ ___ ___
    %     __ _ ___ / __| _ | __|
    %    / _` / _ \ (_ |  _|__ \
    %    \__, \___/\___|_| |___/
    %    |___/                    v 1.0RC1
    %
    %--------------------------------------------------------------------------
    %  Copyright (C) 2021 Geomatics Research & Development srl (GReD)
    %  Written by:       Andrea Gatti
    %  Contributors:     Andrea Gatti ...
    %  A list of all the historical goGPS contributors is in CREDITS.nfo
    %--------------------------------------------------------------------------
    %
    %   This program is free software: you can redistribute it and/or modify
    %   it under the terms of the GNU General Public License as published by
    %   the Free Software Foundation, either version 3 of the License, or
    %   (at your option) any later version.
    %
    %   This program is distributed in the hope that it will be useful,
    %   but WITHOUT ANY WARRANTY; without even the implied warranty of
    %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    %   GNU General Public License for more details.
    %
    %   You should have received a copy of the GNU General Public License
    %   along with this program.  If not, see <http://www.gnu.org/licenses/>.
    %
    %--------------------------------------------------------------------------
    % 01100111 01101111 01000111 01010000 01010011
    %--------------------------------------------------------------------------

    str_is_found = ~isempty(strfind(str,pattern)); %#ok<STREMP>
end


function varargout = metchgui(varargin)
%    alldata = metchgui(node,elem,points,pface) or metchgui(volume,points,pface)
%
%    A GUI to register a point cloud to a mesh or volumetric image
%
%    author: Qianqian Fang <q.fang at neu.edu>
%    date: 12/16/2008
%
%   parameters:
%        node: node coordinate of the surface mesh (nn x 3)
%        elem: element list of the surface mesh (3 columns for
%              triangular mesh, 4 columns for cubic surface mesh)
%        points: the coordinates (3 columns for x/y/z) of the
%              point cloud which you want to register
%        pface:trianglular surface defined on the point cloud.
%              pface is optional; if presents, metch will display
%              a surface object instead of a point cloud.
%
%   the input can also be two parameters in form of metchgui(volume,points),
%    where volume is a 3D image (array).
%
%   outputs:
%        alldata: a structrure containing all processing outputs
%        the fields include:
%         .node: the input node
%         .elem: the input surface mesh elements
%         .volume: if the input volumetric image
%         .A0: the affine rotation for selected point pairs (after Initialize)
%         .b0: the affine translation for selected point pairs (after Initialize)
%         .A: the affine rotation for the point cloud (after Optimize)
%         .b: the affine translation for the point cloud (after Optimize)
%         .points: the input point cloud
%         .pointsinit: the point cloud after initialization
%         .pointsopt: the point cloud after optimization
%         .pointsproj: the point cloud after projecting to the surface
%         .initplot: the handle to the point cloud plot after init
%         .optplot: the handle to the point cloud plot after optimization
%         .projplot: the handle to the point cloud plot after projection
%
%   If user supplys an output variable, the GUI will not return until the
%   user hits the "close" button or close the window; if user does not
%   supply any output, the call will return immediately; any data user
%   intends to save, he has to click on "Save Session" button and provides
%   a mat-file file name. A single structure named "metchsession" will be
%   stored in this file.
%
%   example: (meshasphere/meshunitsphere are defined in iso2mesh http://iso2mesh.sf.net)
%
%       [noderef,faceref,elemref]=meshunitsphere(0.08,10);
%       [no,fc]=removeisolatednode(noderef(:,1:3),faceref(:,1:3));
%       [node,face,elem]=meshasphere([10 20 15],3,0.5,10);
%       [no2,fc2]=removeisolatednode(node(:,1:3),face(:,1:3));
%       alldata = metchgui(no,fc,no2);
%       % or alldata = metchgui(no,fc,no2,fc2);
%
%   Please find more information at http://iso2mesh.sf.net/cgi-bin/index.cgi?metch
%
%   this function is part of "metch" toobox, see COPYING for license

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @metchgui_OpeningFcn, ...
                   'gui_OutputFcn',  @metchgui_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}, 'hasoutput');
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before metchgui is made visible.
function metchgui_OpeningFcn(hObject, eventdata, handles, varargin)
handles.hasoutput=0;
if(isempty(varargin))
    fprintf(1,'Metch GUI must be called with parameters:\nFormat: alldata = metchgui(node,elem,points,pface);\n');
    close(handles.MetchGUI);
    return;
end
if(ischar(varargin{end}) && strcmp(varargin{end},'hasoutput'))
        handles.hasoutput=1;
        varargin(end)=[];
end
handles.output = hObject;

set(handles.btAddMeshPt,'userdata',[handles.axMesh,handles.btAddMeshPt,handles.btAddCloudPt]);
set(handles.btAddCloudPt,'userdata',[handles.axMesh,handles.btAddCloudPt,handles.btAddMeshPt]);

% if uses supplied 2 input variables, assume a volume image and a point cloud
if(isnumeric(varargin{1}) && length(size(varargin{1}))==3)
       vol=varargin{1};
       pt=varargin{2};

       dat.volume=vol;
       dat.points=pt;

       slice=round(size(vol,3)/2);
       hs=imagesc(vol(:,:,slice),'parent',handles.axMesh);
       set(handles.slPos,'max',size(vol,3),'min',1,'value',slice);

       if(length(varargin)>=3)
       	 pface=varargin{3};
       	 dat.pface=pface;
	 trisurf(pface(:,1:3),pt(:,1),pt(:,2),pt(:,3),'parent',handles.axPoints);
       else
         %plot3(pt(:,1),pt(:,2),pt(:,3),'.','parent',handles.axPoints);
         ptcolor=(pt-repmat(min(pt),size(pt,1),1))./repmat(max(pt)-min(pt),size(pt,1),1);
         drawnow;
         scatter3(pt(:,1),pt(:,2),pt(:,3),3,ptcolor,'filled');
       end
       set(handles.MetchGUI,'userdata',dat);

       axis(handles.axMesh,'equal');
       axis(handles.axPoints,'equal');
       axis(handles.axMesh,'off');
       grid(handles.axPoints,'on');
       %axis(handles.axPoints,'off');

       set(handles.axMesh,'tag','axMesh');
       set(handles.axPoints,'tag','axPoints');
       set(handles.slPos,'visible','on');
       set(handles.lbZPos,'visible','on');
       rotate3d(handles.axPoints,'on');
       rotate3d(gcf,'on');
end

% if uses supplied 3 input variables, assume a surface mesh and a point cloud/surface
if(length(varargin)>=3 && length(size(varargin{1}))==2)
       node=varargin{1};
       elem=varargin{2};
       pt=varargin{3};

       dat.node=node;
       dat.elem=elem;
       dat.points=pt;
       if(length(varargin)>=4)
       	 pface=varargin{4};
       	 dat.pface=pface;
       end
       set(handles.MetchGUI,'userdata',dat);

       drawinit(handles,dat)
end
guidata(hObject, handles);

if(handles.hasoutput)
        uiwait(handles.MetchGUI);
end

%---------------------------------------------------------------------------
function drawinit(handles,dat)

node=dat.node;
elem=dat.elem;
pt=dat.points;

hs=trisurf(elem,node(:,1),node(:,2),node(:,3),'parent',handles.axMesh);
%set(hs,'linestyle','none');
%set(hs,'facecolor','b','facealpha',0.8);

%plot3(pt(:,1),pt(:,2),pt(:,3),'.','parent',handles.axPoints);
if(~isfield(dat,'pface'))
	ptcolor=(pt-repmat(min(pt),size(pt,1),1))./repmat(max(pt)-min(pt),size(pt,1),1);
	drawnow;
	scatter3(pt(:,1),pt(:,2),pt(:,3),3,ptcolor,'filled');
else
	trisurf(dat.pface,pt(:,1),pt(:,2),pt(:,3),'parent',handles.axPoints);
end
%hold(handles.axPoints,'on');
%plot3(pt(5:7,1),pt(5:7,2),pt(5:7,3),'ro','parent',handles.axPoints);

axis(handles.axMesh,'equal');
axis(handles.axPoints,'equal');
axis(handles.axMesh,'off');
%axis(handles.axPoints,'off');

set(handles.axMesh,'tag','axMesh');
set(handles.axPoints,'tag','axPoints');

rotate3d(handles.axPoints,'on');
rotate3d(handles.axMesh,'on');
rotate3d(gcf,'on');

%---------------------------------------------------------------------------
function varargout = metchgui_OutputFcn(hObject, eventdata, handles)
if(length(handles) && isfield(handles,'output') && isfield(handles,'hasoutput') && handles.hasoutput)
        handles.output=get(handles.MetchGUI,'userdata');
        varargout{1} =handles.output;
        close(handles.MetchGUI);
end
%---------------------------------------------------------------------------
function lbMesh_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%---------------------------------------------------------------------------
function lbPoints_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%---------------------------------------------------------------------------
function edit1_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%---------------------------------------------------------------------------
function edit2_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%---------------------------------------------------------------------------
function edit3_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

%---------------------------------------------------------------------------
function isSelect_Callback(hObject, eventdata, handles)
if(get(hObject,'Value'))
        datacursormode(gcf,'on');
        set(datacursormode(gcf),'UpdateFcn',@myupdatefcn)
else
        datacursormode(gcf,'off');
        rotate3d(gcf,'on');
end

%---------------------------------------------------------------------------
% the respond function when there is a data-tip to popup
%---------------------------------------------------------------------------
function txt=myupdatefcn(empt,event_obj)
pos = get(event_obj,'Position');
idx=  get(event_obj,'DataIndex');

if(length(pos)==3)
  txt = {['x: ',num2str(pos(1))],...
         ['y: ',num2str(pos(2))],['z: ',num2str(pos(3))],['index:', num2str(idx)]};
elseif(length(pos)==2)
  txt = {['x: ',num2str(pos(1))],['y: ',num2str(pos(2))]};
end
targetup=get(get(event_obj,'Target'),'parent');
set(targetup,'userdata',struct('pos',pos,'idx',idx));
if(targetup==findobj('tag','axMesh'))
         set(findobj('tag','btAddMeshPt'),'enable','on');
         set(findobj('tag','btAddCloudPt'),'enable','off');
elseif(targetup==findobj('tag','axPoints'))
         set(findobj('tag','btAddMeshPt'),'enable','off');
         set(findobj('tag','btAddCloudPt'),'enable','on');
end

%---------------------------------------------------------------------------
function bInit_Callback(hObject, eventdata, handles)
mapto=get(handles.lbMesh,'userdata');
mapfrom=get(handles.lbPoints,'userdata');

maptoidx=get(handles.txMapTo,'userdata');
mapfromidx=get(handles.txMapFrom,'userdata');

if(length(mapto)<4 | length(mapfrom)<4)
        msgbox('You have to select >3 points from the point cloud plot and corresponding points from the mesh','Error','error');
        return;
end
[A,b]=affinemap(mapfrom,mapto)
dat=get(handles.MetchGUI,'userdata');
dat.A0=A;
dat.b0=b;

newpt=(A*dat.points'+repmat(b(:),1,size(dat.points,1)))';
dat.pointsinit=newpt;

hold(handles.axMesh,'on');
if(isfield(dat,'initplot'))
        delete dat.initplot;
        dat.initplot=0;
end
dat.initplot=plot3(newpt(:,1),newpt(:,2),newpt(:,3),'r.','parent',handles.axMesh);

dat.fromidx=mapfromidx;
dat.toidx=maptoidx;
set(handles.MetchGUI,'userdata',dat);

%---------------------------------------------------------------------------
function axPoints_ButtonDownFcn(hObject, eventdata, handles)
if(get(handles.btSelect,'value')==1)
        pp=getCursorInfo(datacursormode(gcf));
        str=[get(handles.lbPoints,'string');mat2str(pp.Position)];
        set(handles.lbPoints,'string',str);
end

%---------------------------------------------------------------------------
function addselectedpt(pos,idx,lb)
if(isempty(get(lb,'value')))
    set(lb,'value',1);
end
listpt=get(lb,'string');
listpt{end+1}=[num2str(idx) ':' mat2str(pos)];
set(lb,'string',listpt);
set(lb,'userdata',[get(lb,'userdata');pos]);

%---------------------------------------------------------------------------
function btAddMeshPt_Callback(hObject, eventdata, handles)
dat=get(handles.axMesh,'userdata');

dat0=get(handles.MetchGUI,'userdata');
if(isfield(dat0,'volume'))
    dat.pos(:,3)=get(handles.slPos,'value');
end

if(isfield(dat,'pos'))
    addselectedpt(dat.pos,dat.idx,handles.lbMesh);
    set(handles.txMapTo,'userdata',[get(handles.txMapTo,'userdata');dat.idx]);
	dat0.mapto=get(handles.lbMesh,'userdata');
    dat0.maptoidx=get(handles.txMapTo,'userdata');
    set(handles.MetchGUI,'userdata',dat0);
else
        msgbox('No point was selected. Please click on "Select" and select a point on the mesh or point cloud','Error','error');
        return;
end

%---------------------------------------------------------------------------
function btAddCloudPt_Callback(hObject, eventdata, handles)
dat=get(handles.axPoints,'userdata');
dat0=get(handles.MetchGUI,'userdata');
if(isfield(dat,'pos'))
    addselectedpt(dat.pos,dat.idx,handles.lbPoints);
    set(handles.txMapFrom,'userdata',[get(handles.txMapFrom,'userdata');dat.idx]);
	dat0.mapfrom=get(handles.lbPoints,'userdata');
    dat0.mapfromidx=get(handles.txMapFrom,'userdata');
    set(handles.MetchGUI,'userdata',dat0);
else
        msgbox('No point was selected. Please click on "Select" and select a point on the mesh or point cloud','Error','error');
        return;
end

%---------------------------------------------------------------------------
function btOptimize_Callback(hObject, eventdata, handles)
dat=get(handles.MetchGUI,'userdata');
if(isfield(dat,'A0') && isfield(dat,'b0')& isfield(dat,'node')& isfield(dat,'elem')& ...
   isfield(dat,'pointsinit')& isfield(dat,'toidx')& isfield(dat,'fromidx'))
        pmask=-1*ones(size(dat.pointsinit,1),1);
        pmask(dat.fromidx)=dat.toidx;
	if(isfield(dat,'A') && isfield(dat,'b'))
	        [Anew,bnew,posnew]=regpt2surf(dat.node,dat.elem,dat.points,pmask,dat.A,dat.b,ones(12,1),20);
	else
                [Anew,bnew,posnew]=regpt2surf(dat.node,dat.elem,dat.points,pmask,dat.A0,dat.b0,ones(12,1),20);
	end
        dat.A=Anew;
        dat.b=bnew;
        dat.pointsopt=posnew;
        if(isfield(dat,'optplot') && dat.optplot)
                delete dat.optplot;
                dat.optplot=0;
        end
        dat.optplot=plot3(posnew(:,1),posnew(:,2),posnew(:,3),'g+','parent',handles.axMesh);
else
        msgbox('You have to select 4 points and click "Initialize" button first','Error','error');
        return;
end
set(handles.MetchGUI,'userdata',dat);

%---------------------------------------------------------------------------
function slPos_Callback(hObject, eventdata, handles)
dat=get(handles.MetchGUI,'userdata');
if(isfield(dat,'volume'))
        hold(handles.axMesh,'off');
        imagesc(dat.volume(:,:,round(get(hObject,'value'))),'parent',handles.axMesh);
        set(handles.axMesh,'tag','axMesh');
        set(handles.lbZPos,'string',num2str(round(get(hObject,'value'))));
end
set(handles.MetchGUI,'userdata',dat);

%---------------------------------------------------------------------------
function slPos_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

%--------------------------------------------------------------------------
function btProj_Callback(hObject, eventdata, handles)
dat=get(handles.MetchGUI,'userdata');
if(isfield(dat,'pointsopt'))
        if(isfield(dat,'projplot') && dat.projplot)
                delete dat.projplot;
                dat.projplot=0;
        end
        nv=nodesurfnorm(dat.node,dat.elem);
        [d2surf,cn]=dist2surf(dat.node,nv,dat.pointsopt);
        [dat.pointsproj dat.elemid dat.weight]=proj2mesh(dat.node,dat.elem,dat.pointsopt,nv,cn);
        hold(handles.axMesh,'on');
        dat.projplot=plot3(dat.pointsproj(:,1),dat.pointsproj(:,2),dat.pointsproj(:,3),'c.','parent',handles.axMesh);
else
        msgbox('You have to first select 4 points, then click "Initialize" and "Optimize" button','Error','error');
        return;
end
set(handles.MetchGUI,'userdata',dat);

%--------------------------------------------------------------------------
function btSaveRes_Callback(hObject, eventdata, handles)
[filename, pathname] = uiputfile('*.mat', 'Save Metch Workspace as');
mapto=get(handles.lbMesh,'userdata');
mapfrom=get(handles.lbPoints,'userdata');

maptoidx=get(handles.txMapTo,'userdata');
mapfromidx=get(handles.txMapFrom,'userdata');

metchsession=get(handles.MetchGUI,'userdata');
if(~isempty(mapto))    metchsession.mapto=mapto; end
if(~isempty(mapfrom))    metchsession.mapfrom=mapfrom; end
if(~isempty(maptoidx))    metchsession.maptoidx=maptoidx; end
if(~isempty(mapfromidx))    metchsession.mapfromidx=mapfromidx; end
fname=[pathname filename];
save(fname,'metchsession');

%--------------------------------------------------------------------------
function btLoadSession_Callback(hObject, eventdata, handles)
[filename, pathname] = uigetfile('*.mat', 'Load Metch Workspace from');
fname=[pathname filename];
load(fname);
handle.output=metchsession;
cla(handles.axMesh);
cla(handles.axPoints);
drawinit(handles,metchsession.node,metchsession.elem,metchsession.points);
%--------------------------------------------------------------------------
function btPlotResults_Callback(hObject, eventdata, handles)

function btClose_Callback(hObject, eventdata, handles)
guidata(hObject,handles);
uiresume;

function btHelp_Callback(hObject, eventdata, handles)
helpmsg={
'Metch GUI: A mesh/volume registration toolbox',
'',
'Author: Qianqian Fang <q.fang at neu.edu>',
'        Martinos Center for Biomedical Imaging',
'        Charlestown, MA 02129, USA',
'',
'== Description of the workflow ==',
'',
' 1. when the GUI pops up, it will display the mesh and the points,',
'    you can rotate both plots so that you can identify the matching ',
'    features',
' 2. switch on "Select" mode, then, click on a land-mark point on the point',
'    plot, when a data-tip shows up, click "Add Selected" button',
' 3. click on the corresponding position on the mesh, and click',
'    "Add Selected"      ',
' 4. repeat the above for at least 4 point pairs (you can select more);',
'    if you want to change views, switch off "Select" box and rotate;',
'    after rotation, switch on "Select" box again',
' 5. click "Initialize": this will create the initial mapping using the',
'    selected point pairs',
' 6. click "Optimize": this will fit the surface with the whole point cloud',
' 7. click "Proj2Mesh": this will project the fitted point clouds onto the',
'    mesh',
' 8. you can quit the GUI by hit "Close", your results will be saved to reg',
' 9. close the window '};

helpdlg(helpmsg);


function mergedData = mergeTextDataAndData(textdata, data, headings)
% Merges textdata and data imported from .xls file assuming that the first
% row of textdata is column headings
%
% USAGE:
%
%    mergedData = mergeTextDataAndData(textdata, data, headings)
%
% INPUTS:
%    textdata:      cell array from .xls import
%    data:          matrix with numeric data from .xls import
%
% OPTIONAL INPUT:
%    headings:      {(1), 0}, zero if no column headings
%
% OUTPUT:
%    mergedData:    merged cell array with all data from .xls import
%
% .. Author: - Ronan Fleming 29/10/2008

if ~exist('headings','var'); %check for headings
    headings=1;
    fprintf('%s\n','Assuming there were headings in the original xls file');
end
if headings==1
    start=2;
else
    start=1;
end

%checking for exceptions in otherwise data columns
fprintf('%s\n','Checking for exceptions in otherwise numeric columns, e.g. NaN');
fprintf('%s\n','Checking for exceptions in otherwise numeric columns, e.g. -');
fprintf('%s\n','Checking for exceptions in otherwise numeric columns, e.g. Not calculated');
fprintf('%s\n','Checking for exceptions in otherwise numeric columns, e.g. #N/A');
fprintf('%s\n','Checking for empty cells in otherwise numeric columns, e.g. []');
[ylt,xlt]=size(textdata);
for y=1:ylt
    for x=1:xlt
        if strcmp(textdata{y,x},'NaN')
            textdata{y,x}='';
        end
    end
end
for y=1:ylt
    for x=1:xlt
        if strcmp(textdata{y,x},'-')
            textdata{y,x}='';
        end
    end
end
for y=1:ylt
    for x=1:xlt
        if strcmp(textdata{y,x},'Not calculated')
            textdata{y,x}='';
        end
    end
end
for y=1:ylt
    for x=1:xlt
        if strcmp(textdata{y,x},'#N/A')
            textdata{y,x}='';
        end
    end
end
%replace empty cells with blank string
for y=1:ylt
    for x=1:xlt
        if isempty(textdata{y,x})
            textdata{y,x}='';
        end
    end
end
%preallocate
mergedData=textdata;
dataCol=1;
beginData=0;
for x=1:xlt
    %check for blank column
    if min(strcmp(textdata(start:ylt,x),''))==1
        beginData=1;
        fprintf('%s\n',['Merging numerical data into column ' int2str(x)]);
        for y=start:ylt
            if (y-start+1)>size(data,1)
                %if the last rows are NaN then this is needed as data will
                %be too short
                mergedData{y,x}=NaN;
            else
                mergedData{y,x}=data(y-start+1,dataCol);
            end
        end
        dataCol=dataCol+1;
        if dataCol>size(data,2)
            break
        end
    else
        %data can have empty columns corresponding to textdata columns
        if beginData==1
            dataCol=dataCol+1;
        end
    end
end
%data column might be the last column of original xls file
if dataCol<size(data,2)+1
    p=1;
    while dataCol<size(data,2)+1
        for y=start:ylt
            mergedData{y,x+p}=data(y-start+1,dataCol);
        end
        p=p+1;
        dataCol=dataCol+1;
    end
end

% for x=1:137 gMW{x,1}=iCore.genes{x}; for y=1:5300 if
% strncmp(iCore.genes{x},mergedData{y,1},length(iCore.genes{x})) gMW{x,2}=d(y); end; end; end;


function MRI = out_mri_bst( MRI, MriFile )
% OUT_MRI_BST: Save a Brainstorm MRI structure.
%
% USAGE:  MRI = out_mri_bst( MRI, MriFile )
%
% INPUT:
%     - MRI     : Brainstorm MRI structure
%     - MriFile : full path to file where to save the MRI in brainstorm format
% OUTPUT:
%     - MRI : Modificed MRI structure
%
% NOTES:
%     - MRI structure:
%         |- Voxsize:   [x y z], size of each MRI voxel, in millimeters
%         |- Cube:      MRI volume
%         |- SCS:       Subject Coordinate System definition
%         |    |- NAS:    [x y z] coordinates of the nasion, in voxels
%         |    |- LPA:    [x y z] coordinates of the left pre-auricular point, in voxels
%         |    |- RPA:    [x y z] coordinates of the right pre-auricular point, in voxels
%         |    |- R:      Rotation to convert MRI coordinates -> SCS coordinatesd
%         |    |- T:      Translation to convert MRI coordinates -> SCS coordinatesd
%         |- Landmarks[]: Additional user-defined landmarks
%         |- NCS:         Normalized Coordinates System (Talairach, MNI, ...)
%         |    |- AC:             [x y z] coordinates of the Anterior Commissure, in voxels
%         |    |- PC:             [x y z] coordinates of the Posterior Commissure, in voxels
%         |    |- IH:             [x y z] coordinates of any Inter-Hemispheric point, in voxels
%         |- Comment:     MRI description

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

% ===== Clean-up MRI structure =====
% Remove (useless or old fieldnames)
Fields2BDeleted     = {'Origin','sag','ax','cor','hFiducials','header','filename'};
SCSFields2BDeleted  = {'Origin','Comment'};

for k = 1:length(Fields2BDeleted)
    if isfield(MRI,Fields2BDeleted{k})
        MRI = rmfield(MRI,Fields2BDeleted{k});
    end
end

if isfield(MRI,'SCS')
    for k = 1:length(SCSFields2BDeleted)
        if isfield(MRI.SCS,SCSFields2BDeleted{k})
            MRI.SCS = rmfield(MRI.SCS,SCSFields2BDeleted{k});
        end
    end
end

if isfield(MRI,'Landmarks') && ~isempty(MRI.Landmarks)
    tmpLandmarks = MRI.Landmarks;
    nLandmarks2Remove = 0;
    if isfield(MRI,'SCS')
        nLandmarks2Remove = 4; %Remove SCS fiducials from Landmark list
    end
    if isfield(MRI,'talCS')
        nLandmarks2Remove = nLandmarks2Remove + 3; % Remove TAL/MNI fiducials
    end
    if isfield(MRI.Landmarks, 'Names') && ~isempty(MRI.Landmarks.Names)
        MRI.Landmarks.Names    = MRI.Landmarks.Names(nLandmarks2Remove+1:end);
    end
    if isfield(MRI.Landmarks, 'MRImmXYZ') && ~isempty(MRI.Landmarks.MRImmXYZ)
        MRI.Landmarks.MRImmXYZ = MRI.Landmarks.MRImmXYZ(:,nLandmarks2Remove+1:end);
    end
    if isfield(MRI.Landmarks, 'Handles')
        MRI.Landmarks = rmfield(MRI.Landmarks,'Handles');
    end
end

if isfield(MRI,'SCS2Landmarks') % don't need to be stored in file
    tmpSCS2Landmarks = MRI.SCS2Landmarks;
    MRI = rmfield(MRI,'SCS2Landmarks');
end

% Remove FileName field
if isfield(MRI,'FileName')
    MRI = rmfield(MRI, 'FileName');
end

% SAVE .mat file
try
    bst_save(MriFile, MRI, 'v7');
catch

end


function str = regexpsplit(varargin)
% mimics regexp(a,b,'split') as introduced in MATLAB version

[a,b] = regexp(varargin{:});

a = [a-1,length(varargin{1})];
b = [1,b+1];

str = arrayfun(@(i) varargin{1}(b(i):a(i)),1:length(a),'uniformoutput',0);
ind = cellfun('isempty',str);
str(ind) = {''};
% if any(ind),  str{ind} = ''; end


function [ev, i] = pne_detected_event(event_log, name, zero)
%PNE_DETECTED_EVENT  Returns the detected event of a particular name
%   EV = PNE_DETECTED_EVENT(EVENT_LOG, NAME)
%   EV = PNE_DETECTED_EVENT(EVENT_LOG, NAME, ZERO)
%   [EV, I] = PNE_DETECTED_EVENT(...)
%
%   Searches through the log of detected events, returning the event with
%   the specified name, if one exists, and optionally only if it is refers
%   to a detected event zero (not an event interval).
%
%   Inputs:
%       EVENT_LOG : struct array of detected events
%       NAME : name of event of interest
%       ZERO : (optional, default = 0)
%           0 - return the event if a zero or an interval are detected for the
%               corresponding event function
%           1 - return the event only if a zero is deteced for the
%               corresponding event function
%
%   Outputs:
%       EV : entry of the EVENT_LOG corresponding to the detected event with
%            the specified NAME, or empty if NAME not in the EVENT_LOG of
%            detected events
%       I : index of EV in EVENT_LOG
%
%   See PNE_DETECT_EVENTS for details on the fields in the EVENT_LOG and EV
%   structs.

%   MP-Opt-Model
%   Copyright (c) 2016-2021, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%   and Shrirang Abhyankar, Argonne National Laboratory
%
%   This file is part of MP-Opt-Model.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://github.com/MATPOWER/mp-opt-model for more info.

if nargin < 3
    zero = 0;
end
ev = [];
i = 0;
for k = 1:length(event_log)
    if strcmp(event_log(k).name, name) && (event_log(k).zero || ~zero)
        ev = event_log(k);
        i = k;
    end
    break
end


function dfdx_sort = map_to_original(dfdx,x, x_indep);

for i = 1:length(x(:))
    x_origvar(i) = getvariables(x(i));
end
x_var = getvariables(x_indep);
dfdx_sort = [];
for i = 1:length(x_origvar)
    dfdx_sort = [dfdx_sort dfdx(:,find(x_origvar(i) == x_var))];
end


function opt = mpoption_info_mosek(selector)
%MPOPTION_INFO_MOSEK  Returns MATPOWER option info for MOSEK.
%
%   DEFAULT_OPTS = MPOPTION_INFO_MOSEK('D')
%   VALID_OPTS   = MPOPTION_INFO_MOSEK('V')
%   EXCEPTIONS   = MPOPTION_INFO_MOSEK('E')
%
%   Returns a structure for MOSEK options for MATPOWER containing ...
%   (1) default options,
%   (2) valid options, or
%   (3) NESTED_STRUCT_COPY exceptions for setting options
%   ... depending on the value of the input argument.
%
%   This function is used by MPOPTION to set default options, check validity
%   of option names or modify option setting/copying behavior for this
%   subset of optional MATPOWER options.
%
%   See also MPOPTION.

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

if nargin < 1
    selector = 'D';
end
if have_feature('mosek')
    switch upper(selector)
        case {'D', 'V'}     %% default and valid options
            opt = struct(...
                'mosek',        struct(...
                    'lp_alg',       0, ...
                    'max_it',       0, ...
                    'gap_tol',      0, ...
                    'max_time',     0, ...
                    'num_threads',  0, ...
                    'opts',         [], ...
                    'opt_fname',    '', ...
                    'opt',          0 ...
                ) ...
            );
        case 'E'            %% exceptions used by nested_struct_copy() for applying
            opt = struct(...
                'name',         { 'mosek.opts' }, ...
                'check',        0 ...
                );
%                 'copy_mode',    { @mosek_options } ...
        otherwise
            error('mpoption_info_mosek: ''%s'' is not a valid input argument', selector);
    end
else
    opt = struct([]);       %% MOSEK is not available
end


report_this_filefun(mfilename('fullpath'));

% make a rate per year plot:
ed = (floor(min(newt2.Date)):1:ceil(max(newt2.Date)));

[ny,hy ] = hist(newt2.Date,ed-0.5);

figure
bar(hy,ny)


function [F, TimeVector,DisplayUnits] = in_fread(sFile, ChannelMat, iEpoch, SamplesBounds, iChannels, ImportOptions)
% IN_FREAD: Read a block a data in any recordings file previously opened with in_fopen().
%
% USAGE:  [F, TimeVector, DisplayUnits] = in_fread(sFile, ChannelMat, iEpoch, SamplesBounds, iChannels, ImportOptions);
%         [F, TimeVector, DisplayUnits] = in_fread(sFile, ChannelMat, iEpoch, SamplesBounds, iChannels);                 : Do not apply any pre-preprocessings
%         [F, TimeVector, DisplayUnits] = in_fread(sFile, ChannelMat, iEpoch, SamplesBounds);                            : Read all channels
%
% INPUTS:
%     - sFile         : Structure for importing files in Brainstorm. Created by in_fopen()
%     - iEpoch        : Indice of the epoch to read (only one value allowed)
%     - SamplesBounds : [smpStart smpStop], First and last sample to read in epoch #iEpoch
%     - iChannels     : Array of indices of the channels to import
%     - ImportOptions : Structure created by interface window panel_import_data.m  (look in db_template.m for a description of all the fields).
%
% OUTPUTS:
%     - F          : [nChannels x nTimes], block of recordings
%     - TimeVector : [1 x nTime], time values in seconds
%     - DisplayUnits : char, unit of the recording

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

%% ===== PARSE INPUTS =====
if (nargin < 6)
    ImportOptions = [];
end
if (nargin < 5)
    iChannels = [];
end
TimeVector = [];
% Read channel ranges for faster access
isChanRange = ismember(sFile.format, {'CTF', 'CTF-CONTINUOUS', 'KDF', 'EEG-EDF', 'EEG-BDF', 'BST-BIN', 'EEG-CURRY', 'EEG-DELTAMED', 'EEG-COMPUMEDICS-PFS', 'EEG-MICROMED', 'EEG-NEURONE', 'EEG-NK', 'EEG-OEBIN'});
if isChanRange
    if isempty(iChannels)
        ChannelRange = [];
        iChanRemove = [];
    else
        if ~isequal(iChannels, sort(iChannels))
            error('You need to sort the channels indices before calling in_fread().');
        end
        ChannelRange = [iChannels(1), iChannels(end)];
        iChanRemove = setdiff(ChannelRange(1):ChannelRange(2), iChannels) - ChannelRange(1) + 1;
    end
end

%% ===== OPEN FILE =====
% Open file (for some formats, it is open in the low-level function)
if ismember(sFile.format, {'FIF', 'CTF', 'KIT', 'RICOH', 'BST-DATA', 'SPM-DAT', 'EEG-ANT-CNT', 'EEG-AXION', 'EEG-EEGLAB', 'EEG-GTEC', 'EEG-NEURONE', 'EEG-NEURALYNX', 'EEG-NICOLET', 'EEG-BLACKROCK', 'EEG-RIPPLE', 'EYELINK', 'NIRS-BRS', 'EEG-EGI-MFF', 'MNE-PYTHON'})
    sfid = [];
else
    sfid = fopen(sFile.filename, 'r', sFile.byteorder);
end

% Check whether optional field precision is available
if ~isempty(ImportOptions) && isfield(ImportOptions, 'Precision')
    precision = ImportOptions.Precision;
else
    precision = [];
end

%% ===== READ RECORDINGS BLOCK =====
DisplayUnits = [];
switch (sFile.format)
    case 'FIF'
        [F,TimeVector] = in_fread_fif(sFile, iEpoch, SamplesBounds, iChannels);
    case {'CTF', 'CTF-CONTINUOUS'}
        isContinuous = strcmpi(sFile.format, 'CTF-CONTINUOUS');
        F = in_fread_ctf(sFile, iEpoch, SamplesBounds, ChannelRange, isContinuous);
    case '4D'
        F = in_fread_4d(sFile, sfid, iEpoch, SamplesBounds, iChannels);
    case 'KIT'
        F = in_fread_kit(sFile, iEpoch, SamplesBounds, iChannels);
    case 'RICOH'
        F = in_fread_ricoh(sFile, iEpoch, SamplesBounds, iChannels);
    case 'KDF'
        F = in_fread_kdf(sFile, sfid, SamplesBounds, ChannelRange);
    case 'ITAB'
        F = in_fread_itab(sFile, sfid, SamplesBounds, iChannels);
    case 'MEGSCAN-HDF5'
        F = in_fread_megscan(sFile, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-ADICHT'
        F = in_fread_adicht(sFile, iEpoch, iChannels, SamplesBounds);
    case 'EEG-ANT-CNT'
        F = in_fread_ant(sFile, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-ANT-MSR'
        F = in_fread_msr(sFile, sfid, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-AXION'
        F = in_fread_axion(sFile, SamplesBounds, iChannels, precision);
    case 'EEG-BCI2000'
        F = in_fread_bci2000(sFile, SamplesBounds);
    case {'EEG-BLACKROCK', 'EEG-RIPPLE'}
        F = in_fread_blackrock(sFile, SamplesBounds, iChannels, precision);
    case 'EEG-BRAINAMP'
        F = in_fread_brainamp(sFile, sfid, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-CURRY'
        F = in_fread_curry(sFile, sfid, iEpoch, SamplesBounds, ChannelRange);
    case 'EEG-DELTAMED'
        F = in_fread_deltamed(sFile, sfid, SamplesBounds, ChannelRange);
    case 'EEG-COMPUMEDICS-PFS'
        F = in_fread_compumedics_pfs(sFile, sfid, SamplesBounds, ChannelRange);
    case {'EEG-EDF', 'EEG-BDF'}
        F = in_fread_edf(sFile, sfid, SamplesBounds, ChannelRange);
    case 'EEG-EEGLAB'
        F = in_fread_eeglab(sFile, iEpoch, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-EGI-RAW'
        F = in_fread_egi(sFile, sfid, iEpoch, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-GTEC'
        F = in_fread_gtec(sFile, iEpoch, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-MANSCAN'
        F = in_fread_manscan(sFile, sfid, iEpoch, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-EGI-MFF'
        F = in_fread_mff(sFile, iEpoch, SamplesBounds, ImportOptions);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-MICROMED'
        F = in_fread_micromed(sFile, sfid, SamplesBounds, ChannelRange);
    case 'EEG-NEURALYNX'
        F = in_fread_neuralynx(sFile, SamplesBounds, iChannels);
    case 'EEG-NEURONE'
        F = in_fread_neurone(sFile, SamplesBounds, ChannelRange);
    case 'EEG-NEUROSCAN-CNT'
        F = in_fread_cnt(sFile, sfid, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-NEUROSCAN-EEG'
        F = in_fread_eeg(sFile, sfid, iEpoch, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-NEUROSCAN-AVG'
        F = in_fread_avg(sFile, sfid, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-NEUROSCOPE'
        F = in_fread_neuroscope(sFile, sfid, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'EEG-NICOLET'
        F = in_fread_nicolet(sFile, iEpoch, SamplesBounds, iChannels);
    case 'EEG-OEBIN'
        F = in_fread_oebin(sFile, sfid, SamplesBounds, ChannelRange);
    case 'EEG-NK'
        F = in_fread_nk(sFile, sfid, iEpoch, SamplesBounds, ChannelRange);
    case 'EEG-SMR'
        F = in_fread_smr(sFile, sfid, SamplesBounds, iChannels);
    case 'EEG-SMRX'
        F = in_fread_smrx(sFile, SamplesBounds, iChannels);
    case 'EYELINK'
        [F, TimeVector] = in_fread_eyelink(sFile, iEpoch, SamplesBounds, iChannels);
    case 'NIRS-BRS'
        F = in_fread_nirs_brs(sFile, SamplesBounds);
        if ~isempty(iChannels)
            F = F(iChannels,:);
        end
    case 'SPM-DAT'
        F = in_fread_spm(sFile, SamplesBounds, iChannels);
    case 'BST-BIN'
        F = in_fread_bst(sFile, sfid, SamplesBounds, ChannelRange);
    case 'BST-DATA'
        if ~isempty(SamplesBounds)
            fileSamples = round(sFile.prop.times * sFile.prop.sfreq);
            iTimes = (SamplesBounds(1):SamplesBounds(2)) - fileSamples(1) + 1;
        else
            iTimes = 1:size(sFile.header.F,2);
        end
        if isempty(iChannels)
            iChannels = 1:size(sFile.header.F,1);
        end
        F = sFile.header.F(iChannels, iTimes);
        % Load display units
        if ~isempty(sFile.filename)
            DataMat = load(sFile.filename, 'DisplayUnits');
            if isfield(DataMat, 'DisplayUnits') && ~isempty(DataMat.DisplayUnits)
                DisplayUnits = DataMat.DisplayUnits;
            end
        end

    case 'EEG-INTAN'
        F = in_fread_intan(sFile, SamplesBounds, iChannels, precision);
    case 'EEG-PLEXON'
        F = in_fread_plexon(sFile, SamplesBounds, iChannels, precision);
    case 'EEG-TDT'
        F = in_fread_tdt(sFile, SamplesBounds, iChannels);
    case {'NWB', 'NWB-CONTINUOUS'}
        isContinuous = strcmpi(sFile.format, 'NWB-CONTINUOUS');
        F = in_fread_nwb(sFile, iEpoch, SamplesBounds, iChannels, isContinuous);
    case 'MNE-PYTHON'
        [F, TimeVector] = in_fread_mne(sFile, ChannelMat, iEpoch, SamplesBounds, iChannels);
    otherwise
        error('Cannot read data from this file');
end

% Force the recordings to be in double precision
if ~isempty(precision) && strcmp(precision, 'single')
    F = single(F);
else
    F = double(F);
end
% Remove channels that were not supposed to be read
if isChanRange && ~isempty(iChanRemove)
    F(iChanRemove,:) = [];
end

%% ===== CLOSE FILE =====
if ~isempty(sfid) && ~isempty(fopen(sfid))
    fclose(sfid);
end

%% ===== TIME =====
% If TimeVector was not defined by the reading functions
if isempty(TimeVector)
    if ~isempty(SamplesBounds)
        TimeVector = (SamplesBounds(1) : SamplesBounds(2)) ./ sFile.prop.sfreq;
    elseif ~isempty(iEpoch) && ~isempty(ImportOptions) && strcmpi(ImportOptions.ImportMode, 'Epoch') && ~isempty(sFile.epochs)
        epochSamples = round(sFile.epochs(iEpoch).times * sFile.prop.sfreq);
        TimeVector = (epochSamples(1) : epochSamples(2)) / sFile.prop.sfreq;
    else
        fileSamples = round(sFile.prop.times * sFile.prop.sfreq);
        TimeVector = (fileSamples(1) : fileSamples(2)) / sFile.prop.sfreq;
    end
end
% If epoching the recordings (ie. reading by events): Use imported time window
if ~isempty(ImportOptions) && strcmpi(ImportOptions.ImportMode, 'Event')
    % TimeVector = TimeVector - TimeVector(1) + ImportOptions.EventsTimeRange(1);
    evtOffset = round(ImportOptions.EventsTimeRange(1) * sFile.prop.sfreq) / sFile.prop.sfreq;
    TimeVector = TimeVector - TimeVector(1) + evtOffset;
end

%% ===== GRADIENT CORRECTION =====
% 3rd-order gradient correction
if ~isempty(ImportOptions) && ImportOptions.UseCtfComp && ~strcmpi(sFile.format, 'BST-DATA') && ~isempty(ChannelMat) && ~isempty(ChannelMat.MegRefCoef) && ~isempty(sFile.prop.currCtfComp) && ~isequal(sFile.prop.currCtfComp, sFile.prop.destCtfComp)
    AllSensorTypes = unique({ChannelMat.Channel(iChannels).Type});
    if isempty(AllSensorTypes) || any(ismember(AllSensorTypes, {'MEG','MEG REF','MEG GRAD','MEG MAG'}))
        iMeg = good_channel(ChannelMat.Channel,[],'MEG');
        iRef = good_channel(ChannelMat.Channel,[],'MEG REF');
        if ~isempty(iChannels) && (length(iChannels) ~= length(ChannelMat.Channel))
            error('CTF compensators require that you read all the channels at the same time.');
        else
            F(iMeg,:) = F(iMeg,:) - ChannelMat.MegRefCoef * F(iRef,:);
        end
    end
end

%% ===== SSP PROJECTORS =====
if ~isempty(ImportOptions) && ImportOptions.UseSsp && ~strcmpi(sFile.format, 'BST-DATA') && ~isempty(ChannelMat) && ~isempty(ChannelMat.Projector)
    % Build projector matrix
    Projector = process_ssp2('BuildProjector', ChannelMat.Projector, 1);
    % Apply projector
    if ~isempty(Projector)
        % Get bad channels
        iBadChan = find(sFile.channelflag == -1);
        % Remove bad channels from the projector (similar as in process_megreg)
        if ~isempty(iBadChan)
            Projector(iBadChan,:) = 0;
            Projector(:,iBadChan) = 0;
            Projector(iBadChan,iBadChan) = eye(length(iBadChan));
        end
        % Apply projector
        if ~isempty(iChannels)
            % Channels that are modified by projector.
            isProjected = sum(Projector ~= 0, 2) > 1;
            if any(isProjected(iChannels))
                % If there are projectors involved and only subselection of channels:
                % We must have all data needed to apply the projector, otherwise it doesn't make sense
                missingChannels = setdiff(find(any(Projector(iChannels,:), 1)), iChannels);
                if ~isempty(missingChannels)
                    bst_report('Warning', 'process_import_data_raw', [], ['Missing channels in order to apply existing SSP/ICA projectors. To read the corrected values for channel "' ChannelMat.Channel(iChannels(1)).Name '", first apply the existing projectors with the process Artifacts > Apply SSP and CTF compensation']);
                else
                    F = Projector(iChannels, iChannels) * F;
                end
            end
        else
            F = Projector * F;
        end
    end
end

%% ===== REMOVE BASELINE ======
if ~isempty(ImportOptions) && ~isempty(ImportOptions.RemoveBaseline)
    % Get times to compute the baseline
    switch (ImportOptions.RemoveBaseline)
        case 'all'
            iTimesBl = 1:length(TimeVector);
        case 'time'
            iTimesBl = find((TimeVector >= ImportOptions.BaselineRange(1)) & (TimeVector <= ImportOptions.BaselineRange(2)));
        case 'no'
            iTimesBl = [];
    end
    % Remove baseline
    if ~isempty(iTimesBl)
        % Exclude system channels from the baseline correction
        if ~isempty(ChannelMat) && ~isempty(ChannelMat.Channel)
            iChanBl = find(~ismember(lower({ChannelMat.Channel.Type}), {'stim','video','sysclock'}));
        else
            iChanBl = 1:size(F,1);
        end
        % Sensor selection for the baseline correction
        if ~isempty(ImportOptions) && isfield(ImportOptions, 'BaselineSensorType') && ~isempty(ImportOptions.BaselineSensorType)
            % Select channels based on sensor types (or names) for baseline correction
            iChanBlSensorType = channel_find(ChannelMat.Channel, ImportOptions.BaselineSensorType);
            % Selected channels minus excluded
            iChanBl = intersect(iChanBlSensorType, iChanBl);
        end
        % Compute baseline
        blValue = mean(F(iChanBl,iTimesBl), 2);
        % Remove from recordings
        % F(iChanBl,:) = F(iChanBl,:) - repmat(blValue, [1,size(F,2)]);
        F(iChanBl,:) = bst_bsxfun(@minus, F(iChanBl,:), blValue);
    end
end

%% ===== RESAMPLE =====
if ~isempty(ImportOptions) && ImportOptions.Resample && (size(F,2) > 1) && (abs(ImportOptions.ResampleFreq - sFile.prop.sfreq) > 0.05)
    [F, TimeVector] = process_resample('Compute', F, TimeVector, ImportOptions.ResampleFreq);
end


% Script for loading images LoadImages.mat and retrieve HOG

%Resizes the image to 4 times its value
%Look at the HOG features obtained to understand need for this step
%Stores features in .mat file so we dont have to run this repeatedly
function [Features] = LoadImages()
	Features = [];
	for j = 1:2
		num = num2str(j);
		str = strcat('../CIFAR10/small_data_batch_',num,'.mat');
		load(str);
		Features = vertcat(Features,data);
    end
end


function endianFlag = LittleEndianCheck(mrSESSION);
%
% endianFlag = LittleEndianCheck(mrSESSION);
%
% Code for Initializing data collected at Stanford Lucas Center:
% Checks whether functional data is likely to use big- or
% little-endian format. The issue is this: around March 1,
% 2005, the 3T scanner was updated, and Gary Clover's spiral
% recon code updated, to use Little-Endian format. Prior to
% this, it was big-endian. This code simply checks the
% date stored for the first functional in mrSESSION, compares
% it against March 1, and sets the endian flag appropriately
% for downstream reconning.
%
% However, since there's a chance older data may be reconned
% using the newer little-endian scripts, if the date is
% before 03/01/05, the code prompts the user first.
%
% If endianFlag is 1, downstream code will treat functionals
% as little-endian; otherwise, will treat it as big-endian.
%
% ras, 05/05
if ~checkfields(mrSESSION,'functionals','date')
    endianFlag = 1; % little endian
    return
end

date = mrSESSION.functionals(1).date;
mo = str2num(date(1:2));
da = str2num(date(4:5));
yr = str2num(date(7:8));

if (yr==5 & mo<3) | (yr<5)
    % older data: prompt
    q = ['This data is before March 2005, so is likely ' ...
         'in Big-Endian format. However, it''s possible ' ...
         'that it''s in Little-Endian format, e.g. if the P*.7 ' ...
         'files were reconned later. How should the data ' ...
         'be read?   (type "help LittleEndianCheck" for more info)'];
    resp = questdlg(q,'mrInitRet','LittleEndian','BigEndian',...
                      'Quit','BigEndian');
    switch lower(resp)
        case 'littleendian', endianFlag = 1;
        case 'bigendian', endianFlag = 0;
        otherwise, error('User aborted');
    end
else
    % newer data: little endian
    endianFlag = 1;
end

return


function res=read_cell(filename,linesep,cellsep)
if nargin < 2, linesep='\n'; end
if nargin < 3, cellsep = '\t'; end
if exist(filename,'file')
    fid = fopen(filename);
else
    % Assume that filename is either a file ide or a string
    fid = filename;
end

fileLines = textscan(fid,'%s','delimiter',linesep,'BufSize',100000);

fileLines = fileLines{1};

if regexp(fileLines{1},cellsep,'once')
    fileLines = regexprep(fileLines,['^' cellsep '|' cellsep '$'],'');
    res = regexp(fileLines,cellsep,'split');
    res = cell2matcell(res);
else
    res = fileLines;
end


function pool_params = load_pooling_params(pool_params_def, varargin)
% load_pooling_params(pool_params_def,...): it initializes the struct that
% contains the adaptive region max pooling parameters and the parameters
% related to the region type
%
% INPUTS:
% 1) pool_params_def: string with path to the configuration file that
% contains the adaptive region max pooling parameters
% The rest input arguments are given in the form of Name,Value pair
% arguments and are related to the region type:
% 'scale_inner': scalar value with the scaling factor of the inner rectangle
% of the region. In case this value is 0 then actually no inner rectangle
% is being used
% 'scale_outer': scalar value with the scaling factor of the outer rectangle
% of the region.
% 'half_bbox': intiger value in the range [1,2,3,4]. If this parameter is set
% to 1, 2, 3, or 4 then each bounding box will be reshaped to its left,
% right, top, or bottom half part correspondingly. This action is performed
% prior to scaling the box according to the scale_inner and scale_outer
% params. If this parameter is missing or if it is empty then the action of
% taking the half part of bounding box is NOT performed.
%
% OUTPUT:
% 1) pool_params: struct that contains the adaptive region max pooling
% parameters and the parameters related to the region type
%
% This file is part of the code that implements the following ICCV2015 accepted paper:
% title: "Object detection via a multi-region & semantic segmentation-aware CNN model"
% authors: Spyros Gidaris, Nikos Komodakis
% institution: Universite Paris Est, Ecole des Ponts ParisTech
% Technical report: http://arxiv.org/abs/1505.01749
% code: https://github.com/gidariss/mrcnn-object-detection
%
% Part of the code in this file comes from the SPP-Net code:
% https://github.com/ShaoqingRen/SPP_net
%
% AUTORIGHTS
% --------------------------------------------------------
% Copyright (c) 2015 Spyros Gidaris
%
% "Object detection via a multi-region & semantic segmentation-aware CNN model"
% Technical report: http://arxiv.org/abs/1505.01749
% Licensed under The MIT License [see LICENSE for details]
% ---------------------------------------------------------
% Copyright (c) 2014, Shaoqing Ren
%
% This file is part of the SPP code and is available
% under the terms of the Simplified BSD License provided in
% LICENSE. Please retain this notice and LICENSE if you use
% this file (or any portion of it) in your project.
% ---------------------------------------------------------

ip = inputParser;
ip.addParamValue('scale_inner',   [], @isnumeric);
ip.addParamValue('scale_outer',   [], @isnumeric);
ip.addParamValue('half_bbox',     [], @isnumeric);

ip.parse(varargin{:});
opts = ip.Results;

%% Read the adaptive region max pooling parameters from the configuration file
[~, ~, ext] = fileparts(pool_params_def);
if isempty(ext), pool_params_def = [pool_params_def, '.m']; end
assert(exist(pool_params_def, 'file') ~= 0);

cur_dir = pwd; % change folder to avoid too long path for eval()
[pool_def_dir, pool_def_file] = fileparts(pool_params_def);

cd(pool_def_dir);
pool_params = eval(pool_def_file);
cd(cur_dir);

%% Set the parameters related to the region type
pool_params.scale_inner = opts.scale_inner;
pool_params.scale_outer = opts.scale_outer;
pool_params.half_bbox   = opts.half_bbox;
end


function [status]=mrLoadRetAnat2SPM(fileRoot)
%function [status]=mrLoadRetAnat2SPM(fileRoot)

global INPLANE;

if (~exist('INPLANE','var'))
    error('INPLANE Structure must exist');
end
if (isempty(INPLANE.anat))
    error('INPLANE.anat must contain data: select view->croppedAnatomy in mrLoadRet to get the anatomies up');
end
if (~exist('fileRoot','var'))
    error('You must enter a file root: e.g. //gwyn/u1/mri/sampleSet/012345/vSPM_test');
end

outDat=INPLANE.anat;

% This is a script to dump out the current INPLANE.anat data in Analyse format so that you can load it in to SPM
% We make some effort to ensure that it conforms to the expected orientation of SPM data
% In the INPLANE structure it's stored as x*y*nSlices
% Now we can't guarantee that we're going to get the orientation right (becasue we're not yet at the stage where we can align funcitonal data to the
% SPM template) but we can try to at least get the nSlices direction right so that the 'coronal' views more or less line up for mrLoadRet and SPM

% Now, just call writeAnatFileSet
fileVector=writeAnatFileSet(outDat,fileRoot);


function [dKSpace, measPara, file] = fLoadkSpace(path)
%% Load kSpace data
%
% (c) Martin Schwartz and Thomas Kuestner
% -------------------------------------------------------------------------

    file='';
    warning('off','MATLAB:dispatcher:nameConflict');
    para = 0;
    % prepare m-files[
    currpath = fileparts(mfilename('fullpath'));

    % load ADCs
    if(ischar(path))
        if(isdir(path))
            [file, path] = uigetfile({'*.mat;*.dat', 'ADC measdata (*.mat, *.dat)'} ,'Select ADC measdata file', 'MultiSelect', 'off', path);
            if(isequal(file,0))
                error('CS_reconstruction(): User Termination');
            end
            [~,filename,ext] = fileparts(file);
        else
            [path, filename, ext] = fileparts(path);
        end
        lReadFromFile = true;
    else
        lReadFromFile = false;
    end

    % read inputs
    if(lReadFromFile)
        if(exist(fullfile(path,[filename,'.mat']),'file'))
            matfile = whos('-file', fullfile(path,[filename,'.mat']));
        end
        if(exist(fullfile(path,[filename,'.mat']),'file') && ~ismember('iLC', {matfile.name}))
            % load mat file to check if it contains iLC/iSP or something else
            data = load(fullfile(path,[filename,'.mat']));
            kSpace = data.kSpace;
            measPara.dim = zeros(1,5);
            measPara.LCall = zeros(1,4);
            dimNames = {'y (phase)', 'x (frequency)', 'z (phase)', 't (time)', 'channels'};
            LCallNames = {'slices', 'acquisitions', 'echos', 'repetitions'};
            dimDefault = ones(1,5);
            LCallDefault = [size(kSpace,1), size(kSpace,4), size(kSpace,5), size(kSpace,3)];
            tmp = size(kSpace{1});
            if(LCallDefault(1) > 1 && length(tmp) > 2)
                dimDefault(1:2) = tmp(1:2);
                dimDefault(4) = tmp(3);
            else
                dimDefault(1:length(tmp)) = tmp;
            end
            dimDefault(5) = size(kSpace,2);
            if(isfield(data,'dim') && isfield(data,'LCall'))
                measPara.dim = data.dim;
                measPara.LCall = data.LCall;
            elseif(isfield(data,'dim') && ~isfield(data,'LCall'))
                measPara.dim = data.dim;
                fprintf('Please specify amount of:\n');
                for i=1:length(measPara.LCall)
                    helper = input(sprintf('%s [%d]: ',LCallNames{i}, LCallDefault(i)),'s');
                    if(isempty(helper)), helper = LCallDefault(i); end;
                    measPara.LCall(i) = helper;
                end

            elseif(~isfield(data,'dim') && isfield(data,'LCall'))
                measPara.LCall = data.LCall;
                fprintf('Please specify dimensions:\n');
                for i=1:length(measPara.dim)
                    helper = input(sprintf('%s [%d]: ',dimNames{i}, dimDefault(i)),'s');
                    if(isempty(helper)), helper = dimDefault(i); end;
                    measPara.dim(i) = helper;
                end

            else
                fprintf('Please specify dimensions:\n');
                for i=1:length(measPara.dim)
                    helper = input(sprintf('%s [%d]: ',dimNames{i}, dimDefault(i)),'s');
                    if(isempty(helper)), helper = dimDefault(i); end;
                    measPara.dim(i) = helper;
                end
                fprintf('Please specify amount of:\n');
                for i=1:length(measPara.LCall)
                    helper = input(sprintf('%s [%d]: ',LCallNames{i}, LCallDefault(i)),'s');
                    if(isempty(helper)), helper = LCallDefault(i); end;
                    measPara.LCall(i) = helper;
                end
            end
            clear 'LCallDefault' 'dimDefault';
            loadedVars = {'dimension', 'oversampling', 'measPara', 'iLC', 'iLCPositions', 'drecksMDH'};
            for i=1:length(loadedVars)
                if(isfield(data,loadedVars{i}))
                    if(any(strcmp(loadedVars{i},{'dimension','oversampling'})))
                        eval(sprintf('measPara.%s = data.%s;', loadedVars{i}, loadedVars{i}));
                    else
                        eval(sprintf('%s = data.%s;', loadedVars{i}, loadedVars{i}));
                    end
                else
                    if(strcmp(loadedVars{i},'measPara'))
                        continue;
                    end
                    eval(sprintf('%s = [];', loadedVars{i}));
                end
            end
            clear 'data';
        else
            [dData, iLC, iEvalInfoMask, drecksMDH, iLCPositions] = fMeas_main(fullfile(path, [filename, ext]));
            measPara = [];
        end
    end

    %% get data dimension and loop counters
    espresso.reconType = 1;
    postproc.aniso = [];
    [measPara, ~, ~] = extractMeasPara( iLC, measPara, espresso, postproc, logical([0 0 0]), drecksMDH, iLCPositions );

    %% extract kSpace data from measurement file
    [kSpace, ~] = extractKSpace_main(dData, iLC, iEvalInfoMask, measPara);

    dKSpace = zeros([size(kSpace{1}),size(kSpace,2)]);
    for iI = 1:size(kSpace,2)
        dKSpace(:,:,:,iI) = kSpace{iI};
    end
    % y-x-z-c -> x-y-z-c
    dKSpace = permute(dKSpace, [2 1 3 4]);

end