/*****************************************************************************/
// Copyright 2006-2019 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file in
// accordance with the terms of the Adobe license agreement accompanying it.
/*****************************************************************************/

/** \file
 * Classes for a 1D floating-point to floating-point function abstraction.
 */

/*****************************************************************************/

#ifndef __dng_1d_function__
#define __dng_1d_function__

/*****************************************************************************/

#include "dng_classes.h"
#include "dng_types.h"

#include <vector>

/*****************************************************************************/

/// @brief A 1D floating-point function.
///
/// The domain (input) is always from 0.0 to 1.0, while the range (output) can
/// be an arbitrary interval.

class dng_1d_function
    {

    public:

        virtual ~dng_1d_function ();

        /// Returns true if this function is the map x -> y such that x == y for all x.
        /// That is if Evaluate(x) == x for all x.

        virtual bool IsIdentity () const;

        /// Return the mapping for value x.
        /// This method must be implemented by a derived class of dng_1d_function and
        /// the derived class determines the lookup method and function used.
        /// \param x A value between 0.0 and 1.0 (inclusive).
        /// \retval Mapped value for x

        virtual real64 Evaluate (real64 x) const = 0;

        /// Return the reverse mapped value for y.
        /// This method can be implemented by derived classes. The default implementation
        /// uses Newton's method to solve for x such that Evaluate(x) == y.
        /// \param y A value to reverse map. Should be within the range of the function
        /// implemented by this dng_1d_function.
        /// \retval A value x such that Evaluate(x) == y (to very close approximation).

        virtual real64 EvaluateInverse (real64 y) const;

    };

/*****************************************************************************/

/// An identity (x -> y such that x == y for all x) mapping function.

class dng_1d_identity: public dng_1d_function
    {

    public:
        /// Always returns true for this class.

        virtual bool IsIdentity () const;

        /// Always returns x for this class.

        virtual real64 Evaluate (real64 x) const;

        /// Always returns y for this class.

        virtual real64 EvaluateInverse (real64 y) const;

        /// This class is a singleton, and is entirely threadsafe. Use this
        /// method to get an instance of the class.

        static const dng_1d_function & Get ();

    };

/*****************************************************************************/

/// A dng_1d_function that represents the composition (curry) of two other
/// dng_1d_functions.

class dng_1d_concatenate: public dng_1d_function
    {

    protected:

        const dng_1d_function &fFunction1;

        const dng_1d_function &fFunction2;

    public:

        /// Create a dng_1d_function which computes
        ///
        /// y = function2.Evaluate (function1.Evaluate (x))
        ///
        /// The range of function1.Evaluate must be a subset of 0.0 to 1.0 inclusive,
        /// otherwise the result of function1(x) will be pinned (clipped) to 0.0
        /// if < 0.0 and to 1.0 if > 1.0.
        ///
        /// \param function1 Inner function of composition.
        /// \param function2 Outer function of composition.

        dng_1d_concatenate (const dng_1d_function &function1,
                            const dng_1d_function &function2);

        /// Only true if both function1 and function2 have IsIdentity equal to true.

        virtual bool IsIdentity () const;

        /// Return the composed mapping for value x.
        /// \param x A value between 0.0 and 1.0 (inclusive).
        /// \retval function2.Evaluate(function1.Evaluate(x)).

        virtual real64 Evaluate (real64 x) const;

        /// Return the reverse mapped value for y.
        /// Be careful using this method with compositions where the inner function
        /// does not have a range 0.0 to 1.0 . (Or better yet, do not use such functions.)
        /// \param y A value to reverse map. Should be within the range of function2.Evaluate.
        /// \retval A value x such that function2.Evaluate(function1.Evaluate(x)) == y
        /// (to very close approximation).

        virtual real64 EvaluateInverse (real64 y) const;

    };

/*****************************************************************************/

/// A dng_1d_function that represents the inverse of another dng_1d_function.

class dng_1d_inverse: public dng_1d_function
    {

    protected:

        const dng_1d_function &fFunction;

    public:

        dng_1d_inverse (const dng_1d_function &f);

        virtual bool IsIdentity () const;

        virtual real64 Evaluate (real64 x) const;

        virtual real64 EvaluateInverse (real64 y) const;

    };

/*****************************************************************************/

// Piecewise-linear 1D function.

class dng_piecewise_linear: public dng_1d_function
    {

    public:

        std::vector<real64> X;
        std::vector<real64> Y;

    public:

        dng_piecewise_linear ();

        virtual ~dng_piecewise_linear ();

        void Reset ();

        void Add (real64 x, real64 y);

        bool IsValid () const
            {
            return (X.size () >= 2) && (X.size () == Y.size ());
            }

        bool NotValid () const
            {
            return !IsValid ();
            }

        virtual bool IsIdentity () const;

        virtual real64 Evaluate (real64 x) const;

        virtual real64 EvaluateInverse (real64 y) const;

        void PutFingerprintData (dng_stream &stream) const;

        bool operator== (const dng_piecewise_linear &piecewise) const;

        bool operator!= (const dng_piecewise_linear &piecewise) const
            {
            return !(*this == piecewise);
            }

    };

/*****************************************************************************/

#endif

/*****************************************************************************/
