// Description: Minkowski_NormBijection class
// Detail: A norm bijection of the Minkowski norm (p-norm)
// Documentation: normbijection.txt
#ifndef PASTELMATH_MINKOWSKI_NORMBIJECTION_H
#define PASTELMATH_MINKOWSKI_NORMBIJECTION_H
#include "pastel/sys/vector/vector_tools.h"
#include "pastel/sys/math/gamma.h"
#include "pastel/sys/math/constants.h"
#include "pastel/math/normbijection/normbijection_concept.h"
#include "pastel/geometry/volume/sphere_volume.h"
namespace Pastel
{
    template <typename Real_>
    class Minkowski_NormBijection
    {
    public:
        using Real = Real_;
        // Using default copy constructor.
        // Using default assignment.
        // Using default destructor.
        explicit Minkowski_NormBijection(
            const Real& power)
            : power_(power)
            , invPower_(inverse(power))
        {
            // The Minkowski norm is a norm only for p >= 1.
            ENSURE_OP(power, >=, 1);
        }
        const Real& power() const
        {
            return power_;
        }
        const Real& inversePower() const
        {
            return invPower_;
        }
        Real lnVolumeUnitSphere(integer dimension) const
        {
            // From Wikipedia "Volume of an n-ball"
            return 
                lnGamma<Real>(inversePower() + 1) - 
                lnGamma<Real>(dimension * inversePower() + 1) +
                dimension * constantLn2<Real>();
        }
        Real toNorm(const Real& normBijection) const
        {
            return std::pow(normBijection, inversePower());
        }
        Real toLnNorm(const Real& normBijection) const
        {
            return inversePower() * std::log(normBijection);
        }
        Real toBijection(const Real& norm) const
        {
            PENSURE_OP(norm, >=, 0);
            return std::pow(norm, power());
        }
        Real scalingFactor(
            const Real& scaling) const
        {
            return std::pow(abs(scaling), power());
        }
        Real axis(
            const Real& axisDistance) const
        {
            return std::pow(axisDistance, power());
        }
        Real signedAxis(
            const Real& axisDistance) const
        {
            return std::pow(abs(axisDistance), power());
        }
        Real addAxis(
            const Real& distance, 
            const Real& newAxisDistance) const
        {
            PENSURE_OP(newAxisDistance, >=, 0);
            return distance + 
                newAxisDistance;
        }
        Real replaceAxis(
            const Real& distance, 
            const Real& oldAxisDistance,
            const Real& newAxisDistance) const
        {
            PENSURE_OP(oldAxisDistance, >=, 0);
            PENSURE_OP(newAxisDistance, >=, 0);
            return (distance - oldAxisDistance) + 
                newAxisDistance;
        }
    private:
        Real power_;
        Real invPower_;
    };
}
#endif