// $Id: paramstat.cpp,v 1.14 2018/01/03 21:32:58 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: Mary Kuhner, Jon Yamato, and Joseph Felsenstein */


#include <cassert>

#include "paramstat.h"
#include "vectorx.h"

//-------------------------------------------------------------------------------------

ParamStatus::ParamStatus(pstatus mytype)
    : m_impl(CreateParamStatus(mytype))
{
    // deliberately blank
}

//-------------------------------------------------------------------------------------

string InvalidStatus::ConstraintDescription(long groupindex) const
{
    if (groupindex != FLAGLONG) return "be held at zero and ignored in output)";
    return " (held at zero and ignored in output)";
} // ConstraintDescription

//-------------------------------------------------------------------------------------

string UnconstrainedStatus::ConstraintDescription(long groupindex) const
{
    if (groupindex != FLAGLONG)
    {
        assert(false); //Groups should not be able to set 'unconstrained'
        return "start at the same value, then vary freely)";
    }
    // return " (unconstrained)";
    // That took too much space, but we could add it back if needed.
    return "";
} // ConstraintDescription

//-------------------------------------------------------------------------------------

string ConstantStatus::ConstraintDescription(long groupindex) const
{
    if (groupindex != FLAGLONG) return "stay at the same constant value)";
    return " (held constant)";
} // ConstraintDescription

//-------------------------------------------------------------------------------------

string ConstantStatus::ToggleIndividualStatus(force_type ftype) const
{
    if (ftype == force_COAL || ftype == force_REGION_GAMMA) return ToString(pstat_unconstrained);
    else return ToString(pstat_invalid);
} // ToggleGroupStatus

//-------------------------------------------------------------------------------------

string ConstantStatus::ToggleGroupStatus(force_type ftype) const
{
    if (ftype == force_COAL) return ToString(pstat_identical);
    else return ToString(pstat_invalid);
} // ToggleGroupStatus

//-------------------------------------------------------------------------------------

string IdenticalStatus::ConstraintDescription(long) const
{
    return "be identical)";
} // ConstraintDescription

//-------------------------------------------------------------------------------------

string IdenticalHeadStatus::ConstraintDescription(long) const
{
    return "be identical)";
} // ConstraintDescription

//-------------------------------------------------------------------------------------

void IdenticalHeadStatus::SetWithConstraints(long, double newval,
                                             DoubleVec1d& pvecnums,
                                             const ULongVec1d& groupmembers,
                                             double) const
{
    unsigned long i;
    for (i = 0; i < groupmembers.size(); ++i)
    {
        pvecnums[i] = newval;
    }
} //SetWithConstraints

//-------------------------------------------------------------------------------------

string MultiplicativeStatus::ConstraintDescription(long) const
{
    return "have a constant ratio)";
} // ConstraintDescription

//-------------------------------------------------------------------------------------

string MultiplicativeHeadStatus::ConstraintDescription(long) const
{
    return "have a constant ratio)";
} // ConstraintDescription

//-------------------------------------------------------------------------------------

void MultiplicativeHeadStatus::SetWithConstraints(long pindex, double newval,
                                                  DoubleVec1d& pvecnums,
                                                  const ULongVec1d& groupmembers,
                                                  double multiplier) const
{
    unsigned long i;

    // NB This code, trickily, sets the first member of the group without
    // using the multiplier, and then sets the rest (currently there must be
    // no more than one of them) using the value * multiplier.
    // Calling code must log the multiplier if newval is a log!

    pvecnums[pindex] = newval;
    for (i = 1; i < groupmembers.size(); ++i)
    {
        pvecnums[i] = newval * multiplier;
    }
} //SetWithConstraints

//-------------------------------------------------------------------------------------

#if 0 // MREMOVE
string EpochTimeStatus::ConstraintDescription(long) const
{
    return ""; //yes, we return nothing, the case is similar to 'unconstrained'
} // ConstraintDescription
#endif

//-------------------------------------------------------------------------------------

ParamStatusImpl* ParamStatus::CreateParamStatus(pstatus mytype)
{
    switch (mytype)
    {
        case pstat_invalid:
            return new InvalidStatus;
        case pstat_unconstrained:
            return new UnconstrainedStatus;
        case pstat_constant:
            return new ConstantStatus;
        case pstat_identical:
            return  new IdenticalStatus;
        case pstat_identical_head:
            return  new IdenticalHeadStatus;
        case pstat_multiplicative:
            return  new MultiplicativeStatus;
        case pstat_multiplicative_head:
            return  new MultiplicativeHeadStatus;
#if 0 // MREMOVE
        case pstat_epochtime:
            return  new EpochTimeStatus;
#endif
        default:
            assert(false);  // unknown parameter status
            break;
    }
    assert(false);
    return new InvalidStatus;  // this line can't be reached
}

//-------------------------------------------------------------------------------------

ParamStatus::ParamStatus(const ParamStatus& src)
    : m_impl(CreateParamStatus(src.Status()))
{
    // deliberately blank
}

//-------------------------------------------------------------------------------------

ParamStatus& ParamStatus::operator=(const ParamStatus& src)
{
    delete m_impl;
    m_impl = CreateParamStatus(src.Status());
    return *this;
}

//-------------------------------------------------------------------------------------

// These can't be in the header due to circularity
pstatus ParamStatus::Status() const
{ return m_impl->Status(); }

bool ParamStatus::Valid() const
{ return m_impl->Valid(); };

bool ParamStatus::Inferred() const
{ return m_impl->Inferred(); };

string ParamStatus::ConstraintDescription(long groupindex) const
{ return m_impl->ConstraintDescription(groupindex); };

bool ParamStatus::Varies() const
{ return m_impl->Varies(); };

bool ParamStatus::Grouped() const
{ return m_impl->Grouped(); };

void ParamStatus::SetWithConstraints(long pindex, double newval,
                                     vector<double>& pvecnums,
                                     const vector<unsigned long>& groupmembers,
                                     double multiplier) const
{ return m_impl->SetWithConstraints(pindex, newval, pvecnums, groupmembers,
                                    multiplier); };

string ParamStatus::ToggleIndividualStatus(force_type ftype) const
{ return ToString(m_impl->ToggleIndividualStatus(ftype)); };

string ParamStatus::ToggleGroupStatus(force_type ftype) const
{ return ToString(m_impl->ToggleGroupStatus(ftype)); };

//-------------------------------------------------------------------------------------

ParamStatus::~ParamStatus()
{
    delete m_impl;
}

//_____________________________________________________________________________________
