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

#include <cppunit/extensions/HelperMacros.h>

#include "test.h"

namespace Sonik
{
  class UtilTest : public CppUnit::TestFixture
  {
    CPPUNIT_TEST_SUITE( UtilTest );
    CPPUNIT_TEST( testBound );
    CPPUNIT_TEST( testSampleConvert );
    CPPUNIT_TEST( testInterleave );
    CPPUNIT_TEST( testTimeConv );
    CPPUNIT_TEST( testUnitConv );
    CPPUNIT_TEST( testTimeToScreenM );
    CPPUNIT_TEST( testTimeToScreenL );
    CPPUNIT_TEST( testTimeToScreenU );
    CPPUNIT_TEST_SUITE_END();

  public:

    void testBound();
    void testInterleave();
    void testSampleConvert();
    void testTimeConv();
    void testUnitConv();
    void testTimeToScreenM();
    void testTimeToScreenL();
    void testTimeToScreenU();
  };
}

CPPUNIT_TEST_SUITE_REGISTRATION(Sonik::UtilTest);

void Sonik::UtilTest::testBound()
{
  CPPUNIT_ASSERT_EQUAL(Sonik::bound(-1, 0, 0), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound( 0, 0, 0), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound( 1, 0, 0), 0);

  CPPUNIT_ASSERT_EQUAL(Sonik::bound(-1, 0, 1), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound( 0, 0, 1), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound( 1, 0, 1), 0);

  CPPUNIT_ASSERT_EQUAL(Sonik::bound(-1, 0, 2), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound( 0, 0, 2), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound( 1, 0, 2), 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound( 2, 0, 2), 1);

  CPPUNIT_ASSERT_EQUAL(Sonik::bound(  0, 100,   0), 100);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound(100, 100,   0), 100);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound(101, 100,   0), 100);

  CPPUNIT_ASSERT_EQUAL(Sonik::bound(  0, 100,   1), 100);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound(100, 100,   1), 100);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound(101, 100,   1), 100);

  CPPUNIT_ASSERT_EQUAL(Sonik::bound(  0, 100, 100), 100);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound(100, 100, 100), 100);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound(150, 100, 100), 150);
  CPPUNIT_ASSERT_EQUAL(Sonik::bound(200, 100, 100), 199);
}

void Sonik::UtilTest::testSampleConvert()
{
  CPPUNIT_FAIL("Not implemented");
}

void Sonik::UtilTest::testInterleave()
{
  CPPUNIT_FAIL("Not implemented");
}

