// ****************************************************************************
//
//          Aevol - An in silico experimental evolution platform
//
// ****************************************************************************
//
// Copyright: See the AUTHORS file provided with the package or <www.aevol.fr>
// Web: http://www.aevol.fr/
// E-mail: See <http://www.aevol.fr/contact/>
// Original Authors : Guillaume Beslon, Carole Knibbe, David Parsons
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// ****************************************************************************

#ifndef AEVOL_FOLDING_H_
#define AEVOL_FOLDING_H_

#include <vector>

namespace aevol {

template <typename T, typename U>
auto from_base_n(const std::vector<int8_t>& raw, T& val, U exponent) {
  val = 0;
  for (auto digit : raw) {
    val = val * exponent + digit;
  }
  return val;
}

template <typename T, typename U>
auto from_base_n_reversed(const std::vector<int8_t>& raw, T& val, U exponent) {
  val = 0;
  auto digit_factor = 1.0;
  for (auto digit : raw) {
    val += digit * digit_factor;
    digit_factor *= exponent;
  }
  return val;
}

/**
 * \brief Normalize value in (possibly tweaked) base-N into [0, 1]
 *
 * To normalize normal base-N numbers, set exponent == base
 */
template <typename T, typename U, typename V>
auto normalize(T value, U base, V exponent, size_t nb_digits) {
  return value / ((base - 1) * (pow(exponent, nb_digits) - 1) / (exponent - 1));
}

/**
 * \brief Decode from "Modular" Gray Code to normal code
 *
 * Decode from "Modular" Gray code as defined in Bouyuklieva et al., M. Generating m-Ary Gray Codes and Related
 * Algorithms. Algorithms 2024, 17, 311. https://doi.org/10.3390/a17070311).
 */
inline auto gray_to_base_n(const std::vector<int8_t>& gray, uint8_t base) {
  auto decoded = decltype(gray){};
  if (gray.size() == 0) return decoded;
  decoded.push_back(gray[0]);

  for (auto i = size_t{1}; i < gray.size(); ++i) {
    decoded.push_back((decoded[i-1] + gray[i]) % base);
  }

  return decoded;
}

}  // namespace aevol

#endif  // AEVOL_FOLDING_H_
