#ifndef __BaseStar_h__
#define __BaseStar_h__

#include <gsl/gsl_roots.h>
#include <gsl/gsl_sf_erf.h>

#include "constants.h"
#include "typedefs.h"
#include "profiling.h"
#include "utils.h"
#include "vector3d.h"

#include "Options.h"
#include "Log.h"
#include "Errors.h"

class BaseStar {

public:

    BaseStar();

    BaseStar(const unsigned long int p_RandomSeed,
             const double            p_MZAMS,
             const double            p_Metallicity,
             const KickParameters    p_KickParameters,
             const double            p_RotationalVelocity = -1.0);

    virtual ~BaseStar() {}

    // object identifiers - all classes have these
    OBJECT_ID           ObjectId() const                                                        { return m_ObjectId; }
    OBJECT_TYPE         ObjectType() const                                                      { return m_ObjectType; }
    STELLAR_TYPE        InitialStellarType() const                                              { return m_InitialStellarType; }
    STELLAR_TYPE        StellarType() const                                                     { return m_StellarType; }
    STELLAR_TYPE        StellarTypePrev() const                                                 { return m_StellarTypePrev; }

    // getters - alphabetically
            double              Age() const                                                     { return m_Age; }
            double              AngularMomentum() const                                         { return CalculateGyrationRadius() * m_Radius * RSOL_TO_AU * m_Radius * RSOL_TO_AU * m_Omega; }
            double              BindingEnergy_Fixed() const                                     { return m_BindingEnergies.fixed; }
            double              BindingEnergy_Nanjing() const                                   { return m_BindingEnergies.nanjing; }
            double              BindingEnergy_Loveridge() const                                 { return m_BindingEnergies.loveridge; }
            double              BindingEnergy_LoveridgeWinds() const                            { return m_BindingEnergies.loveridgeWinds; }
            double              BindingEnergy_Kruckow() const                                   { return m_BindingEnergies.kruckow; }
            bool                CHonMS() const                                                  { return m_CHE; }
            double              COCoreMass() const                                              { return m_COCoreMass; }
            double              CoreMass() const                                                { return m_CoreMass; }
            int                 DominantMassLossRate() const                                    { return static_cast<int>(m_DominantMassLossRate); }
            double              Dt() const                                                      { return m_Dt; }
            double              DtPrev() const                                                  { return m_DtPrev; }
            ERROR               Error() const                                                   { return m_Error; }
            bool                ExperiencedCCSN() const                                         { return (m_SupernovaDetails.events.past & SN_EVENT::CCSN) == SN_EVENT::CCSN; }
            bool                ExperiencedECSN() const                                         { return (m_SupernovaDetails.events.past & SN_EVENT::ECSN) == SN_EVENT::ECSN; }
            bool                ExperiencedPISN() const                                         { return (m_SupernovaDetails.events.past & SN_EVENT::PISN) == SN_EVENT::PISN; }
            bool                ExperiencedPPISN() const                                        { return (m_SupernovaDetails.events.past & SN_EVENT::PPISN) == SN_EVENT::PPISN; }
            SN_EVENT            ExperiencedSN_Type() const                                      { return utils::SNEventType(m_SupernovaDetails.events.past); }
            bool                ExperiencedUSSN() const                                         { return (m_SupernovaDetails.events.past & SN_EVENT::USSN) == SN_EVENT::USSN; }
            double              HeCoreMass() const                                              { return m_HeCoreMass; }
            bool                IsCCSN() const                                                  { return (m_SupernovaDetails.events.current & SN_EVENT::CCSN) == SN_EVENT::CCSN; }
    virtual bool                IsDegenerate() const                                            { return false; }   // default is not degenerate - White Dwarfs, NS and BH are degenerate
            bool                IsECSN() const                                                  { return (m_SupernovaDetails.events.current & SN_EVENT::ECSN) == SN_EVENT::ECSN; }
    virtual bool                IsMassRatioUnstable(const double p_AccretorMass, const bool p_AccretorIsDegenerate) const { return false; } // default is stable
            bool                IsOneOf(const STELLAR_TYPE_LIST p_List) const;
            bool                IsPISN() const                                                  { return (m_SupernovaDetails.events.current & SN_EVENT::PISN) == SN_EVENT::PISN; }
            bool                IsPPISN() const                                                 { return (m_SupernovaDetails.events.current & SN_EVENT::PPISN) == SN_EVENT::PPISN; }
            bool                IsUSSN() const                                                  { return (m_SupernovaDetails.events.current & SN_EVENT::USSN) == SN_EVENT::USSN; }
            double              Lambda_Dewi() const                                             { return m_Lambdas.dewi; }
            double              Lambda_Fixed() const                                            { return m_Lambdas.fixed; }
            double              Lambda_Kruckow() const                                          { return m_Lambdas.kruckow; }
            double              Lambda_KruckowBottom() const                                    { return m_Lambdas.kruckowBottom; }
            double              Lambda_KruckowMiddle() const                                    { return m_Lambdas.kruckowMiddle; }
            double              Lambda_KruckowTop() const                                       { return m_Lambdas.kruckowTop; }
            double              Lambda_Loveridge() const                                        { return m_Lambdas.loveridge; }
            double              Lambda_LoveridgeWinds() const                                   { return m_Lambdas.loveridgeWinds; }
            double              Lambda_Nanjing() const                                          { return m_Lambdas.nanjing; }
            bool                LBV_Phase_Flag() const                                          { return m_LBVphaseFlag; }
            double              Luminosity() const                                              { return m_Luminosity; }
            double              Mass() const                                                    { return m_Mass; }
            double              Mass0() const                                                   { return m_Mass0; }
            double              MassPrev() const                                                { return m_MassPrev; }
            STYPE_VECTOR        MassTransferDonorHistory() const                                { return m_MassTransferDonorHistory; }
            std::string         MassTransferDonorHistoryString() const;
            double              Mdot() const                                                    { return m_Mdot; }
            double              Metallicity() const                                             { return m_Metallicity; }
            double              MZAMS() const                                                   { return m_MZAMS; }
            double              Omega() const                                                   { return m_Omega; }
            double              OmegaCHE() const                                                { return m_OmegaCHE; }
            double              OmegaBreak() const                                              { return CalculateOmegaBreak(); }
            double              OmegaPrev() const                                               { return m_OmegaPrev; }
            double              OmegaZAMS() const                                               { return m_OmegaZAMS; }
            COMPAS_VARIABLE     PropertyValue(const T_ANY_PROPERTY p_Property) const;
            double              Pulsar_MagneticField() const                                    { return m_PulsarDetails.magneticField; }
            double              Pulsar_SpinPeriod() const                                       { return m_PulsarDetails.spinPeriod; }
            double              Pulsar_SpinFrequency() const                                    { return m_PulsarDetails.spinFrequency; }
            double              Pulsar_SpinDownRate() const                                     { return m_PulsarDetails.spinDownRate; }
            double              Radius() const                                                  { return m_Radius; }
            double              RadiusPrev() const                                              { return m_RadiusPrev; }
            unsigned long int   RandomSeed() const                                              { return m_RandomSeed; }
            double              RZAMS() const                                                   { return m_RZAMS; }
            double              SN_CoreMassAtCOFormation() const                                { return m_SupernovaDetails.coreMassAtCOFormation; }
            double              SN_COCoreMassAtCOFormation() const                              { return m_SupernovaDetails.COCoreMassAtCOFormation; }
            SupernovaDetailsT   SN_Details() const                                              { return m_SupernovaDetails; }
            double              SN_DrawnKickMagnitude() const                                   { return m_SupernovaDetails.drawnKickMagnitude; }
            double              SN_EccentricAnomaly() const                                     { return m_SupernovaDetails.eccentricAnomaly; }
            double              SN_FallbackFraction() const                                     { return m_SupernovaDetails.fallbackFraction; }
            double              SN_HeCoreMassAtCOFormation() const                              { return m_SupernovaDetails.HeCoreMassAtCOFormation; }
            bool                SN_IsHydrogenPoor() const                                       { return m_SupernovaDetails.isHydrogenPoor; }
            double              SN_KickMagnitude() const                                        { return m_SupernovaDetails.kickMagnitude; }
            double              SN_MeanAnomaly() const                                          { return m_SupernovaDetails.meanAnomaly; }
            double              SN_Phi() const                                                  { return m_SupernovaDetails.phi; }
            double              SN_TotalMassAtCOFormation() const                               { return m_SupernovaDetails.totalMassAtCOFormation; }
            double              SN_TrueAnomaly() const                                          { return m_SupernovaDetails.trueAnomaly; }
            double              SN_Theta() const                                                { return m_SupernovaDetails.theta; }
            SN_EVENT            SN_Type() const                                                 { return utils::SNEventType(m_SupernovaDetails.events.current); }
            double              SN_KickMagnitudeRandom() const                                  { return m_SupernovaDetails.kickMagnitudeRandom; }
            double              Speed() const                                                   { return m_ComponentVelocity.Magnitude(); }
            COMPAS_VARIABLE     StellarPropertyValue(const T_ANY_PROPERTY p_Property) const;
            double              Tau() const                                                     { return m_Tau; }
            double              Temperature() const                                             { return m_Temperature; }
            double              Time() const                                                    { return m_Time; }
            double              Timescale(TIMESCALE p_Timescale) const                          { return m_Timescales[static_cast<int>(p_Timescale)]; }
            double              XExponent() const                                               { return m_XExponent; }

