#ifndef INCLUDED_DISTRIBUTION_
#define INCLUDED_DISTRIBUTION_

#include <iosfwd>
#include <cmath>

#include "../typedefs/typedefs.h"

    // The Distribution receives the spread value and distribution name,
    // Either both or none most be specified. If none is specified then
    // spread = 0, and calling vary() simply returns the received argument

class Distribution
{
    enum
    {
        MAX_VARY_TRIES = 10, // max #attempts to obtain a valid varied value
    };

    friend std::istream &operator>>(std::istream &in, Distribution &dist);
    friend std::ostream &operator<<(std::ostream &out,
                                             Distribution const &dist);

    double      d_confValue = 0;                // configured (e.g., SD) value
    DistType    d_distType = N_DISTRIBUTIONS;   // with, e.g., vectors

                                            // points to the function handling
                                            // the random value variation (the
                                            // vary*/accept functions below)
    double (Distribution::*d_value)(double orgValue) const;

    bool d_exp;

    static StringVect s_name;
    static unsigned s_width;
    static unsigned s_precision;

    static double (Distribution::*s_varyParam[])(double orgValue) const;

    public:
        Distribution(VaryType varyType);

                                                // returns N_DISTRIBUTIONS if
                                                // an undefined name is used
        static DistType find(std::string const &distName);

                                                // intWidth: width of the
                                                // integral part
        static void fmt(unsigned intWidth, unsigned precision);

        void ln();                              // ln(d_confValue)          .f

        DistType type() const;                                          //  .f

        double value(double orgValue) const;    // return varied value (if  .f
                                                // s_vary) or orgValue or
                                                // throw an exception

                                                // must succeed or err
        static DistType xlat(LineInfo const &lineInfo,
                         std::string const &distName);

        static std::string const &name(DistType dist);

    private:
        std::istream &extract(std::istream &in);
        std::ostream &insert(std::ostream &out) const;

        void prepareVary(std::istream &in);         // -> extract()

        double varyMean(double orgValue) const;     // these members are
        double varyNonNeg(double orgValue) const;   // called via d_value
        double varyProb(double orgValue) const;
        double varyBeta(double orgValue) const;
        double accept(double orgValue) const;       // if not s_vary
};

#include "distribution.f"

#endif
