/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

//////////////////////////////////////////////////////////////////////////////////////////
////////////////// tests for discrete linear transforms (FFT, DCT ...) ///////////////////
//////////////////////////////////////////////////////////////////////////////////////////

#include "cxcoretest.h"
#include <float.h>

typedef struct CvTsComplex32f
{
    float re, im;
}
CvTsComplex32f;

typedef struct CvTsComplex64f
{
    double re, im;
}
CvTsComplex64f;

static CvMat* cvTsInitDFTWave( int n, int inv )
{
    int i;
    double angle = (inv ? 1 : -1)*CV_PI*2/n;
    CvTsComplex64f wi, w1;
    CvMat* wave = cvCreateMat( 1, n, CV_64FC2 );
    CvTsComplex64f* w = (CvTsComplex64f*)wave->data.db;

    w1.re = cos(angle);
    w1.im = sin(angle);
    w[0].re = wi.re = 1.;
    w[0].im = wi.im = 0.;

    for( i = 1; i < n; i++ )
    {
        double t = wi.re*w1.re - wi.im*w1.im;
        wi.im = wi.re*w1.im + wi.im*w1.re;
        wi.re = t;
        w[i] = wi;
    }

    return wave;
}


static void cvTsDFT_1D( const CvMat* _src, CvMat* _dst, int flags, CvMat* wave=0 )
{
    int i, j, k, n = _dst->cols + _dst->rows - 1;
    const CvMat* wave0 = wave;
    double scale = (flags & CV_DXT_SCALE) ? 1./n : 1.;
    assert( _src->cols + _src->rows - 1 == n );
    int src_step = 1, dst_step = 1;
    CvTsComplex64f* w;

    assert( CV_ARE_TYPES_EQ(_src,_dst) && _src->rows*_src->cols == _dst->rows*_dst->cols );

    if( !wave )
        wave = cvTsInitDFTWave( n, flags & CV_DXT_INVERSE );

    w = (CvTsComplex64f*)wave->data.db;
    if( !CV_IS_MAT_CONT(_src->type) )
        src_step = _src->step/CV_ELEM_SIZE(_src->type);
    if( !CV_IS_MAT_CONT(_dst->type) )
        dst_step = _dst->step/CV_ELEM_SIZE(_dst->type);

    if( CV_MAT_TYPE(_src->type) == CV_32FC2 )
    {
        CvTsComplex32f* dst = (CvTsComplex32f*)_dst->data.fl;
        for( i = 0; i < n; i++, dst += dst_step )
        {
            CvTsComplex32f* src = (CvTsComplex32f*)_src->data.fl;
            CvTsComplex64f sum = {0,0};
            int delta = i;
            k = 0;

            for( j = 0; j < n; j++, src += src_step )
            {
                sum.re += src->re*w[k].re - src->im*w[k].im;
                sum.im += src->re*w[k].im + src->im*w[k].re;
                k += delta;
                k -= (k >= n ? n : 0);
            }

            dst->re = (float)(sum.re*scale);
            dst->im = (float)(sum.im*scale);
        }
    }
    else if( CV_MAT_TYPE(_src->type) == CV_64FC2 )
    {
        CvTsComplex64f* dst = (CvTsComplex64f*)_dst->data.db;
        for( i = 0; i < n; i++, dst += dst_step )
        {
            CvTsComplex64f* src = (CvTsComplex64f*)_src->data.db;
            CvTsComplex64f sum = {0,0};
            int delta = i;
            k = 0;

            for( j = 0; j < n; j++, src += src_step )
            {
                sum.re += src->re*w[k].re - src->im*w[k].im;
                sum.im += src->re*w[k].im + src->im*w[k].re;
                k += delta;
                k -= (k >= n ? n : 0);
            }

            dst->re = sum.re*scale;
            dst->im = sum.im*scale;
        }
    }
    else
        assert(0);

    if( !wave0 )
        cvReleaseMat( &wave );
}


