/*
 *
 *    soniK digital audio editor
 *    Copyright (C) 2003-2004, 2006  Robert Walker <rob@tenfoot.org.uk>
 *
 *    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, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
#ifndef SONIK_SIGPROC_TCC
#define SONIK_SIGPROC_TCC

#include <cmath>

//
//
// Simple statistics
//
//

/**
 * find the position and value of the minimum value in buf
 */
template <typename T>
void Sonik::min(const T* buf, size_t len, size_t& pos, T& val)
{
  if (len == 0)
    return;

  pos = 0;
  const T* c = buf;
  val = *c++;
  for (size_t i = 1; i < len; ++i, ++c)
    if (*c < val)
    {
      val = *c;
      pos = i;
    }
}

/**
 * find the position and value of the minimum value in buf
 */
template <typename T>
void Sonik::min(const auto_buffer<T>& buf, size_t& pos, T& val)
{
  Sonik::min(buf.data(), buf.size(), pos, val);
}

/**
 * find the position and value of the minimum value in buf
 */
template <typename T>
void Sonik::min(const auto_buffer<const T>& buf, size_t& pos, T& val)
{
  Sonik::min(buf.data(), buf.size(), pos, val);
}

/**
 * find the position and value of the maximum value in buf
 */
template <typename T>
void Sonik::max(const T* buf, size_t len, size_t& pos, T& val)
{
  if (len == 0)
    return;

  pos = 0;
  const T* c = buf;
  val = *c++;
  for (size_t i = 1; i < len; ++i, ++c)
    if (*c > val)
    {
      val = *c;
      pos = i;
    }
}

/**
 * find the position and value of the maximum value in buf
 */
template <typename T>
void Sonik::max(const auto_buffer<T>& buf, size_t& pos, T& val)
{
  Sonik::max(buf.data(), buf.size(), pos, val);
}

/**
 * find the position and value of the maximum value in buf
 */
template <typename T>
void Sonik::max(const auto_buffer<const T>& buf, size_t& pos, T& val)
{
  Sonik::max(buf.data(), buf.size(), pos, val);
}

/**
 * find the position and value of the minimum and maximum values in buf
 */
template <typename T>
void Sonik::minmax(const T* buf, size_t len,
                   size_t& minPos, T& minVal,
                   size_t& maxPos, T& maxVal)
{
  if (len == 0)
    return;

  const T* c = buf;
  minPos = maxPos = 0;
  minVal = maxVal = *c++;
  for (size_t i = 1; i < len; ++i, ++c)
  {
    if (*c < minVal)
    {
      minVal = *c;
      minPos = i;
    }
    if (*c > maxVal)
    {
      maxVal = *c;
      maxPos = i;
    }
  }
}

/**
 * find the position and value of the minimum and maximum values in buf
 */
template <typename T>
void Sonik::minmax(const auto_buffer<T>& buf,
                   size_t& minPos, T& minVal, size_t& maxPos, T& maxVal)
{
  Sonik::minmax(buf.data(), buf.size(), minPos, minVal, maxPos, maxVal);
}

/**
 * find the position and value of the minimum and maximum values in buf
 */
template <typename T>
void Sonik::minmax(const auto_buffer<const T>& buf,
                   size_t& minPos, T& minVal, size_t& maxPos, T& maxVal)
{
  Sonik::minmax(buf.data(), buf.size(), minPos, minVal, maxPos, maxVal);
}

/**
 * find the position and value of the minimum absolute value in buf
 */
template <typename T>
void Sonik::absMin(const T* buf, size_t len, size_t& pos, T& val)
{
  if (len == 0)
    return;

  pos = 0;
  const T* c = buf;
  val = *c++;
  T aVal = Sonik::abs(val);
  for (size_t i = 1; i < len; ++i, ++c)
  {
    T aC = Sonik::abs(*c);
    if (aC < aVal)
    {
      val = *c;
      aVal = aC;
      pos = i;
    }
  }
}

/**
 * find the position and value of the minimum absolute value in buf
 */
template <typename T>
void Sonik::absMin(const auto_buffer<T>& buf, size_t& pos, T& val)
{
  Sonik::absMin(buf.data(), buf.size(), pos, val);
}

/**
 * find the position and value of the minimum absolute value in buf
 */
