// $Id: chain.h,v 1.35 2018/01/03 21:33:03 mkkuhner Exp $

/*
Copyright 2002-2005 Mary K. Kuhner, Peter Beerli, and Joseph Felsenstein

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

/* Authors: Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein */


/*******************************************************************
  Chain is a helper class of the ChainManager whose main
  responsiblities are to choose the right arranger to use, call that
  arranger, then manage tree sampling using Arranger::Accept() and
  TreeSummary::SummarizeTree() as appropriate.  It also manages the
  runtime progress bar using RunReport expertise.  Finally it tracks
  some summary statistics on a chain such as the number of
  rearrangements accepted.

  Each Chain owns a Tree representing the chain's previous state,
  (Chain::oldtree); and a stable of arrangers used in the rearrangement
  process (Chain::arrangers).

  To use a Chain, you must call StartRegion() and StartReplicate()
  (in that order) and SetChainType() (at any point before StartChain())
  to initialize the Chain.  Then the functions StartChain(), DoOneChain(),
  and EndChain() run the actual single chain.

  Statistics on the run are passed back in the form of a ChainOut by
  EndChain().

  Written by Jon Yamato

  Changes:
  Mary:  changed from swapping Trees (expensive, and I can't
  swap the pointers without rewriting a lot of code) to swapping
  temperatures and associated variables.  This fixes a bug in
  heating, and will also make parallelization easier.

  Mary:  refactored "temperature identity" into a class, moved it to
  a separate file (tempident.h/cpp).  2004/03/23

********************************************************************/

#ifndef CHAIN_H
#define CHAIN_H

#include "chainout.h"
#include "arrangervec.h"
#include "tempident.h"
#include "collmanager.h"
#include "chainstate.h"

class ChainParameters;
class RunReport;
class Random;
class Region;

class Chain
{
  private:

    // This set of variables is part of a Chain's "temperature
    // identity" and will be swapped if the Chain's temperature is
    // swapped.

    TemperatureIdentity m_tempident;

    // This set of variables is not part of "temperature identity"
    // and remains the same when temperatures are swapped.

    long m_ndiscard;                     // length of burn-in
    long m_chaintype;                    // initial versus final
    long m_nsamples;                     // number to sample
    long m_sampleinterval;               // how long between samples
    long m_realstep;                     // what step of the chain are we on?
    ArrangerVec m_arrangers;             // we own these
    Random& m_randomsource;
    RunReport& m_runreport;
    CollectionManager& m_collmanager;
    ChainState m_chainstate;

    Chain();                              // undefined

    void CopyMembers(const Chain& src);
    Arranger* ChooseArranger(double rnd);
    void SwapTemperatures(Chain& other);
    void SwapParameters(Chain& other);
    void DestroyStick(const ForceSummary & forces);

  public:

    Chain(Random& rnd, RunReport& runrep, const ChainParameters& chainparam,
          CollectionManager& collection, double temper);
    Chain(const Chain& src);
    ~Chain();
    Chain& operator=(const Chain& src);

    void SetChainType(long chtype, const ChainParameters& chparam);

    void SetTemperature(double n)      { m_tempident.SetTemperature(n); };
    void ClearSwaps() { m_tempident.ClearSwaps(); };
    void ClearTotalSwaps() { m_tempident.ClearTotalSwaps(); };
    void SwapTried() { m_tempident.SwapTried(); };
    void SetChainStateOldTree(Tree *thistree) { m_chainstate.SetOldTree(thistree); };

    double GetCurrentDataLlike();
    double GetTemperature()       const { return m_tempident.GetTemperature(); };
    double GetSwapRate()          const { return m_tempident.GetSwapRate(); };
    double GetTotalSwapRate()     const { return m_tempident.GetTotalSwapRate(); };

    void StartRegion(Tree* regiontree);
    void EndRegion();

    // must call StartRegion() before calling
    void StartReplicate(const ForceSummary & forcesum, Region& region);
    void EndReplicate()                {};

    // must call SetChainType() before calling
    void StartChain(long chnumber, long chtype, const ForceSummary & forces,
                    const ForceParameters& starts);

    // StartBayesianChain() does not change the parameters that the
    // Chain currently has; this fixes pervasive problems with Divergence
    // where trying to change parameters without chaning the tree corrupts
    // the tree.
    void StartBayesianChain(long chnumber, long chtype, const ForceSummary & forces);

    void DoOneChain(long nsteps, bool lastchain);
    ChainOut EndChain();

    // for heating
    bool SwapTemperatureIdentities(Chain& hot);
    bool IsCold() const { return m_tempident.IsCold(); };

    // for optimizing the data model (MixedKS)
    Tree* GetTree() { return m_chainstate.GetTree(); };
    void  RecalculateDataLikes();
};

#endif // CHAIN_H

//____________________________________________________________________________________