static void cvTsDFT_2D( const CvMat* src, CvMat* dst, int flags )
{
    int i;
    CvMat* tmp = cvCreateMat( dst->cols, dst->rows, dst->type );
    CvMat* wave = cvTsInitDFTWave( dst->cols, flags & CV_DXT_INVERSE );

    // 1. row-wise transform
    for( i = 0; i < dst->rows; i++ )
    {
        CvMat src_row, dst_row;
        cvGetRow( src, &src_row, i );
        cvGetCol( tmp, &dst_row, i );
        cvTsDFT_1D( &src_row, &dst_row, flags, wave );
    }

    if( !(flags & CV_DXT_ROWS) )
    {
        if( dst->cols != dst->rows )
        {
            cvReleaseMat( &wave );
            wave = cvTsInitDFTWave( dst->rows, flags & CV_DXT_INVERSE );
        }

        // 2. column-wise transform
        for( i = 0; i < dst->cols; i++ )
        {
            CvMat src_row, dst_row;
            cvGetRow( tmp, &src_row, i );
            cvGetCol( dst, &dst_row, i );
            cvTsDFT_1D( &src_row, &dst_row, flags, wave );
        }
    }
    else
        cvTsTranspose( tmp, dst );

    cvReleaseMat( &wave );
    cvReleaseMat( &tmp );
}


static CvMat* cvTsInitDCTWave( int n, int inv )
{
    int i, k;
    double angle = CV_PI*0.5/n;
    CvMat* wave = cvCreateMat( n, n, CV_64FC1 );

    double scale = sqrt(1./n);
    for( k = 0; k < n; k++ )
        wave->data.db[k] = scale;
    scale *= sqrt(2.);
    for( i = 1; i < n; i++ )
        for( k = 0; k < n; k++ )
            wave->data.db[i*n + k] = scale*cos( angle*i*(2*k + 1) );

    if( inv )
        cvTsTranspose( wave, wave );

    return wave;
}


static void cvTsDCT_1D( const CvMat* _src, CvMat* _dst, int flags, CvMat* wave=0 )
{
    int i, j, n = _dst->cols + _dst->rows - 1;
    const CvMat* wave0 = wave;
    assert( _src->cols + _src->rows - 1 == n);
    int src_step = 1, dst_step = 1;
    double* w;

    assert( CV_ARE_TYPES_EQ(_src,_dst) && _src->rows*_src->cols == _dst->rows*_dst->cols );

    if( !wave )
        wave = cvTsInitDCTWave( n, flags & CV_DXT_INVERSE );
    w = wave->data.db;

    if( !CV_IS_MAT_CONT(_src->type) )
        src_step = _src->step/CV_ELEM_SIZE(_src->type);
    if( !CV_IS_MAT_CONT(_dst->type) )
        dst_step = _dst->step/CV_ELEM_SIZE(_dst->type);

    if( CV_MAT_TYPE(_src->type) == CV_32FC1 )
    {
        float *dst = _dst->data.fl;

        for( i = 0; i < n; i++, dst += dst_step )
        {
            const float* src = _src->data.fl;
            double sum = 0;

            for( j = 0; j < n; j++, src += src_step )
                sum += src[0]*w[j];
            w += n;
            dst[0] = (float)sum;
        }
    }
    else if( CV_MAT_TYPE(_src->type) == CV_64FC1 )
    {
        double *dst = _dst->data.db;

        for( i = 0; i < n; i++, dst += dst_step )
        {
            const double* src = _src->data.db;
            double sum = 0;

            for( j = 0; j < n; j++, src += src_step )
                sum += src[0]*w[j];
            w += n;
            dst[0] = sum;
        }
    }
    else
        assert(0);

    if( !wave0 )
        cvReleaseMat( &wave );
}


static void cvTsDCT_2D( const CvMat* src, CvMat* dst, int flags )
{
    int i;
    CvMat* tmp = cvCreateMat( dst->cols, dst->rows, dst->type );
    CvMat* wave = cvTsInitDCTWave( dst->cols, flags & CV_DXT_INVERSE );

    // 1. row-wise transform
    for( i = 0; i < dst->rows; i++ )
    {
        CvMat src_row, dst_row;
        cvGetRow( src, &src_row, i );
        cvGetCol( tmp, &dst_row, i );
        cvTsDCT_1D( &src_row, &dst_row, flags, wave );
    }

    if( !(flags & CV_DXT_ROWS) )
    {
        if( dst->cols != dst->rows )
        {
            cvReleaseMat( &wave );
            wave = cvTsInitDCTWave( dst->rows, flags & CV_DXT_INVERSE );
        }

        // 2. column-wise transform
        for( i = 0; i < dst->cols; i++ )
        {
            CvMat src_row, dst_row;
            cvGetRow( tmp, &src_row, i );
            cvGetCol( dst, &dst_row, i );
            cvTsDCT_1D( &src_row, &dst_row, flags, wave );
        }
    }
    else
    {
        cvTranspose( tmp, dst );
    }

    cvReleaseMat( &wave );
    cvReleaseMat( &tmp );
}