void Sonik::UtilTest::testTimeConv()
{
  CPPUNIT_ASSERT_EQUAL(  Sonik::hour(0, 10000), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::minute(0, 10000), 0);
  CPPUNIT_ASSERT_EQUAL(   Sonik::sec(0, 10000), 0);
  CPPUNIT_ASSERT_EQUAL(  Sonik::msec(0, 10000), 0);
//   CPPUNIT_ASSERT_EQUAL( Sonik::frame(0, 10000), 0);

  CPPUNIT_ASSERT_EQUAL(  Sonik::hour(0, 48000), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::minute(0, 48000), 0);
  CPPUNIT_ASSERT_EQUAL(   Sonik::sec(0, 48000), 0);
  CPPUNIT_ASSERT_EQUAL(  Sonik::msec(0, 48000), 0);
//   CPPUNIT_ASSERT_EQUAL( Sonik::frame(0, 48000), 0);

  CPPUNIT_ASSERT_EQUAL(Sonik::msec(10000, 10000),    0);
  CPPUNIT_ASSERT_EQUAL(Sonik::msec(10005, 10000),    0);
  CPPUNIT_ASSERT_EQUAL(Sonik::msec( 5000, 10000),  500);
  CPPUNIT_ASSERT_EQUAL(Sonik::msec( 1000, 10000),  100);
  CPPUNIT_ASSERT_EQUAL(Sonik::msec(   10, 10000),    1);

  CPPUNIT_ASSERT_EQUAL(Sonik::msec(48000, 48000),    0);
  CPPUNIT_ASSERT_EQUAL(Sonik::msec(48009, 48000),    0);
  CPPUNIT_ASSERT_EQUAL(Sonik::msec(24000, 48000),  500);
  CPPUNIT_ASSERT_EQUAL(Sonik::msec( 4800, 48000),  100);
  CPPUNIT_ASSERT_EQUAL(Sonik::msec(   48, 48000),    1);

  CPPUNIT_ASSERT_EQUAL(Sonik::sec(48000500, 48000),   40);
  CPPUNIT_ASSERT_EQUAL(Sonik::sec(24000500, 48000),   20);
  CPPUNIT_ASSERT_EQUAL(Sonik::sec(  480500, 48000),   10);
  CPPUNIT_ASSERT_EQUAL(Sonik::sec(  480000, 48000),   10);
  CPPUNIT_ASSERT_EQUAL(Sonik::sec(   48700, 48000),    1);
  CPPUNIT_ASSERT_EQUAL(Sonik::sec(   48000, 48000),    1);
  CPPUNIT_ASSERT_EQUAL(Sonik::sec(   28200, 48000),    0);

  CPPUNIT_ASSERT_EQUAL(Sonik::minute(28823400, 48000),   10);
  CPPUNIT_ASSERT_EQUAL(Sonik::minute(28800000, 48000),   10);
  CPPUNIT_ASSERT_EQUAL(Sonik::minute( 2882340, 48000),    1);
  CPPUNIT_ASSERT_EQUAL(Sonik::minute( 2880000, 48000),    1);
  CPPUNIT_ASSERT_EQUAL(Sonik::minute( 1440000, 48000),    0);

  CPPUNIT_ASSERT_EQUAL(Sonik::hour(1728000000, 48000), 10);
  CPPUNIT_ASSERT_EQUAL(Sonik::hour( 172800200, 48000),  1);
  CPPUNIT_ASSERT_EQUAL(Sonik::hour( 172800000, 48000),  1);
  CPPUNIT_ASSERT_EQUAL(Sonik::hour( 172700000, 48000),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::hour(   1440000, 48000),  0);

  CPPUNIT_ASSERT_EQUAL(Sonik::toMSecs(        0, 10000),        0);
  CPPUNIT_ASSERT_EQUAL(Sonik::toMSecs(       10, 10000),        1);
  CPPUNIT_ASSERT_EQUAL(Sonik::toMSecs(      100, 10000),       10);
  CPPUNIT_ASSERT_EQUAL(Sonik::toMSecs(     5000, 10000),      500);
  CPPUNIT_ASSERT_EQUAL(Sonik::toMSecs(    10000, 10000),     1000);
  CPPUNIT_ASSERT_EQUAL(Sonik::toMSecs(   123456, 10000),    12345);
  CPPUNIT_ASSERT_EQUAL(Sonik::toMSecs(987654321, 10000), 98765432);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToSamples(48000, 0, 0, 0, 0),        0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToSamples(48000, 0, 0, 0, 1),       48);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToSamples(48000, 0, 0, 1, 0),    48000);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToSamples(48000, 0, 1, 0, 0),  2880000);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToSamples(48000, 1, 0, 0, 0), 172800000);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToSamples(48000, 1, 1, 1, 1), 175728048);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToSamples(48000, 23, 59, 59), 86399 * 48000);
}

void Sonik::UtilTest::testUnitConv()
{
  CPPUNIT_FAIL("Not implemented");
}

