%Translation
%
% @wholeCellModelID Process_Translation
% @name             Translation
% @description
%   Biology
%   ===============
%   Translation is the first step in the synthesis of function proteins whereby
%   the ribosome, accessory enzymes, and tRNAs transcode mRNAs and produce
%   amino acid polymers. Following translation proteins must be matured to
%   become fully functional:
%    1. Protein Processing I: N-terminal methionine deformylation and cleavage
%    2. Protein Translocation: Translocation of integral membrane proteins,
%       lipoproteins, and secreted proteins into and through the cell membrane
%    3. Protein Processing II: Diacylglyceryl addition to lipoproteins. Signal
%       sequence cleavage of lipo- and secreted proteins.
%    4. Protein Folding: Folding and prosthetic group coordination of proteins.
%    5. Protein Modification: Addition of covalently attached chemical groups to
%       proteins.
%    6. Macromolecular Complexation: Incorporation of protein monomers into
%       larger macromolecular assemblies.
%    7. Ribosome Assembly: Special case of macromolecular complexation of the
%       ribosome which requires GTPases.
%    8. Terminal Organelle Assembly: Special case of translocation which
%       sequesters several protein species in a membrane-bound bleb referred to
%       as the terminal organelle.
%    9. Protein Activation: Functional activation and inactivation of proteins
%       in response to their surrounding chemical environment.
%
%   Translation begins with the recruitment of the 30S and 50S ribosomal
%   particles and initiation factor 3 (IF3) to an mRNA molecule. Next the
%   ribosomal particles assembly into a 70S ribosome on the mRNA molecule with
%   the help of initiation factors. Third the ribosome polymerizes amino
%   acids with the help of elongation factors. Finally release factor
%   (MG_258) recognizes the stop codon UAG, release factor hydolyzes the
%   peptidyl tRNA bond and dissociates, ribosome recycling factor
%   dissociates the E-site tRna, elongation factor G release the release
%   factor and 50S ribosome, and initiation factor 3 dissociates the 30S
%   ribosome, P-site tRNA, and mRNA.
%
%   When a ribosome stalls its lastest tRNA is expelled and replaced by the
%   tRNA-like domain of a tmRNA molecule. Next the mRNA-like domain of the tmRNA
%   expels the bound mRNA. Third the ribosome resumes polymerization, now
%   using the tmRNA's mRNA-like domain as its template. This results in the
%   production of an amino acid polymer containing a C-terminal proteolysis tag.
%   Finally, the proteolysis tag will be recognized by the protein degradation
%   machinery, and the amino acid polymer will be degraded into its individual
%   component amino acids.
%
%   This process simulates protein translation by ribosomes and acessory
%   initiation, elongation, and termination factors. The process also simulates
%   the identification of stalled ribosomes by the tmRNA, the replacement of the
%   tRNA and mRNA with the tmRNA, and the synthesis of the proteolysis tag
%   encoded by the tmRNA's mRNA-like domain.
%
%   Knowledge Base
%   ===============
%   The knowledge base contains the region of the genome each mRNA species
%   corresponds to, and the location of the tmRNA mRNA-like domain on the
%   genome. This information is converted into a sequence of tRNAs which are
%   required to polymerize each protein and proteolysis tag by the knowledge
%   base class' computeTRNASequences method.
%
%   Representation
%   ===============
%   The properties substrates, enzymes, monomers, mRNAs, freeTRNAs,
%   aminoacylatedTRNAs, and aminoacylatedTMRNA represents the counts of
%   metabolites, translation enzymes, nascent peptides, mature mRNAs, free
%   tRNAs, aminoacylated tRNAs, and aminoacylated tmRNAs.
%
%   monomerTRNASequences and tmRNAProteolysisTagTRNASequence represent the
%   sequences of tRNAs required to polymerize each protein and proteolysis tag.
%   These tRNA sequences are computed from the DNA sequence of the mRNA gene /
%   tmRNA gene by the knowledge  base classs' computeTRNASequences method.
%
%   ribosomeStates represents the state/pseudostate (actively translating, free,
%   non-existent) of each ribosome. boundMRNAs indicates the mRNA
%   species each actively translating ribosome is bound to. The non-existent
%   pseudostate is used to keep track of elements in ribosomeStates
%   and boundMRNAs which have been allocated.
%   nascentMonomerLengths and proteolysisTagLengths represeent
%   the position of actively  translating ribosomes on mRNAs, and if stalled on
%   a tmRNA.
%
%   Initialization
%   ===============
%   All protein monomers are initialized to the mature state, and in their
%   correct localization. This is achieved by the simulation class
%   initializeState method.
%
%   Ribosomes are initialized to their steady state:
%   - Each ribosome is randomly assigned (without replacement) to mRNA species,
%     weighted by the current expression of the mRNAs,.
%   - Each ribosome is randomly assigned to positions within the assigned mRNA
%     with uniform probably.
%   - No ribosomes are initialized to the stalled state, since the expected
%     occupancy is negligible. No tmRNAs are initialized to the bound state.
%
%   tRNAs and tmRNAs are initialized to the aminoacyated state by
%   simulation.initializeState.
%
%   Simulation
%   ===============
%   Evolves the states of ribosomes among two states:
%   - actively translating
%   - free
%
%   Transition to from the free state to the actively translating state is
%   allowed when
%   - ribosome binding factor A is present
%   - initiation factors (IF-1, IF-2, and IF-3) are present
%   - one unit of energy (GTP) is available.
%   At this time the ribosome binds an mRNA randomly with uniform probability.
%
%   At the next iteration the initiation factors are released when the first
%   amino acid, f-methionine binds and elongation begins, assuming that
%   elongation factors (EF-tu, TS, and G) and energy (GTP) is available.
%
%   ASSUMPTION made here is that one of each is sufficient for each ribosome,
%   but need a separate set for each ribosome. So each ribosome can only
%   translate if a full set exists for it. Also, for cases in which
%   translation of a peptide finishes partway through the time step, the EFs
%   are free, but haven't added the complexity of another ribosome being able
%   to take up EFs partway through the timestep.
%
%   Finally, once all amino acids of a protein have been translated,
%   termination occurs if
%   - at least one terminator (RF-1) available
%   - at least one recycling factor available
%   - at least one elongation factor G available
%   - at least one trigger factor available
%   - energy is available.
%   The ribosome will be available to bind mRNA at the following iteration.
%
%   Ribosomes are created in the free state.
%
%   Algorithm
%   +++++++++++++++
%   1. Up to limit of elongation factors, randomly select actively translating
%      ribosomes to elongate.
%   2. Up to limit of initiation factors and energy, randomly select ribosomal
%      particles to initiatate. These ribosome will be able to start elongating
%      at the next time step. Randomly select mRNA species for each initiating
%      mRNA to bind to, weighted by the counts of each mRNA species. Update
%      ribosomeStates, boundMRNAs. Update substrates.
%   3. Allocate available amino acids and energy among actively translating
%      ribosomes. Update ribosome nascentMonomerLengths, or
%      proteolysisTagLengths for stalled ribosomes with the number of
%      polymerized bases. Update substrates.
%   4. If ribosome has reached end of (t)mRNA and termination factors
%      available, increase count of protein and dissolve (t)mRNA-ribosome
%      complex. Update substrates.
%   5. If ribosome hasn't advanced, then with a small probability transition
%      ribosome to stalled state. Expel mRNA and replace with tmRNA. Update
%      ribosomeStates. Update substrates.
%
%   References
%   ===============
%   1. Petry S, Weixlbaumer A, Ramakrishnan V (2008). The termination of
%      translation. Curr Opin Struct Biol. 18(1):70-7. [PUB_0226].
%
% 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: 1/4/2010

