#ifndef INCLUDED_DISTRIBUTION_
#define INCLUDED_DISTRIBUTION_

#include <iosfwd>

#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_value = 0;                // used as defaults
    DistType    d_type = N_DISTRIBUTIONS;   // with, e.g., vectors

    double (Distribution::*d_vary)(double orgValue) const = 
                                                &Distribution::varyMean;

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

    public:
        Distribution(VaryType varyType);
  
        double     value() const;
        DistType   type() const;

        double vary(double orgValue) const; // obtain varied value or
                                            // throw an exception

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

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

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

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

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

        void prepareVary();

        double varyMean(double orgValue) const;   // these members are 
        double varyNonNeg(double orgValue) const;  // called via d_vary
        double varyProb(double orgValue) const;
};
       
// static 
inline std::string const &Distribution::name(DistType dist) 
{
    return s_name[dist];
}

inline double Distribution::vary(double orgValue) const
{
    return (this->*d_vary)(orgValue);
}

inline std::istream &operator>>(std::istream &in, Distribution &dist)
{
    return dist.extract(in);
}

inline std::ostream &operator<<(std::ostream &out, Distribution const &dist)
{
    return dist.insert(out);
}

inline double Distribution::value() const
{
    return d_value;
}

inline DistType Distribution::type() const
{
    return d_type;
}

#endif
