// -*- C++ -*-
//
// This file is part of YODA -- Yet more Objects for Data Analysis
// Copyright (C) 2008-2024 The YODA collaboration (see AUTHORS for details)
//
#ifndef YODA_DbnUtils_h
#define YODA_DbnUtils_h

#include "YODA/Exceptions.h"
#include "YODA/Utils/MathUtils.h"
#include "YODA/Utils/MetaUtils.h"
#include <cmath>
#include <string>
#include <array>

namespace YODA {

  /// @name Mixin of convenience methods using CRTP
  /// @{

  /// @brief CRTP mixin introducing convenience aliases along X axis.
  template <class Derived>
  struct XDbnMixin {

    /// @name Dbn axis scalings
    /// @{

    void scaleX(double factor) { static_cast<Derived*>(this)->scale(1,factor); }

    /// @}

    /// @name Dbn statistics
    /// @{

    double xMean() const { return static_cast<const Derived*>(this)->mean(1); }
    double xVariance() const { return static_cast<const Derived*>(this)->variance(1); }
    double xStdDev() const { return static_cast<const Derived*>(this)->stdDev(1); }
    double xStdErr() const { return static_cast<const Derived*>(this)->stdErr(1); }
    double xRMS() const { return static_cast<const Derived*>(this)->RMS(1); }
    double sumWX() const { return static_cast<const Derived*>(this)->sumW(1); }
    double sumWX2() const { return static_cast<const Derived*>(this)->sumW2(1); }

    /// @}

  };

  /// @brief CRTP mixin introducing convenience aliases along Y axis.
  template <class Derived>
  struct YDbnMixin {

    /// @name Dbn axis scalings
    /// @{

    void scaleY(double factor) { static_cast<Derived*>(this)->scale(2,factor); }

    void scaleXY(double fx, double fy) {
      static_cast<Derived*>(this)->scale(1,fx);
      static_cast<Derived*>(this)->scale(2,fy);
    }

    /// @}

    /// @name Dbn statistics
    /// @{

    double yMean() const { return static_cast<const Derived*>(this)->mean(2); }
    double yVariance() const { return static_cast<const Derived*>(this)->variance(2); }
    double yStdDev() const { return static_cast<const Derived*>(this)->stdDev(2); }
    double yStdErr() const { return static_cast<const Derived*>(this)->stdErr(2); }
    double yRMS() const { return static_cast<const Derived*>(this)->RMS(2); }
    double sumWY() const { return static_cast<const Derived*>(this)->sumW(2); }
    double sumWY2() const { return static_cast<const Derived*>(this)->sumW2(2); }
    double sumWXY() const { return static_cast<const Derived*>(this)->crossTerm(0,1); }

    /// @}

  };

  /// @brief CRTP mixin introducing convenience aliases along Z axis.
  template <class Derived>
  struct ZDbnMixin {

    /// @name Dbn axis scalings
    /// @{

    void scaleZ(double factor) {
      static_cast<Derived*>(this)->scale(3,factor);
    }

    void scaleXZ(double fx, double fz) {
      static_cast<Derived*>(this)->scale(1,fx);
      static_cast<Derived*>(this)->scale(3,fz);
    }

    void scaleYZ(double fy, double fz) {
      static_cast<Derived*>(this)->scale(2,fy);
      static_cast<Derived*>(this)->scale(3,fz);
    }

    void scaleXYZ(double fx, double fy, double fz) {
      static_cast<Derived*>(this)->scale(1,fx);
      static_cast<Derived*>(this)->scale(2,fy);
      static_cast<Derived*>(this)->scale(3,fz);
    }

    /// @}

    /// @name Dbn statistics
    /// @{

    double zMean() const { return static_cast<const Derived*>(this)->mean(3); }
    double zVariance() const { return static_cast<const Derived*>(this)->variance(3); }
    double zStdDev() const { return static_cast<const Derived*>(this)->stdDev(3); }
    double zStdErr() const { return static_cast<const Derived*>(this)->stdErr(3); }
    double zRMS() const { return static_cast<const Derived*>(this)->RMS(3); }
    double sumWZ() const { return static_cast<const Derived*>(this)->sumW(3); }
    double sumWZ2() const { return static_cast<const Derived*>(this)->sumW2(3); }
    double sumWXZ() const { return static_cast<const Derived*>(this)->crossTerm(0,2); }
    double sumWYZ() const { return static_cast<const Derived*>(this)->crossTerm(1,2); }

    /// @}

  };

  /// @}

}

#endif
