/*
 *
 *    soniK digital audio editor
 *    Copyright (C) 2003-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
 *
 */
#include "sonik_sigproc.h"

#include <cppunit/extensions/HelperMacros.h>

#include "test.h"

namespace Sonik
{
  class SigProcTest : public CppUnit::TestFixture
  {
    CPPUNIT_TEST_SUITE( SigProcTest );
    CPPUNIT_TEST( testStats );
    CPPUNIT_TEST( testManip );
    CPPUNIT_TEST_SUITE_END();

  public:

    void testStats();
    void testManip();
  };
}

CPPUNIT_TEST_SUITE_REGISTRATION(Sonik::SigProcTest);

typedef Sonik::auto_buffer<const int> CIntBuffer;
typedef Sonik::auto_buffer<int> IntBuffer;

void Sonik::SigProcTest::testStats()
{
  const int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  const int b[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
  const int c[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
  const int d[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  const float e[] = { 7.631, 9.703, 7.844, 1.071, 6.193,
                      3.612, 5.940, 6.050, 5.332, 9.207 };

  const int f[] = { 0, -1, 2, -3, 4, -5, 6, -7, 8, -9 };
  const int g[] = { 9, -8, 7, -6, 5, -4, 3, -2, 1, 0 };
  const int h[] = { 0, -1, 2, -3, 4, -5, 6, -7, 8, -9,
                    8, -7, 6, -5, 4, -3, 2, -1, 0 };
  const int i[] = { 9, -8, 7, -6, 5, -4, 3, -2, 1, -0,
                    1, -2, 3, -4, 5, -6, 7, -8, 9 };
  const float j[] = { 9.912, 0.322, -4.573, -7.988, 3.878,
                      0.297, 4.449, 6.323, -6.377, -6.491 };

  size_t minPos, maxPos;
  int min, max;
  float fmin, fmax;

  {
    Sonik::min(a, 10, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);

    Sonik::min(b, 10, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 9u);
    CPPUNIT_ASSERT_EQUAL(min, 0);

    Sonik::min(c, 19, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);

    Sonik::min(d, 19, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 9u);
    CPPUNIT_ASSERT_EQUAL(min, 0);

    Sonik::min(e, 10, minPos, fmin);
    CPPUNIT_ASSERT_EQUAL(minPos, 3u);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(fmin, 1.071, 1.0e-6);

    CIntBuffer auto_a(a, 10);
    Sonik::min(auto_a, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL((size_t)auto_a.data(), (size_t)a);
    auto_a.release();
  }

  {
    Sonik::max(a, 10, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::max(b, 10, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::max(c, 19, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::max(d, 19, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::max(e, 10, maxPos, fmax);
    CPPUNIT_ASSERT_EQUAL(maxPos, 1u);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(fmax, 9.703, 1.0e-6);

    CIntBuffer auto_a(a, 10);
    Sonik::max(auto_a, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, 9);
    CPPUNIT_ASSERT_EQUAL((size_t)auto_a.data(), (size_t)a);
    auto_a.release();
  }

  {
    Sonik::minmax(a, 10, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::minmax(b, 10, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 9u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::minmax(c, 19, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::minmax(d, 19, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 9u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::minmax(e, 10, minPos, fmin, maxPos, fmax);
    CPPUNIT_ASSERT_EQUAL(maxPos, 1u);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(fmax, 9.703, 1.0e-6);
    CPPUNIT_ASSERT_EQUAL(minPos, 3u);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(fmin, 1.071, 1.0e-6);

    CIntBuffer auto_a(a, 10);
    Sonik::minmax(auto_a, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, 9);
    CPPUNIT_ASSERT_EQUAL((size_t)auto_a.data(), (size_t)a);
    auto_a.release();
  }

  {
    CPPUNIT_ASSERT_EQUAL(Sonik::abs(1), 1);
    CPPUNIT_ASSERT_EQUAL(Sonik::abs(-1), 1);
    CPPUNIT_ASSERT_EQUAL(Sonik::abs(0), 0);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::abs(1.5f), 1.5f, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::abs(-2.3f), 2.3f, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::abs(0.0f), 0.0f, 1.0e-6);
  }

  {
    Sonik::absMin(f, 10, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);

    Sonik::absMin(g, 10, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 9u);
    CPPUNIT_ASSERT_EQUAL(min, 0);

    Sonik::absMin(h, 19, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);

    Sonik::absMin(i, 19, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 9u);
    CPPUNIT_ASSERT_EQUAL(min, 0);

    Sonik::absMin(j, 10, minPos, fmin);
    CPPUNIT_ASSERT_EQUAL(minPos, 5u);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(fmin, 0.297, 1.0e-6);

    CIntBuffer auto_f(f, 10);
    Sonik::absMin(auto_f, minPos, min);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL((size_t)auto_f.data(), (size_t)f);
    auto_f.release();
  }

  {
    Sonik::absMax(f, 10, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, -9);

    Sonik::absMax(g, 10, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::absMax(h, 19, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, -9);

    Sonik::absMax(i, 19, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::absMax(j, 10, maxPos, fmax);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(fmax, 9.912, 1.0e-6);

    CIntBuffer auto_f(f, 10);
    Sonik::absMax(auto_f, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, -9);
    CPPUNIT_ASSERT_EQUAL((size_t)auto_f.data(), (size_t)f);
    auto_f.release();
  }

  {
    Sonik::absMinmax(f, 10, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, -9);

    Sonik::absMinmax(g, 10, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 9u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::absMinmax(h, 19, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, -9);

    Sonik::absMinmax(i, 19, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 9u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_EQUAL(max, 9);

    Sonik::absMinmax(j, 10, minPos, fmin, maxPos, fmax);
    CPPUNIT_ASSERT_EQUAL(minPos, 5u);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(fmin, 0.297, 1.0e-6);
    CPPUNIT_ASSERT_EQUAL(maxPos, 0u);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(fmax, 9.912, 1.0e-6);

    CIntBuffer auto_f(f, 10);
    Sonik::absMinmax(auto_f, minPos, min, maxPos, max);
    CPPUNIT_ASSERT_EQUAL(minPos, 0u);
    CPPUNIT_ASSERT_EQUAL(min, 0);
    CPPUNIT_ASSERT_EQUAL(maxPos, 9u);
    CPPUNIT_ASSERT_EQUAL(max, -9);
    CPPUNIT_ASSERT_EQUAL((size_t)auto_f.data(), (size_t)f);
    auto_f.release();
  }

  {

    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(a, 10), 4.5, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(b, 10), 4.5, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(c, 19), 4.263158, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(d, 19), 4.736842, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(e, 10), 6.2583, 1.0e-6);

    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(f, 10), -0.5, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(g, 10), 0.5, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(h, 19), -0.05263158, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(i, 19), 0.5263158, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(j, 10), -0.024800, 1.0e-6);

    CIntBuffer auto_a(a, 10);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::mean(auto_a), 4.5, 1.0e-6);
    CPPUNIT_ASSERT_EQUAL((size_t)auto_a.data(), (size_t)a);
    auto_a.release();
  }

  {
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(a, 10), 4.5, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(b, 10), 4.5, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(c, 19), 4.263158, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(d, 19), 4.736842, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(e, 10), 6.2583, 1.0e-6);

    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(f, 10), 4.5, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(g, 10), 4.5, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(h, 19), 4.263158, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(i, 19), 4.736842, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(j, 10), 5.061000, 1.0e-6);

    CIntBuffer auto_a(a, 10);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::absMean(auto_a), 4.5, 1.0e-6);
    CPPUNIT_ASSERT_EQUAL((size_t)auto_a.data(), (size_t)a);
    auto_a.release();
  }

  {
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(a, 10), 5.3385391, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(b, 10), 5.3385391, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(c, 19), 5.0731491, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(d, 19), 5.4772256, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(e, 10), 6.7196779, 1.0e-6);

    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(f, 10), 5.3385391, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(g, 10), 5.3385391, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(h, 19), 5.0731491, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(i, 19), 5.4772256, 1.0e-6);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(j, 10), 5.8375691, 1.0e-6);

    CIntBuffer auto_a(a, 10);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(Sonik::rms(auto_a), 5.3385391, 1.0e-6);
    CPPUNIT_ASSERT_EQUAL((size_t)auto_a.data(), (size_t)a);
    auto_a.release();
  }
}

void Sonik::SigProcTest::testManip()
{
  CPPUNIT_FAIL("Not implemented");
}