static void cvTsConvertFromCCS( const CvMat* _src0, const CvMat* _src1,
                                CvMat* _dst, int flags )
{
    if( _dst->rows > 1 && (_dst->cols > 1 || (flags & CV_DXT_ROWS)) )
    {
        int i, count = _dst->rows, len = _dst->cols;
        int is_2d = (flags & CV_DXT_ROWS) == 0;
        CvMat src0_row, src1_row, dst_row;
        for( i = 0; i < count; i++ )
        {
            int j = !is_2d || i == 0 ? i : count - i;
            cvGetRow( _src0, &src0_row, i );
            cvGetRow( _src1, &src1_row, j );
            cvGetRow( _dst, &dst_row, i );
            cvTsConvertFromCCS( &src0_row, &src1_row, &dst_row, 0 );
        }

        if( is_2d )
        {
            cvGetCol( _src0, &src0_row, 0 );
            cvGetCol( _dst, &dst_row, 0 );
            cvTsConvertFromCCS( &src0_row, &src0_row, &dst_row, 0 );
            if( (len & 1) == 0 )
            {
                cvGetCol( _src0, &src0_row, _src0->cols - 1 );
                cvGetCol( _dst, &dst_row, len/2 );
                cvTsConvertFromCCS( &src0_row, &src0_row, &dst_row, 0 );
            }
        }
    }
    else
    {
        int i, n = _dst->cols + _dst->rows - 1, n2 = (n+1) >> 1;
        int cn = CV_MAT_CN(_src0->type);
        int src_step = cn, dst_step = 1;

        if( !CV_IS_MAT_CONT(_dst->type) )
            dst_step = _dst->step/CV_ELEM_SIZE(_dst->type);

        if( !CV_IS_MAT_CONT(_src0->type) )
            src_step = _src0->step/CV_ELEM_SIZE(_src0->type & CV_MAT_DEPTH_MASK);

        if( CV_MAT_DEPTH(_dst->type) == CV_32F )
        {
            CvTsComplex32f* dst = (CvTsComplex32f*)_dst->data.fl;
            const float* src0 = _src0->data.fl;
            const float* src1 = _src1->data.fl;
            int delta0, delta1;

            dst->re = src0[0];
            dst->im = 0;

            if( (n & 1) == 0 )
            {
                dst[n2*dst_step].re = src0[(cn == 1 ? n-1 : n2)*src_step];
                dst[n2*dst_step].im = 0;
            }

            delta0 = src_step;
            delta1 = delta0 + (cn == 1 ? src_step : 1);
            if( cn == 1 )
                src_step *= 2;

            for( i = 1; i < n2; i++, delta0 += src_step, delta1 += src_step )
            {
                float t0 = src0[delta0];
                float t1 = src0[delta1];

                dst[i*dst_step].re = t0;
                dst[i*dst_step].im = t1;

                t0 = src1[delta0];
                t1 = -src1[delta1];

                dst[(n-i)*dst_step].re = t0;
                dst[(n-i)*dst_step].im = t1;
            }
        }
        else
        {
            CvTsComplex64f* dst = (CvTsComplex64f*)_dst->data.db;
            const double* src0 = _src0->data.db;
            const double* src1 = _src1->data.db;
            int delta0, delta1;

            dst->re = src0[0];
            dst->im = 0;

            if( (n & 1) == 0 )
            {
                dst[n2*dst_step].re = src0[(cn == 1 ? n-1 : n2)*src_step];
                dst[n2*dst_step].im = 0;
            }

            delta0 = src_step;
            delta1 = delta0 + (cn == 1 ? src_step : 1);
            if( cn == 1 )
                src_step *= 2;

            for( i = 1; i < n2; i++, delta0 += src_step, delta1 += src_step )
            {
                double t0 = src0[delta0];
                double t1 = src0[delta1];

                dst[i*dst_step].re = t0;
                dst[i*dst_step].im = t1;

                t0 = src1[delta0];
                t1 = -src1[delta1];

                dst[(n-i)*dst_step].re = t0;
                dst[(n-i)*dst_step].im = t1;
            }
        }
    }
}


