// ============================================================ //
//                                                              //
//   File      : lazy.h                                         //
//   Purpose   : helpers for lazy evaluation                    //
//                                                              //
//   Coded by Ralf Westram (coder@reallysoft.de) in July 2017   //
//   http://www.arb-home.de/                                    //
//                                                              //
// ============================================================ //

#ifndef LAZY_H
#define LAZY_H

#ifndef ARB_ASSERT_H
#include <arb_assert.h>
#endif
#ifndef _GLIBCXX_CMATH
#include <cmath>
#endif
#ifndef ARBTOOLS_H
#include "arbtools.h"
#endif


template<typename T, T UNDEFINED>
class Lazy {
    /*! defines a type with an explicit UNDEFINED value.
     * Default ctor uses UNDEFINED value.
     * Allows to test whether instance has been assigned a value.
     * Fails assertion if UNDEFINED value is retrieved or assigned.
     */
    T val;
public:
    Lazy() : val(UNDEFINED) {}
    explicit Lazy(T init) : val(init) { arb_assert(has_value()); } // use to assign new value

    bool needs_eval() const { return val == UNDEFINED; }
    bool has_value() const { return !needs_eval(); }

    operator T() const { // @@@ return const&?
        arb_assert(has_value()); // no value assigned
        return val;
    }
    const Lazy& operator = (const T& newval) {
        val = newval;
        arb_assert(has_value());
        return *this;
    }
};

template<typename T>
class LazyFloat {
    /*! same as Lazy for floating point (uses NAN as UNDEFINED value)
     */
    T val;

public:
    LazyFloat() : val(NAN) {}
    explicit LazyFloat(T init) : val(init) { arb_assert(has_value()); } // use to assign new value

    bool needs_eval() const { return is_nan(val); }
    bool has_value() const { return !needs_eval(); }

    operator T() const { // @@@ return const&?
        arb_assert(has_value()); // no value assigned
        return val;
    }
    const LazyFloat& operator = (const T& newval) {
        val = newval;
        arb_assert(has_value());
        return *this;
    }
};

#else
#error lazy.h included twice
#endif // LAZY_H