    // setters
            void                SetInitialType(STELLAR_TYPE p_InitialType)                      { m_InitialStellarType = p_InitialType; }                                           // JR Could do some sanity checks here
            void                SetOmega(double p_vRot)                                         { if (p_vRot >= 0.0) m_Omega = p_vRot; };                                           // Do nothing if sanity check fails (JR: I don't really like this, but I think unavoidable - at least for now)

            void                SetSNCurrentEvent(SN_EVENT p_SNEvent)                           { m_SupernovaDetails.events.current |= p_SNEvent; }                                 // Set supernova primary event/state for current timestep
            void                SetSNPastEvent(const SN_EVENT p_SNEvent)                        { m_SupernovaDetails.events.past |= p_SNEvent; }                                    // Set supernova primary event/state for any past timestep

            void                UpdateComponentVelocity(const Vector3d p_newVelocity);

            void                UpdateMassTransferDonorHistory();

    // member functions - alphabetically
            void            ApplyMassTransferRejuvenationFactor()                                               { m_Age *= CalculateMassTransferRejuvenationFactor(); }             // Apply age rejuvenation factor

            void            CalculateBindingEnergies(const double p_CoreMass, const double p_EnvMass, const double p_Radius);

            double          CalculateDynamicalTimescale() const                                                 { return CalculateDynamicalTimescale_Static(m_Mass, m_Radius); }         // Use class member variables

            double          CalculateEddyTurnoverTimescale();

    virtual void            CalculateGBParams(const double p_Mass, DBL_VECTOR &p_GBParams) { }                                                                                // Default is NO-OP
    virtual void            CalculateGBParams()                                                                 { CalculateGBParams(m_Mass0, m_GBParams); }                         // Use class member variables

    virtual double          CalculateGyrationRadius() const                                                     { return 0.0; }                                                     // Default is 0.0

            void            CalculateLambdas()                                                                  { CalculateLambdas(m_Mass - m_CoreMass); }                          // Use class member variables
            void            CalculateLambdas(const double p_EnvMass);

    virtual DBL_DBL         CalculateMassAcceptanceRate(const double p_DonorMassRate,
                                                        const double p_AccretorMassRate = 0.0);

            double          CalculateMassLossValues(const bool p_UpdateMDot = false, const bool p_UpdateMDt = false);                                                               // JR: todo: better name?

    virtual double          CalculateMomentOfInertia(const double p_RemnantRadius = 0.0) const                  { return 0.0; }                                                     // Use inheritance hierarchy
    virtual double          CalculateMomentOfInertiaAU(const double p_RemnantRadius = 0.0) const                { return 0.0; }                                                     // Use inheritance hierarchy

            double          CalculateNuclearTimescale() const                                                   { return CalculateNuclearTimescale_Static(m_Mass, m_Luminosity); }  // Use class member variables

            double          CalculateOmegaCHE(const double p_MZAMS, const double p_Metallicity) const;

            double          CalculateRadialChange() const                                                       { return (utils::Compare(m_RadiusPrev,0)<=0)? 0 : std::abs(m_Radius - m_RadiusPrev) / m_RadiusPrev; }                    // Return fractional radial change (if previous radius is negative or zero, return 0 to avoid NaN

            double          CalculateRadialExpansionTimescale() const                                           { return CalculateRadialExpansionTimescale_Static(m_StellarType, m_StellarTypePrev, m_Radius, m_RadiusPrev, m_DtPrev); } // Use class member variables

            void            CalculateSNAnomalies(const double p_Eccentricity);

            double          CalculateSNKickMagnitude(const double p_RemnantMass, const double p_EjectaMass, const STELLAR_TYPE p_StellarType);

            double          CalculateThermalMassAcceptanceRate(const double p_Radius) const;
            double          CalculateThermalMassAcceptanceRate() const                                          { return CalculateThermalMassAcceptanceRate(m_Radius); }

    virtual double          CalculateThermalMassLossRate() const                                                { return m_Mass / CalculateThermalTimescale(); }                    // Use class member variables - and inheritance hierarchy

    virtual double          CalculateThermalTimescale(const double p_Radius) const;                                                                                // Use inheritance hierarchy
    virtual double          CalculateThermalTimescale() const                                                   { return CalculateThermalTimescale(m_Radius); }                     // Use inheritance hierarchy

            double          CalculateTimestep();

    virtual double          CalculateZeta(ZETA_PRESCRIPTION p_ZetaPrescription)                                 { return 0.0; }                                                     // Use inheritance hierarchy

            void            ClearCurrentSNEvent()                                                               { m_SupernovaDetails.events.current = SN_EVENT::NONE; }             // Clear supernova event/state for current timestep

    virtual ENVELOPE        DetermineEnvelopeType() const                                                       { return ENVELOPE::REMNANT; }                                       // Default is REMNANT - but should never be called

            void            IncrementOmega(const double p_OmegaDelta)                                           { m_Omega += p_OmegaDelta; }                                        // Apply delta to current m_Omega

            void            ResolveAccretion(const double p_AccretionMass)                                      { m_Mass = std::max(0.0, m_Mass + p_AccretionMass); }               // Handles donation and accretion - won't let mass go negative

    virtual STELLAR_TYPE    ResolveEnvelopeLoss(bool p_NoCheck = false)                                         { return m_StellarType; }

    virtual void            ResolveMassLoss();

    virtual STELLAR_TYPE    ResolveRemnantAfterEnvelopeLoss()                                                   { return m_StellarType; }

            void            SetStellarTypePrev(const STELLAR_TYPE p_StellarTypePrev)                            { m_StellarTypePrev = p_StellarTypePrev; }

            void            StashSupernovaDetails(const STELLAR_TYPE p_StellarType)                             { LOGGING->StashSSESupernovaDetails(this, p_StellarType); }

    virtual void            UpdateAgeAfterMassLoss() { }                                                                                // Default is NO-OP

            STELLAR_TYPE    UpdateAttributesAndAgeOneTimestep(const double p_DeltaMass,
                                                              const double p_DeltaMass0,
                                                              const double p_DeltaTime,
                                                              const bool   p_ForceRecalculate);

    virtual void            UpdateInitialMass() { }                                                                                // Default is NO-OP

    virtual void            UpdateMagneticFieldAndSpin(const bool   p_CommonEnvelope,
                                                       const bool   p_RecyclesNS,
                                                       const double p_Stepsize,
                                                       const double p_MassGainPerTimeStep,
                                                       const double p_Epsilon) { }                                                                                // Default is NO-OP

    // printing functions
            bool            PrintDetailedOutput(const int p_Id) const                                           { return OPTIONS->DetailedOutput() ? LOGGING->LogSSEDetailedOutput(this, p_Id) : true; } // Write record to SSE Detailed Output log file
            bool            PrintSupernovaDetails() const                                                       { return LOGGING->LogSSESupernovaDetails(this); }                   // Write record to SSE Supernovae log file
            bool            PrintStashedSupernovaDetails()                                                      { return LOGGING->LogStashedSSESupernovaDetails(this); }            // Write record to SSE Supernovae log file
            bool            PrintSwitchLog() const                                                              { return OPTIONS->SwitchLog() ? LOGGING->LogSSESwitchLog(this) : true; } // Write record to SSE Switchlog log file
            bool            PrintSystemParameters() const                                                       { return LOGGING->LogSSESystemParameters(this); }                   // Write record to SSE System Parameters file

protected:

    OBJECT_ID               m_ObjectId;                                 // Instantiated object's unique object id
    OBJECT_TYPE             m_ObjectType;                               // Instantiated object's object type
    STELLAR_TYPE            m_InitialStellarType;                       // Stellar type at birth, defined in Hurley et al. 2000
    STELLAR_TYPE            m_StellarType;                              // Stellar type defined in Hurley et al. 2000

    ERROR                   m_Error;                                    // Records most recent error encountered for this star

    // member variables - alphabetical in groups

    bool                    m_CHE;                                      // CHE flag - true if the star spent entire MS as a CH star; false if evolved CH->MS

    // Stellar variables
    unsigned long int       m_RandomSeed;                               // Seeds the random number generator for this star

    // Zero Age Main Sequence
    double                  m_LZAMS;                                    // ZAMS Luminosity
    double                  m_MZAMS;                                    // ZAMS Mass
    double                  m_OmegaZAMS;                                // ZAMS Angular Frequency
    double                  m_OmegaCHE;                                 // Minimum angular frequency at which CHE will occur (calculated at ZAMS)
    double                  m_RZAMS;                                    // ZAMS Radius
    double                  m_TZAMS;                                    // ZAMS Temperature

    // Effective Zero Age Main Sequence
    double                  m_LZAMS0;                                   // Effective ZAMS Luminosity
    double                  m_RZAMS0;                                   // Effective ZAMS Radius
    double                  m_TZAMS0;                                   // Effective ZAMS Temperature