static void cvTsFixCCS( CvMat* mat, int cols, int flags )
{
    int i, rows = mat->rows;
    int rows2 = flags & CV_DXT_ROWS ? rows : rows/2 + 1, cols2 = cols/2 + 1;

    assert( cols2 == mat->cols );

    if( CV_MAT_TYPE(mat->type) == CV_32FC2 )
    {
        for( i = 0; i < rows2; i++ )
        {
            CvTsComplex32f* row = (CvTsComplex32f*)(mat->data.ptr + mat->step*i);
            if( (flags & CV_DXT_ROWS) || i == 0 || (i == rows2 - 1 && rows % 2 == 0) )
            {
                row[0].im = 0;
                if( cols % 2 == 0 )
                    row[cols2-1].im = 0;
            }
            else
            {
                CvTsComplex32f* row2 = (CvTsComplex32f*)(mat->data.ptr + mat->step*(rows-i));
                row2[0].re = row[0].re;
                row2[0].im = -row[0].im;

                if( cols % 2 == 0 )
                {
                    row2[cols2-1].re = row[cols2-1].re;
                    row2[cols2-1].im = -row[cols2-1].im;
                }
            }
        }
    }
    else if( CV_MAT_TYPE(mat->type) == CV_64FC2 )
    {
        for( i = 0; i < rows2; i++ )
        {
            CvTsComplex64f* row = (CvTsComplex64f*)(mat->data.ptr + mat->step*i);
            if( (flags & CV_DXT_ROWS) || i == 0 || (i == rows2 - 1 && rows % 2 == 0) )
            {
                row[0].im = 0;
                if( cols % 2 == 0 )
                    row[cols2-1].im = 0;
            }
            else
            {
                CvTsComplex64f* row2 = (CvTsComplex64f*)(mat->data.ptr + mat->step*(rows-i));
                row2[0].re = row[0].re;
                row2[0].im = -row[0].im;

                if( cols % 2 == 0 )
                {
                    row2[cols2-1].re = row[cols2-1].re;
                    row2[cols2-1].im = -row[cols2-1].im;
                }
            }
        }
    }
}


static const CvSize dxt_sizes[] = {{16,1}, {256,1}, {1024,1}, {65536,1},
    {10,1}, {100,1}, {1000,1}, {100000,1}, {256, 256}, {1024,1024}, {-1,-1}};
static const int dxt_depths[] = { CV_32F, CV_64F, -1 };
static const char* dxt_param_names[] = { "size", "depth", "transform_type", 0 };
static const char* dft_transforms[] = { "Fwd_CToC", "Inv_CToC", "Fwd_RToPack", "Inv_PackToR", 0 };
static const char* mulsp_transforms[] = { "Fwd_CToC", "Fwd_RToPack", 0 };
static const char* dct_transforms[] = { "Fwd", "Inv", 0 };

class CxCore_DXTBaseTestImpl : public CvArrTest
{
public:
    CxCore_DXTBaseTestImpl( const char* test_name, const char* test_funcs,
                        bool _allow_complex=false, bool _allow_odd=false,
                        bool _spectrum_mode=false );
protected:
    void get_test_array_types_and_sizes( int test_case_idx, CvSize** sizes, int** types );
    int prepare_test_case( int test_case_idx );
    double get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ );
    void get_timing_test_array_types_and_sizes( int test_case_idx,
                                                CvSize** sizes, int** types,
                                                CvSize** whole_sizes, bool* are_images );
    void print_timing_params( int test_case_idx, char* ptr, int params_left );
    int write_default_params( CvFileStorage* fs );
    int flags; // transformation flags
    bool allow_complex, // whether input/output may be complex or not:
                        // true for DFT and MulSpectrums, false for DCT
         allow_odd,     // whether input/output may be have odd (!=1) dimensions:
                        // true for DFT and MulSpectrums, false for DCT
         spectrum_mode, // (2 complex/ccs inputs, 1 complex/ccs output):
                        // true for MulSpectrums, false for DFT and DCT
         inplace,       // inplace operation (set for each individual test case)
         temp_dst;      // use temporary destination (for real->ccs DFT and ccs MulSpectrums)
    const char** transform_type_list;
};