void Sonik::UtilTest::testTimeToScreenM()
{
  // middle of sample

  // zero scroll pos
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 0, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 0, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 0, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 0, 1.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 0, 2.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 0, 4.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 0, 8.0),   0);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 0, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 0, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 0, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 0, 1.0),   1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 0, 2.0),   2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 0, 4.0),   4);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 0, 8.0),   8);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 0, 0.125),   8);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 0, 0.25),   16);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 0, 0.5),    32);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 0, 1.0),    64);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 0, 2.0),   128);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 0, 4.0),   256);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 0, 8.0),   512);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 0, 0.125),  12);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 0, 0.25),   25);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 0, 0.5),    50);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 0, 1.0),   100);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 0, 2.0),   200);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 0, 4.0),   400);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 0, 8.0),   800);

  // 1 scroll pos
  // (unless z < 1.0 as scr pos must be multiple of 1/z)
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 8, 0.125), -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 4, 0.25),  -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 2, 0.5),   -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 1, 1.0),   -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 1, 2.0),   -2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 1, 4.0),   -4);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 1, 8.0),   -8);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 8, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 4, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 2, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 1, 1.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 1, 2.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 1, 4.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(1, 1, 8.0),   0);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 8, 0.125),   7);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 4, 0.25),   15);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 2, 0.5),    31);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 1, 1.0),    63);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 1, 2.0),   126);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 1, 4.0),   252);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(64, 1, 8.0),   504);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 8, 0.125),  11);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 4, 0.25),   24);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 2, 0.5),    49);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 1, 1.0),    99);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 1, 2.0),   198);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 1, 4.0),   396);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(100, 1, 8.0),   792);

  // big scroll pos
  // (if z < 1.0, scr pos must be multiple of 1/z)
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 4000, 0.125), -4000 / 8);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 4000, 0.25),  -4000 / 4);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 4000, 0.5),   -4000 / 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 4000, 1.0),   -4000);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 4000, 2.0),   -4000 * 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 4000, 4.0),   -4000 * 4);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(0, 4000, 8.0),   -4000 * 8);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4000, 4000, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4000, 4000, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4000, 4000, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4000, 4000, 1.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4000, 4000, 2.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4000, 4000, 4.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4000, 4000, 8.0),   0);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4064, 4000, 0.125),   8);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4064, 4000, 0.25),   16);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4064, 4000, 0.5),    32);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4064, 4000, 1.0),    64);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4064, 4000, 2.0),   128);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4064, 4000, 4.0),   256);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4064, 4000, 8.0),   512);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4100, 4000, 0.125),  12);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4100, 4000, 0.25),   25);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4100, 4000, 0.5),    50);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4100, 4000, 1.0),   100);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4100, 4000, 2.0),   200);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4100, 4000, 4.0),   400);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenM(4100, 4000, 8.0),   800);
}


void Sonik::UtilTest::testTimeToScreenL()
{
  // left edge of sample

  // zero scroll pos
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 0, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 0, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 0, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 0, 1.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 0, 2.0),   -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 0, 4.0),   -2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 0, 8.0),   -4);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 0, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 0, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 0, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 0, 1.0),   1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 0, 2.0),   2 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 0, 4.0),   4 - 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 0, 8.0),   8 - 4);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 0, 0.125),   8);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 0, 0.25),   16);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 0, 0.5),    32);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 0, 1.0),    64);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 0, 2.0),   128 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 0, 4.0),   256 - 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 0, 8.0),   512 - 4);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 0, 0.125),  12);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 0, 0.25),   25);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 0, 0.5),    50);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 0, 1.0),   100);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 0, 2.0),   200 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 0, 4.0),   400 - 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 0, 8.0),   800 - 4);

  // 1 scroll pos
  // (unless z < 1.0 as scr pos must be multiple of 1/z)
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 8, 0.125), -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 4, 0.25),  -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 2, 0.5),   -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 1, 1.0),   -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 1, 2.0),   -2 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 1, 4.0),   -4 - 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 1, 8.0),   -8 - 4);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 8, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 4, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 2, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 1, 1.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 1, 2.0),   -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 1, 4.0),   -2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(1, 1, 8.0),   -4);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 8, 0.125),   7);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 4, 0.25),   15);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 2, 0.5),    31);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 1, 1.0),    64 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 1, 2.0),   128 - 2 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 1, 4.0),   256 - 4 - 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(64, 1, 8.0),   512 - 8 - 4);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 8, 0.125),  11);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 4, 0.25),   24);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 2, 0.5),    49);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 1, 1.0),   100 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 1, 2.0),   200 - 2 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 1, 4.0),   400 - 4 - 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(100, 1, 8.0),   800 - 8 - 4);

  // big scroll pos
  // (if z < 1.0, scr pos must be multiple of 1/z)
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 4000, 0.125), -4000 / 8);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 4000, 0.25),  -4000 / 4);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 4000, 0.5),   -4000 / 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 4000, 1.0),   -4000);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 4000, 2.0),   -4000 * 2 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 4000, 4.0),   -4000 * 4 - 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(0, 4000, 8.0),   -4000 * 8 - 4);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4000, 4000, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4000, 4000, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4000, 4000, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4000, 4000, 1.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4000, 4000, 2.0),   -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4000, 4000, 4.0),   -2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4000, 4000, 8.0),   -4);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4064, 4000, 0.125),   8);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4064, 4000, 0.25),   16);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4064, 4000, 0.5),    32);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4064, 4000, 1.0),    64);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4064, 4000, 2.0),   128 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4064, 4000, 4.0),   256 - 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4064, 4000, 8.0),   512 - 4);

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4100, 4000, 0.125),  12);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4100, 4000, 0.25),   25);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4100, 4000, 0.5),    50);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4100, 4000, 1.0),   100);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4100, 4000, 2.0),   200 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4100, 4000, 4.0),   400 - 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenL(4100, 4000, 8.0),   800 - 4);
}