    // Current timestep variables
    double                  m_Age;                                      // Current effective age (changes with mass loss/gain)(myrs)
    double                  m_COCoreMass;                               // Current CO core mass (Msol)
    double                  m_CoreMass;                                 // Current core mass (Msol)
    double                  m_CoreRadius;                               // Current core radius (Rsol)                   JR: todo: I don't think this is used anywhere...
    double                  m_Dt;                                       // Current timestep (myrs)
    double                  m_HeCoreMass;                               // Current He core mass (Msol)
    bool                    m_LBVphaseFlag;                             // Flag to know if the star satisfied the conditions, at any point in its evolution, to be considered a Luminous Blue Variable (LBV)
    double                  m_Luminosity;                               // Current luminosity (Lsol)
    double                  m_Mass;                                     // Current mass (Msol)
    double                  m_Mass0;                                    // Current effective initial mass (Msol)        JR: todo: fix this one day - it is not always initial mass
    double                  m_MinimumLuminosityOnPhase;                 // JR: Only required for CHeB stars, but only needs to be calculated once per star
    double                  m_Mdot;                                     // Current mass loss rate (Msol per ?)
    MASS_LOSS_TYPE          m_DominantMassLossRate;                     // Current dominant mass loss rate
    double                  m_Mu;                                       // Current small envelope parameter mu
    double                  m_Omega;                                    // Current angular frequency (yr-1)
    double                  m_Radius;                                   // Current radius (Rsol)
    double                  m_Tau;                                      // Relative time
    double                  m_Temperature;                              // Current temperature (Tsol)
    double                  m_Time;                                     // Current physical time the star has been evolved(myrs)

    // Previous timestep variables
    double                  m_DtPrev;                                   // Previous timestep
    double                  m_MassPrev;                                 // Previous mass (Msol)
    double                  m_OmegaPrev;                                // Previous angular frequency (yr-1)
    double                  m_RadiusPrev;                               // Previous radius (Rsol)
    STELLAR_TYPE            m_StellarTypePrev;                          // Stellar type at previous timestep

    // Metallicity variables
    double                  m_LogMetallicityRho;                        // logMetallicityXi + 1.0       - called rho in Hurley et al 2000
    double                  m_LogMetallicitySigma;                      // log10(Metallicity)           - called sigma in Hurley et al 2000
    double                  m_LogMetallicityXi;                         // log10(Metallicity / Zsol)    - called xi in Hurley et al 2000
    double                  m_Metallicity;                              // Metallicity

    // Metallicity dependent constants
    double                  m_Alpha1;                                   // alpha1 in Hurly et al. 2000, just after eq 49
    double                  m_Alpha3;                                   // alpha4 in Hurley et al. 2000, just after eq 56
    double                  m_Alpha4;                                   // alpha4 in Hurley et al. 2000, just after eq 57
    double                  m_XExponent;                                // exponent to which R depends on M - 'x' in Hurley et al. 2000, eq 47

    // constants only calculated once
    double                  m_BaryonicMassOfMaximumNeutronStarMass;      // baryonic mass of MaximumNeutronStarMass

    // JR:
    // I initially implemented the following vectors as unordered_maps.  The code worked
    // quite well, except for one small problem - access times (presumably due to hashing)
    // were prohibitive when accessed hundreds of thousands, and in some cases, millions,
    // of times as we evolve the star.  So I used vectors instead - the code is not as
    // elegant, but performance is better by an order of magnitude

    // Timescales, Giant Branch parameters, mass cutoffs
    DBL_VECTOR              m_GBParams;                                 // Giant Branch Parameters
    DBL_VECTOR              m_MassCutoffs;                              // Mass cutoffs
    DBL_VECTOR              m_Timescales;                               // Timescales

    // Luminosity, Radius, a(n) and b(n) coefficients
    DBL_VECTOR              m_AnCoefficients;                           // a(n) coefficients
    DBL_VECTOR              m_BnCoefficients;                           // b(n) coefficients
    DBL_VECTOR              m_LCoefficients;                            // Luminosity coefficients
    DBL_VECTOR              m_RCoefficients;                            // Radius coefficients

    // Luminosity, Radius and Gamma constants
    // JR:
    // These are calculated in CalculateAnCoefficients()
    // Calculating the a(n) coefficients requires one of the R constants, and the L, R and Gamma
    // constants are calculated using the a(n) coefficients, so it seemed logical to calculate
    // them all in the same function
    DBL_VECTOR              m_GammaConstants;                           // Gamma constants
    DBL_VECTOR              m_LConstants;                               // Luminosity constants
    DBL_VECTOR              m_RConstants;                               // Radius constants

    // Binding energies, Lambdas and Zetas
    BindingEnergiesT        m_BindingEnergies;                          // Binding enery values
    LambdasT                m_Lambdas;                                  // Lambda values

    // Stellar details squirrelled away...
    SupernovaDetailsT       m_SupernovaDetails;                         // Supernova attributes
    PulsarDetailsT          m_PulsarDetails;                            // Pulsar attributes

    // Star vector velocity
    Vector3d                m_ComponentVelocity;                        // Isolated star velocity vector (binary's center-of-mass velocity for bound binary)

    // Star mass transfer history
    STYPE_VECTOR            m_MassTransferDonorHistory;                 // List of MT donor stellar types - mostly relevent for binary stars

    // member functions - alphabetically
            void                AgeOneTimestepPreamble(const double p_DeltaTime);

            double              ApplyBlackHoleKicks(const double p_vK, const double p_FallbackFraction, const double p_BlackHoleMass);

            double              CalculateAlpha1() const;
            double              CalculateAlpha3() const;
            double              CalculateAlpha4() const;

            void                CalculateAnCoefficients(DBL_VECTOR &p_AnCoefficients,
                                                        DBL_VECTOR &p_LConstants,
                                                        DBL_VECTOR &p_RConstants,
                                                        DBL_VECTOR &p_GammaConstants);

    virtual void                CalculateAndSetPulsarParameters() { }                                                                                // NO-OP for most stellar types

            double              CalculateBindingEnergy(const double p_CoreMass, const double p_EnvMass, const double p_Radius, const double p_Lambda) const;

            void                CalculateBnCoefficients(DBL_VECTOR &p_BnCoefficients);

    virtual double              CalculateCOCoreMassAtPhaseEnd() const                                                   { return m_COCoreMass; }                                                    // Default is NO-OP
    virtual double              CalculateCOCoreMassOnPhase() const                                                      { return m_COCoreMass; }                                                    // Default is NO-OP

    virtual double              CalculateCoreMassAtPhaseEnd() const                                                     { return m_CoreMass; }                                                      // Default is NO-OP
    static  double              CalculateCoreMassGivenLuminosity_Static(const double p_Luminosity, const DBL_VECTOR &p_GBParams);
    virtual double              CalculateCoreMassOnPhase() const                                                        { return m_CoreMass; }                                                      // Default is NO-OP

    static  double              CalculateDynamicalTimescale_Static(const double p_Mass, const double p_Radius);

    virtual double              CalculateEddingtonCriticalRate() const                                                  { return 2.08E-3 / 1.7 * m_Radius * MYR_TO_YEAR; }       // Hurley+, 2002, Eq. (67); should never be called...

            double              CalculateGBRadiusXExponent() const;

    virtual double              CalculateHeCoreMassAtPhaseEnd() const                                                   { return m_HeCoreMass; }                                                    // Default is NO-OP
    virtual double              CalculateHeCoreMassOnPhase() const                                                      { return m_HeCoreMass; }                                                    // Default is NO-OP

    static  double              CalculateHeRateConstant_Static()                                                        { return HE_RATE_CONSTANT; }                                                // Only >= CHeB stars need AHe, but no drama if other stars calculate (retrieve it) - it's only a constant (we could just use the constant inline...)
    static  double              CalculateHHeRateConstant_Static()                                                       { return HHE_RATE_CONSTANT; }                                               // Only TPAGB stars need AHHe, but no drama if other stars calculate (retrieve it) - it's only a constant (we could just use the constant inline...)

    static  double              CalculateInitialEnvelopeMass_Static(const double p_Mass);

    virtual double              CalculateLambdaDewi() const                                                             { SHOW_WARN(ERROR::NO_LAMBDA_DEWI, "Default used: 1.0"); return 1.0; }      // Not supported: show error
            double              CalculateLambdaKruckow(const double p_Radius, const double p_Alpha) const;
            double              CalculateLambdaLoveridgeEnergyFormalism(const double p_EnvMass, const double p_IsMassLoss = false) const;
    virtual double              CalculateLambdaNanjing() const                                                          { SHOW_WARN(ERROR::NO_LAMBDA_NANJING, "Default used: 1.0"); return 1.0; }   // Not supported: show error

            void                CalculateLCoefficients(const double p_LogMetallicityXi, DBL_VECTOR &p_LCoefficients);

            double              CalculateLifetimeToBAGB(const double p_tHeI, const double p_tHe) const;
            double              CalculateLifetimeToBGB(const double p_Mass) const;

            double              CalculateLogBindingEnergyLoveridge(bool p_IsMassLoss) const;