CxCore_DXTBaseTestImpl::CxCore_DXTBaseTestImpl( const char* test_name, const char* test_funcs,
                                        bool _allow_complex, bool _allow_odd, bool _spectrum_mode )
    : CvArrTest( test_name, test_funcs, "" ),
    flags(0), allow_complex(_allow_complex), allow_odd(_allow_odd),
    spectrum_mode(_spectrum_mode), inplace(false), temp_dst(false)
{
    test_array[INPUT].push(NULL);
    if( spectrum_mode )
        test_array[INPUT].push(NULL);
    test_array[OUTPUT].push(NULL);
    test_array[REF_OUTPUT].push(NULL);
    test_array[TEMP].push(NULL);
    test_array[TEMP].push(NULL);

    max_log_array_size = 9;
    element_wise_relative_error = spectrum_mode;

    size_list = (CvSize*)dxt_sizes;
    whole_size_list = 0;
    depth_list = (int*)dxt_depths;
    cn_list = 0;
    transform_type_list = 0;
}


void CxCore_DXTBaseTestImpl::get_test_array_types_and_sizes( int test_case_idx,
                                                CvSize** sizes, int** types )
{
    CvRNG* rng = ts->get_rng();
    int bits = cvTsRandInt(rng);
    int depth = cvTsRandInt(rng)%2 + CV_32F;
    int cn = !allow_complex || !(bits & 256) ? 1 : 2;
    CvSize size;
    CvArrTest::get_test_array_types_and_sizes( test_case_idx, sizes, types );

    flags = bits & (CV_DXT_INVERSE | CV_DXT_SCALE | CV_DXT_ROWS | CV_DXT_MUL_CONJ);
    if( spectrum_mode )
        flags &= ~CV_DXT_INVERSE;
    types[TEMP][0] = types[TEMP][1] = types[INPUT][0] =
        types[OUTPUT][0] = CV_MAKETYPE(depth, cn);
    size = sizes[INPUT][0];

    //size.width = size.width % 10 + 1;
    //size.height = size.width % 10 + 1;
    //size.width = 1;
    //flags &= ~CV_DXT_ROWS;
    temp_dst = false;

    if( flags & CV_DXT_ROWS && (bits&1024) )
    {
        if( bits&16 )
            size.width = 1;
        else
            size.height = 1;
        flags &= ~CV_DXT_ROWS;
    }

    const int P2_MIN_SIZE = 32;
    if( ((bits >> 10) & 1) == 0 )
    {
        size.width = (size.width / P2_MIN_SIZE)*P2_MIN_SIZE;
        size.width = MAX(size.width, 1);
        size.height = (size.height / P2_MIN_SIZE)*P2_MIN_SIZE;
        size.height = MAX(size.height, 1);
    }

    if( !allow_odd )
    {
        if( size.width > 1 && (size.width&1) != 0 )
            size.width = (size.width + 1) & -2;

        if( size.height > 1 && (size.height&1) != 0 && !(flags & CV_DXT_ROWS) )
            size.height = (size.height + 1) & -2;
    }

    sizes[INPUT][0] = sizes[OUTPUT][0] = size;
    sizes[TEMP][0] = sizes[TEMP][1] = cvSize(0,0);

    if( spectrum_mode )
    {
        if( cn == 1 )
        {
            types[OUTPUT][0] = depth + 8;
            sizes[TEMP][0] = size;
        }
        sizes[INPUT][0] = sizes[INPUT][1] = size;
        types[INPUT][1] = types[INPUT][0];
    }
    else if( /*(cn == 2 && (bits&32)) ||*/ (cn == 1 && allow_complex) )
    {
        types[TEMP][0] = depth + 8; // CV_??FC2
        sizes[TEMP][0] = size;
        size = cvSize(size.width/2+1, size.height);

        if( flags & CV_DXT_INVERSE )
        {
            if( cn == 2 )
            {
                types[OUTPUT][0] = depth;
                sizes[INPUT][0] = size;
            }
            types[TEMP][1] = types[TEMP][0];
            sizes[TEMP][1] = sizes[TEMP][0];
        }
        else
        {
            if( allow_complex )
                types[OUTPUT][0] = depth + 8;

            if( cn == 2 )
            {
                types[INPUT][0] = depth;
                types[TEMP][1] = types[TEMP][0];
                sizes[TEMP][1] = size;
            }
            else
            {
                types[TEMP][1] = depth;
                sizes[TEMP][1] = sizes[TEMP][0];
            }
            temp_dst = true;
        }
    }

    inplace = false;
    if( spectrum_mode ||
        (!temp_dst && types[INPUT][0] == types[OUTPUT][0]) ||
        (temp_dst && types[INPUT][0] == types[TEMP][1]) )
        inplace = (bits & 64) != 0;

    types[REF_OUTPUT][0] = types[OUTPUT][0];
    sizes[REF_OUTPUT][0] = sizes[OUTPUT][0];
}