void Sonik::UtilTest::testTimeToScreenU()
{
  // right edge of sample

  // zero scroll pos
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 0, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 0, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 0, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 0, 1.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 0, 2.0),   (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 0, 4.0),   (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 0, 8.0),   (4-1));

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 0, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 0, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 0, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 0, 1.0),   1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 0, 2.0),   2 + (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 0, 4.0),   4 + (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 0, 8.0),   8 + (4-1));

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 0, 0.125),   8);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 0, 0.25),   16);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 0, 0.5),    32);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 0, 1.0),    64);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 0, 2.0),   128 + (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 0, 4.0),   256 + (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 0, 8.0),   512 + (4-1));

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 0, 0.125),  12);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 0, 0.25),   25);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 0, 0.5),    50);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 0, 1.0),   100);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 0, 2.0),   200 + (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 0, 4.0),   400 + (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 0, 8.0),   800 + (4-1));

  // 1 scroll pos
  // (unless z < 1.0 as scr pos must be multiple of 1/z)
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 8, 0.125), -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 4, 0.25),  -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 2, 0.5),   -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 1, 1.0),   -1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 1, 2.0),   -2 + (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 1, 4.0),   -4 + (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 1, 8.0),   -8 + (4-1));

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 8, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 4, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 2, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 1, 1.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 1, 2.0),   (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 1, 4.0),   (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(1, 1, 8.0),   (4-1));

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 8, 0.125),   7);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 4, 0.25),   15);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 2, 0.5),    31);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 1, 1.0),    64 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 1, 2.0),   128 - 2 + (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 1, 4.0),   256 - 4 + (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(64, 1, 8.0),   512 - 8 + (4-1));

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 8, 0.125),  11);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 4, 0.25),   24);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 2, 0.5),    49);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 1, 1.0),   100 - 1);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 1, 2.0),   200 - 2 + (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 1, 4.0),   400 - 4 + (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(100, 1, 8.0),   800 - 8 + (4-1));

  // big scroll pos
  // (if z < 1.0, scr pos must be multiple of 1/z)
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 4000, 0.125), -4000 / 8);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 4000, 0.25),  -4000 / 4);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 4000, 0.5),   -4000 / 2);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 4000, 1.0),   -4000);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 4000, 2.0),   -4000 * 2 + (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 4000, 4.0),   -4000 * 4 + (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(0, 4000, 8.0),   -4000 * 8 + (4-1));

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4000, 4000, 0.125), 0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4000, 4000, 0.25),  0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4000, 4000, 0.5),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4000, 4000, 1.0),   0);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4000, 4000, 2.0),   (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4000, 4000, 4.0),   (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4000, 4000, 8.0),   (4-1));

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4064, 4000, 0.125),   8);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4064, 4000, 0.25),   16);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4064, 4000, 0.5),    32);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4064, 4000, 1.0),    64);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4064, 4000, 2.0),   128 + (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4064, 4000, 4.0),   256 + (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4064, 4000, 8.0),   512 + (4-1));

  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4100, 4000, 0.125),  12);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4100, 4000, 0.25),   25);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4100, 4000, 0.5),    50);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4100, 4000, 1.0),   100);
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4100, 4000, 2.0),   200 + (1-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4100, 4000, 4.0),   400 + (2-1));
  CPPUNIT_ASSERT_EQUAL(Sonik::timeToScreenU(4100, 4000, 8.0),   800 + (4-1));
}

// TODO: screen to time
// TODO: map screen -> time -> screen & time -> screen -> time