            double              CalculateLuminosityAtBAGB(double p_Mass) const;
    virtual double              CalculateLuminosityAtPhaseEnd() const                                                   { return m_Luminosity; }                                                    // Default is NO-OP
            double              CalculateLuminosityAtZAMS(const double p_MZAMS);
            double              CalculateLuminosityGivenCoreMass(const double p_CoreMass) const;
    virtual double              CalculateLuminosityOnPhase() const                                                      { return m_Luminosity; }                                                    // Default is NO-OP

            void                CalculateMassCutoffs(const double p_Metallicity, const double p_LogMetallicityXi, DBL_VECTOR &p_MassCutoffs);

    static  double              CalculateMassLoss_Static(const double p_Mass, const double p_Mdot, const double p_Dt);

            double              CalculateMassLossRate();
    virtual double              CalculateMassLossRateHurley();
            double              CalculateMassLossRateKudritzkiReimers() const;
            double              CalculateMassLossRateLBV(const LBV_PRESCRIPTION p_LBV_prescription);
            double              CalculateMassLossRateLBVHurley(const double p_HD_limit_fac) const;
            double              CalculateMassLossRateLBVBelczynski() const;
            double              CalculateMassLossRateNieuwenhuijzenDeJager() const;
            double              CalculateMassLossRateOB(const double p_Teff);
            double              CalculateMassLossRateVassiliadisWood() const;
    virtual double              CalculateMassLossRateVink();
            double              CalculateMassLossRateWolfRayetZDependent(const double p_Mu) const;
            double              CalculateMassLossRateWolfRayet3() const;                                                                                // JR: Never called - do we need it?
            double              CalculateMassLossRateWolfRayet(const double p_Mu) const;

    virtual double              CalculateMassTransferRejuvenationFactor() const;

            double              CalculateMaximumCoreMass(double p_Mass) const;

    static  double              CalculateNuclearTimescale_Static(const double p_Mass, const double p_Luminosity);

            double              CalculateOmegaBreak() const;

    static  double              CalculateOStarRotationalVelocityAnalyticCDF_Static(const double p_Ve);
    static  double              CalculateOStarRotationalVelocityAnalyticCDFInverse_Static(double p_Ve, void *p_Params);
    static  double              CalculateOStarRotationalVelocity_Static(const double p_Xmin, const double p_Xmax);

            double              CalculatePerturbationB(const double p_Mass) const;
            double              CalculatePerturbationC(double p_Mass) const;
    virtual double              CalculatePerturbationMu() const                                                         { return m_Mu; }                                                            // Default is NO-OP
    virtual double              CalculatePerturbationMuAtPhaseEnd() const                                               { return CalculatePerturbationMuOnPhase(); }                                // Same as on phase
    virtual double              CalculatePerturbationMuOnPhase() const                                                  { return CalculatePerturbationMu(); }
            double              CalculatePerturbationQ(const double p_Radius, const double p_Rc) const;
            double              CalculatePerturbationR(const double p_Mu, const double p_Mass, const double p_Radius, const double p_Rc) const;
            double              CalculatePerturbationS(const double p_Mu, const double p_Mass) const;

    static  double              CalculateRadialExpansionTimescale_Static(const STELLAR_TYPE p_StellarType,
                                                                     const STELLAR_TYPE p_StellarTypePrev,
                                                                     const double       p_Radius,
                                                                     const double       p_RadiusPrev,
                                                                     const double       p_DtPrev);

            virtual double      CalculateRadialExtentConvectiveEnvelope() const                                         { return m_Radius; }                                                        // default for stars with no convective envelope

    virtual double              CalculateRadiusAtPhaseEnd() const                                                       { return m_Radius; }                                                        // Default is NO-OP
            double              CalculateRadiusAtZAMS(const double p_MZAMS) const;
    virtual double              CalculateRadiusOnPhase() const                                                          { return m_Radius; }                                                        // Default is NO-OP
    virtual std::tuple <double, STELLAR_TYPE> CalculateRadiusAndStellarTypeOnPhase() const                              { return std::make_tuple(CalculateRadiusOnPhase(), m_StellarType); }

            void                CalculateRCoefficients(const double p_LogMetallicityXi, DBL_VECTOR &p_RCoefficients);

            double              CalculateRotationalVelocity(double p_MZAMS) const;

    virtual double              CalculateTauOnPhase() const                                                             { return m_Tau; }                                                           // Default is NO-OP
    virtual double              CalculateTauAtPhaseEnd() const                                                          { return m_Tau; }                                                           // Default is NO-OP

    virtual double              CalculateTemperatureAtPhaseEnd() const                                                  { return CalculateTemperatureAtPhaseEnd(m_Luminosity, m_Radius); }
    virtual double              CalculateTemperatureAtPhaseEnd(const double p_Luminosity, const double p_Radius) const  { return CalculateTemperatureOnPhase(p_Luminosity, p_Radius); }             // Same as on phase
            double              CalculateTemperatureKelvinOnPhase(const double p_Luminosity, const double p_Radius) const;
    virtual double              CalculateTemperatureOnPhase() const                                                     { return CalculateTemperatureOnPhase(m_Luminosity, m_Radius); }
    virtual double              CalculateTemperatureOnPhase(const double p_Luminosity, const double p_Radius) const     { return CalculateTemperatureOnPhase_Static(p_Luminosity, p_Radius); }
    static  double              CalculateTemperatureOnPhase_Static(const double p_Luminosity, const double p_Radius);

    virtual void                CalculateTimescales()                                                                   { CalculateTimescales(m_Mass0, m_Timescales); }                             // Use class member variables
    virtual void                CalculateTimescales(const double p_Mass, DBL_VECTOR &p_Timescales) { }                                                                                // Default is NO-OP

            double              CalculateZadiabaticHurley2002(const double p_CoreMass) const;
            double              CalculateZadiabaticSPH(const double p_CoreMass) const;
            double              CalculateZadiabatic(ZETA_PRESCRIPTION p_ZetaPrescription);

            double              CalculateZAMSAngularFrequency(const double p_MZAMS, const double p_RZAMS) const;

    virtual double              ChooseTimestep(const double p_Time) const                                               { return m_Dt; }

            double              DrawKickMagnitudeBrayEldridge(const double p_EjectaMass,
                                                              const double p_RemnantMass,
                                                              const double p_Alpha,
                                                              const double p_Beta) const;

            double              DrawKickMagnitudeDistributionFlat(const double p_MaxVK, const double p_Rand) const;
            double              DrawKickMagnitudeDistributionMaxwell(const double p_Sigma, const double p_Rand) const;

            double              DrawRemnantKickMuller(const double p_COCoreMass) const;

            double              DrawRemnantKickMullerMandel(const double p_COCoreMass,
                                                            const double p_Rand,
                                                            const double p_RemnantMass) const;

            double              DrawSNKickMagnitude(const double p_Sigma,
                                                    const double p_COCoreMass,
                                                    const double p_Rand,
                                                    const double p_EjectaMass,
                                                    const double p_RemnantMass) const;

    virtual void                EvolveOneTimestepPreamble() { };                                                                                // Default is NO-OP

            STELLAR_TYPE        EvolveOnPhase();

    virtual STELLAR_TYPE        EvolveToNextPhase()                                                                     { return m_StellarType; }

    virtual bool                IsEndOfPhase() const                                                                    { return false; }
    virtual bool                IsSupernova() const                                                                     { return false; }

            double              LimitTimestep(const double p_Dt);

    /*
     * Perturb Luminosity and Radius
     *
     * See Hurley et al. 2000, section 6.3
     *
     * The default is no perturbation - this function does nothing and is called
     * only if the stellar class doesn't define its own perturbation function.
     * See the stellar class perturbation functions for perturbation details specific
     * to the stellar class.
     *
     * Perturbation is disabled by default when DEBUG is enabled - except when
     * DEBUG_PERTURB is defined (see below).  The stellar class perturbation
     * functions are defined away if DEBUG is defined - so this generic Star
     * function is called (and does nothing).
     *
     * If DEBUG_PERTURB is defined then perturbation is not disabled while debbuging.
     * To enable perturbation while DEBUG is enabled, define DEBUG_PERTURB.
     */
    virtual void                PerturbLuminosityAndRadius() { }                                                                                // NO-OP
    virtual void                PerturbLuminosityAndRadiusAtPhaseEnd()                                                  { PerturbLuminosityAndRadiusOnPhase(); }                                    // Same as on phase
    virtual void                PerturbLuminosityAndRadiusOnPhase()                                                     { PerturbLuminosityAndRadius(); }

            STELLAR_TYPE        ResolveEndOfPhase();
    virtual void                ResolveHeliumFlash() { }
    virtual STELLAR_TYPE        ResolveSkippedPhase()                                                                   { return EvolveToNextPhase(); }                                             // Default is evolve to next phase
    virtual STELLAR_TYPE        ResolveSupernova()                                                                      { return m_StellarType; }                                                   // Default is NO-OP

    virtual void                SetSNHydrogenContent()                                                                  { m_SupernovaDetails.isHydrogenPoor = false; }                              // Default is false

            bool                ShouldBeMasslessRemnant() const                                                         { return (m_Mass <= 0.0 || m_StellarType==STELLAR_TYPE::MASSLESS_REMNANT); }
    virtual bool                ShouldEvolveOnPhase() const                                                             { return true; }
    virtual bool                ShouldSkipPhase() const                                                                 { return false; }                                                           // Default is false