void CxCore_DXTBaseTestImpl::get_timing_test_array_types_and_sizes( int test_case_idx,
                                                    CvSize** sizes, int** types,
                                                    CvSize** whole_sizes, bool* are_images )
{
    CvArrTest::get_timing_test_array_types_and_sizes( test_case_idx,
                            sizes, types, whole_sizes, are_images );
    const char* transform_type = cvReadString( find_timing_param( "transform_type" ), "" );
    int depth = CV_MAT_DEPTH(types[INPUT][0]);
    int in_type = depth, out_type = depth;

    if( strcmp( transform_type, "Fwd_CToC" ) == 0 || strcmp( transform_type, "Inv_CToC" ) == 0 )
        in_type = out_type = CV_MAKETYPE(depth,2);

    if( strncmp( transform_type, "Fwd", 3 ) == 0 )
        flags = CV_DXT_FORWARD;
    else
        flags = CV_DXT_INV_SCALE;

    types[INPUT][0] = in_type;
    if( spectrum_mode )
        types[INPUT][1] = in_type;
    types[OUTPUT][0] = types[REF_OUTPUT][0] = out_type;
    sizes[TEMP][0] = cvSize(0,0);

    inplace = false;
}


int CxCore_DXTBaseTestImpl::write_default_params( CvFileStorage* fs )
{
    int code = CvArrTest::write_default_params(fs);
    if( code < 0 || ts->get_testing_mode() != CvTS::TIMING_MODE )
        return code;
    write_string_list( fs, "transform_type", transform_type_list );
    return code;
}


void CxCore_DXTBaseTestImpl::print_timing_params( int test_case_idx, char* ptr, int params_left )
{
    sprintf( ptr, "%s,", cvReadString( find_timing_param("transform_type"), "" ) );
    ptr += strlen(ptr);
    params_left--;
    CvArrTest::print_timing_params( test_case_idx, ptr, params_left );
}


double CxCore_DXTBaseTestImpl::get_success_error_level( int test_case_idx, int i, int j )
{
    return CvArrTest::get_success_error_level( test_case_idx, i, j );
}


int CxCore_DXTBaseTestImpl::prepare_test_case( int test_case_idx )
{
    int code = CvArrTest::prepare_test_case( test_case_idx );
    if( code > 0 && ts->get_testing_mode() == CvTS::CORRECTNESS_CHECK_MODE )
    {
        int in_type = CV_MAT_TYPE(test_mat[INPUT][0].type);
        int out_type = CV_MAT_TYPE(test_mat[OUTPUT][0].type);

        if( CV_MAT_CN(in_type) == 2 && CV_MAT_CN(out_type) == 1 )
            cvTsFixCCS( &test_mat[INPUT][0], test_mat[OUTPUT][0].cols, flags );

        if( inplace )
            cvTsCopy( &test_mat[INPUT][test_case_idx & (int)spectrum_mode],
                temp_dst ? &test_mat[TEMP][1] :
                in_type == out_type ? &test_mat[OUTPUT][0] :
                &test_mat[TEMP][0] );
    }

    return code;
}

CxCore_DXTBaseTestImpl dxt_test( "dxt", "" );


class CxCore_DXTBaseTest : public CxCore_DXTBaseTestImpl
{
public:
    CxCore_DXTBaseTest( const char* test_name, const char* test_funcs,
                        bool _allow_complex=false, bool _allow_odd=false,
                        bool _spectrum_mode=false );
};