template <typename T>
void Sonik::absMin(const auto_buffer<const T>& buf, size_t& pos, T& val)
{
  Sonik::absMin(buf.data(), buf.size(), pos, val);
}

/**
 * find the position and value of the maximum absolute value in buf
 */
template <typename T>
void Sonik::absMax(const T* buf, size_t len, size_t& pos, T& val)
{
  if (len == 0)
    return;

  pos = 0;
  const T* c = buf;
  val = *c++;
  T aVal = Sonik::abs(val);
  for (size_t i = 1; i < len; ++i, ++c)
  {
    T aC = Sonik::abs(*c);
    if (aC > aVal)
    {
      val = *c;
      aVal = aC;
      pos = i;
    }
  }
}

/**
 * find the position and value of the maximum absolute value in buf
 */
template <typename T>
void Sonik::absMax(const auto_buffer<T>& buf, size_t& pos, T& val)
{
  Sonik::absMax(buf.data(), buf.size(), pos, val);
}

/**
 * find the position and value of the maximum absolute value in buf
 */
template <typename T>
void Sonik::absMax(const auto_buffer<const T>& buf, size_t& pos, T& val)
{
  Sonik::absMax(buf.data(), buf.size(), pos, val);
}

/**
 * find the position and value of the minimum and maximum absolute values in buf
 */
template <typename T>
void Sonik::absMinmax(const T* buf, size_t len,
                      size_t& minPos, T& minVal,
                      size_t& maxPos, T& maxVal)
{
  if (len == 0)
    return;

  const T* c = buf;
  T aMin, aMax;
  minPos = maxPos = 0;
  aMin = aMax = Sonik::abs(*c);
  minVal = maxVal = *c++;
  for (size_t i = 1; i < len; ++i, ++c)
  {
    T aC = Sonik::abs(*c);
    if (aC < aMin)
    {
      minVal = *c;
      aMin = aC;
      minPos = i;
    }
    if (aC > aMax)
    {
      maxVal = *c;
      aMax = aC;
      maxPos = i;
    }
  }
}

/**
 * find the position and value of the minimum and maximum absolute values in buf
 */
template <typename T>
void Sonik::absMinmax(const auto_buffer<T>& buf,
                      size_t& minPos, T& minVal, size_t& maxPos, T& maxVal)
{
  Sonik::absMinmax(buf.data(), buf.size(), minPos, minVal, maxPos, maxVal);
}

/**
 * find the position and value of the minimum and maximum absolute values in buf
 */
template <typename T>
void Sonik::absMinmax(const auto_buffer<const T>& buf,
                      size_t& minPos, T& minVal, size_t& maxPos, T& maxVal)
{
  Sonik::absMinmax(buf.data(), buf.size(), minPos, minVal, maxPos, maxVal);
}

/**
 * calculate the mean value of buf
 */
template <typename T>
double Sonik::mean(const T* buf, size_t len)
{
  if (len == 0)
    return 0;

  double s = 0.0;
  const T* c = buf;
  for (size_t i = 0; i < len; ++i, ++c)
    s += *c;

  return (s / len);
}

/**
 * calculate the mean value of buf
 */
template <typename T>
double Sonik::mean(const auto_buffer<T>& buf)
{
  return Sonik::mean(buf.data(), buf.size());
}

/**
 * calculate the mean value of buf
 */
template <typename T>
double Sonik::mean(const auto_buffer<const T>& buf)
{
  return Sonik::mean(buf.data(), buf.size());
}

/**
 * calculate the mean absolute value of buf
 */
template <typename T>
double Sonik::absMean(const T* buf, size_t len)
{
  if (len == 0)
    return 0;

  double s = 0.0;
  const T* c = buf;
  for (size_t i = 0; i < len; ++i, ++c)
    s += Sonik::abs(*c);

  return (s / len);
}

/**
 * calculate the absolute mean value of buf
 */
template <typename T>
double Sonik::absMean(const auto_buffer<T>& buf)
{
  return Sonik::absMean(buf.data(), buf.size());
}

/**
 * calculate the absolute mean value of buf
 */
template <typename T>
double Sonik::absMean(const auto_buffer<const T>& buf)
{
  return Sonik::absMean(buf.data(), buf.size());
}

/**
 * calculate the rms value of buf
 */