            void                UpdateAttributesAndAgeOneTimestepPreamble(const double p_DeltaMass, const double p_DeltaMass0, const double p_DeltaTime);

};

#endif // __BaseStar_h__


//====---- Sudoku/Value.h                                             ----====//
//
// Strongly typed Value type.
// Unsigned for use as a bit-mask.
//====--------------------------------------------------------------------====//
#pragma once

#include "Size.h"

#include <gsl/gsl>

#include <vector>

#include <algorithm> // all_of
#include <limits>
#include <stdexcept>
#include <type_traits> // is_same

#include <cstddef> // size_t

namespace Sudoku
{
class Value
{
public:
	Value() noexcept = default;
	explicit constexpr Value(size_t val) noexcept : value_(val) {}

	explicit constexpr operator size_t() const noexcept { return value_; }
	explicit constexpr operator bool() const noexcept { return value_ != 0; }
	// note: static_assert performs an explicit conversion to bool

	friend constexpr bool operator==(const Value& a, const Value& b) noexcept
	{ // Friend definitions: https://abseil.io/tips/99
		return a.value_ == b.value_;
	}
	friend constexpr bool operator<(const Value& a, const Value& b) noexcept
	{
		return a.value_ < b.value_;
	}

	constexpr Value& operator++() noexcept;
	constexpr Value operator++(int) noexcept;

private:
	size_t value_{0};
};

//====--------------------------------------------------------------------====//
// Free-function declarations
constexpr bool operator!=(const Value&, const Value&) noexcept;
constexpr bool operator>(const Value&, const Value&) noexcept;
constexpr bool operator<=(const Value&, const Value&) noexcept;
constexpr bool operator>=(const Value&, const Value&) noexcept;

template<typename T, int N>
constexpr Value to_Value(T val);

template<int N>
constexpr bool is_valid(const Value&) noexcept;
template<int E>
inline constexpr bool is_valid_option(const Value&) noexcept;
template<int N>
bool is_valid(const std::vector<Value>&) noexcept;

//====--------------------------------------------------------------------====//

// Test input value
template<int N>
inline constexpr bool is_valid(const Value& value) noexcept
{
	return is_valid_option<elem_size<N>>(value);
}

// Test input value, for use with Options<E>
template<int E>
inline constexpr bool is_valid_option(const Value& value) noexcept
{
	return value > Value{0} && value <= Value{E};
}

// Test input values
template<int N>
bool is_valid(const std::vector<Value>& values) noexcept
{
	return (
		!values.empty() &&
		std::all_of(values.cbegin(), values.cend(), [](Value i) {
			return is_valid<N>(i);
		}));
}

//====--------------------------------------------------------------------====//
// Checked conversion to Value
template<int N, typename T>
inline constexpr Value to_Value(T val)
{
	if constexpr (std::is_same_v<Value, T>)
	{
		if (val > static_cast<Value>(elem_size<N>))
		{
			throw std::domain_error("Value input too large");
		}
		return val;
	}
	else if constexpr (std::is_unsigned_v<T>)
	{
		static_assert(elem_size<N> < std::numeric_limits<T>::max());
		return to_Value<N>(static_cast<Value>(val));
	}
	else
	{
		static_assert(std::is_integral_v<T>);
		static_assert(std::numeric_limits<T>::max() > elem_size<N>);
		if (val < 0)
		{
			throw std::domain_error("Value can not be negative");
		}
		return to_Value<N>(gsl::narrow_cast<size_t>(val));
	}
}

//====--------------------------------------------------------------------====//
constexpr Value& Value::operator++() noexcept
{ // pre-increment
	++value_;
	return *this;
}
// NOLINTNEXTLINE(readability/casting) false-positive cpplint
constexpr Value Value::operator++(int) noexcept
{ // post-increment
	const Value pre{*this};
	operator++();
	return pre;
}

inline constexpr bool operator!=(const Value& left, const Value& right) noexcept
{
	return !(left == right);
}
inline constexpr bool operator<=(const Value& left, const Value& right) noexcept
{
	return !(right < left);
}
inline constexpr bool operator>(const Value& left, const Value& right) noexcept
{
	return right < left;
}
inline constexpr bool operator>=(const Value& left, const Value& right) noexcept
{
	return !(left < right);
}
static_assert(Value{4} == Value{4}); //-V501 //-V112
static_assert(Value{1} != Value{0});
static_assert(Value{3} > Value{2});
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
static_assert(Value{10} >= Value{7}); // NOLINT(readability-magic-numbers)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
static_assert(Value{8} <= Value{9}); // NOLINT(readability-magic-numbers)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
static_assert(Value{5} < Value{6}); // NOLINT(readability-magic-numbers)

} // namespace Sudoku


/*
 *  SPDX-FileCopyrightText: Copyright 2021, Siavash Ameli <sameli@berkeley.edu>
 *  SPDX-License-Identifier: BSD-3-Clause
 *  SPDX-FileType: SOURCE
 *
 *  This program is free software: you can redistribute it and/or modify it
 *  under the terms of the license found in the LICENSE.txt file in the root
 *  directory of this source tree.
 */

#ifndef _C_BASIC_ALGEBRA_CBLAS_INTERFACE_H_
#define _C_BASIC_ALGEBRA_CBLAS_INTERFACE_H_

// =======
// Headers
// =======

#include "../_definitions/definitions.h"  // USE_CBLAS

#if (USE_CBLAS == 1)

#include <cblas.h>  // CBLAS_LAYOUT, CBLAS_TRANSPOSE, cblas_sgemv, cblas_dgemv,
                    // cblas_scopy, cblas_dcopy, cblas_saxpy, cblas_daxpy,
                    // cblas_snrm2, cblas_dnrm2, cblas_sscal, cblas_dscal

typedef CBLAS_ORDER CBLAS_LAYOUT;  // backward compatibility with CBLAS_LAYOUT

// ===============
// cblas interface
// ===============

/// \namespace cblas_interface
///
/// \brief     A collection of templates to wrapper cblas functions.

namespace cblas_interface
{
    // cblas xgemv
    template <typename DataType>
    void xgemv(
            CBLAS_LAYOUT layout,
            CBLAS_TRANSPOSE TransA,
            const int M,
            const int N,
            const DataType alpha,
            const DataType* A,
            const int lda,
            const DataType* X,
            const int incX,
            const DataType beta,
            DataType* Y,
            const int incY);

    // cblas xcopy
    template <typename DataType>
    void xcopy(
            const int N,
            const DataType* X,
            const int incX,
            DataType* Y,
            const int incY);

    // cblas xaxpy
    template <typename DataType>
    void xaxpy(
            const int N,
            const DataType alpha,
            const DataType* X,
            const int incX,
            DataType* Y,
            const int incY);

    // cblas xdot
    template <typename DataType>
    DataType xdot(
            const int N,
            const DataType* X,
            const int incX,
            const DataType* Y,
            const int incY);

    // cblas xnrm2
    template <typename DataType>
    DataType xnrm2(
            const int N,
            const DataType* X,
            const int incX);

    // cblas xscal
    template <typename DataType>
    void xscal(
            const int N,
            const DataType alpha,
            DataType* X,
            const int incX);

}  // namespace cblas_interface

#endif  // USE_CBLAS
#endif  // _C_BASIC_ALGEBRA_CBLAS_INTERFACE_H_


/*
 * C version of Diffusive Nested Sampling (DNest4) by Brendon J. Brewer
 *
 * Yan-Rong Li, liyanrong@mail.ihep.ac.cn
 * Jun 30, 2016
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <string.h>
#include <mpi.h>
#include <gsl/gsl_rng.h>

#include "dnest.h"
#include "model1.h"

int which_level_update;
int num_params;

DNestFptrSet *fptrset_thismodel;

void model1()
{
  int i, argc=0, narg=6;
  char **argv;

  argv = malloc(narg*sizeof(char *));
  for(i=0; i<narg; i++)
  {
    argv[i] = malloc(200*sizeof(char));
  }
  strcpy(argv[argc++], "dnest");
  strcpy(argv[argc++], "-s");
  strcpy(argv[argc++], "restart_dnest1.txt");
  strcpy(argv[argc++], "-l"); //level-dependnet sampling
  strcpy(argv[argc++], "-g");
  strcpy(argv[argc++], "1");

  /* setup szie of modeltype, which is used for dnest */
  num_params = 20;

  /* allocate memory */
  fptrset_thismodel = dnest_malloc_fptrset();

  /* setup functions used for dnest*/
  fptrset_thismodel->from_prior = from_prior_thismodel;
  fptrset_thismodel->log_likelihoods_cal = log_likelihoods_cal_thismodel;
  fptrset_thismodel->log_likelihoods_cal_initial = log_likelihoods_cal_thismodel;
  fptrset_thismodel->log_likelihoods_cal_restart = log_likelihoods_cal_thismodel;
  fptrset_thismodel->perturb = perturb_thismodel;
  fptrset_thismodel->print_particle = print_particle_thismodel;
  fptrset_thismodel->restart_action = restart_action_model1;

  /* run dnest */
  dnest(argc, argv, fptrset_thismodel, num_params, NULL, NULL, NULL, "./", "OPTIONS1", NULL, NULL);

  /* free memory */
  dnest_free_fptrset(fptrset_thismodel);

  for(i=0; i<narg; i++)
    free(argv[i]);
  free(argv);
}