CxCore_DXTBaseTest::CxCore_DXTBaseTest( const char* test_name, const char* test_funcs,
                                        bool _allow_complex, bool _allow_odd, bool _spectrum_mode )
    : CxCore_DXTBaseTestImpl( test_name, test_funcs, _allow_complex, _allow_odd, _spectrum_mode )
{
    size_list = 0;
    depth_list = 0;
    default_timing_param_names = dxt_param_names;
    transform_type_list = dft_transforms;
}


////////////////////// FFT ////////////////////////
class CxCore_DFTTest : public CxCore_DXTBaseTest
{
public:
    CxCore_DFTTest();
protected:
    void run_func();
    void prepare_to_validation( int test_case_idx );
};


CxCore_DFTTest::CxCore_DFTTest() : CxCore_DXTBaseTest( "dxt-dft", "cvDFT", true, true, false )
{
}


void CxCore_DFTTest::run_func()
{
    CvArr* dst = temp_dst ? test_array[TEMP][1] : test_array[OUTPUT][0];
    CvArr* src = inplace ? dst : test_array[INPUT][0];

    if(!(flags & CV_DXT_INVERSE))
        cvDFT( src, dst, flags );
    else
    {
        cv::Mat _src = cv::cvarrToMat(src), _dst = cv::cvarrToMat(dst);
        idft(_src, _dst, flags & ~CV_DXT_INVERSE);
    }
}


void CxCore_DFTTest::prepare_to_validation( int /*test_case_idx*/ )
{
    CvMat* src = &test_mat[INPUT][0];
    CvMat* dst = &test_mat[REF_OUTPUT][0];
    CvMat* tmp_src = src;
    CvMat* tmp_dst = dst;
    int src_cn = CV_MAT_CN( src->type );
    int dst_cn = CV_MAT_CN( dst->type );

    if( src_cn != 2 || dst_cn != 2 )
    {
        tmp_src = &test_mat[TEMP][0];

        if( !(flags & CV_DXT_INVERSE ) )
        {
            CvMat* cvdft_dst = &test_mat[TEMP][1];
            cvTsConvertFromCCS( cvdft_dst, cvdft_dst,
                                &test_mat[OUTPUT][0], flags );
            cvTsZero( tmp_src );
            cvTsInsert( src, tmp_src, 0 );
        }
        else
        {
            cvTsConvertFromCCS( src, src, tmp_src, flags );
            tmp_dst = &test_mat[TEMP][1];
        }
    }

    if( src->rows == 1 || (src->cols == 1 && !(flags & CV_DXT_ROWS)) )
        cvTsDFT_1D( tmp_src, tmp_dst, flags );
    else
        cvTsDFT_2D( tmp_src, tmp_dst, flags );

    if( tmp_dst != dst )
        cvTsExtract( tmp_dst, dst, 0 );
}


CxCore_DFTTest dft_test;


////////////////////// DCT ////////////////////////
class CxCore_DCTTest : public CxCore_DXTBaseTest
{
public:
    CxCore_DCTTest();
protected:
    void run_func();
    void prepare_to_validation( int test_case_idx );
};


CxCore_DCTTest::CxCore_DCTTest() : CxCore_DXTBaseTest( "dxt-dct", "cvDCT", false, false, false )
{
    transform_type_list = dct_transforms;
}


void CxCore_DCTTest::run_func()
{
    CvArr* dst = test_array[OUTPUT][0];
    CvArr* src = inplace ? dst : test_array[INPUT][0];

    if(!(flags & CV_DXT_INVERSE))
        cvDCT( src, dst, flags );
    else
    {
        cv::Mat _src = cv::cvarrToMat(src), _dst = cv::cvarrToMat(dst);
        idct(_src, _dst, flags & ~CV_DXT_INVERSE);
    }
}


void CxCore_DCTTest::prepare_to_validation( int /*test_case_idx*/ )
{
    CvMat* src = &test_mat[INPUT][0];
    CvMat* dst = &test_mat[REF_OUTPUT][0];

    if( src->rows == 1 || (src->cols == 1 && !(flags & CV_DXT_ROWS)) )
        cvTsDCT_1D( src, dst, flags );
    else
        cvTsDCT_2D( src, dst, flags );
}


CxCore_DCTTest dct_test;


////////////////////// MulSpectrums ////////////////////////
class CxCore_MulSpectrumsTest : public CxCore_DXTBaseTest
{
public:
    CxCore_MulSpectrumsTest();
protected:
    void run_func();
    void prepare_to_validation( int test_case_idx );
};


