#include "opt1d.h"

#include <iostream>
#include <cassert>
#include <vector>
#include <cstdlib> // random()

typedef double real_t;
typedef std::vector< real_t > vec_t;

namespace
{
std::ostream& dbg_out = std::cerr;

void print_vec( std::ostream& out, const vec_t& vec )
{
    if( vec.empty() )
    {
        out << "(empty)";
        return;
    }
    
    out << vec.at(0);
    for( int i = 1; i < vec.size(); ++i )
        out << ' ' << vec.at(i);
}

// A random number between 0 and 1.
inline double uniform()
{
    return double( random() % 100000 ) / 100000;
}

void run_test( const vec_t& original_values, const vec_t& values_spacing, real_t gradient0, real_t gradientN, const char* test_name = "_untitled" )
{
    dbg_out << "================ test" << test_name << " ================\n";
    
    assert( original_values.size() == values_spacing.size() + 1 );
    
    dbg_out << "Input values:\n";
    print_vec( dbg_out, original_values );
    dbg_out << '\n';
    
    dbg_out << "Input value spacing:\n";
    print_vec( dbg_out, values_spacing );
    dbg_out << '\n';
    
    dbg_out << "Gradient 0: " << gradient0 << '\n';
    dbg_out << "Gradient N: " << gradientN << '\n';
    
    vec_t result_values( original_values.size(), -31337 );
    toposmooth::smooth1d_monotonically_decreasing(
        original_values.size(),
        &original_values[0],
        &result_values[0],
        &values_spacing[0],
        gradient0,
        gradientN
        );
    
    dbg_out << "Result values:\n";
    print_vec( dbg_out, result_values );
    dbg_out << '\n';
    
    dbg_out << '\n';
}

void test1()
{
    vec_t original_values( 10 );
    for( int i = 0; i < original_values.size(); ++i )
    {
        original_values.at(i) = 0.;
        // original_values.at(i) = 1. - double(i)/double(original_values.size()-1);
    }
    vec_t values_spacing( original_values.size() - 1, 1. );
    
    run_test( original_values, values_spacing, 0., 0., " all zeros" );
}

void test2()
{
    vec_t original_values( 10 );
    for( int i = 0; i < original_values.size(); ++i )
    {
        // original_values.at(i) = 0.;
        original_values.at(i) = 1. - double(i)/double(original_values.size()-1);
    }
    vec_t values_spacing( original_values.size() - 1, 1. );
    
    run_test(
        original_values, values_spacing,
        // Gradients of 0, so it should go smoothly.
        // 0., 0.
        // With these values, the solution should be a straight line:
        original_values[1] - original_values[0],
        original_values[original_values.size()-1] - original_values[original_values.size()-2],
        " sloping line"
        );
}

void test3()
{
    vec_t original_values( 10 );
    for( int i = 0; i < original_values.size(); ++i )
    {
        original_values.at(i) = 0.;
        // original_values.at(i) = 1. - double(i)/double(original_values.size()-1);
    }
    vec_t values_spacing( original_values.size() - 1, 1. );
    for( int i = 0; i < values_spacing.size(); ++i )
    {
        // Values between .9 and 1.1.
        values_spacing.at(i) = .9 + .2 * uniform();
    }
    
    run_test( original_values, values_spacing, 0., 0., " all zeros, variable spacing" );
}

void test4()
{
    vec_t original_values( 10 );
    for( int i = 0; i < original_values.size(); ++i )
    {
        // original_values.at(i) = 0.;
        original_values.at(i) = 1. - double(i)/double(original_values.size()-1);
    }
    vec_t values_spacing( original_values.size() - 1, 1. );
    for( int i = 0; i < values_spacing.size(); ++i )
    {
        // Values between .9 and 1.1.
        values_spacing.at(i) = .9 + .2 * uniform();
    }
    
    run_test(
        original_values, values_spacing,
        // Gradients of 0, so it should go smoothly.
        // 0., 0.
        // With these values, the solution should be a straight line:
        original_values[1] - original_values[0],
        original_values[original_values.size()-1] - original_values[original_values.size()-2],
        " sloping line, variable spacing"
        );
}

void test5()
{
    vec_t original_values( 10 );
    for( int i = 0; i < original_values.size(); ++i )
    {
        // original_values.at(i) = 0.;
        original_values.at(i) = 1. - double(i)/double(original_values.size()-1);
    }
    vec_t values_spacing( original_values.size() - 1, 1. );
    
    run_test(
        original_values, values_spacing,
        // Gradients of 0, so it should go smoothly.
        0., 0.,
        " sloping line, gradients 0"
        );
}

}

int main( int argc, char* argv[] )
{
    test1();
    test2();
    test3();
    test4();
    
    test5();
    
    return 0;
}