/*====================================================*/
/* users responsible for following struct definitions */

void from_prior_thismodel(void *model)
{
  int i;
  double *params = (double *)model;
  for(i=0; i<num_params; i++)
  {
    params[i] = -0.5 + dnest_rand();
  }
}

double log_likelihoods_cal_thismodel(const void *model)
{
  double *params = (double *)model;
  double logL;
  const double u = 0.01;
	const double v = 0.1;
	const double C = log(1.0/sqrt(2*M_PI));

	double logl1 = num_params*(C - log(u));
	double logl2 = num_params*(C - log(v));

  int i;
	for(i=0; i<num_params; i++)
	{
		logl1 += -0.5*pow(( params[i] - 0.031)/u, 2);
		logl2 += -0.5*pow(  params[i]/v, 2);
	}
	logl1 += log(100.);

  double max = fmax(logl1, logl2);

  logL = log( exp(logl1-max) + exp(logl2-max) ) + max;

  return logL;
}

double perturb_thismodel(void *model)
{
  double *params = (double *)model;
  double logH = 0.0, width, limit1, limit2;
  int which = dnest_rand_int(num_params), which_level;
  int size_levels;

  which_level_update = dnest_get_which_level_update();
  size_levels = dnest_get_size_levels();
  which_level = which_level_update > (size_levels - 20)?(size_levels-20):which_level_update;
  if(which_level > 0)
  {
    limit1 = limits[(which_level-1) * num_params *2 + which *2 ];
    limit2 = limits[(which_level-1) * num_params *2 + which *2 + 1];
  }
  else
  {
    limit1 = -0.5;
    limit2 = 0.5;
  }

  width = (limit2 - limit1);
	params[which] += width * dnest_randh();
	dnest_wrap(&params[which], limit1, limit2);
  return logH;
}

void print_particle_thismodel(FILE *fp, const void *model)
{
  int i;
  double *params = (double *)model;
  for(i=0; i<num_params; i++)
  {
    fprintf(fp, "%f ", params[i] );
  }
  fprintf(fp, "\n");
}

/*========================================================*/

void restart_action_model1(int iflag)
{
  return;
}


/**
 *
 * @file core_ztrmdm.c
 *
 *  PLASMA core_blas kernel
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.4.5
 * @author Dulceneia Becker
 * @date 2011-1-18
 **/
/*
 * @precisions normal z -> c d s
 */
#include <lapacke.h>
#include "dplasma_cores.h"
#include "dplasma_zcores.h"

#if defined(PARSEC_HAVE_STRING_H)
#include <string.h>
#endif  /* defined(PARSEC_HAVE_STRING_H) */
#if defined(PARSEC_HAVE_STDARG_H)
#include <stdarg.h>
#endif  /* defined(PARSEC_HAVE_STDARG_H) */
#include <stdio.h>
#ifdef PARSEC_HAVE_LIMITS_H
#include <limits.h>
#endif

#include <cblas.h>
#include <core_blas.h>

#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))

int CORE_ztrmdm(int uplo, int N, PLASMA_Complex64_t *A, int LDA);

/***************************************************************************//**
 *
 * @ingroup CORE_PLASMA_Complex64_t
 *
 * CORE_ztrmdm scales the strictly upper or strictly lower triangular part of a
 * square matrix by the inverse of a diagonal matrix, ie performs either
 *
 *    A := L / D  or  A := U / D (only for triangular part above or below diagonal)
 *
 * where:
 *
 *    L is a strictly lower triangular matrix stored as the strictly lower triangle in A
 *    U is a strictly upper triangular matrix stored as the strictly upper triangle in A
 *    D is a diagonal matrix stored as the diagonal in A
 *
 * The diagonal elements of A are not changed.
 *
*******************************************************************************
 *
 * @param[in] uplo
 *         INTEGER
 *         @arg PlasmaLower: Lower triangle of A is stored and scaled.
 *         @arg PlasmaUpper: Upper triangle of A is stored and scaled.
 *
 * @param[in] N
 *         INTEGER
 *         The number of rows and columns of A.  N >= 0.
 *
 * @param[in,out] A
 *         PLASMA_Complex64_t array, dimension (LDA,N)
 *
 *         On entry, the triangular matrix A. If uplo = 'U', the leading
 *         N-by-N upper triangular part of A contains the upper
 *         triangular part of the matrix A, and the strictly lower
 *         triangular part of A is not referenced.  If uplo = 'L', the
 *         leading N-by-N lower triangular part of A contains the lower
 *         triangular part of the matrix A, and the strictly upper
 *         triangular part of A is not referenced.
 *
 *         On exit, the strictly lower or upper triangular part of A
 *         scaled by the diagonal elements of A.
 *
 * @param[in] LDA
 *         INTEGER
 *         The leading dimension of the array A.  LDA >= max(1,N).
 *
*******************************************************************************
 *
 * @return
 *          \retval PLASMA_SUCCESS successful exit
 *          \retval <0 if -i, the i-th argument had an illegal value
 *
 ******************************************************************************/
#if defined(PLASMA_PARSEC_HAVE_WEAK)
#pragma weak CORE_ztrmdm = PCORE_ztrmdm
#define CORE_ztrmdm PCORE_ztrmdm
#endif
int CORE_ztrmdm(int uplo, int N, PLASMA_Complex64_t *A, int LDA)
{
    static PLASMA_Complex64_t zone = 1.0;

    PLASMA_Complex64_t alpha;
    int j;

    /* Check input arguments */
    if (uplo != PlasmaUpper && uplo != PlasmaLower) {
        coreblas_error(1, "Illegal value of UPLO");
        return -1;
    }
    if (N < 0) {
        coreblas_error(2, "Illegal value of N");
        return -2;
    }
    if (LDA < max(1, N)) {
        coreblas_error(1, "Illegal value of LDA");
        return -4;
    }

    /* Quick return */
    if (max(N, 0) == 0)
        return PLASMA_SUCCESS;

    /**/

    if (uplo==PlasmaLower) {
        for (j=0; j<(N-1); j++) {
                alpha = zone / A[LDA*j+j];
                cblas_zscal(N-j-1, CBLAS_SADDR(alpha), &A[LDA*j+j+1], 1);
        }

    } else if (uplo==PlasmaUpper) {
        for (j=1; j<N; j++) {
                alpha = zone / A[LDA*j+j];
                cblas_zscal(j, CBLAS_SADDR(alpha), &A[LDA*j], 1);
        }

    }

    return PLASMA_SUCCESS;

}


#include <gsl/gsl_vector.h>

#include "icp.h"
#include "../csm_all.h"

int compatible(struct sm_params*params, int i, int j);

int compatible(struct sm_params*params, int i, int j) {
	if(!params->do_alpha_test) return 1;

	double theta0 = 0; /* FIXME */
	if((params->laser_sens->alpha_valid[i]==0) ||
	 (params->laser_ref->alpha_valid[j]==0))
	 return 1;

	double alpha_i = params->laser_sens->alpha[i];
	double alpha_j = params->laser_ref->alpha[j];
	double tolerance = deg2rad(params->do_alpha_test_thresholdDeg);

	/** FIXME remove alpha test */
	double theta = angleDiff(alpha_j, alpha_i);
	if(fabs(angleDiff(theta,theta0))>
		tolerance+deg2rad(params->max_angular_correction_deg)) {
	 return 0;
	} else {
	 return 1;
	}
}

void find_correspondences(struct sm_params*params) {
	const LDP laser_ref  = params->laser_ref;
	const LDP laser_sens = params->laser_sens;

	int i;
	for(i=0;i<laser_sens->nrays;i++) {
		if(!ld_valid_ray(laser_sens,i)) {
/*			sm_debug("dumb: i %d is invalid \n", i);*/
			ld_set_null_correspondence(laser_sens, i);
			continue;
		}

		double *p_i_w = laser_sens->points_w[i].p;

		int j1 = -1;
		double best_dist = 10000;

		int from; int to; int start_cell;
		possible_interval(p_i_w, laser_ref, params->max_angular_correction_deg,
			params->max_linear_correction, &from, &to, &start_cell);

/*		sm_debug("dumb: i %d from  %d to %d \n", i, from, to); */
		int j;
		for(j=from;j<=to;j++) {
			if(!ld_valid_ray(laser_ref,j)) {
/*				sm_debug("dumb: i %d      j %d invalid\n", i, j);*/
				continue;
			}
			double dist = distance_squared_d(p_i_w, laser_ref->points[j].p);
/*			sm_debug("dumb: i %d j1 %d j %d d %f\n", i,j1,j,dist);*/
			if(dist>square(params->max_correspondence_dist)) continue;

			if( (-1 == j1) || (dist < best_dist) ) {
				if(compatible(params, i, j)) {
					j1 = j;
					best_dist = dist;
				}
			}
		}

		if(j1==-1) {/* no match */
			ld_set_null_correspondence(laser_sens, i);
			continue;
		}
		/* Do not match with extrema*/
		if(j1==0 || (j1 == (laser_ref->nrays-1))) {/* no match */
			ld_set_null_correspondence(laser_sens, i);
			continue;
		}

		int j2;
		int j2up   = ld_next_valid_up   (laser_ref, j1);
		int j2down = ld_next_valid_down (laser_ref, j1);
		if((j2up==-1)&&(j2down==-1)) {
			ld_set_null_correspondence(laser_sens, i);
			continue;
		}
		if(j2up  ==-1) { j2 = j2down; } else
		if(j2down==-1) { j2 = j2up; } else {
			double dist_up   = distance_squared_d(p_i_w, laser_ref->points[j2up  ].p);
			double dist_down = distance_squared_d(p_i_w, laser_ref->points[j2down].p);
			j2 = dist_up < dist_down ? j2up : j2down;
		}

		ld_set_correspondence(laser_sens, i, j1, j2);
		laser_sens->corr[i].dist2_j1 = best_dist;
		laser_sens->corr[i].type =
			params->use_point_to_line_distance ? corr_pl : corr_pp;

	}

}