CxCore_MulSpectrumsTest::CxCore_MulSpectrumsTest() :
    CxCore_DXTBaseTest( "dxt-mulspectrums", "cvMulSpectrums", true, true, true )
{
    transform_type_list = mulsp_transforms;
}


void CxCore_MulSpectrumsTest::run_func()
{
    CvArr* dst = test_array[TEMP].size() > 0 && test_array[TEMP][0] ?
                 test_array[TEMP][0] : test_array[OUTPUT][0];
    CvArr *src1 = test_array[INPUT][0], *src2 = test_array[INPUT][1];

    if( inplace )
    {
        if( ts->get_current_test_info()->test_case_idx & 1 )
            src2 = dst;
        else
            src1 = dst;
    }

    cvMulSpectrums( src1, src2, dst, flags );
}


static void cvTsMulComplex( const CvMat* A, const CvMat* B, CvMat* C, int flags )
{
    int i, j, depth = CV_MAT_DEPTH(A->type), cols = A->cols*2;

    assert( CV_ARE_SIZES_EQ(A,B) && CV_ARE_SIZES_EQ(B,C) &&
            CV_ARE_TYPES_EQ(A,B) && CV_ARE_TYPES_EQ(B,C) &&
            CV_MAT_CN(A->type) == 2 && CV_MAT_DEPTH(A->type) >= CV_32F );

    for( i = 0; i < C->rows; i++ )
    {
        if( depth == CV_32F )
        {
            const float* a = (float*)(A->data.ptr + A->step*i);
            const float* b = (float*)(B->data.ptr + B->step*i);
            float* c = (float*)(C->data.ptr + C->step*i);

            if( !(flags & CV_DXT_MUL_CONJ) )
                for( j = 0; j < cols; j += 2 )
                {
                    double re = (double)a[j]*b[j] - (double)a[j+1]*b[j+1];
                    double im = (double)a[j+1]*b[j] + (double)a[j]*b[j+1];

                    c[j] = (float)re;
                    c[j+1] = (float)im;
                }
            else
                for( j = 0; j < cols; j += 2 )
                {
                    double re = (double)a[j]*b[j] + (double)a[j+1]*b[j+1];
                    double im = (double)a[j+1]*b[j] - (double)a[j]*b[j+1];

                    c[j] = (float)re;
                    c[j+1] = (float)im;
                }
        }
        else
        {
            const double* a = (double*)(A->data.ptr + A->step*i);
            const double* b = (double*)(B->data.ptr + B->step*i);
            double* c = (double*)(C->data.ptr + C->step*i);

            if( !(flags & CV_DXT_MUL_CONJ) )
                for( j = 0; j < cols; j += 2 )
                {
                    double re = a[j]*b[j] - a[j+1]*b[j+1];
                    double im = a[j+1]*b[j] + a[j]*b[j+1];

                    c[j] = re;
                    c[j+1] = im;
                }
            else
                for( j = 0; j < cols; j += 2 )
                {
                    double re = a[j]*b[j] + a[j+1]*b[j+1];
                    double im = a[j+1]*b[j] - a[j]*b[j+1];

                    c[j] = re;
                    c[j+1] = im;
                }
        }
    }
}


void CxCore_MulSpectrumsTest::prepare_to_validation( int /*test_case_idx*/ )
{
    CvMat* src1 = &test_mat[INPUT][0];
    CvMat* src2 = &test_mat[INPUT][1];
    CvMat* dst = &test_mat[OUTPUT][0];
    CvMat* dst0 = &test_mat[REF_OUTPUT][0];
    CvMat* temp = test_array[TEMP].size() > 0 && test_array[TEMP][0] ? &test_mat[TEMP][0] : 0;
    int cn = CV_MAT_CN(src1->type);

    if( cn == 1 )
    {
        cvTsConvertFromCCS( src1, src1, dst, flags );
        cvTsConvertFromCCS( src2, src2, dst0, flags );
        src1 = dst;
        src2 = dst0;
    }

    cvTsMulComplex( src1, src2, dst0, flags );
    if( cn == 1 )
    {
        assert( temp != 0 );
        cvTsConvertFromCCS( temp, temp, dst, flags );
    }
}


CxCore_MulSpectrumsTest mulspectrums_test;


/* End of file. */