template <typename T>
double Sonik::rms(const T* buf, size_t len)
{
  if (len == 0)
    return 0;

  double s = 0.0;
  const T* c = buf;
  for (size_t i = 0; i < len; ++i, ++c)
    s += (*c) * (*c);

  return sqrt(s / len);
}

/**
 * calculate the rms value of buf
 */
template <typename T>
double Sonik::rms(const auto_buffer<T>& buf)
{
  return Sonik::rms(buf.data(), buf.size());
}

/**
 * calculate the rms value of buf
 */
template <typename T>
double Sonik::rms(const auto_buffer<const T>& buf)
{
  return Sonik::rms(buf.data(), buf.size());
}

//
//
// Manipulation
//
//

/**
 * fill buf with constant
 */
template <typename T>
void Sonik::fill(T* buf, size_t len, const T& v)
{
  while (len--)
    *(buf++) = v;
}

/**
 * fill buf with constant
 */
template <typename T>
void Sonik::fill(auto_buffer<T>& buf, const T& v)
{
  Sonik::fill(buf.data(), buf.size(), v);
}

/**
 * add constant to each sample in buf
 */
template <typename T>
void Sonik::offset(T* buf, size_t len, T d)
{
  while (len--)
    *(buf++) += d;
}

/**
 * add constant to each sample in buf
 */
template <typename T>
void Sonik::offset(auto_buffer<T>& buf, T d)
{
  Sonik::offset(buf.data(), buf.size(), d);
}

/**
 * multiply each sample in buf by constant
 */
template <typename T>
void Sonik::scale(T* buf, size_t len, float s)
{
  while (len--)
    *(buf++) *= s;
}

/**
 * multiply each sample in buf by constant
 */
template <typename T>
void Sonik::scale(auto_buffer<T>& buf, float s)
{
  Sonik::scale(buf.data(), buf.size(), s);
}

/**
 * log10 of each sample in buf
 */
template <typename T>
void Sonik::log10(T* buf, size_t len)
{
  while (len--)
  {
    *buf = std::log10(*buf);
    buf++;
  }
}

/**
 * log10 of each sample in buf
 */
template <typename T>
void Sonik::log10(auto_buffer<T>& buf)
{
  Sonik::log10(buf.data(), buf.size());
}

/**
 * add corresponing samples in two buffers (a = a + b)
 */
template <typename T>
void Sonik::add(T* a, T* b, size_t len)
{
  while (len--)
  {
    *a = *a + *b;
    a++;
    b++;
  }
}

/**
 * add corresponing samples in two buffers (a = a + b)
 */
template <typename T>
void Sonik::add(auto_buffer<T>& a, auto_buffer<T>& b)
{
  assert(a.size() == b.size());
  Sonik::add(a.data(), b.data(), a.size());
}

/**
 * subtract corresponing samples in two buffers (a = a - b)
 */
template <typename T>
void Sonik::sub(T* a, T* b, size_t len)
{
  while (len--)
  {
    *a = *a - *b;
    a++;
    b++;
  }
}

/**
 * subtract corresponing samples in two buffers (a = a - b)
 */
template <typename T>
void Sonik::sub(auto_buffer<T>& a, auto_buffer<T>& b)
{
  assert(a.size() == b.size());
  Sonik::sub(a.data(), b.data(), a.size());
}

/**
 * multiply corresponing samples in two buffers (a = a * b)
 */
template <typename T>
void Sonik::mul(T* a, T* b, size_t len)
{
  while (len--)
  {
    *a = *a * *b;
    a++;
    b++;
  }
}

/**
 * multiply corresponing samples in two buffers (a = a * b)
 */
template <typename T>
void Sonik::mul(auto_buffer<T>& a, auto_buffer<T>& b)
{
  assert(a.size() == b.size());
  Sonik::mul(a.data(), b.data(), a.size());
}

/**
 * reverse buffer
 */
template <typename T>
void Sonik::reverse(T* buf, size_t len)
{
  T *a = buf, *b = buf + len - 1;
  T tmp;

  while (a < b)
  {
    tmp = *a;
    *a = *b;
    *b = tmp;

    a++;
    b--;
  }
}


/**
 * reverse buffer
 */
template <typename T>
void Sonik::reverse(auto_buffer<T>& buf)
{
  Sonik::reverse(buf.data(), buf.size());
}


#endif // SONIK_SIGPROC_TCC