/* gsl_histogram2d_maxval.c
 * Copyright (C) 2000  Simone Piccardi
 *
 * This library 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 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
/***************************************************************
 *
 * File gsl_histogram2d_maxval.c:
 * Routine to find maximum and minumum content of a 2D hisogram.
 * Need GSL library and header.
 * Contains the routines:
 * gsl_histogram2d_max_val find max content values
 * gsl_histogram2d_min_val find min content values
 * gsl_histogram2d_bin_max find coordinates of max contents bin
 * gsl_histogram2d_bin_min find coordinates of min contents bin
 *
 * Author: S. Piccardi
 * Jan. 2000
 *
 ***************************************************************/
#include <config.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_histogram2d.h>
/*
 * Return the maximum contents value of a 2D histogram
 */
double
gsl_histogram2d_max_val (const gsl_histogram2d * h)
{
  const size_t nx = h->nx;
  const size_t ny = h->ny;
  size_t i;
  double max = h->bin[0 * ny + 0];

  for (i = 0; i < nx * ny; i++)
    {
      if (h->bin[i] > max)
	{
	  max = h->bin[i];
	}
    }
  return max;
}

/*
 * Find the bin index for maximum value of a 2D histogram
 */

void
gsl_histogram2d_max_bin (const gsl_histogram2d * h, size_t * imax_out, size_t * jmax_out)
{
  const size_t nx = h->nx;
  const size_t ny = h->ny;
  size_t imax = 0, jmax = 0, i, j;
  double max = h->bin[0 * ny + 0];

  for (i = 0; i < nx; i++)
    {
      for (j = 0; j < ny; j++)
	{
	  double x = h->bin[i * ny + j];

	  if (x > max)
	    {
	      max = x;
	      imax = i;
	      jmax = j;
	    }
	}
    }

  *imax_out = imax;
  *jmax_out = jmax;
}

/*
 * Return the minimum contents value of a 2D histogram
 */

double
gsl_histogram2d_min_val (const gsl_histogram2d * h)
{
  const size_t nx = h->nx;
  const size_t ny = h->ny;
  size_t i;
  double min = h->bin[0 * ny + 0];

  for (i = 0; i < nx * ny; i++)
    {
      if (h->bin[i] < min)
	{
	  min = h->bin[i];
	}
    }

  return min;
}
/*
 * Find the bin index for minimum value of a 2D histogram
 */
void
gsl_histogram2d_min_bin (const gsl_histogram2d * h, size_t * imin_out, size_t * jmin_out)
{
  const size_t nx = h->nx;
  const size_t ny = h->ny;
  size_t imin = 0, jmin = 0, i, j;
  double min = h->bin[0 * ny + 0];

  for (i = 0; i < nx; i++)
    {
      for (j = 0; j < ny; j++)
	{
	  double x = h->bin[i * ny + j];

	  if (x < min)
	    {
	      min = x;
	      imin = i;
	      jmin = j;
	    }
	}
    }

  *imin_out = imin;
  *jmin_out = jmin;
}


#include <gsl/gsl_math.h>
#include "gsl_cblas.h"
#include "cblas.h"

void
cblas_dsyr2k (const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo,
	      const enum CBLAS_TRANSPOSE Trans, const int N, const int K,
	      const double alpha, const double *A, const int lda,
	      const double *B, const int ldb, const double beta, double *C,
	      const int ldc)
{
#define BASE double
#include "source_syr2k_r.h"
#undef BASE
}


/**
 * @file batchg_zgemv.c
 *
 * Part of API test for Batched BLAS routines.
 *
 * @author  Samuel  D. Relton
 * @author  Pedro   V. Lara
 * @author  Mawussi Zounon
 * @date 2016-06-01
 *
 * @precisions normal z -> c d s
 *
 **/

#include <cblas.h>
#include "bblas.h"

#define COMPLEX

void batchg_zgemv(
	const enum BBLAS_TRANS *trans,
	const int *m, const int *n,
	const BBLAS_Complex64_t *alpha,
	const BBLAS_Complex64_t **arrayA, const int *lda,
	const BBLAS_Complex64_t **arrayx, const int *incx,
	const BBLAS_Complex64_t *beta,
	BBLAS_Complex64_t **arrayy, const int *incy,
	const int group_count, const int *group_size,
	int* info)
{
	/* Local variables */
	char func_name[15] = "batchg_zgemv";
	int group_iter = 0;
	int offset = 0; // How many subproblems solved so far

	/* Check group_count */
	if (group_count < 0)
	{
		xerbla_batch(func_name, BBLAS_ERR_GROUP_COUNT, -1);
		return;
	}

	/* Check group_size and call fixed batch computation */
	for (group_iter = 0; group_iter < group_count; group_iter++)
	{
		if (group_size[group_iter] < 0)
		{
			xerbla_batch(func_name, BBLAS_ERR_GROUP_SIZE, group_iter);
			info[group_iter] = BBLAS_ERR_GROUP_SIZE;
			continue;
		}

		/* Call fixed batch computation on the group */
		batchf_zgemv(
			trans[group_iter],
			m[group_iter], n[group_iter],
			alpha[group_iter],
			arrayA+offset, lda[group_iter],
			arrayx+offset, incx[group_iter],
			beta[group_iter],
			arrayy+offset, incy[group_iter],
			group_size[group_iter], info[group_iter]);

		/* Update offset */
		offset += group_size[group_iter];
	} // End of group loop
}
#undef COMPLEX


#ifndef KGS_NULLSPACE_H
#define KGS_NULLSPACE_H

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <string>
#include <vector>

#include "math/SVD.h"
//#include "math/QR.h"

class Molecule; //Forward declaration

/**
 * Computes, stores, and maintains the nullspace of a gsl_matrix.
 * Supports projections of any gradient represented as a gsl_vector onto the null-space
 * and also analyzing and updating rigidity. Note that the Nullspace object only
 * accurately reflects the matrix (SVD) if the UpdateFromMatrix function has been called.
 * This is not done on construction.
 *
 * An SVD-pointer is passed to the Nullspace on construction. This pointer is kept through
 * the life-time of the Nullspace object (but ownership is not assumed) and if the contents
 * of the matrix is changed, the Nullspace can be update to reflect the change using the
 * abstract UpdateFromMatrix function.
 */
class Nullspace {
 protected:
  Nullspace(gsl_matrix* M);

 public:

  virtual ~Nullspace();

  /** Projects a vector on the nullspace */
  void projectOnNullSpace(gsl_vector *to_project, gsl_vector *after_project) const;

  /** Analyzes which dihedrals and hydrogen bonds are rigidified by constraints */
  void performRigidityAnalysis(gsl_matrix *HBondJacobian,gsl_matrix *DBondJacobian,gsl_matrix *HydrophobicBondJacobian);

  /** Update the Nullspace (and underlying SVD/QR) to reflect an updated state of the matrix */
  virtual void updateFromMatrix() = 0;

  /** Return the nullspace size */
  int getNullspaceSize() const { return m_nullspaceSize; }

  /** Return number of cycle DOFs */
  int getNumDOFs() const { return n; }

  /** Return number of dihedral DOFs that were rigidified in the last call to RigidityAnalysis. */
  int getNumRigidDihedrals() const { return numRigidDihedrals; }

  /** Return number of h-bond dihedrals that were rigidified in the last call to RigidityAnalysis. */
  int getNumRigidHBonds() const { return numRigidHBonds; }
  int getNumRigidDBonds() const { return numRigidDBonds; }
  int getNumRigidHydrophobicBonds() const { return numRigidHydrophobicBonds;}
  /** Return the underlying matrix. */
  gsl_matrix* getMatrix() const{ return m_matrix; }

  /** Return the basis of the nullspace as columns of a matrix */
  gsl_matrix *getBasis() const;

  /**
   * Returns true iff the angle specified by the argument is rigidified.
   * This result is only accurate if UpdateFromMatrix and RigidityAnalysis have both
   * been called.
   */
  bool isCovBondRigid(int angle_id){ return fabs(gsl_vector_get(m_rigidCovBonds, angle_id)-1.0)<0.001; }