%TODO: Bind specific mRNAs rather than kinds of mRNAs.
%
%TODO: Prevent ribosomes from colliding/overlapping on an mRNA. Currently this
%      is only somewhat ensured by allowing only one ribosome to bind each mRNA
%      per time step.
%
%TODO: Associate elongation factors to mRNA for just 1 amino acid, not
%      elongationRate amino acids. With this change, all mRNAs will
%      translate at each second but with a slower rate. Related to the
%      assumption above. That is we should translate total of min(energy,
%      min(EFs)*rate*this.simulationTime) amino acids, where rate is a
%      rate of EF recycling per second, that is how many times an
%      individual EF can be used per second.
classdef Translation < edu.stanford.covert.cell.sim.Process
    %property annotations
    properties (Constant)
        optionNames__              = {}; %names of option properties
        fixedConstantNames__       = {   %names of fixed constant properties
            'ribosomeElongationRate'
            'tmRNABindingProbability'
		    };
        fittedConstantNames__      = {}; %names of fitted constant properties
        localStateNames__          = {   %names of simulation state properties redundant with timecourses in this or other processes or the simulation
            'mRNAs'
            'freeTRNAs'
            'aminoacylatedTRNAs'
            'aminoacylatedTMRNA'
            'monomers'
            };
    end

    %IDs, names, and local indices
    properties
        stimuliWholeCellModelIDs = {}; %whole cell model IDs of stimuli

        substrateWholeCellModelIDs = { %whole cell model IDs of substrates
            'ALA'; 'ARG'; 'ASN'; 'ASP'; 'CYS'; 'GLN'; 'GLU'; 'GLY'; 'HIS'; 'ILE';
            'LEU'; 'LYS'; 'MET'; 'PHE'; 'PRO'; 'SER'; 'THR'; 'TRP'; 'TYR'; 'VAL'; 'FMET';
            'GTP'; 'GDP'; 'PI'; 'H2O'; 'H'};
        substrateIndexs_aminoAcids  = (1:20)'; %indices within substrates of amino acids
        substrateIndexs_fMet        = 21;      %index within substrates of formylmethionine
        substrateIndexs_gtp         = 22;      %index within substrates of GTP
        substrateIndexs_gdp         = 23;      %index within substrates of GDP
        substrateIndexs_phosphate   = 24;      %index within substrates of phosphate
        substrateIndexs_water       = 25;      %index within substrates of water
        substrateIndexs_hydrogen    = 26;      %index within substrates of hydrogen

        enzymeWholeCellModelIDs = { %whole cell model ids of enzymes
            'MG_173_MONOMER';   %translation initiation factor IF-1
            'MG_142_MONOMER';   %translation initiation factor IF-2
            'MG_196_MONOMER';   %translation initiation factor IF-3
            'MG_089_DIMER';     %translation elongation factor G
            'MG_026_MONOMER';   %translation elongation factor P
            'MG_451_DIMER';     %translation elongation factor Tu
            'MG_433_DIMER';     %translation elongation factor Ts
            'MG_258_MONOMER';   %peptide chain release factor 1
            'MG_435_MONOMER';   %ribosome recycling factor
            'RIBOSOME_30S';     %30S ribosomal subunit
            'RIBOSOME_30S_IF3'; %30S ribosomal subunit - translation initiation factor IF-3 complex
            'RIBOSOME_50S';     %50S ribosomal subunit
            'RIBOSOME_70S';     %70S ribosome
            'MG_0004';          %transfer-messenger RNA, tmRNA, MCS6, 10Sa RNA
            'MG_059_MONOMER';   %SsrA-binding protein
            'MG_083_MONOMER'};  %peptidyl-tRNA hydrolase
        enzymeIndexs_translationFactors          = (1:9)';    %indices within enzymes of translation factors
        enzymeIndexs_initiationFactors           = (1:3)';    %indices within enzymes of initiation factors
        enzymeIndexs_initiationFactor1           = 1;         %index within enzymes of initiation factor 1
        enzymeIndexs_initiationFactor2           = 2;         %index within enzymes of initiation factor 2
        enzymeIndexs_initiationFactor3           = 3;         %index within enzymes of initiation factor 3
        enzymeIndexs_elongationFactors           = (4:7)';    %indices within enzymes of elongation factors
        enzymeIndexs_elongationGFactor           = 4;         %index within enzymes of elongation factor G
        enzymeIndexs_terminationFactor           = 8;         %index within enzymes of termination factor
        enzymeIndexs_recyclingFactor             = 9;         %index within enzymes of recylcing factor
        enzymeIndexs_ribosome30S                 = 10;        %index within enzymes of 30S ribosomal particle
        enzymeIndexs_ribosome30SIF3              = 11;        %index within enzymes of 30S ribosomal particle-initiation factor 3 complex
        enzymeIndexs_ribosome50S                 = 12;        %index within enzymes of 50S ribosomal particle
        enzymeIndexs_ribosome70S                 = 13;        %index within enzymes of 70S ribosome
        enzymeIndexs_abortiveTranslationEnzymes  = (14:16)';  %indices within enzymes of abortive translation enzymes
        enzymeIndexs_tmRNA                       = 14;        %index within enzymes of tmRNA
        enzymeIndexs_tmRNABindingProtein         = 15;        %index within enzymes of tmRNA binding protein
        enzymeIndexs_peptidylTRNAHydrolase       = 16;        %index within enzymes of peptidyl-tRNA hydrolase

        freeTRNAWholeCellModelIDs          %IDs of free (non-aminoacylated) tRNAs
        aminoacylatedTRNAWholeCellModelIDs %IDs of aminoacylated tRNAs
        aminoacylatedTMRNAWholeCellModelID %ID of aminoacylated tmRNA

        tmRNAGlobalIndex %index of tmRNA within simulation.matureRNAIndexs
    end

    %fixed biological constants
    properties
        ribosomeElongationRate             %ribosomse elongation rate (amino acids per second per ribosome) [PUB_0564]
        tmRNABindingProbability            %probability that tmRNA is recruited to non-elongating, active ribosome
    end

    %global state (stored locally for convenience)
    properties
        monomers                       %numbers of protein monomers
        mRNAs                          %numbers of mRNAs
        freeTRNAs                      %numbers of free (non-aminoacylated) tRNAs
        freeTMRNA                      %numbers of free tmRNA
        aminoacylatedTRNAs             %numbers of aminoacylated tRNAs
        aminoacylatedTMRNA             %numbers of aminoacylated tmRNA
        boundTMRNA                     %numbers of bound tmRNA
    end

    %global state (referenced locally for convenience)
    properties
        polypeptide                    %New polypeptides state class
        ribosome                       %Ribosome state class
    end

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

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

            this.polypeptide = simulation.state('Polypeptide');
            this.ribosome = simulation.state('Ribosome');
            this.states = [this.states; {this.polypeptide; this.ribosome}];
        end

        %initialize constants
        function initializeConstants(this, knowledgeBase, simulation, varargin)
            this.initializeConstants@edu.stanford.covert.cell.sim.Process(...
                knowledgeBase, simulation, varargin{:});

            %tmRNA
            this.tmRNAGlobalIndex = this.enzymeRNAGlobalIndexs(this.enzymeRNALocalIndexs==this.enzymeIndexs_tmRNA);

            %RNA
            this.freeTRNAWholeCellModelIDs          = this.rna.wholeCellModelIDs(this.rna.matureIndexs(this.rna.matureTRNAIndexs));
            this.aminoacylatedTRNAWholeCellModelIDs = this.rna.wholeCellModelIDs(this.rna.aminoacylatedIndexs(this.rna.matureTRNAIndexs));
            this.aminoacylatedTMRNAWholeCellModelID = this.rna.wholeCellModelIDs(this.rna.aminoacylatedIndexs(this.tmRNAGlobalIndex));
        end

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

            r = this.rna;
            m = this.monomer;

            if size(r.counts, 3) == 1
                this.mRNAs = ...
                    r.matureRNAGeneComposition(this.gene.mRNAIndexs, r.matureMRNAIndexs) * ...
                    r.counts(r.matureIndexs(r.matureMRNAIndexs), this.compartment.cytosolIndexs);
            else
                this.mRNAs = multiprod(...
                    r.matureRNAGeneComposition(this.gene.mRNAIndexs, r.matureMRNAIndexs), ...
                    r.counts(r.matureIndexs(r.matureMRNAIndexs), this.compartment.cytosolIndexs, :), [1 2], 1);
            end
            this.monomers           = m.counts(m.nascentIndexs, this.compartment.cytosolIndexs, :);
            this.freeTRNAs          = r.counts(r.matureIndexs(this.rna.matureTRNAIndexs), this.compartment.cytosolIndexs, :);
            this.freeTMRNA          = r.counts(r.matureIndexs(this.tmRNAGlobalIndex), this.compartment.cytosolIndexs, :);
            this.aminoacylatedTRNAs = r.counts(r.aminoacylatedIndexs(this.rna.matureTRNAIndexs), this.compartment.cytosolIndexs, :);
            this.aminoacylatedTMRNA = r.counts(r.aminoacylatedIndexs(this.tmRNAGlobalIndex), this.compartment.cytosolIndexs, :);
            this.boundTMRNA         = r.counts(r.boundIndexs(this.tmRNAGlobalIndex), this.compartment.cytosolIndexs, :);
        end

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

            r = this.rna;
            m = this.monomer;

            m.counts(m.nascentIndexs, this.compartment.cytosolIndexs, :)  = this.monomers;
            r.counts(r.matureIndexs(this.rna.matureTRNAIndexs), this.compartment.cytosolIndexs, :) = this.freeTRNAs;
            r.counts(r.matureIndexs(this.tmRNAGlobalIndex), this.compartment.cytosolIndexs, :) = this.freeTMRNA;
            r.counts(r.aminoacylatedIndexs(this.rna.matureTRNAIndexs), this.compartment.cytosolIndexs, :) = this.aminoacylatedTRNAs;
            r.counts(r.aminoacylatedIndexs(this.tmRNAGlobalIndex), this.compartment.cytosolIndexs, :) = this.aminoacylatedTMRNA;
            r.counts(r.boundIndexs(this.tmRNAGlobalIndex), this.compartment.cytosolIndexs, :) = this.boundTMRNA;
        end
    end

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

            this.monomers           = zeros(length(this.polypeptide.monomerLengths), 1, numTimePoints);
            this.mRNAs              = zeros(length(this.gene.mRNAIndexs), 1, numTimePoints);
            this.freeTRNAs          = zeros(length(this.rna.matureTRNAIndexs), 1, numTimePoints);
            this.freeTMRNA          = zeros(1, 1, numTimePoints);
            this.aminoacylatedTRNAs = zeros(length(this.rna.matureTRNAIndexs), 1, numTimePoints);
            this.aminoacylatedTMRNA = zeros(1, 1, numTimePoints);
            this.boundTMRNA         = zeros(1, 1, numTimePoints);
        end
    end

    %model
    methods
        %Calculate
        %- contribution to FBA objective
        %- minimum expression consistent with cell cycle length
        %
        %Contributions, by process, to protein synthesis:
        %                            AAs --(translation)----> nascent monomer + water
        %          nascent monomer + H2O --(processing)-----> processed protein + AAs
        %processed monomer + metabolites --(folding)--------> folded monomer
        %   folded monomer + metabolites --(modification)---> mature monomer + metabolites
        %              modified monomers --(complexation)---> nascent complexs
        % nascent complexs + metabolites --(folding)--------> mature complexes
        %          monomer/complex + H2O --(decay)----------> AAs + modified AAs
        %        phosphorylated AA + H2O --(catabolism)-----> AA + Pi
        %            lipoyl lysine + H2O --(catabolism)-----> lysine + lipoate
        function [bmProd, byProd, minEnzExp, maxEnzExp] = calcResourceRequirements_LifeCycle(this, ~, states)
            %% initialize
            bmProd = zeros(size(this.substrateWholeCellModelIDs));
            byProd = zeros(size(this.substrateWholeCellModelIDs));
            minEnzExp = zeros(size(this.enzymeWholeCellModelIDs));
            maxEnzExp = Inf(size(this.enzymeWholeCellModelIDs));

            %% substrate and byproducts

            %breaking tRNA-peptidyl bond
            bmProd(this.substrateIndexs_water) = ...
                bmProd(this.substrateIndexs_water) + ...
                this.polypeptide.monomerLengths' * states.monomerProductions;
            byProd(this.substrateIndexs_hydrogen) = ...
                byProd(this.substrateIndexs_hydrogen) + ...
                this.polypeptide.monomerLengths' * states.monomerProductions;

            %peptide bond formation
            byProd(this.substrateIndexs_water) = ...
                byProd(this.substrateIndexs_water) + ...
                max(0, this.polypeptide.monomerLengths - 1)' * states.monomerProductions;

            %energy
            translationEnergy = ...
                2 * this.polypeptide.monomerLengths' * states.monomerProductions + ...  %elongation
                3 * sum(states.monomerProductions);                                     %initiation, termination
            bmProd(this.substrateIndexs_gtp)       = bmProd(this.substrateIndexs_gtp)       + translationEnergy;
            bmProd(this.substrateIndexs_water)     = bmProd(this.substrateIndexs_water)     + translationEnergy;
            byProd(this.substrateIndexs_gdp)       = byProd(this.substrateIndexs_gdp)       + translationEnergy;
            byProd(this.substrateIndexs_phosphate) = byProd(this.substrateIndexs_phosphate) + translationEnergy;
            byProd(this.substrateIndexs_hydrogen)  = byProd(this.substrateIndexs_hydrogen)  + translationEnergy;

            %% enzymes

            %Ribosome
            fractionInitiating = this.ribosomeElongationRate *  ...
                states.monomerProductions0' * (1 ./ this.polypeptide.monomerLengths) / ...
                sum(states.monomerProductions0); %fraction of ribosomes that are initiating
            aaPolymerized0 = (states.monomerProductions0' * this.polypeptide.monomerLengths);
            minEnzExp(this.enzymeIndexs_ribosome30S) = 1.65 * aaPolymerized0 / this.ribosomeElongationRate / (1 - fractionInitiating);
            minEnzExp(this.enzymeIndexs_ribosome50S) = 1.65 * aaPolymerized0 / this.ribosomeElongationRate / (1 - fractionInitiating);

            %initiation, elongation, termination factors
            minEnzExp(this.enzymeIndexs_initiationFactors)     = 3 * minEnzExp(this.enzymeIndexs_ribosome30S) *      fractionInitiating;
            minEnzExp(this.enzymeIndexs_elongationFactors)     = 3 * minEnzExp(this.enzymeIndexs_ribosome30S) * (1 - fractionInitiating);
            minEnzExp(this.enzymeIndexs_terminationFactor)     = 3;
            minEnzExp(this.enzymeIndexs_recyclingFactor)       = 3;
            minEnzExp(this.enzymeIndexs_elongationGFactor)     = 3 + minEnzExp(this.enzymeIndexs_elongationGFactor);

            %abortive translation enzymes
            minEnzExp(this.enzymeIndexs_tmRNA)                 = 3;
            minEnzExp(this.enzymeIndexs_tmRNABindingProtein)   = 3;
            minEnzExp(this.enzymeIndexs_peptidylTRNAHydrolase) = 3;
        end

        %Initialize ribosome, bound mRNAs, translation factor states
        %Assumptions:
        %- Metabolic cost of the transcription of these amino acids is negligible
        %- Probability that so many ribosomes bind a given mRNA that they can't
        %  pack along the mRNA without steric interactions is negligible. Thus we
        %  don't try to handle this case separately.
        function initializeState(this)
            %references to states
            rib = this.ribosome;
            pol = this.polypeptide;

            %Check ribosomes and polypeptides are synchronized
            if ...
                    ~isequal(rib.boundMRNAs, pol.boundMRNAs) || ...
                    ~isequal(rib.mRNAPositions, pol.nascentMonomerLengths) || ...
                    ~isequal(rib.tmRNAPositions, pol.proteolysisTagLengths)
                throw(MException('Translation:error', 'Ribosomes and polypeptides must be synchronized'));
            end

            %enzymes
            this.enzymes = this.enzymes + this.boundEnzymes;
            this.boundEnzymes(:) = 0;
            this.enzymes(this.enzymeIndexs_ribosome30S) = this.enzymes(this.enzymeIndexs_ribosome30S) + this.enzymes(this.enzymeIndexs_ribosome70S);
            this.enzymes(this.enzymeIndexs_ribosome50S) = this.enzymes(this.enzymeIndexs_ribosome50S) + this.enzymes(this.enzymeIndexs_ribosome70S);
            this.enzymes(this.enzymeIndexs_ribosome70S) = 0;
            this.enzymes(this.enzymeIndexs_ribosome30S) = this.enzymes(this.enzymeIndexs_ribosome30S) + this.enzymes(this.enzymeIndexs_ribosome30SIF3);
            this.enzymes(this.enzymeIndexs_initiationFactor3) = this.enzymes(this.enzymeIndexs_initiationFactor3) + this.enzymes(this.enzymeIndexs_ribosome30SIF3);
            this.enzymes(this.enzymeIndexs_ribosome30SIF3) = 0;

            freeTranslationFactors  = this.enzymes(this.enzymeIndexs_translationFactors);
            boundTranslationFactors = this.boundEnzymes(this.enzymeIndexs_translationFactors);
            ribosome30S             = this.enzymes(this.enzymeIndexs_ribosome30S);
            ribosome30SIF3          = this.enzymes(this.enzymeIndexs_ribosome30SIF3);
            ribosome50S             = this.enzymes(this.enzymeIndexs_ribosome50S);
            ribosome70S             = this.enzymes(this.enzymeIndexs_ribosome70S);

            boundRibosome70S = min([
                ribosome30S;
                ribosome50S;
                freeTranslationFactors(this.enzymeIndexs_elongationFactors);
                sum(this.mRNAs)]);
            ribosome30S = ribosome30S - boundRibosome70S;
            ribosome50S = ribosome50S - boundRibosome70S;
            freeTranslationFactors(this.enzymeIndexs_elongationFactors)  = freeTranslationFactors(this.enzymeIndexs_elongationFactors)  - boundRibosome70S;
            boundTranslationFactors(this.enzymeIndexs_elongationFactors) = boundTranslationFactors(this.enzymeIndexs_elongationFactors) + boundRibosome70S;

            %allocate
            rib.states                = repmat(rib.notExistValue, [2*boundRibosome70S, 1, 1]);
            pol.boundMRNAs            = zeros(2 * boundRibosome70S, 1, 1);
            pol.nascentMonomerLengths = zeros(2 * boundRibosome70S, 1, 1);
            pol.proteolysisTagLengths = zeros(2 * boundRibosome70S, 1, 1);

            %bind random mRNA
            for i = 1:boundRibosome70S
                pol.boundMRNAs(i) = this.randStream.randsample(numel(this.mRNAs), 1, true, this.mRNAs .* pol.monomerLengths);
            end

            %actively bound
            %- partition sequence among RNA polymerases bound to transcription unit
            %- randomly select position of RNA polymerases within partition
            for i = 1:length(this.mRNAs)
                boundRibosomes = find(pol.boundMRNAs == i);
                partitions = round(...
                    (0:length(boundRibosomes)) * ...
                    (pol.monomerLengths(i) + this.ribosomeElongationRate - 1) / length(boundRibosomes) -...
                    1/2*this.ribosomeElongationRate);

                for j = 1:length(boundRibosomes)
                    minState = partitions(j)   + 1/2 * this.ribosomeElongationRate;
                    maxState = partitions(j+1) - 1/2 * this.ribosomeElongationRate;
                    rib.states(boundRibosomes(j)) = rib.activeValue;
                    if maxState < minState
                        pol.nascentMonomerLengths(boundRibosomes(j)) = round(max(1, min(pol.monomerLengths(i), mean(partitions(j:j+1)))));
                    else
                        pol.nascentMonomerLengths(boundRibosomes(j)) = round(minState + this.randStream.rand * (maxState - minState));
                    end
                end
            end

            %synchronize ribosome and polypeptide states
            rib.boundMRNAs = pol.boundMRNAs;
            rib.mRNAPositions = pol.nascentMonomerLengths;
            rib.tmRNAPositions = pol.proteolysisTagLengths;

            %store enzymes
            this.enzymes(this.enzymeIndexs_translationFactors)      = freeTranslationFactors;
            this.boundEnzymes(this.enzymeIndexs_translationFactors) = boundTranslationFactors;
            this.enzymes(this.enzymeIndexs_ribosome30S)             = ribosome30S;
            this.enzymes(this.enzymeIndexs_ribosome30SIF3)          = ribosome30SIF3;
            this.enzymes(this.enzymeIndexs_ribosome50S)             = ribosome50S;
            this.enzymes(this.enzymeIndexs_ribosome70S)             = ribosome70S;
            this.boundEnzymes(this.enzymeIndexs_ribosome70S)        = boundRibosome70S;

            %decrement counts of protein
            this.copyToState();

            g = this.gene;
            rna = this.rna;
            mon = this.monomer;
            cpx = this.complex;
            pcComp = sum(cpx.proteinComplexComposition(setdiff(1:end, g.mRNAIndexs), :, :), 3);

            initMonCnts = mon.counts;
            initCpxCnts = cpx.counts;
            matureProteinWt = max(0, sum(mon.dryWeight + cpx.dryWeight) - pol.dryWeight);
            while sum(mon.dryWeight + cpx.dryWeight) > matureProteinWt
                idx = this.randStream.randsample(numel(mon.counts) + numel(cpx.counts), 1, false, [mon.counts(:); cpx.counts(:)]);
                if idx <= numel(mon.counts)
                    mon.counts(idx) = mon.counts(idx) - 1;
                else
                    idx = idx - numel(mon.counts);
                    [i, ~] = ind2sub(size(cpx.counts), idx);
                    i = mod(i - 1, numel(cpx.matureIndexs)) + 1;
                    if any(pcComp(:, i))
                        rna.counts(rna.matureIndexs(setdiff(1:end, rna.matureMRNAIndexs))) = ...
                            + rna.counts(rna.matureIndexs(setdiff(1:end, rna.matureMRNAIndexs))) ...
                            + pcComp(:, i);
                    end
                    cpx.counts(idx) = cpx.counts(idx) - 1;
                end
            end
            mon.counts = ...
                + mon.counts ...
                + mon.updateExternalState(-(initMonCnts - mon.counts), true);
            cpx.counts = ...
                + cpx.counts ...
                + cpx.updateExternalState(-(initCpxCnts - cpx.counts), true);

            this.copyFromState();
        end

        %resource requirements
        function result = calcResourceRequirements_Current(this)
            initiationFactor3 = this.enzymes(this.enzymeIndexs_initiationFactor3);
            ribosome30S       = this.enzymes(this.enzymeIndexs_ribosome30S);
            ribosome30SIF3    = this.enzymes(this.enzymeIndexs_ribosome30SIF3);
            ribosome50S       = this.enzymes(this.enzymeIndexs_ribosome50S);
            nRibosomes = ...
                this.enzymes(this.enzymeIndexs_ribosome70S) + ...
                this.boundEnzymes(this.enzymeIndexs_ribosome70S) + ...
                min(ribosome30SIF3 + min(ribosome30S, initiationFactor3), ribosome50S);

            nInitiations = nRibosomes;
            nElongations = min([
                sum(this.aminoacylatedTRNAs) + this.aminoacylatedTMRNA
                nRibosomes * this.ribosomeElongationRate]);
            nTerminations = nRibosomes;

            %energy for initiation, elongation, termination
            result = zeros(size(this.substrates));
            result(this.substrateIndexs_gtp)   = nInitiations + 2 * nElongations + 3 * nTerminations;
            result(this.substrateIndexs_water) = nInitiations + 2 * nElongations + 3 * nTerminations;

            %water to cleave peptidyl-tRNA bound
            result(this.substrateIndexs_water) = - nInitiations - nElongations;

            %water from peptide bond formation
            result(this.substrateIndexs_water) = + nElongations;
        end

        %simulation
        function evolveState(this)
            %% references to states
            rib = this.ribosome;
            pol = this.polypeptide;

            %% Check ribosomes and polypeptides are synchronized
            if ...
                    ~isequal(rib.boundMRNAs, pol.boundMRNAs) || ...
                    ~isequal(rib.mRNAPositions, pol.nascentMonomerLengths) || ...
                    ~isequal(rib.tmRNAPositions, pol.proteolysisTagLengths)
                throw(MException('Translation:error', 'Ribosomes and polypeptides must be synchronized'));
            end

            %% allocate memory and initialize variables

            % Numbers of enzymes
            freeTranslationFactors     = this.enzymes(this.enzymeIndexs_translationFactors);
            boundTranslationFactors    = this.boundEnzymes(this.enzymeIndexs_translationFactors);
            ribosome30S                = this.enzymes(this.enzymeIndexs_ribosome30S);
            ribosome30SIF3             = this.enzymes(this.enzymeIndexs_ribosome30SIF3);
            ribosome50S                = this.enzymes(this.enzymeIndexs_ribosome50S);
            ribosome70S                = this.enzymes(this.enzymeIndexs_ribosome70S);
            boundRibosome70S           = this.boundEnzymes(this.enzymeIndexs_ribosome70S);
            freeTMRNA                  = this.freeTMRNA; %#ok<*PROP>
            freeTMRNABindingProtein    = this.enzymes(this.enzymeIndexs_tmRNABindingProtein);
            freePeptidylTRNAHydrolase  = this.enzymes(this.enzymeIndexs_peptidylTRNAHydrolase);
            boundTMRNA                 = this.boundTMRNA;
            aminoacylatedTmRNA         = this.aminoacylatedTMRNA;

            % form ribosomes
            new_ribosome30SIF3 = min(ribosome30S, freeTranslationFactors(this.enzymeIndexs_initiationFactor3));
            ribosome30SIF3 = ribosome30SIF3 + new_ribosome30SIF3;
            ribosome30S    = ribosome30S - new_ribosome30SIF3;
            freeTranslationFactors(this.enzymeIndexs_initiationFactor3) = freeTranslationFactors(this.enzymeIndexs_initiationFactor3) - new_ribosome30SIF3;

            %allocate space to store states of new ribosomes
            if boundRibosome70S + ribosome30SIF3 > numel(rib.states)
                rib.states = [
                    rib.states;
                    repmat(rib.notExistValue, [boundRibosome70S + ribosome30SIF3 1])];
                pol.boundMRNAs = [
                    pol.boundMRNAs;
                    zeros([boundRibosome70S + ribosome30SIF3 1])];
                pol.nascentMonomerLengths = [
                    pol.nascentMonomerLengths;
                    zeros([boundRibosome70S + ribosome30SIF3 1])];
                pol.proteolysisTagLengths = [
                    pol.proteolysisTagLengths;
                    zeros([boundRibosome70S + ribosome30SIF3 1])];
            end

            %ribosomes
            elongationRate = this.ribosomeElongationRate;               %ribosome elongation rate (aa/s)
            actRibs = find(...                                          %indices of active ribosomes
                rib.states == rib.activeValue | ...
                rib.states == rib.stalledValue);
            notExistRibs = find(rib.states == rib.notExistValue);       %indices of free ribosomes 30SIF3
            elngRibs = false(size(rib.states, 1), 1);                   %indicate of ribosome actively translating
            elngSeqs = zeros(size(rib.states, 1), elongationRate);      %tRNA sequences of mRNAs segments that should be translated
            termRibs = false(size(rib.states, 1), 1);
            termRibs(rib.states == rib.activeValue, 1) = ...
                pol.monomerLengths(pol.boundMRNAs(rib.states == rib.activeValue, 1), 1) == ...
                pol.nascentMonomerLengths(rib.states == rib.activeValue, 1);
            termRibs = termRibs | pol.proteolysisTagLengths == pol.proteolysisTagLength;

            %mRNAs
            bndProbs = this.mRNAs;                                      %unnormalized probability of binding mRNAs

            %tRNAs
            nTRNAs = length(this.freeTRNAs);                            %number of tRNA species
            tRNAs  = this.freeTRNAs + this.aminoacylatedTRNAs;          %total numbers of tRNAs (free and aminoacylated)

            %energy
            energyAllocation = max(0, this.substrates(this.substrateIndexs_gtp));
            availableEnergy  = energyAllocation;                        %energy (GTP) allocated to translation

            %water
            availableWater = this.substrates(this.substrateIndexs_water);

            %% recycle elongation factors
            freeTranslationFactors(this.enzymeIndexs_elongationFactors) = ...
                freeTranslationFactors(this.enzymeIndexs_elongationFactors) + ...
                boundTranslationFactors(this.enzymeIndexs_elongationFactors);
            boundTranslationFactors(this.enzymeIndexs_elongationFactors) = 0;

            %% select bound mRNAs to elongate
            %elongate if all elongation factors available
            nElongatingRibosomes = min([
                numel(actRibs)
                boundRibosome70S
                floor(min(availableEnergy, availableWater) / 2)]);

            randomOrder = this.randStream.randperm(boundRibosome70S);
            for idx = 1:nElongatingRibosomes
                i = actRibs(randomOrder(idx));

                %Use elongation factors to elongate
                if ~termRibs(i)
                    if all(freeTranslationFactors(this.enzymeIndexs_elongationFactors))
                        freeTranslationFactors(this.enzymeIndexs_elongationFactors)  = freeTranslationFactors(this.enzymeIndexs_elongationFactors)  - 1;
                        boundTranslationFactors(this.enzymeIndexs_elongationFactors) = boundTranslationFactors(this.enzymeIndexs_elongationFactors) + 1;
                    else
                        continue;
                    end
                end

                %Extract portion of sequence to be translated
                switch rib.states(i)
                    case rib.activeValue
                        idxs = pol.nascentMonomerLengths(i) + 1:min([
                            pol.monomerLengths(pol.boundMRNAs(i));
                            pol.nascentMonomerLengths(i) + elongationRate]);
                        elngSeqs(i, :) = padarray(pol.monomerTRNASequences{pol.boundMRNAs(i)}(idxs)', [0 elongationRate-length(idxs)], 0, 'post');
                        elngRibs(i) = true;
                    case rib.stalledValue
                        idxs = pol.proteolysisTagLengths(i) + 1:min([
                            pol.proteolysisTagLength;
                            pol.proteolysisTagLengths(i) + elongationRate]);
                        elngSeqs(i, :) = padarray(pol.proteolysisTagTRNASequence(idxs)', [0 elongationRate-length(idxs)], 0, 'post');
                        elngRibs(i) = true;
                end
            end
            nElongatingRibosomes = sum(elngRibs);

            %% make/update free ribosomes, initiate translation
            %Ribosome 70S assembly proceeds as:
            %30S-IF3 + IF2 + IF1 --> 30S-IF1-IF2-IF3-mRNA --> 30S-IF1-IF2-IF3-mRNA-tRNA
            %--> 30S-50S-IF1-IF2-mRNA-tRNA --> 30S-50S-mRNA-tRNA
            %All of these steps occur quite rapidly, so in our model if enough factors,
            %subunits, RNAs exist, then a 70S ribosome can be formed and
            %translation initiated.
            %(tRNA is bound in elongation, not initiation.)

            %Initiate if
            %- 30S-IF3 and 50S available
            %- initiation factors IF1 IF2 available
            %- energy available
            %- ribosome binding factor available
            %- mRNAs available
            %- (already verified by knowledge base computeTRNASequences
            %   method that first codon is formylmethionine)
            nInitiatingRibosomes = min([
                ribosome30SIF3;
                ribosome50S;
                freeTranslationFactors(this.enzymeIndexs_initiationFactor1);
                freeTranslationFactors(this.enzymeIndexs_initiationFactor2);
                max(0, min(availableEnergy, availableWater) - 2 * nElongatingRibosomes);
                sum(this.mRNAs)]);
            if nInitiatingRibosomes > 0
                ribosome30SIF3   = ribosome30SIF3   - nInitiatingRibosomes;
                ribosome50S      = ribosome50S      - nInitiatingRibosomes;
                boundRibosome70S = boundRibosome70S + nInitiatingRibosomes;
                freeTranslationFactors(this.enzymeIndexs_initiationFactor3) = ...
                    freeTranslationFactors(this.enzymeIndexs_initiationFactor3) + ...
                    nInitiatingRibosomes;
                availableEnergy = availableEnergy - nInitiatingRibosomes;
                availableWater = availableWater - nInitiatingRibosomes;
                for idx = 1:nInitiatingRibosomes
                    i = notExistRibs(idx);

                    %randomly choose mRNA to bind
                    pol.boundMRNAs(i) = this.randStream.randsample(numel(bndProbs), 1, true, bndProbs);

                    %update state of ribosome
                    rib.states(i) = rib.activeValue;

                    %update binding probabilities to prevent additional ribosomes
                    %from initiating translation with same mRNA at same time
                    bndProbs(pol.boundMRNAs(i)) = max(0, bndProbs(pol.boundMRNAs(i)) - 1);
                end
            end

            %% Translate active sequences
            %compute progress of translation
            nInit = sum((pol.nascentMonomerLengths(elngRibs) + pol.proteolysisTagLengths(elngRibs)) == 0);
            availableEnergyWater = floor(min(availableEnergy, availableWater - nInit));
            [translationProgress, this.aminoacylatedTRNAs, translationCost, availableEnergyWater2] = ...
                edu.stanford.covert.cell.sim.util.polymerize(...
                elngSeqs(elngRibs, :), ...
                this.aminoacylatedTRNAs, 1:nTRNAs, 0, availableEnergyWater, 2, this.randStream);
            if any(translationProgress)
                %update water, gtp
                availableWater = ...
                    + availableWater ...
                    - (availableEnergyWater - availableEnergyWater2) ...
                    - sum((pol.nascentMonomerLengths(elngRibs) + pol.proteolysisTagLengths(elngRibs)) == 0 & translationProgress > 0);
                availableEnergy = availableEnergy - (availableEnergyWater - availableEnergyWater2);

                %advance positions of ribosomes
                pol.nascentMonomerLengths(elngRibs) = ...
                    pol.nascentMonomerLengths(elngRibs) + ...
                    translationProgress .* (rib.states(elngRibs) == rib.activeValue);
                if any(rib.states(elngRibs) == rib.stalledValue)
                    pol.proteolysisTagLengths(elngRibs) = ...
                        pol.proteolysisTagLengths(elngRibs) + ...
                        translationProgress .* (rib.states(elngRibs) == rib.stalledValue);
                end
            end

            %% Terminate, remaining in elongating state, or convert to stalled state
            %If these conditions are true
            %(1) ribosome has translated entire sequence,
            %(2) termination factor is available (pfrA)--dissociates
            %    peptide from complex needs 1GTP,
            %(3) ribosome recycling factor (frr) and EF-G (fusA) available
            %    (use 1GTP) to split off the 50S subunit,
            %(4) IF-3 (infC) available to split off mRNA and tRNA and
            %(5) energy is available (total 2GTPs)
            %
            %Then terminate translation:
            %- use energy to terminate
            %- release ribosomes which have completed translation
            %- increment expression of the corresponding protein
            %- IF3 remains bound to 30S

            randomOrder = this.randStream.randperm(length(elngRibs));
            tmpProg = zeros(size(elngRibs));
            tmpProg(elngRibs) = translationProgress;
            for idx = 1:length(elngRibs)
                i = randomOrder(idx);
                if elngRibs(i) && (tmpProg(i) || pol.nascentMonomerLengths(i) >= pol.monomerLengths(pol.boundMRNAs(i)))
                    if (pol.nascentMonomerLengths(i) >= pol.monomerLengths(pol.boundMRNAs(i)) || ...
                            pol.proteolysisTagLengths(i) >= pol.proteolysisTagLength) && ...
                            freeTranslationFactors(this.enzymeIndexs_terminationFactor) && ...
                            freeTranslationFactors(this.enzymeIndexs_recyclingFactor) && ...
                            (freeTranslationFactors(this.enzymeIndexs_elongationGFactor) || tmpProg(i)) && ...
                            freeTranslationFactors(this.enzymeIndexs_initiationFactor3) && ...
                            availableEnergy >= 2 && ...
                            availableWater >= 2

                        %use energy, (tRNAs have already been released in elongation)
                        availableEnergy = availableEnergy - 2;
                        availableWater = availableWater - 2;

                        switch rib.states(i)
                            case rib.activeValue
                                %update protein count
                                this.monomers(pol.boundMRNAs(i)) = this.monomers(pol.boundMRNAs(i)) + 1;
                            case rib.stalledValue
                                %update stalled polypeptide count
                                pol.abortedPolypeptides = [
                                    pol.abortedPolypeptides;
                                    pol.boundMRNAs(i) pol.nascentMonomerLengths(i) pol.proteolysisTagLength];

                                %release tmRNA
                                boundTMRNA = boundTMRNA - 1;
                                freeTMRNA  = freeTMRNA  + 1;
                        end

                        %release ribosome
                        rib.states(i) = rib.notExistValue;

                        %release mRNA
                        pol.nascentMonomerLengths(i) = 0;
                        pol.proteolysisTagLengths(i) = 0;
                        pol.boundMRNAs(i)            = 0;

                        %release ribosome, split into 30S and 50S
                        boundRibosome70S = boundRibosome70S - 1;
                        ribosome30S      = ribosome30S      + 1;
                        ribosome50S      = ribosome50S      + 1;
                    end
                elseif rib.states(i) == rib.activeValue && ...
                        pol.nascentMonomerLengths(i) > 0 && ...
                        aminoacylatedTmRNA > 0 && ...
                        freeTMRNABindingProtein > 0  && ...
                        freePeptidylTRNAHydrolase > 0 && ...
                        this.randStream.rand < this.tmRNABindingProbability

                    %set ribosome to stalled state
                    rib.states(i) = rib.stalledValue;

                    %increment bound tmRNAs
                    boundTMRNA = boundTMRNA + 1;

                    %decrement free aminoacylated tmRNAs
                    aminoacylatedTmRNA = aminoacylatedTmRNA - 1;

                    %increment proteolysis tag length by 1 for alanine
                    %bound to tRNA-like portion of tmRNA
                    pol.proteolysisTagLengths(i) = 1;
                end
            end

            %% synchronize ribosome and polypeptide states
            rib.boundMRNAs = pol.boundMRNAs;
            rib.mRNAPositions = pol.nascentMonomerLengths;
            rib.tmRNAPositions = pol.proteolysisTagLengths;

            %% store enzymes
            this.enzymes(this.enzymeIndexs_translationFactors)         = freeTranslationFactors;
            this.boundEnzymes(this.enzymeIndexs_translationFactors)    = boundTranslationFactors;
            this.enzymes(this.enzymeIndexs_ribosome30S)                = ribosome30S;
            this.enzymes(this.enzymeIndexs_ribosome30SIF3)             = ribosome30SIF3;
            this.enzymes(this.enzymeIndexs_ribosome50S)                = ribosome50S;
            this.enzymes(this.enzymeIndexs_ribosome70S)                = ribosome70S;
            this.boundEnzymes(this.enzymeIndexs_ribosome70S)           = boundRibosome70S;
            this.freeTMRNA                                             = freeTMRNA;
            this.enzymes(this.enzymeIndexs_tmRNABindingProtein)        = freeTMRNABindingProtein;
            this.enzymes(this.enzymeIndexs_peptidylTRNAHydrolase)      = freePeptidylTRNAHydrolase;
            this.boundTMRNA                                            = boundTMRNA;
            this.aminoacylatedTMRNA                                    = aminoacylatedTmRNA;

            %% acount for used substrates
            %aminoacylated tRNAs
            this.freeTRNAs = tRNAs - this.aminoacylatedTRNAs;

            %energy (energy-availableEnergy), water
            this.substrates(this.substrateIndexs_gtp)       = this.substrates(this.substrateIndexs_gtp)       - (energyAllocation - availableEnergy);
            this.substrates(this.substrateIndexs_water)     = availableWater;
            this.substrates(this.substrateIndexs_gdp)       = this.substrates(this.substrateIndexs_gdp)       + (energyAllocation - availableEnergy);
            this.substrates(this.substrateIndexs_phosphate) = this.substrates(this.substrateIndexs_phosphate) + (energyAllocation - availableEnergy);
            this.substrates(this.substrateIndexs_hydrogen)  = this.substrates(this.substrateIndexs_hydrogen)  + (energyAllocation - availableEnergy) + sum(translationCost);
        end
    end

    %get methods of dependent local state
    methods
        function value = getDryWeight(this)
            if size(this.monomers, 3) == 1
                value = this.getDryWeight@edu.stanford.covert.cell.sim.Process() + (...
                    this.monomer.molecularWeights(this.monomer.nascentIndexs)' * this.monomers           + ...
                    this.rna.molecularWeights(this.rna.matureIndexs(this.rna.matureTRNAIndexs))'          * this.freeTRNAs          + ...
                    this.rna.molecularWeights(this.rna.aminoacylatedIndexs(this.rna.matureTRNAIndexs))'   * this.aminoacylatedTRNAs + ...
                    this.rna.molecularWeights(this.rna.aminoacylatedIndexs(this.tmRNAGlobalIndex))'       * this.aminoacylatedTMRNA) ...
                    / edu.stanford.covert.util.ConstantUtil.nAvogadro;
            else
                value = this.getDryWeight@edu.stanford.covert.cell.sim.Process() + (...
                    permute(this.monomer.molecularWeights(this.monomer.nascentIndexs)' * permute(this.monomers,           [1 3 2]),[1 3 2]) + ...
                    permute(this.rna.molecularWeights(this.rna.matureIndexs(this.rna.matureTRNAIndexs))'        * permute(this.freeTRNAs,          [1 3 2]),[1 3 2]) + ...
                    permute(this.rna.molecularWeights(this.rna.aminoacylatedIndexs(this.rna.matureTRNAIndexs))' * permute(this.aminoacylatedTRNAs, [1 3 2]),[1 3 2]) + ...
                    permute(this.rna.molecularWeights(this.rna.aminoacylatedIndexs(this.tmRNAGlobalIndex))'     * permute(this.aminoacylatedTMRNA, [1 3 2]),[1 3 2])) ...
                    / edu.stanford.covert.util.ConstantUtil.nAvogadro;
            end
        end
    end
end


function t = fieldnames(obj)
% Fieldnames of a file-array object
% _______________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

%
% $Id: fieldnames.m 1143 2008-02-07 19:33:33Z spm $

t = {...
    'fname'
    'dim'
    'dtype'
    'offset'
    'scl_slope'
    'scl_inter'
};


mins=60;
debug.set_debug(1);

%% Sakurajima
%chanmatch = 'chan=~/[BESH]H[ENZ]/  || chan=~/BD[FL123]/';
%ds = datasource('antelope', ...
       %'/raid/data/sakurajima/Seismic_Infrasound/SAK_dbs/GT/dbSAK_GT');
%setup('Sakurajima', datenum(2015,5,18), datenum(2015,6,7), 'pf/setup_Sakurajima.pf', chanmatch)
%iceweb(ds, 'thissubnet', 'Sakurajima', 'snum', datenum(2015,5,18), 'enum', datenum(2015,6,7), 'delaymins', 0, 'matfile', 'pf/Sakurajima.mat', 'nummins', mins, 'runmode', 'archive');

%% Montserrat
% chanmatch = 'chan=~/[BESH]H[ENZ]/  || chan=~/BD[FL123]/';
%chanmatch = 'chan=~/[BESH]H[ENZ]/';
chanmatch = 'chan=~/[BESH]HZ/';
ds = datasource('antelope', ...
        '/raid/data/MONTSERRAT/antelope/db/db%04d%02d%02d',...
        'year','month','day');
setup('Montserrat', datenum(1995,7,1), datenum(2009,1,1), 'pf/setup_Montserrat.pf', chanmatch)
%montserrat_remove_analog_sites
iceweb(ds, 'thissubnet', 'Montserrat', 'snum', datenum(1998,2,19), 'enum', datenum(1998,2,19,23,59,59), 'delaymins', 0, 'matfile', 'pf/Montserrat.mat', 'nummins', mins, 'runmode', 'test');

%% Others yet to be converted to iceweb
% NOTE:
%   iceweb is just run in a single matlab session without waveform mat files and no rtexec
%   tremor_loadwaveformdata is for running concurrently with an rtexec system mixed with live data and creates waveform mat files)
%tremor_loadwaveformdata('thissubnet', 'Pavlof', 'snum', datenum(2013, 5, 17), 'enum', datenum(2013, 5, 18), 'delaymins', 0, 'matfile', 'pf/tremor_runtime.mat', 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thissubnet', 'Pavlof', 'snum', datenum(2013, 5, 16), 'enum', datenum(2013, 5, 17), 'delaymins', 0, 'matfile', 'pf/tremor_runtime.mat', 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thissubnet', 'Pavlof', 'snum', datenum(2013, 5, 15), 'enum', datenum(2013, 5, 16), 'delaymins', 0, 'matfile', 'pf/tremor_runtime.mat', 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thissubnet', 'Pavlof', 'snum', datenum(2013, 5, 14), 'enum', datenum(2013, 5, 15), 'delaymins', 0, 'matfile', 'pf/tremor_runtime.mat', 'nummins', mins, 'runmode', 'archive');

%tremor_loadwaveformdata('thisubnet', 'Redoubt', 'snum', datenum(2009, 3, 23, 0, 0, 0), 'enum', datenum(2009, 4, 5), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Redoubt', 'snum', datenum(2009, 3, 19), 'enum', datenum(2009, 3, 23), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Redoubt', 'snum', datenum(2009, 1, 1, 0, 0, 0), 'enum', datenum(2009, 3, 19), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Redoubt', 'snum', datenum(2009, 4, 5), 'enum', datenum(2009, 4, 12), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Redoubt', 'snum', datenum(2009, 4, 12), 'enum', datenum(2009, 6, 1), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Redoubt', 'snum', datenum(2008, 11, 1), 'enum', datenum(2009, 1, 1), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Redoubt', 'snum', datenum(2008, 9, 1), 'enum', datenum(2008, 11, 1), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Okmok', 'snum', datenum(2008, 7, 15), 'enum', datenum(2008, 8, 20), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Pavlof', 'snum', datenum(2007, 8, 11), 'enum', datenum(2007, 9, 15), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Augustine', 'snum', datenum(2005, 12, 1), 'enum', datenum(2006, 3, 15), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Shishaldin', 'snum', datenum(1999, 2, 15), 'enum', datenum(1999, 5, 27), 'nummins', mins, 'runmode', 'archive');
%tremor_loadwaveformdata('thisubnet', 'Pavlof', 'snum', datenum(1996, 9, 10), 'enum', datenum(1997, 1, 1), 'nummins', mins, 'runmode', 'archive');


function prettifiedJSON = prettyJson(rawJSON)
% prettyJson
%   Read in raw JSON text that is generated by encodejson function and
%   usually in one line, then convert to pretty format with proper
%   indentations and line breaks
%
% Input:
%   rawJSON             input char array in JSON format
%
% Output:
%   prettifiedJSON      prettified char array in JSON format
%
% NOTE: This function is to optimize displaying effect of JSON string through
% recognizing general delimited characters, but may change the file content
% and cause problem in several uncommon cases. Please avoid the use of this
% function on Matlab structures that contain elements with any of the three
% character combinations: `":[` and `",` and `"],`.
%
% Usage: prettifiedJSON = prettyJson(rawJSON)
%

% handle input
if isstring(rawJSON)
    % if input is supplied as string
    rawJSON = char(rawJSON);
elseif ~ischar(rawJSON)
    % if input is not supplied as char array
    error('The input data type is Not correct!');
end
jsonStr = rawJSON;

% process input string to pretty JSON format
jsonStr = regexprep(jsonStr,'^{','{\n\t','lineanchors');  % left  curly  bracket
jsonStr = regexprep(jsonStr,'}$','\n}\n','lineanchors');  % right curly  bracket
jsonStr = strrep(jsonStr,'":[','":[\n\t\t');              % left  square bracket
jsonStr = strrep(jsonStr,'",','",\n\t\t');                % internal elements in array
jsonStr = strrep(jsonStr,'],','],\n\t');                  % last element in array
jsonStr = compose(jsonStr);                               % translate escape-characters

prettifiedJSON = jsonStr{1};                              % convert from cell to char

end


function mAP = rfcn_test(conf, imdb, roidb, varargin)
% Image-level testing using final D model and RPN proposals
% --------------------------------------------------------
% D&T implementation
% Modified from MATLAB  R-FCN (https://github.com/daijifeng001/R-FCN/)
% and Faster R-CNN (https://github.com/shaoqingren/faster_rcnn)
% Copyright (c) 2017, Christoph Feichtenhofer
% Licensed under The MIT License [see LICENSE for details]
% --------------------------------------------------------

%% inputs
    ip = inputParser;
    ip.addRequired('conf',                              @isstruct);
    ip.addRequired('imdb',                              @isstruct);
    ip.addRequired('roidb',                             @isstruct);
    ip.addParamValue('net_def_file',    '', 			@isstr);
    ip.addParamValue('net_file',        '', 			@isstr);
    ip.addParamValue('cache_name',      '', 			@isstr);
    ip.addParamValue('suffix',          '',             @isstr);
    ip.addParamValue('ignore_cache',    false,          @islogical);
    ip.addParamValue('proposal_path',    '',          @isstr);
    ip.addParamValue('visualize_every',   Inf,          @isscalar);
    ip.addParamValue('test_classes',    [] 			);

    ip.parse(conf, imdb, roidb, varargin{:});
    opts = ip.Results;
    num_proposals = 300;
    if isfield(conf, 'num_proposals')
      num_proposals = conf.num_proposals;
    end
    visualize = false;

%%  set cache dir
    cache_dir = fullfile(conf.root_path, 'output', 'rfcn_cachedir', opts.cache_name, imdb.name);
    mkdir_if_missing(cache_dir);

%%  init log
    timestamp = datestr(datevec(now()), 'yyyymmdd_HHMMSS');
    mkdir_if_missing(fullfile(cache_dir, 'log'));
    log_file = fullfile(cache_dir, 'log', ['test_', timestamp, '.txt']);
    diary(log_file);

    num_images = length(imdb.image_ids);
    num_classes = imdb.num_classes;

    try
      aboxes = cell(num_classes, 1);
      if opts.ignore_cache
          throw('');
      end
      for i = 1:num_classes
        load(fullfile(cache_dir, [imdb.classes{i} '_boxes_' imdb.name opts.suffix]));
        aboxes{i} = boxes;
      end
    catch
%%      testing
        % init caffe net
        caffe_log_file_base = fullfile(cache_dir, 'caffe_log');
        caffe.init_log(caffe_log_file_base);
        caffe_net = caffe.Net(opts.net_def_file, 'test');
        caffe_net.copy_from(opts.net_file);

        % set random seed
        prev_rng = seed_rand(conf.rng_seed);
        caffe.set_random_seed(conf.rng_seed);

        % set gpu/cpu
        if conf.use_gpu
            caffe.set_mode_gpu();
        else
            caffe.set_mode_cpu();
        end

        % determine the maximum number of rois in testing
        max_rois_num_in_gpu = 10000;

        disp('opts:');
        disp(opts);
        disp('conf:');
        disp(conf);

        %heuristic: keep an average of 160 detections per class per images prior to NMS
        max_per_set = 160 * num_images;
        % heuristic: keep at most 400 detection per class per image prior to NMS
        max_per_image = 400;
        % detection thresold for each class (this is adaptively set based on the max_per_set constraint)
        thresh = -inf * ones(num_classes, 1);
        % top_scores will hold one minheap of scores per class (used to enforce the max_per_set constraint)
        top_scores = cell(num_classes, 1);
        % all detections are collected into:
        %    all_boxes[cls][image] = N x 5 array of detections in
        %    (x1, y1, x2, y2, score)
        aboxes = cell(num_classes, 1);
        box_inds = cell(num_classes, 1);
        for i = 1:num_classes
            aboxes{i} = cell(length(imdb.image_ids), 1);
            box_inds{i} = cell(length(imdb.image_ids), 1);
        end
        count = 0;
        t_start = tic;
        for i = 1:1:num_images
            d = roidb.rois(i);
            if ~isempty(opts.test_classes),
              testThis = false;
              for ii = 1:length(opts.test_classes)
                if any(opts.test_classes(ii) == d.class), testThis = true; end
              end
              if ~testThis, continue; end
            end
            count = count + 1;
            fprintf('%s: test (%s) %d/%d ', procid(), imdb.name, count, num_images);
            th = tic;

            if ~isempty(opts.proposal_path)
              file = imdb.image_ids{i};
              file(file == '\') = '/';
              ind = find(file == '/', 1, 'first');
              videoFrame = file ;
              if strcmp(file(1:20), 'ILSVRC2015_VID_train')
                videoFrame = file(ind+1:end);
              end
              ab_fetch_file = [opts.proposal_path '/' videoFrame '.mat'];
              if ~exist(ab_fetch_file,'file')
                  fprintf('In roidb_from_proposal_manual.m --> error:file does not exist : \n %s \n', ab_fetch_file);
                  fprintf('generate this file and run script again, pausing for now...\n');
                  pause;
              end

              abst = load(ab_fetch_file);
              if isfield(abst, 'boxes')
                proposals = abst.boxes;
              else
                proposals = abst.aboxes;
              end

              if num_proposals < 1
                  proposals = proposals(proposals(:,5) > num_proposals,1:4);
              else
                  proposals = proposals(1:min(num_proposals,size(proposals,1)),1:4);
              end
              if isempty(proposals), continue; end

              d.boxes = cat(1, d.boxes, proposals);
              d.gt = cat(1, d.gt, zeros(size(proposals,1),1));
            end

            im_path = imdb.image_at(i);
            if isfield(conf, 'input_modality') && ~strcmp(conf.input_modality, 'rgb')

              img_file_u = strrep(im_path, ['Data' filesep 'VID'], ...
                ['Data' filesep 'VID' filesep 'tvl1_flow_600' filesep 'u'] ) ;
              img_file_v = strrep(im_path, ['Data' filesep 'VID'], ...
                ['Data' filesep 'VID' filesep 'tvl1_flow_600' filesep 'v'] ) ;
              try
                 nFrames = 1;
                 if isfield(conf, 'nFrames')
                   nFrames = conf.nFrames;
                 end
                [path_u, ~, ~] = fileparts(img_file_u);
                [path_v, frame, ext] = fileparts(img_file_v);
                frames = str2num(frame)-nFrames+1:str2num(frame)+nFrames-1;
                vid = fileparts(videoFrame);
                frames(frames <0) = 0; frames(frames > imdb.nFrames(vid)-2) =  imdb.nFrames(vid)-2;
                im_u = []; im_v = [];
                for frame=frames
                  img_file_u = fullfile(path_u, [sprintf('%06d',frame), ext]) ;
                  img_file_v = fullfile(path_v, [sprintf('%06d',frame), ext]) ;
                  im_u{end+1} = imread(img_file_u); im_v{end+1} = imread(img_file_v);
                end
                im_u = cat(3,im_u{:});im_v = cat(3,im_v{:});

              catch % the last frame is simply replicated in optical flow case
                continue;
              end
                switch conf.input_modality
                  case 'flow'
                    flow = single(cat(3,im_u,im_v)) - 128;
                    flow = bsxfun(@minus,flow, median(median(flow,1),2)) ;
                    mag_flow = sqrt(sum(flow.^2,3)) - 128;
                    im = (cat(3,flow,mag_flow));
                    im = imresize( im, imdb.sizes(i,:));
                  case 'grayflow'
                    imgray = imresize( rgb2gray(imread(im_path)), sz(1:2));
                    im = cat(3,im_u,im_v,imgray);
                  case 'rgbflow'

                    if isfield(conf, 'subflow_mean') && ~conf.subflow_mean
                      flow = single(cat(3,im_u,im_v));
                    else
                      flow = single(cat(3,im_u,im_v)) - 128;
                    end

                    imrgb = single(imread(im_path));

                    if size(imrgb,3) < 3
                      imrgb = imrgb(:,:,[1 1 1]);
                    end
                    sz = size(imrgb);
                    im = cat(3,imrgb,imresize( flow, sz(1:2)));

                end
              else
                  im = imread(im_path);
              end

            [boxes, scores] = rfcn_im_detect(conf, caffe_net, im, d.boxes(d.gt==0, :), max_rois_num_in_gpu);

            for j = 1:num_classes
                inds = find(scores(:, j) > thresh(j));
                if ~isempty(inds)
                    [~, ord] = sort(scores(inds, j), 'descend');
                    ord = ord(1:min(length(ord), max_per_image));
                    inds = inds(ord);
                    cls_boxes = boxes(inds, (1+(j-1)*4):((j)*4));
                    cls_scores = scores(inds, j);
                    aboxes{j}{i} = [aboxes{j}{i}; cat(2, single(cls_boxes), single(cls_scores))];
                    box_inds{j}{i} = [box_inds{j}{i}; inds];
                else
                    aboxes{j}{i} = [aboxes{j}{i}; zeros(0, 5, 'single')];
                    box_inds{j}{i} = box_inds{j}{i};
                end
            end

            fprintf(' time %.3fs\n', toc(th));

            if mod(count,opts.visualize_every) == 0
             boxes_cell = cell(length(imdb.classes), 1);

              vis_thres = 0.3;
              for k = 1:length(boxes_cell)
                  boxes_cell{k} = [boxes(:, (1+(k-1)*4):(k*4)), scores(:, k)];

                  boxes_cell{k} = boxes_cell{k}(nms(boxes_cell{k}, 0.3), :);

                  I = boxes_cell{k}(:, 5) >= vis_thres;
                  boxes_cell{k} = boxes_cell{k}(I, :);
              end
              figure(1);
              showboxes(uint8(im(:,:,1:3)), boxes_cell, imdb.classes, 'default'); drawnow;
              if size(im,3) > 3
                  figure(2);
                  showboxes(uint8(im(:,:,4:6)+128), boxes_cell, imdb.classes, 'default'); drawnow;
              end

            end

            if mod(count, 1000) == 0
                for j = 1:num_classes
                [aboxes{j}, box_inds{j}, thresh(j)] = ...
                    keep_top_k(aboxes{j}, box_inds{j}, i, max_per_set, thresh(j));
                end
                disp(thresh);
            end
        end

        for j = 1:num_classes
            [aboxes{j}, box_inds{j}, thresh(j)] = ...
                keep_top_k(aboxes{j}, box_inds{j}, i, max_per_set, thresh(j));
        end
        disp(thresh);

        for i = 1:num_classes
            top_scores{i} = sort(top_scores{i}, 'descend');
            if (length(top_scores{i}) > max_per_set)
                thresh(i) = top_scores{i}(max_per_set);
            end

            % go back through and prune out detections below the found threshold
            for j = 1:length(imdb.image_ids)
                if ~isempty(aboxes{i}{j})
                    I = find(aboxes{i}{j}(:,end) < thresh(i));
                    aboxes{i}{j}(I,:) = [];
                    box_inds{i}{j}(I,:) = [];
                end
            end

            save_file = fullfile(cache_dir, [imdb.classes{i} '_boxes_' imdb.name opts.suffix]);
            boxes = aboxes{i};
            inds = box_inds{i};
            save(save_file, 'boxes', 'inds');
            clear boxes inds;
        end
        fprintf('test all images in %f seconds.\n', toc(t_start));

        caffe.reset_all();
        rng(prev_rng);
    end

    % ------------------------------------------------------------------------
    % Peform AP evaluation
    % ------------------------------------------------------------------------

    if isequal(imdb.eval_func, @imdb_eval_voc)
        new_parpool();
        parfor model_ind = 1:num_classes
          cls = imdb.classes{model_ind};
          res(model_ind) = imdb.eval_func(cls, aboxes{model_ind}, imdb, opts.cache_name, opts.suffix);
        end
    else
      if 0
        % sanity test:
        boxes_gt = cell(num_classes,1);
        classes = {roidb.rois.class};
        boxes = {roidb.rois.boxes};
        boxes = cellfun(@(x) cat(2,x,ones(size(x,1),1)),boxes,'UniformOutput' , false);
        for c=1:num_classes
          boxes_gt{c} = cell(num_images,1);
        end
        for j=1:num_images
          if ~isempty(classes{j})
            for c=unique(classes{j})'
              boxes_gt{c}{j} = boxes{j}(classes{j}==c,:);
            end
          end
        end
        res_gt = imdb.eval_func(boxes_gt, imdb, conf, opts.suffix);
      end
      % ilsvrc
      res = imdb.eval_func(aboxes, imdb, conf, opts.suffix);
    end

    if ~isempty(res)
        fprintf('\n~~~~~~~~~~~~~~~~~~~~\n');
        fprintf('Results:\n');
        aps = [res(:).ap]' * 100;
        disp(aps);
        disp(mean(aps));
        fprintf('~~~~~~~~~~~~~~~~~~~~\n');
        mAP = mean(aps);
    else
        mAP = nan;
    end

    diary off;
end

% ------------------------------------------------------------------------
function [boxes, box_inds, thresh] = keep_top_k(boxes, box_inds, end_at, top_k, thresh)
% ------------------------------------------------------------------------
    % Keep top K
    X = cat(1, boxes{1:end_at});
    if isempty(X)
        return;
    end
    scores = sort(X(:,end), 'descend');
    thresh = scores(min(length(scores), top_k));
    for image_index = 1:end_at
        if ~isempty(boxes{image_index})
            bbox = boxes{image_index};
            keep = find(bbox(:,end) >= thresh);
            boxes{image_index} = bbox(keep,:);
            box_inds{image_index} = box_inds{image_index}(keep);
        end
    end
end


function F = vertcat(varargin)

F = horzcat(varargin{:});


function s = s_adjustl ( s )

%*****************************************************************************80
%
%% S_ADJUSTL flushes a string left.
%
%  Discussion:
%
%    Both blanks and tabs are treated as "white space".
%
%    This routine is similar to the FORTRAN90 ADJUSTL routine.
%
%  Example:
%
%    Input             Output
%
%    '     Hello'      'Hello     '
%    ' Hi there!  '    'Hi there!   '
%    'Fred  '          'Fred  '
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    31 January 2001
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string S, the string to be shifted left.
%
%    Output, string S, has been shifted left.
%
  TAB = char ( 9 );
%
%  Check the length of the string to the last nonblank.
%  If nonpositive, return.
%
  len = s_len_trim ( s );

  if ( len <= 0 )
    return
  end
%
%  Find NONB, the location of the first nonblank, nontab.
%
  nonb = 0;

  for i = 1 : len

    if ( s(i) ~= ' ' && s(i) ~= TAB )
      nonb = i;
      break
    end

  end

  if ( nonb == 0 )
    s = ' ';
    return
  end
%
%  Shift the string left.
%
  if ( 1 < nonb )
    for i = 1 : len + 1 - nonb
      s(i) = s(i+nonb-1);
    end
  end
%
%  Blank out the end of the string.
%
  s(len+2-nonb:len) = ' ';

  return
end


classdef LinkSphere < frost.Animator.Sphere
    % Sphere depicting center of mass of a rigid link for
    % frost.Animator.Display
    %
    % @author omar @date 2017-06-01
    %
    % Copyright (c) 2017, UMICH Biped Lab
    % All right reserved.
    %
    % Redistribution and use in source and binary forms, with or without
    % modification, are permitted only in compliance with the BSD 3-Clause
    % license, see
    % http://www.opensource.org/licenses/BSD-3-Clause

    methods
        function obj = LinkSphere(ax, model, linkFrame, name, varargin)
            obj = obj@frost.Animator.Sphere(ax, model, linkFrame, name, varargin{:});

            obj.radius = 0.03;
            obj.surface.FaceColor = 'blue';
        end
    end
end


function varargout = process_stdrow( varargin )
% PROCESS_STDROW: Uniformize the list of rows for a set of time-frequency files.
%
% USAGE: OutputFiles = process_stdrow('Run', sProcess, sInputs)
%        [DestRowNames, AllRowNames, iRowsSrc, iRowsDest, msgError] = process_stdrow('GetUniformRows', FileNames, Method)
%        [DestRowNames, AllRowNames, iRowsSrc, iRowsDest, msgError] = process_stdrow('GetUniformRows', LoadedFiles, Method)

% @=============================================================================
% 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, 2013-2016

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Uniform signal names';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Standardize';
    sProcess.Index       = 301;
    sProcess.Description = '';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'timefreq', 'matrix'};
    sProcess.OutputTypes = {'timefreq', 'matrix'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 2;
    % Definition of the options
    % === TARGET LIST OF ROW NAMES
    sProcess.options.method.Comment = {'Keep only the common row names
=> Remove all the others', ...
                                       'Keep all the row names
=> Fill the missing signals with zeros', ...
                                       'Use the first file in the list as a template'};
    sProcess.options.method.Type    = 'radio';
    sProcess.options.method.Value   = 1;
    % === OVERWRITE
    sProcess.options.overwrite.Comment = 'Overwrite input files';
    sProcess.options.overwrite.Type    = 'checkbox';
    sProcess.options.overwrite.Value   = 0;
    sProcess.options.overwrite.Group   = 'output';
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    switch (sProcess.options.method.Value)
        case 1,    Comment = [sProcess.Comment, ' (remove extra)'];
        case 2,    Comment = [sProcess.Comment, ' (add missing)'];
        case 3,    Comment = [sProcess.Comment, ' (use first)'];
    end
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputs) %#ok<DEFNU>
    % Options
    switch (sProcess.options.method.Value)
        case 1,  Method = 'common';
        case 2,  Method = 'all';
        case 3,  Method = 'first';
    end
    isOverwrite = sProcess.options.overwrite.Value;
    OutputFiles = {};

    % ===== GET TARGET LIST OF SIGNALS =====
    % Get list of common signals
    [DestRowNames, AllRowNames, iRowsSrc, iRowsDest, msgError] = GetUniformRows({sInputs.FileName}, Method);
    % Report errors and warnings
    if ~isempty(msgError)
        if isempty(DestRowNames)
            bst_report('Error', sProcess, sInputs, msgError);
            return;
        else
            bst_report('Warning', sProcess, sInputs, msgError);
        end
    end
    % Check if there are any difference in the row names
    if all(cellfun(@(c)isequal(c,DestRowNames), AllRowNames))
        bst_report('Error', sProcess, sInputs, 'All the input files have identical row names.');
        return;
    end

    % ===== PROCESS FILES =====
    % Process each input file
    for iInput = 1:length(sInputs)
        % If it's a file that was not changed: skip to next file
        if isequal(DestRowNames, AllRowNames{iInput})
            OutputFiles{iInput} = file_fullpath(sInputs(iInput).FileName);
            continue;
        end

        % List of added rows
        iAddedRows = setdiff(1:length(DestRowNames), iRowsDest{iInput});
        iRemRows   = setdiff(1:length(AllRowNames{iInput}), iRowsSrc{iInput});
        % Add a history entry
        strHistory = 'Uniform list of signals:';
        if ~isempty(iAddedRows)
            strTmp = '';
            for i = 1:length(iAddedRows)
                strTmp = [strTmp, DestRowNames{iAddedRows(i)}, ','];
            end
            strHistory = [strHistory, sprintf(' %d added (%s)', length(iAddedRows), strTmp(1:end-1))];
        end
        if ~isempty(iRemRows)
            strTmp = '';
            for i = 1:length(iRemRows)
                strTmp = [strTmp, AllRowNames{iInput}{iRemRows(i)}, ','];
            end
            strHistory = [strHistory, sprintf(' %d removed (%s)', length(iRemRows), strTmp(1:end-1))];
        end

        % Load the data file
        fileMat = load(file_fullpath(sInputs(iInput).FileName));
        newFileMat = fileMat;
        % Saved structure depends on the file type
        switch lower(sInputs(iInput).FileType)
            case 'timefreq'
                newFileMat.TF = zeros(length(DestRowNames), size(fileMat.TF,2), size(fileMat.TF,3));
                newFileMat.TF(iRowsDest{iInput},:) = fileMat.TF(iRowsSrc{iInput},:);
                newFileMat.RowNames = DestRowNames;
                % Do not keep the Std field in the output
                if isfield(newFileMat, 'Std') && ~isempty(newFileMat.Std)
                    newFileMat.Std = [];
                end
            case 'matrix'
                newFileMat.Value = zeros(length(DestRowNames), size(fileMat.Value,2));
                newFileMat.Value(iRowsDest{iInput},:) = fileMat.Value(iRowsSrc{iInput},:);
                newFileMat.Description = DestRowNames;
        end
        % Add comment
        newFileMat.Comment = [newFileMat.Comment ' | stdrow'];
        % Add a history entry
        newFileMat = bst_history('add', newFileMat, 'stdrow', strHistory);

        % Overwrite the input file
        if isOverwrite
            OutputFiles{iInput} = file_fullpath(sInputs(iInput).FileName);
            bst_save(OutputFiles{iInput}, newFileMat, 'v6');
        % Create a new file
        else
            % Output filename: add file tag
            OutputFiles{iInput} = strrep(file_fullpath(sInputs(iInput).FileName), '.mat', '_stdrow.mat');
            OutputFiles{iInput} = file_unique(OutputFiles{iInput});
            % Save file
            bst_save(OutputFiles{iInput}, newFileMat, 'v6');
            % Add file to database structure
            db_add_data(sInputs(iInput).iStudy, OutputFiles{iInput}, newFileMat);
        end
    end
end

%% ===== GET UNIFORM ROWS =====
function [DestRowNames, AllRowNames, iRowsSrc, iRowsDest, msgError] = GetUniformRows(FileNames, Method)
    AllRowNames   = cell(1,length(FileNames));
    nRows         = zeros(1,length(FileNames));
    iRowsSrc      = cell(1,length(FileNames));
    iRowsDest     = cell(1,length(FileNames));
    unionRowNames = {};
    interRowNames = {};
    DestRowNames  = [];
    msgError      = [];
    % Check all the input files
    for iInput = 1:length(FileNames)
        % Load row names
        switch (file_gettype(FileNames{iInput}))
            case 'timefreq'
                if ischar(FileNames{iInput})
                    fileMat = in_bst_timefreq(FileNames{iInput}, 0, 'DataType', 'RowNames', 'RefRowNames');
                else
                    fileMat = FileNames{iInput};
                end
                % Check file type: Cannot process source files
                if strcmpi(fileMat.DataType, 'results') || ~iscell(fileMat.RowNames)
                    msgError = 'STDROW> Cannot process source maps, or any file that does not have explicit row names.';
                    return;
                elseif (length(fileMat.RefRowNames) > 1) && ~isequal(fileMat.RowNames, fileMat.RefRowNames)
                    msgError = 'STDROW> Cannot process connectivity [AxB] results.';
                    return;
                end
                % Add row names to the list
                AllRowNames{iInput} = fileMat.RowNames;
            case 'matrix'
                if ischar(FileNames{iInput})
                    fileMat = in_bst_matrix(FileNames{iInput}, 'Description');
                else
                    fileMat = FileNames{iInput};
                end
                % Check file type
                if (size(fileMat.Description,2) > 1)
                    msgError = 'Cannot process a matrix file in which the "Description" fields has more than one column.';
                    return;
                end
                % Add row names to the list
                AllRowNames{iInput} = fileMat.Description;
            otherwise
                error('Unsupported file format.');
        end

        % Remove the @filename at the end of the row names
        for iRow = 1:length(AllRowNames{iInput})
            iAt = find(AllRowNames{iInput}{iRow} == '@', 1);
            if ~isempty(iAt) && any(AllRowNames{iInput}{iRow}(iAt+1:end) == '/')
                AllRowNames{iInput}{iRow} = strtrim(AllRowNames{iInput}{iRow}(1:iAt-1));
            end
        end

        % Keep track of row numbers
        nRows(iInput) = length(AllRowNames{iInput});
        % Union of all the row names
        unionRowNames = union(unionRowNames, AllRowNames{iInput});
        % Intersection of all the row names
        if isempty(interRowNames)
            interRowNames = AllRowNames{iInput};
        else
            interRowNames = intersect(interRowNames, AllRowNames{iInput});
        end
    end
    % Check if there are rowns left
    if isempty(interRowNames) && strcmpi(Method, 'common')
        msgError = 'No common row names in these data sets.';
        return;
    end

    % ===== COMMON ROW LIST =====
    % Get the row list that has the more/less rows
    switch (Method)
        % Only common rows
        case 'common'
            % Get the minimum number of rows
            [tmp, iRef] = min(nRows);
            % Get rows
            DestRowNames = AllRowNames{iRef};
            % Remove unecessary rows
            iRemove = find(~ismember(DestRowNames, interRowNames));
            if ~isempty(iRemove)
                DestRowNames(iRemove) = [];
            end
        % All rows
        case 'all'
            % Get the maximum number of rows
            [tmp, iRef] = max(nRows);
            % Get rows
            DestRowNames = AllRowNames{iRef};
            % Add all the other rows
            iAdd = find(~ismember(unionRowNames, DestRowNames));
            if ~isempty(iAdd)
                if (size(DestRowNames,1) == 1)
                    DestRowNames = [DestRowNames, unionRowNames{iAdd}];
                else
                    DestRowNames = [DestRowNames; unionRowNames{iAdd}];
                end
            end
        % First file
        case 'first'
            DestRowNames = AllRowNames{1};
    end

    % ===== GET INDICES FOR EACH FILE =====
    % Process each input file
    for iInput = 1:length(FileNames)
        % If it's a file that was not changed: skip to next file
        if isequal(DestRowNames, AllRowNames{iInput})
            iRowsSrc{iInput}  = 1:length(DestRowNames);
            iRowsDest{iInput} = 1:length(DestRowNames);
            continue;
        end
        % Create list of orders for rows
        for iChan = 1:length(DestRowNames)
            iTmp = find(strcmpi(DestRowNames{iChan}, AllRowNames{iInput}));
            iTmp = setdiff(iTmp, iRowsSrc{iInput});
            if (length(iTmp) > 1)
                if (length(iTmp) > 1)
                    msgError = 'Several signals with the same name, re-ordering might be inaccurate.';
                    iTmp = iTmp(1);
                end
            end
            if ~isempty(iTmp)
                iRowsDest{iInput}(end+1) = iChan;
                iRowsSrc{iInput}(end+1)  = iTmp;
            end
        end
    end
end


function reconOffline(pfileDir,writeEfileFlag,bigEndian,seqType);
% reconOffline([pfileDir,writeEfileFlag,bigEndian,seqType]);
%
% (For linux and Mac OSX only):
% Use gary's recon code (in /biac2/kgs/dataTools/) to
% make Pmags and E-file headers from Pfiles without
% logging into lucas.
%
% pfileDir: where to find the Pfiles. Defaults to the
% current directory, and also checks if there's a Raw/Pfiles
% directory relative to that directory. So you can run this
% straight from the session directory.
%
% writeEfileFlag: if 0, won't re-write the E-file headers. By
% default, it does (1).
%
% bigEndian: flag on whether the Pfiles are big-endian or
% little-endian format. Short answer: if the data were collected
% (at stanford lucas center) prior to late February 2005, set
% this flag to 1; otherwise, ignore it (it defaults to 0).
%
% seqType: flag for sequence type, when the sequence has special needs.
% defaults to 0 (standard sequence), 1 = 3D sequence (needs to run 3dcode),
% other values currently ignored...
%
% This uses grecons.lnx and the -O (one file) and -b (byte swap, *Endian dependent)
% flags -- otherwise the Pmags come out messy.
%
% 09/04 ras.
% 03/05 ras: updated to deal w/ little-endian files, after
% the 3T at Lucas switched to the Excite system.
% 01/07 dar: included seqType flag, updated call to target
% "grecons.lnx" (not "grecons")
% 10/10 kw/al: updated to version 95 of grecon(grecon15) and writeihdr
% To do:
% +could use a good way to auto-detect if 3dcode should be run.

if ~isunix
    fprintf('Sorry, unix systems only (linux and Mac OS X).\n');
    return
end

if ~exist('pfileDir','var') | isempty(pfileDir)
    pfileDir = pwd;
end

if ~exist('writeEfileFlag','var') | isempty(writeEfileFlag)
    writeEfileFlag = 1;
end

if ~exist('bigEndian','var') | isempty(bigEndian)
    bigEndian = 0;
end

if ~exist('seqType','var') | isempty(seqType)
    seqType = 0;
end

pattern = fullfile(pfileDir,'P*.7');
w = dir(pattern);

% may not be pfiles in the current directory, but
% in Raw/Pfiles. Check this as a backup:
if isempty(w)
    pfileDir = fullfile(pfileDir,'Raw','Pfiles');
    pattern = fullfile(pfileDir,'P*.7');
    w = dir(pattern);
end

% if still no pfiles, you're outta luck
if isempty(w)
    error(sprintf('No Pfiles found in %s.',pfileDir));
end

callingDir = pwd;
cd(pfileDir);

bfilepattern = fullfile(pfileDir,'B0*'); %check for preexisting B0* files.  If present, will not delete later
b = dir(bfilepattern);

for i = 1:length(w)
    fname = w(i).name;

    if writeEfileFlag==1
        cmd = sprintf('/biac2/kgs/dataTools/writeihdr15 %s',fname);
        unix(cmd);
        fprintf('Wrote E-file header for %s.\n',fname);
    end

    if seqType == 1
        %this block MUST come before recon call...creates files to be used for
        %3d recon - Original 'P*****.7' is converted to '3d_P*****.7' which becomes
        %the active file
        cmd = sprintf('/biac2/kgs/dataTools/3dcode %s 3d_%s',fname,fname);
        unix(cmd);
        fprintf('3dcode has converted %s to 3d_%s - ready for recon\n',fname,fname);
        oldfname = fname;
        fname = sprintf('3d_%s',fname);
    end

    if bigEndian
        % big endian: for scans before the upgrade in Feb 2005
        cmd = sprintf('/biac2/kgs/dataTools/grecons.lnx -O -b %s',fname);
    else
        % little endian: after the upgrade, Pfiles now don't
        % need to be byte-swapped
        cmd = sprintf('/biac2/kgs/dataTools/grecons15.lnx -O %s',fname);
    end
    unix(cmd);

    if seqType == 1
        cmd = sprintf('mv %s.mag %s.mag', fname, oldfname);
        unix(cmd);
        fprintf('\n\n\t*****Reconned %s to %s.mag*****\n\n',fname,oldfname);
        fname = oldfname;
    else
        fprintf('\n\n\t*****Reconned %s*****\n\n',fname);
    end

    if isempty(b)
        % move the B0 estimate files into their own subdirectory,
        % and rename the B0.* files to I.*, so code like loadVolume
        % which looks for the pattern I* can find them. (ras, 02/06)
        % modified to create subdirectory for each scan (dar 01/07)
        try
            if ~isempty(dir('B0.*'))
                dirname = sprintf('B0_%s',fname(1:6));
                cmd = sprintf('mkdir %s',dirname);
                unix(cmd)
                nfiles = length(dir('B0.*'));
                for i = 1:nfiles
                    src = sprintf('B0.%03.0f', i);
                    tgt = sprintf('%s/I.%03.0f',dirname, i);
                    movefile(src, tgt);
                end
            end
        end
    end
end
if ~isempty(dir('B0_*'))
    mkdir B0
    cmd = sprintf('mv B0_*/ B0/')
    unix(cmd)
end
cd(callingDir);
return


function [remoteDoses, planC] = listRemoteDoseArrays(planC, saveFlag)
%"listRemoteDoseArrays"
%   Returns a structure array of dose array variables that are stored
%   remotely, for the purpose of wrapping the remote files in the archive
%   when it is saved, as well as updating the remote files.
%
%   If saveFlag is set to 1 the contents of the data field for each
%   remotely stored variable is written to that variable's file.
%   The variable is then marked as unloaded and returned as part of
%   the modified planC.  By default this is off.
%
%   If no planC is passed, listRemoteDoseArrays operates on the global
%   planC.
%
%JRA 04/26/05
%
%Usage:
%   [remoteDoses, planC] = listRemoteDoseArrays(planC, saveFlag)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

if ~exist('saveFlag')
    saveFlag = 0;
end

if ~exist('planC')
    global planC
end
indexS = planC{end};

remoteDoses = [];
nDoses = length(planC{indexS.dose});

%Iterate over doses, checking for remote dose arrays.
for doseNum=1:nDoses

    %Get the dose array for future checking.
    structure = planC{indexS.dose}(doseNum).doseArray;

    if ~isLocal(structure)

        %Found a remote array, add it to our list.
        remoteDoses = [remoteDoses structure];

        %If the saveFlag is specified, write the data field to the file.
        if saveFlag
            if structure.isLoaded
                structure = saveRemoteVariable(structure);
            end
        end
    end
end
return;


function [model, lbls] = modelReadFromFile(fileName, varargin)

% MODELREADFROMFILE Read model from a file FID produced by the C++ implementation.
% FORMAT
% DESC loads in from a file a model produced by C++ code.
% C++ GP implementation.
% ARG fileName : the file ID from where the data is loaded.
% RETURN model : the model loaded in from the file.
%
% COPYRIGHT : Neil D. Lawrence, 2008
%
% SEEALSO : modelReadFromFile

% MLTOOLS

FID = fopen(fileName);
if FID==-1
  error(['Cannot find file ' fileName])
end
model = modelReadFromFID(FID, varargin{:});
fclose(FID);


function pa_wavplay(varargin)
%    pa_wavplay: playback a matrix to multichannel sound hardware
%
%    Usage: pa_wavplay([devicetype])
%           pa_wavplay(buffer, [samplerate], [deviceid], [devicetype])
%
%    pa_wavplay is a tool for playing multi-channel audio through your ASIO
%    device. Arguments in [] are optional
%
%    pa_wavplay([devicetype]) will list all your audio devices for that
%    device type then exit.
%
%    - buffer       is the matrix to play
%    - samplerate	is the sampling frequency. Default: 44100
%    - deviceid 	is the device id to use for output. Default: 0
%    - devicetype   determines which sound driver to use
%        'win'      Windows Multimedia Device
%        'dx'       DirectX DirectSound driver
%        'asio'     ASIO Driver (default)
%
%    For stereo playback, buffer should be an N-by-2 matrix. The number of
%    audio channels supported is hardware dependent.
%
%    samplerate should be a valid integer sample rate, eg. 22050 44100 etc.
%
%    As of pa_wavplay 0.2, buffer should be either of type double or
%    single. pa_wavplay uses 32-bit floats (single) internally.
%
%    SEE ALSO: pa_wavrecord, pa_wavplayrecord

% check right num of args
error(nargchk(0,4,nargin));

% defaults
device_opt = 1;
device = 0;
fs = 44100;

% no args, print devices for asio
if (nargin==0)
    pawavplaya;
    return;
end

% if devtype specified
if ischar(varargin{end}),
   s=varargin{end};
   varargin(end)=[];

   device_opt = strmatch(s,{'asio','win', 'dx'});
   if isempty(device_opt),
       error(['Unrecognized DEVICETYPE: ' s]);
   end

   if (nargin==1)
       if device_opt==1, % asio
           pawavplaya;
       end
       if device_opt==2, % win
           pawavplayw;
       end
       if device_opt==3, % dx
           pawavplayx;
       end
       return;
    end
end

% data buffer
y = varargin{1};

% sample rate
if length(varargin)>=2,
   fs=varargin{2};
end

% device id
if length(varargin) >= 3,
    if (~(ischar(varargin{3})))
        device = varargin{3};
    end
end

if device_opt==1, % asio
    fprintf('Using ASIO driver\n');
    pawavplaya(y, device, fs, 0, 0, 0, -1);

elseif device_opt==2, % win
    fprintf('Using WMME driver\n');
    pawavplayw(y, device, fs, 0, 0, 0, -1);

elseif device_opt==3, % dx
    fprintf('Using DirectX driver\n');
    pawavplayx(y, device, fs, 0, 0, 0, -1);
end

% [EOF] pa_wavplay.m