  bool isHBondRigid(int bond_id){ return fabs(gsl_vector_get(m_rigidHBonds, bond_id)-1.0)<0.001; }

  bool isDBondRigid(int bond_id){ return fabs(gsl_vector_get(m_rigidDBonds, bond_id)-1.0)<0.001; }

  bool isHydrophobicBondRigid(int bond_id){ return fabs(gsl_vector_get(m_rigidHydrophobicBonds,bond_id)-1.0)<0.001; }

 protected:
  int m_nullspaceSize;         ///< Size of nullspace (rank of jacobian)
  int m, n;                    ///< Dimensions of underlying matrix (jacobian)
  gsl_matrix* m_matrix;        ///< Original matrix. Only set if using QR decomp (as it needs to be transposed)

  gsl_matrix* m_nullspaceBasis;///< Basis of the nullspace

 private:
  gsl_vector* m_rigidCovBonds; ///< Binary vector indicating which m_dofs are rigid
  gsl_vector* m_rigidHBonds;   ///< Binary vector indicating which h-bonds are rigid
  gsl_vector* m_rigidDBonds;   ///< Binary vector indicating which d-bonds are rigid
  gsl_vector* m_rigidHydrophobicBonds;  ///< Binary vector indicating which hydrophobic-bonds are rigid
  int numCoordinatedDihedrals; ///< Coordinated dihedrals
  int numRigidDihedrals;       ///< Rigid dihedrals
  int numRigidHBonds;///< Rigid hydrogen bonds
  int numRigidDBonds;///< Rigid hydrogen bonds
  int numRigidHydrophobicBonds; ///< Rigid hydrophobic bonds
  /// These values have to be chosen according to the numerical analysis
//  static constexpr double SINGVAL_TOL = 1.0e-12; //0.000000000001; // only generic 10^-12
  static constexpr double RIGID_TOL =   1.0e-9; //0.0000000001; //most molecules work between 1e-4 and 1e-10, exceptions only between 1e-8 and 1e-9

//  friend class Configuration;
};

#endif //KGS_nullptrSPACE_H


#ifndef TEST_MATRIX_UTIL
#define TEST_MATRIX_UTIL
#include "matrix_util.h"
//#include <cblas.h>
//int test_blas(int result);

int test_my_zgemm(int result);

int test_inv2by2(int result);

int test_inv2by2inv2by2_overwrite(int result);
int test_outer(int result);

int test_my_zaxpy(int result);

int test_my_zdot(int result);

#endif


/**
 *
 * @file testing_dtrsm.c
 *
 *  PLASMA testing routines
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @generated d Tue Jan  7 11:45:18 2014
 *
 **/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <plasma.h>
#include <cblas.h>
#include <lapacke.h>
#include <core_blas.h>
#include "testing_dmain.h"

#undef COMPLEX
#define REAL

static int check_solution(PLASMA_enum side, PLASMA_enum uplo, PLASMA_enum trans, PLASMA_enum diag,
                          int M, int N, double alpha,
                          double *A, int LDA,
                          double *Bref, double *Bplasma, int LDB);

int testing_dtrsm(int argc, char **argv)
{
    /* Check for number of arguments*/
    if ( argc != 5 ) {
        USAGE("TRSM", "alpha M N LDA LDB",
              "   - alpha  : alpha coefficient\n"
              "   - M      : number of rows of matrices B\n"
              "   - N      : number of columns of matrices B\n"
              "   - LDA    : leading dimension of matrix A\n"
              "   - LDB    : leading dimension of matrix B\n");
        return -1;
    }

    double alpha = (double) atol(argv[0]);
    int M     = atoi(argv[1]);
    int N     = atoi(argv[2]);
    int LDA   = atoi(argv[3]);
    int LDB   = atoi(argv[4]);

    double eps;
    int info_solution;
    int s, u, t, d, i;
    int LDAxM = LDA*max(M,N);
    int LDBxN = LDB*max(M,N);

    double *A      = (double *)malloc(LDAxM*sizeof(double));
	#pragma omp register([LDAxM]A)
    double *B      = (double *)malloc(LDBxN*sizeof(double));
	#pragma omp register([LDBxN]B)
    double *Binit  = (double *)malloc(LDBxN*sizeof(double));
	#pragma omp register([LDBxN]Binit)
    double *Bfinal = (double *)malloc(LDBxN*sizeof(double));
	#pragma omp register([LDBxN]Bfinal)

    /* Check if unable to allocate memory */
    if ( (!A) || (!B) || (!Binit) || (!Bfinal)){
        printf("Out of Memory \n ");
        return -2;
    }

    eps = LAPACKE_dlamch_work('e');

    printf("\n");
    printf("------ TESTS FOR PLASMA DTRSM ROUTINE -------  \n");
    printf("            Size of the Matrix B : %d by %d\n", M, N);
    printf("\n");
    printf(" The matrix A is randomly generated for each test.\n");
    printf("============\n");
    printf(" The relative machine precision (eps) is to be %e \n",eps);
    printf(" Computational tests pass if scaled residuals are less than 10.\n");

    /*----------------------------------------------------------
     *  TESTING DTRSM
     */

    /* Initialize A, B, C */
    LAPACKE_dlarnv_work(IONE, ISEED, LDAxM, A);
    LAPACKE_dlarnv_work(IONE, ISEED, LDBxN, B);
    for(i=0;i<max(M,N);i++)
      A[LDA*i+i] = A[LDA*i+i] + 2.0;

    for (s=0; s<2; s++) {
        for (u=0; u<2; u++) {
#ifdef COMPLEX
            for (t=0; t<3; t++) {
#else
            for (t=0; t<2; t++) {
#endif
                for (d=0; d<2; d++) {

                    memcpy(Binit,  B, LDBxN*sizeof(double));
                    memcpy(Bfinal, B, LDBxN*sizeof(double));

                    /* PLASMA DTRSM */
                    PLASMA_dtrsm(side[s], uplo[u], trans[t], diag[d],
                                 M, N, alpha, A, LDA, Bfinal, LDB);

                    /* Check the solution */
                    info_solution = check_solution(side[s], uplo[u], trans[t], diag[d],
                                                   M, N, alpha, A, LDA, Binit, Bfinal, LDB);

                    printf("***************************************************\n");
                    if (info_solution == 0) {
                        printf(" ---- TESTING DTRSM (%s, %s, %s, %s) ...... PASSED !\n",
                               sidestr[s], uplostr[u], transstr[t], diagstr[d]);
                    }
                    else {
                        printf(" ---- TESTING DTRSM (%s, %s, %s, %s) ... FAILED !\n",
                               sidestr[s], uplostr[u], transstr[t], diagstr[d]);
                    }
                    printf("***************************************************\n");
                }
            }
        }
    }

    free(A); free(B);
    free(Binit); free(Bfinal);

    return 0;
}

/*--------------------------------------------------------------
 * Check the solution
 */
static int check_solution(PLASMA_enum side, PLASMA_enum uplo, PLASMA_enum trans, PLASMA_enum diag,
                          int M, int N, double alpha,
                          double *A, int LDA,
                          double *Bref, double *Bplasma, int LDB)
{
    int info_solution;
    double Anorm, Binitnorm, Bplasmanorm, Blapacknorm, Rnorm, result;
    double eps;
    double mzone = (double)-1.0;

    double *work = (double *)malloc(max(M, N)* sizeof(double));
    int Am, An;

    if (side == PlasmaLeft) {
        Am = M; An = M;
    } else {
        Am = N; An = N;
    }

    Anorm       = LAPACKE_dlantr_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), lapack_const(uplo), lapack_const(diag),
                                Am, An, A, LDA, work);
    Binitnorm   = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M, N, Bref,    LDB, work);
    Bplasmanorm = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M, N, Bplasma, LDB, work);

    cblas_dtrsm(CblasColMajor, (CBLAS_SIDE)side, (CBLAS_UPLO)uplo, (CBLAS_TRANSPOSE)trans,
                (CBLAS_DIAG)diag, M, N, (alpha), A, LDA, Bref, LDB);

    Blapacknorm = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M, N, Bref, LDB, work);

    cblas_daxpy(LDB * N, (mzone), Bplasma, 1, Bref, 1);

    Rnorm = LAPACKE_dlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), M, N, Bref, LDB, work);

    eps = LAPACKE_dlamch_work('e');

    printf("Rnorm %e, Anorm %e, Binitnorm %e, Bplasmanorm %e, Blapacknorm %e\n",
           Rnorm, Anorm, Binitnorm, Bplasmanorm, Blapacknorm);

    result = Rnorm / ((Anorm + Blapacknorm) * max(M,N) * eps);

    printf("============\n");
    printf("Checking the norm of the difference against reference DTRSM \n");
    printf("-- ||Cplasma - Clapack||_oo/((||A||_oo+||B||_oo).N.eps) = %e \n", result);

    if ( isinf(Blapacknorm) || isinf(Bplasmanorm) || isnan(result) || isinf(result) || (result > 10.0) ) {
        printf("-- The solution is suspicious ! \n");
        info_solution = 1;
    }
    else {
        printf("-- The solution is CORRECT ! \n");
        info_solution= 0 ;
    }
    free(work);

    return info_solution;
}