root/modules/core/test/test_arithm.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. context
  2. op
  3. refop
  4. getValueRange
  5. getRandomSize
  6. getRandomType
  7. getMaxErr
  8. generateScalars
  9. refop
  10. op
  11. op
  12. op
  13. op
  14. op
  15. getMaxErr
  16. op
  17. getMaxErr
  18. getValueRange
  19. op
  20. refop
  21. getMaxErr
  22. op
  23. refop
  24. getMaxErr
  25. op
  26. refop
  27. getMaxErr
  28. op
  29. refop
  30. op
  31. refop
  32. op
  33. refop
  34. getMaxErr
  35. all
  36. op
  37. refop
  38. getMaxErr
  39. op
  40. refop
  41. getMaxErr
  42. op
  43. refop
  44. getMaxErr
  45. op
  46. refop
  47. getMaxErr
  48. op
  49. refop
  50. getMaxErr
  51. generateScalars
  52. op
  53. refop
  54. getRandomType
  55. getMaxErr
  56. generateScalars
  57. op
  58. refop
  59. getRandomType
  60. getMaxErr
  61. op
  62. refop
  63. getRandomType
  64. getMaxErr
  65. op
  66. refop
  67. getRandomType
  68. getMaxErr
  69. inRangeS_
  70. inRange_
  71. inRange
  72. inRangeS
  73. op
  74. refop
  75. getMaxErr
  76. generateScalars
  77. op
  78. refop
  79. getMaxErr
  80. ddepth
  81. op
  82. refop
  83. getRandomType
  84. getMaxErr
  85. generateScalars
  86. op
  87. refop
  88. getMaxErr
  89. generateScalars
  90. flip
  91. setIdentity
  92. getRandomSize
  93. op
  94. refop
  95. generateScalars
  96. getMaxErr
  97. getRandomSize
  98. op
  99. refop
  100. getMaxErr
  101. getRandomSize
  102. op
  103. refop
  104. getMaxErr
  105. op
  106. refop
  107. getMaxErr
  108. exp
  109. log
  110. getRandomType
  111. getValueRange
  112. op
  113. refop
  114. getMaxErr
  115. getRandomType
  116. getValueRange
  117. op
  118. refop
  119. getMaxErr
  120. cartToPolar
  121. getRandomType
  122. op
  123. refop
  124. generateScalars
  125. getMaxErr
  126. op
  127. refop
  128. getMaxErr
  129. op
  130. refop
  131. getMaxErr
  132. getRandomType
  133. op
  134. refop
  135. getMaxErr
  136. op
  137. refop
  138. getMaxErr
  139. getRandomType
  140. op
  141. refop
  142. generateScalars
  143. getMaxErr
  144. getRandomType
  145. saveOutput
  146. op
  147. refop
  148. getMaxErr
  149. TEST_P
  150. run
  151. TEST
  152. TEST
  153. TEST
  154. TEST
  155. TEST
  156. TEST_P
  157. SetUp
  158. TEST_P
  159. TEST_P
  160. TEST_P
  161. TEST_P
  162. TEST_P
  163. TEST_P
  164. TEST_P
  165. TEST_P
  166. TEST
  167. TEST

#include "test_precomp.hpp"

using namespace cv;
using namespace std;

namespace cvtest
{

const int ARITHM_NTESTS = 1000;
const int ARITHM_RNG_SEED = -1;
const int ARITHM_MAX_CHANNELS = 4;
const int ARITHM_MAX_NDIMS = 4;
const int ARITHM_MAX_SIZE_LOG = 10;

struct BaseElemWiseOp
{
    enum { FIX_ALPHA=1, FIX_BETA=2, FIX_GAMMA=4, REAL_GAMMA=8, SUPPORT_MASK=16, SCALAR_OUTPUT=32 };
    BaseElemWiseOp(int _ninputs, int _flags, double _alpha, double _beta,
                   Scalar _gamma=Scalar::all(0), int _context=1)
    : ninputs(_ninputs), flags(_flags), alpha(_alpha), beta(_beta), gamma(_gamma), context(_context) {}
    BaseElemWiseOp() { flags = 0; alpha = beta = 0; gamma = Scalar::all(0); ninputs = 0; context = 1; }
    virtual ~BaseElemWiseOp() {}
    virtual void op(const vector<Mat>&, Mat&, const Mat&) {}
    virtual void refop(const vector<Mat>&, Mat&, const Mat&) {}
    virtual void getValueRange(int depth, double& minval, double& maxval)
    {
        minval = depth < CV_32S ? cvtest::getMinVal(depth) : depth == CV_32S ? -1000000 : -1000.;
        maxval = depth < CV_32S ? cvtest::getMaxVal(depth) : depth == CV_32S ? 1000000 : 1000.;
    }

    virtual void getRandomSize(RNG& rng, vector<int>& size)
    {
        cvtest::randomSize(rng, 2, ARITHM_MAX_NDIMS, cvtest::ARITHM_MAX_SIZE_LOG, size);
    }

    virtual int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL_BUT_8S, 1,
                                  ninputs > 1 ? ARITHM_MAX_CHANNELS : 4);
    }

    virtual double getMaxErr(int depth) { return depth < CV_32F ? 1 : depth == CV_32F ? 1e-5 : 1e-12; }
    virtual void generateScalars(int depth, RNG& rng)
    {
        const double m = 3.;

        if( !(flags & FIX_ALPHA) )
        {
            alpha = exp(rng.uniform(-0.5, 0.1)*m*2*CV_LOG2);
            alpha *= rng.uniform(0, 2) ? 1 : -1;
        }
        if( !(flags & FIX_BETA) )
        {
            beta = exp(rng.uniform(-0.5, 0.1)*m*2*CV_LOG2);
            beta *= rng.uniform(0, 2) ? 1 : -1;
        }

        if( !(flags & FIX_GAMMA) )
        {
            for( int i = 0; i < 4; i++ )
            {
                gamma[i] = exp(rng.uniform(-1, 6)*m*CV_LOG2);
                gamma[i] *= rng.uniform(0, 2) ? 1 : -1;
            }
            if( flags & REAL_GAMMA )
                gamma = Scalar::all(gamma[0]);
        }

        if( depth == CV_32F )
        {
            Mat fl, db;

            db = Mat(1, 1, CV_64F, &alpha);
            db.convertTo(fl, CV_32F);
            fl.convertTo(db, CV_64F);

            db = Mat(1, 1, CV_64F, &beta);
            db.convertTo(fl, CV_32F);
            fl.convertTo(db, CV_64F);

            db = Mat(1, 4, CV_64F, &gamma[0]);
            db.convertTo(fl, CV_32F);
            fl.convertTo(db, CV_64F);
        }
    }

    int ninputs;
    int flags;
    double alpha;
    double beta;
    Scalar gamma;
    int context;
};


struct BaseAddOp : public BaseElemWiseOp
{
    BaseAddOp(int _ninputs, int _flags, double _alpha, double _beta, Scalar _gamma=Scalar::all(0))
    : BaseElemWiseOp(_ninputs, _flags, _alpha, _beta, _gamma) {}

    void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        Mat temp;
        if( !mask.empty() )
        {
            cvtest::add(src[0], alpha, src.size() > 1 ? src[1] : Mat(), beta, gamma, temp, src[0].type());
            cvtest::copy(temp, dst, mask);
        }
        else
            cvtest::add(src[0], alpha, src.size() > 1 ? src[1] : Mat(), beta, gamma, dst, src[0].type());
    }
};


struct AddOp : public BaseAddOp
{
    AddOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        if( mask.empty() )
            add(src[0], src[1], dst);
        else
            add(src[0], src[1], dst, mask);
    }
};


struct SubOp : public BaseAddOp
{
    SubOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, -1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        if( mask.empty() )
            subtract(src[0], src[1], dst);
        else
            subtract(src[0], src[1], dst, mask);
    }
};


struct AddSOp : public BaseAddOp
{
    AddSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 0, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        if( mask.empty() )
            add(src[0], gamma, dst);
        else
            add(src[0], gamma, dst, mask);
    }
};


struct SubRSOp : public BaseAddOp
{
    SubRSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, -1, 0, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        if( mask.empty() )
            subtract(gamma, src[0], dst);
        else
            subtract(gamma, src[0], dst, mask);
    }
};


struct ScaleAddOp : public BaseAddOp
{
    ScaleAddOp() : BaseAddOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        scaleAdd(src[0], alpha, src[1], dst);
    }
    double getMaxErr(int depth)
    {
        return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-4 : 1e-12;
    }
};


struct AddWeightedOp : public BaseAddOp
{
    AddWeightedOp() : BaseAddOp(2, REAL_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        addWeighted(src[0], alpha, src[1], beta, gamma[0], dst);
    }
    double getMaxErr(int depth)
    {
        return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-10;
    }
};

struct MulOp : public BaseElemWiseOp
{
    MulOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    void getValueRange(int depth, double& minval, double& maxval)
    {
        minval = depth < CV_32S ? cvtest::getMinVal(depth) : depth == CV_32S ? -1000000 : -1000.;
        maxval = depth < CV_32S ? cvtest::getMaxVal(depth) : depth == CV_32S ? 1000000 : 1000.;
        minval = std::max(minval, -30000.);
        maxval = std::min(maxval, 30000.);
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::multiply(src[0], src[1], dst, alpha);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::multiply(src[0], src[1], dst, alpha);
    }
    double getMaxErr(int depth)
    {
        return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-12;
    }
};

struct DivOp : public BaseElemWiseOp
{
    DivOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::divide(src[0], src[1], dst, alpha);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::divide(src[0], src[1], dst, alpha);
    }
    double getMaxErr(int depth)
    {
        return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-12;
    }
};

struct RecipOp : public BaseElemWiseOp
{
    RecipOp() : BaseElemWiseOp(1, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::divide(alpha, src[0], dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::divide(Mat(), src[0], dst, alpha);
    }
    double getMaxErr(int depth)
    {
        return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-12;
    }
};

struct AbsDiffOp : public BaseAddOp
{
    AbsDiffOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, -1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        absdiff(src[0], src[1], dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::add(src[0], 1, src[1], -1, Scalar::all(0), dst, src[0].type(), true);
    }
};

struct AbsDiffSOp : public BaseAddOp
{
    AbsDiffSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA, 1, 0, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        absdiff(src[0], gamma, dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::add(src[0], 1, Mat(), 0, -gamma, dst, src[0].type(), true);
    }
};

struct LogicOp : public BaseElemWiseOp
{
    LogicOp(char _opcode) : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)), opcode(_opcode) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        if( opcode == '&' )
            bitwise_and(src[0], src[1], dst, mask);
        else if( opcode == '|' )
            bitwise_or(src[0], src[1], dst, mask);
        else
            bitwise_xor(src[0], src[1], dst, mask);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        Mat temp;
        if( !mask.empty() )
        {
            cvtest::logicOp(src[0], src[1], temp, opcode);
            cvtest::copy(temp, dst, mask);
        }
        else
            cvtest::logicOp(src[0], src[1], dst, opcode);
    }
    double getMaxErr(int)
    {
        return 0;
    }
    char opcode;
};

struct LogicSOp : public BaseElemWiseOp
{
    LogicSOp(char _opcode)
    : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+(_opcode != '~' ? SUPPORT_MASK : 0), 1, 1, Scalar::all(0)), opcode(_opcode) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        if( opcode == '&' )
            bitwise_and(src[0], gamma, dst, mask);
        else if( opcode == '|' )
            bitwise_or(src[0], gamma, dst, mask);
        else if( opcode == '^' )
            bitwise_xor(src[0], gamma, dst, mask);
        else
            bitwise_not(src[0], dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        Mat temp;
        if( !mask.empty() )
        {
            cvtest::logicOp(src[0], gamma, temp, opcode);
            cvtest::copy(temp, dst, mask);
        }
        else
            cvtest::logicOp(src[0], gamma, dst, opcode);
    }
    double getMaxErr(int)
    {
        return 0;
    }
    char opcode;
};

struct MinOp : public BaseElemWiseOp
{
    MinOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::min(src[0], src[1], dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::min(src[0], src[1], dst);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};

struct MaxOp : public BaseElemWiseOp
{
    MaxOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::max(src[0], src[1], dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::max(src[0], src[1], dst);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};

struct MinSOp : public BaseElemWiseOp
{
    MinSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::min(src[0], gamma[0], dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::min(src[0], gamma[0], dst);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};

struct MaxSOp : public BaseElemWiseOp
{
    MaxSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::max(src[0], gamma[0], dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::max(src[0], gamma[0], dst);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};

struct CmpOp : public BaseElemWiseOp
{
    CmpOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) { cmpop = 0; }
    void generateScalars(int depth, RNG& rng)
    {
        BaseElemWiseOp::generateScalars(depth, rng);
        cmpop = rng.uniform(0, 6);
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::compare(src[0], src[1], dst, cmpop);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::compare(src[0], src[1], dst, cmpop);
    }
    int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL_BUT_8S, 1, 1);
    }

    double getMaxErr(int)
    {
        return 0;
    }
    int cmpop;
};

struct CmpSOp : public BaseElemWiseOp
{
    CmpSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) { cmpop = 0; }
    void generateScalars(int depth, RNG& rng)
    {
        BaseElemWiseOp::generateScalars(depth, rng);
        cmpop = rng.uniform(0, 6);
        if( depth < CV_32F )
            gamma[0] = cvRound(gamma[0]);
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::compare(src[0], gamma[0], dst, cmpop);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::compare(src[0], gamma[0], dst, cmpop);
    }
    int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL_BUT_8S, 1, 1);
    }
    double getMaxErr(int)
    {
        return 0;
    }
    int cmpop;
};


struct CopyOp : public BaseElemWiseOp
{
    CopyOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {  }
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        src[0].copyTo(dst, mask);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        cvtest::copy(src[0], dst, mask);
    }
    int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL, 1, ARITHM_MAX_CHANNELS);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};


struct SetOp : public BaseElemWiseOp
{
    SetOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>&, Mat& dst, const Mat& mask)
    {
        dst.setTo(gamma, mask);
    }
    void refop(const vector<Mat>&, Mat& dst, const Mat& mask)
    {
        cvtest::set(dst, gamma, mask);
    }
    int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL, 1, ARITHM_MAX_CHANNELS);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};

template<typename _Tp, typename _WTp> static void
inRangeS_(const _Tp* src, const _WTp* a, const _WTp* b, uchar* dst, size_t total, int cn)
{
    size_t i;
    int c;
    for( i = 0; i < total; i++ )
    {
        _Tp val = src[i*cn];
        dst[i] = (a[0] <= val && val <= b[0]) ? uchar(255) : 0;
    }
    for( c = 1; c < cn; c++ )
    {
        for( i = 0; i < total; i++ )
        {
            _Tp val = src[i*cn + c];
            dst[i] = a[c] <= val && val <= b[c] ? dst[i] : 0;
        }
    }
}

template<typename _Tp> static void inRange_(const _Tp* src, const _Tp* a, const _Tp* b, uchar* dst, size_t total, int cn)
{
    size_t i;
    int c;
    for( i = 0; i < total; i++ )
    {
        _Tp val = src[i*cn];
        dst[i] = a[i*cn] <= val && val <= b[i*cn] ? 255 : 0;
    }
    for( c = 1; c < cn; c++ )
    {
        for( i = 0; i < total; i++ )
        {
            _Tp val = src[i*cn + c];
            dst[i] = a[i*cn + c] <= val && val <= b[i*cn + c] ? dst[i] : 0;
        }
    }
}


static void inRange(const Mat& src, const Mat& lb, const Mat& rb, Mat& dst)
{
    CV_Assert( src.type() == lb.type() && src.type() == rb.type() &&
              src.size == lb.size && src.size == rb.size );
    dst.create( src.dims, &src.size[0], CV_8U );
    const Mat *arrays[]={&src, &lb, &rb, &dst, 0};
    Mat planes[4];

    NAryMatIterator it(arrays, planes);
    size_t total = planes[0].total();
    size_t i, nplanes = it.nplanes;
    int depth = src.depth(), cn = src.channels();

    for( i = 0; i < nplanes; i++, ++it )
    {
        const uchar* sptr = planes[0].ptr();
        const uchar* aptr = planes[1].ptr();
        const uchar* bptr = planes[2].ptr();
        uchar* dptr = planes[3].ptr();

        switch( depth )
        {
        case CV_8U:
            inRange_((const uchar*)sptr, (const uchar*)aptr, (const uchar*)bptr, dptr, total, cn);
            break;
        case CV_8S:
            inRange_((const schar*)sptr, (const schar*)aptr, (const schar*)bptr, dptr, total, cn);
            break;
        case CV_16U:
            inRange_((const ushort*)sptr, (const ushort*)aptr, (const ushort*)bptr, dptr, total, cn);
            break;
        case CV_16S:
            inRange_((const short*)sptr, (const short*)aptr, (const short*)bptr, dptr, total, cn);
            break;
        case CV_32S:
            inRange_((const int*)sptr, (const int*)aptr, (const int*)bptr, dptr, total, cn);
            break;
        case CV_32F:
            inRange_((const float*)sptr, (const float*)aptr, (const float*)bptr, dptr, total, cn);
            break;
        case CV_64F:
            inRange_((const double*)sptr, (const double*)aptr, (const double*)bptr, dptr, total, cn);
            break;
        default:
            CV_Error(CV_StsUnsupportedFormat, "");
        }
    }
}


static void inRangeS(const Mat& src, const Scalar& lb, const Scalar& rb, Mat& dst)
{
    dst.create( src.dims, &src.size[0], CV_8U );
    const Mat *arrays[]={&src, &dst, 0};
    Mat planes[2];

    NAryMatIterator it(arrays, planes);
    size_t total = planes[0].total();
    size_t i, nplanes = it.nplanes;
    int depth = src.depth(), cn = src.channels();
    union { double d[4]; float f[4]; int i[4];} lbuf, rbuf;
    int wtype = CV_MAKETYPE(depth <= CV_32S ? CV_32S : depth, cn);
    scalarToRawData(lb, lbuf.d, wtype, cn);
    scalarToRawData(rb, rbuf.d, wtype, cn);

    for( i = 0; i < nplanes; i++, ++it )
    {
        const uchar* sptr = planes[0].ptr();
        uchar* dptr = planes[1].ptr();

        switch( depth )
        {
        case CV_8U:
            inRangeS_((const uchar*)sptr, lbuf.i, rbuf.i, dptr, total, cn);
            break;
        case CV_8S:
            inRangeS_((const schar*)sptr, lbuf.i, rbuf.i, dptr, total, cn);
            break;
        case CV_16U:
            inRangeS_((const ushort*)sptr, lbuf.i, rbuf.i, dptr, total, cn);
            break;
        case CV_16S:
            inRangeS_((const short*)sptr, lbuf.i, rbuf.i, dptr, total, cn);
            break;
        case CV_32S:
            inRangeS_((const int*)sptr, lbuf.i, rbuf.i, dptr, total, cn);
            break;
        case CV_32F:
            inRangeS_((const float*)sptr, lbuf.f, rbuf.f, dptr, total, cn);
            break;
        case CV_64F:
            inRangeS_((const double*)sptr, lbuf.d, rbuf.d, dptr, total, cn);
            break;
        default:
            CV_Error(CV_StsUnsupportedFormat, "");
        }
    }
}


struct InRangeSOp : public BaseElemWiseOp
{
    InRangeSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::inRange(src[0], gamma, gamma1, dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::inRangeS(src[0], gamma, gamma1, dst);
    }
    double getMaxErr(int)
    {
        return 0;
    }
    void generateScalars(int depth, RNG& rng)
    {
        BaseElemWiseOp::generateScalars(depth, rng);
        Scalar temp = gamma;
        BaseElemWiseOp::generateScalars(depth, rng);
        for( int i = 0; i < 4; i++ )
        {
            gamma1[i] = std::max(gamma[i], temp[i]);
            gamma[i] = std::min(gamma[i], temp[i]);
        }
    }
    Scalar gamma1;
};


struct InRangeOp : public BaseElemWiseOp
{
    InRangeOp() : BaseElemWiseOp(3, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        Mat lb, rb;
        cvtest::min(src[1], src[2], lb);
        cvtest::max(src[1], src[2], rb);

        cv::inRange(src[0], lb, rb, dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        Mat lb, rb;
        cvtest::min(src[1], src[2], lb);
        cvtest::max(src[1], src[2], rb);

        cvtest::inRange(src[0], lb, rb, dst);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};


struct ConvertScaleOp : public BaseElemWiseOp
{
    ConvertScaleOp() : BaseElemWiseOp(1, FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)), ddepth(0) { }
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        src[0].convertTo(dst, ddepth, alpha, gamma[0]);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::convert(src[0], dst, CV_MAKETYPE(ddepth, src[0].channels()), alpha, gamma[0]);
    }
    int getRandomType(RNG& rng)
    {
        int srctype = cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL, 1, ARITHM_MAX_CHANNELS);
        ddepth = cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL, 1, 1);
        return srctype;
    }
    double getMaxErr(int)
    {
        return ddepth <= CV_32S ? 2 : ddepth < CV_64F ? 1e-3 : 1e-12;
    }
    void generateScalars(int depth, RNG& rng)
    {
        if( rng.uniform(0, 2) )
            BaseElemWiseOp::generateScalars(depth, rng);
        else
        {
            alpha = 1;
            gamma = Scalar::all(0);
        }
    }
    int ddepth;
};


struct ConvertScaleAbsOp : public BaseElemWiseOp
{
    ConvertScaleAbsOp() : BaseElemWiseOp(1, FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::convertScaleAbs(src[0], dst, alpha, gamma[0]);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::add(src[0], alpha, Mat(), 0, Scalar::all(gamma[0]), dst, CV_8UC(src[0].channels()), true);
    }
    double getMaxErr(int)
    {
        return 1;
    }
    void generateScalars(int depth, RNG& rng)
    {
        if( rng.uniform(0, 2) )
            BaseElemWiseOp::generateScalars(depth, rng);
        else
        {
            alpha = 1;
            gamma = Scalar::all(0);
        }
    }
};


static void flip(const Mat& src, Mat& dst, int flipcode)
{
    CV_Assert(src.dims == 2);
    dst.create(src.size(), src.type());
    int i, j, k, esz = (int)src.elemSize(), width = src.cols*esz;

    for( i = 0; i < dst.rows; i++ )
    {
        const uchar* sptr = src.ptr(flipcode == 1 ? i : dst.rows - i - 1);
        uchar* dptr = dst.ptr(i);
        if( flipcode == 0 )
            memcpy(dptr, sptr, width);
        else
        {
            for( j = 0; j < width; j += esz )
                for( k = 0; k < esz; k++ )
                    dptr[j + k] = sptr[width - j - esz + k];
        }
    }
}


static void setIdentity(Mat& dst, const Scalar& s)
{
    CV_Assert( dst.dims == 2 && dst.channels() <= 4 );
    double buf[4];
    scalarToRawData(s, buf, dst.type(), 0);
    int i, k, esz = (int)dst.elemSize(), width = dst.cols*esz;

    for( i = 0; i < dst.rows; i++ )
    {
        uchar* dptr = dst.ptr(i);
        memset( dptr, 0, width );
        if( i < dst.cols )
            for( k = 0; k < esz; k++ )
                dptr[i*esz + k] = ((uchar*)buf)[k];
    }
}


struct FlipOp : public BaseElemWiseOp
{
    FlipOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) { flipcode = 0; }
    void getRandomSize(RNG& rng, vector<int>& size)
    {
        cvtest::randomSize(rng, 2, 2, cvtest::ARITHM_MAX_SIZE_LOG, size);
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::flip(src[0], dst, flipcode);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::flip(src[0], dst, flipcode);
    }
    void generateScalars(int, RNG& rng)
    {
        flipcode = rng.uniform(0, 3) - 1;
    }
    double getMaxErr(int)
    {
        return 0;
    }
    int flipcode;
};

struct TransposeOp : public BaseElemWiseOp
{
    TransposeOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    void getRandomSize(RNG& rng, vector<int>& size)
    {
        cvtest::randomSize(rng, 2, 2, cvtest::ARITHM_MAX_SIZE_LOG, size);
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::transpose(src[0], dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::transpose(src[0], dst);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};

struct SetIdentityOp : public BaseElemWiseOp
{
    SetIdentityOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA, 1, 1, Scalar::all(0)) {}
    void getRandomSize(RNG& rng, vector<int>& size)
    {
        cvtest::randomSize(rng, 2, 2, cvtest::ARITHM_MAX_SIZE_LOG, size);
    }
    void op(const vector<Mat>&, Mat& dst, const Mat&)
    {
        cv::setIdentity(dst, gamma);
    }
    void refop(const vector<Mat>&, Mat& dst, const Mat&)
    {
        cvtest::setIdentity(dst, gamma);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};

struct SetZeroOp : public BaseElemWiseOp
{
    SetZeroOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    void op(const vector<Mat>&, Mat& dst, const Mat&)
    {
        dst = Scalar::all(0);
    }
    void refop(const vector<Mat>&, Mat& dst, const Mat&)
    {
        cvtest::set(dst, Scalar::all(0));
    }
    double getMaxErr(int)
    {
        return 0;
    }
};


static void exp(const Mat& src, Mat& dst)
{
    dst.create( src.dims, &src.size[0], src.type() );
    const Mat *arrays[]={&src, &dst, 0};
    Mat planes[2];

    NAryMatIterator it(arrays, planes);
    size_t j, total = planes[0].total()*src.channels();
    size_t i, nplanes = it.nplanes;
    int depth = src.depth();

    for( i = 0; i < nplanes; i++, ++it )
    {
        const uchar* sptr = planes[0].ptr();
        uchar* dptr = planes[1].ptr();

        if( depth == CV_32F )
        {
            for( j = 0; j < total; j++ )
                ((float*)dptr)[j] = std::exp(((const float*)sptr)[j]);
        }
        else if( depth == CV_64F )
        {
            for( j = 0; j < total; j++ )
                ((double*)dptr)[j] = std::exp(((const double*)sptr)[j]);
        }
    }
}

static void log(const Mat& src, Mat& dst)
{
    dst.create( src.dims, &src.size[0], src.type() );
    const Mat *arrays[]={&src, &dst, 0};
    Mat planes[2];

    NAryMatIterator it(arrays, planes);
    size_t j, total = planes[0].total()*src.channels();
    size_t i, nplanes = it.nplanes;
    int depth = src.depth();

    for( i = 0; i < nplanes; i++, ++it )
    {
        const uchar* sptr = planes[0].ptr();
        uchar* dptr = planes[1].ptr();

        if( depth == CV_32F )
        {
            for( j = 0; j < total; j++ )
                ((float*)dptr)[j] = (float)std::log(fabs(((const float*)sptr)[j]));
        }
        else if( depth == CV_64F )
        {
            for( j = 0; j < total; j++ )
                ((double*)dptr)[j] = std::log(fabs(((const double*)sptr)[j]));
        }
    }
}

struct ExpOp : public BaseElemWiseOp
{
    ExpOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_FLT, 1, ARITHM_MAX_CHANNELS);
    }
    void getValueRange(int depth, double& minval, double& maxval)
    {
        maxval = depth == CV_32F ? 50 : 100;
        minval = -maxval;
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cv::exp(src[0], dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        cvtest::exp(src[0], dst);
    }
    double getMaxErr(int depth)
    {
        return depth == CV_32F ? 1e-5 : 1e-12;
    }
};


struct LogOp : public BaseElemWiseOp
{
    LogOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
    int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_FLT, 1, ARITHM_MAX_CHANNELS);
    }
    void getValueRange(int depth, double& minval, double& maxval)
    {
        maxval = depth == CV_32F ? 50 : 100;
        minval = -maxval;
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        Mat temp;
        cvtest::exp(src[0], temp);
        cv::log(temp, dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        Mat temp;
        cvtest::exp(src[0], temp);
        cvtest::log(temp, dst);
    }
    double getMaxErr(int depth)
    {
        return depth == CV_32F ? 1e-5 : 1e-12;
    }
};


static void cartToPolar(const Mat& mx, const Mat& my, Mat& mmag, Mat& mangle, bool angleInDegrees)
{
    CV_Assert( (mx.type() == CV_32F || mx.type() == CV_64F) &&
              mx.type() == my.type() && mx.size == my.size );
    mmag.create( mx.dims, &mx.size[0], mx.type() );
    mangle.create( mx.dims, &mx.size[0], mx.type() );
    const Mat *arrays[]={&mx, &my, &mmag, &mangle, 0};
    Mat planes[4];

    NAryMatIterator it(arrays, planes);
    size_t j, total = planes[0].total();
    size_t i, nplanes = it.nplanes;
    int depth = mx.depth();
    double scale = angleInDegrees ? 180/CV_PI : 1;

    for( i = 0; i < nplanes; i++, ++it )
    {
        if( depth == CV_32F )
        {
            const float* xptr = planes[0].ptr<float>();
            const float* yptr = planes[1].ptr<float>();
            float* mptr = planes[2].ptr<float>();
            float* aptr = planes[3].ptr<float>();

            for( j = 0; j < total; j++ )
            {
                mptr[j] = std::sqrt(xptr[j]*xptr[j] + yptr[j]*yptr[j]);
                double a = atan2((double)yptr[j], (double)xptr[j]);
                if( a < 0 ) a += CV_PI*2;
                aptr[j] = (float)(a*scale);
            }
        }
        else
        {
            const double* xptr = planes[0].ptr<double>();
            const double* yptr = planes[1].ptr<double>();
            double* mptr = planes[2].ptr<double>();
            double* aptr = planes[3].ptr<double>();

            for( j = 0; j < total; j++ )
            {
                mptr[j] = std::sqrt(xptr[j]*xptr[j] + yptr[j]*yptr[j]);
                double a = atan2(yptr[j], xptr[j]);
                if( a < 0 ) a += CV_PI*2;
                aptr[j] = a*scale;
            }
        }
    }
}


struct CartToPolarToCartOp : public BaseElemWiseOp
{
    CartToPolarToCartOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0))
    {
        context = 3;
        angleInDegrees = true;
    }
    int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_FLT, 1, 1);
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        Mat mag, angle, x, y;

        cv::cartToPolar(src[0], src[1], mag, angle, angleInDegrees);
        cv::polarToCart(mag, angle, x, y, angleInDegrees);

        Mat msrc[] = {mag, angle, x, y};
        int pairs[] = {0, 0, 1, 1, 2, 2, 3, 3};
        dst.create(src[0].dims, src[0].size, CV_MAKETYPE(src[0].depth(), 4));
        cv::mixChannels(msrc, 4, &dst, 1, pairs, 4);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        Mat mag, angle;
        cvtest::cartToPolar(src[0], src[1], mag, angle, angleInDegrees);
        Mat msrc[] = {mag, angle, src[0], src[1]};
        int pairs[] = {0, 0, 1, 1, 2, 2, 3, 3};
        dst.create(src[0].dims, src[0].size, CV_MAKETYPE(src[0].depth(), 4));
        cv::mixChannels(msrc, 4, &dst, 1, pairs, 4);
    }
    void generateScalars(int, RNG& rng)
    {
        angleInDegrees = rng.uniform(0, 2) != 0;
    }
    double getMaxErr(int)
    {
        return 1e-3;
    }
    bool angleInDegrees;
};


struct MeanOp : public BaseElemWiseOp
{
    MeanOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0))
    {
        context = 3;
    };
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        dst.create(1, 1, CV_64FC4);
        dst.at<Scalar>(0,0) = cv::mean(src[0], mask);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        dst.create(1, 1, CV_64FC4);
        dst.at<Scalar>(0,0) = cvtest::mean(src[0], mask);
    }
    double getMaxErr(int)
    {
        return 1e-5;
    }
};


struct SumOp : public BaseElemWiseOp
{
    SumOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SCALAR_OUTPUT, 1, 1, Scalar::all(0))
    {
        context = 3;
    };
    void op(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        dst.create(1, 1, CV_64FC4);
        dst.at<Scalar>(0,0) = cv::sum(src[0]);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat&)
    {
        dst.create(1, 1, CV_64FC4);
        dst.at<Scalar>(0,0) = cvtest::mean(src[0])*(double)src[0].total();
    }
    double getMaxErr(int)
    {
        return 1e-5;
    }
};


struct CountNonZeroOp : public BaseElemWiseOp
{
    CountNonZeroOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SCALAR_OUTPUT+SUPPORT_MASK, 1, 1, Scalar::all(0))
    {}
    int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL, 1, 1);
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        Mat temp;
        src[0].copyTo(temp);
        if( !mask.empty() )
            temp.setTo(Scalar::all(0), mask);
        dst.create(1, 1, CV_32S);
        dst.at<int>(0,0) = cv::countNonZero(temp);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        Mat temp;
        cvtest::compare(src[0], 0, temp, CMP_NE);
        if( !mask.empty() )
            cvtest::set(temp, Scalar::all(0), mask);
        dst.create(1, 1, CV_32S);
        dst.at<int>(0,0) = saturate_cast<int>(cvtest::mean(temp)[0]/255*temp.total());
    }
    double getMaxErr(int)
    {
        return 0;
    }
};


struct MeanStdDevOp : public BaseElemWiseOp
{
    Scalar sqmeanRef;
    int cn;

    MeanStdDevOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0))
    {
        cn = 0;
        context = 7;
    };
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        dst.create(1, 2, CV_64FC4);
        cv::meanStdDev(src[0], dst.at<Scalar>(0,0), dst.at<Scalar>(0,1), mask);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        Mat temp;
        cvtest::convert(src[0], temp, CV_64F);
        cvtest::multiply(temp, temp, temp);
        Scalar mean = cvtest::mean(src[0], mask);
        Scalar sqmean = cvtest::mean(temp, mask);

        sqmeanRef = sqmean;
        cn = temp.channels();

        for( int c = 0; c < 4; c++ )
            sqmean[c] = std::sqrt(std::max(sqmean[c] - mean[c]*mean[c], 0.));

        dst.create(1, 2, CV_64FC4);
        dst.at<Scalar>(0,0) = mean;
        dst.at<Scalar>(0,1) = sqmean;
    }
    double getMaxErr(int)
    {
        CV_Assert(cn > 0);
        double err = sqmeanRef[0];
        for(int i = 1; i < cn; ++i)
            err = std::max(err, sqmeanRef[i]);
        return 3e-7 * err;
    }
};


struct NormOp : public BaseElemWiseOp
{
    NormOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0))
    {
        context = 1;
        normType = 0;
    };
    int getRandomType(RNG& rng)
    {
        int type = cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL_BUT_8S, 1, 4);
        for(;;)
        {
            normType = rng.uniform(1, 8);
            if( normType == NORM_INF || normType == NORM_L1 ||
                normType == NORM_L2 || normType == NORM_L2SQR ||
                normType == NORM_HAMMING || normType == NORM_HAMMING2 )
                break;
        }
        if( normType == NORM_HAMMING || normType == NORM_HAMMING2 )
        {
            type = CV_8U;
        }
        return type;
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        dst.create(1, 2, CV_64FC1);
        dst.at<double>(0,0) = cv::norm(src[0], normType, mask);
        dst.at<double>(0,1) = cv::norm(src[0], src[1], normType, mask);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        dst.create(1, 2, CV_64FC1);
        dst.at<double>(0,0) = cvtest::norm(src[0], normType, mask);
        dst.at<double>(0,1) = cvtest::norm(src[0], src[1], normType, mask);
    }
    void generateScalars(int, RNG& /*rng*/)
    {
    }
    double getMaxErr(int)
    {
        return 1e-6;
    }
    int normType;
};


struct MinMaxLocOp : public BaseElemWiseOp
{
    MinMaxLocOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0))
    {
        context = ARITHM_MAX_NDIMS*2 + 2;
    };
    int getRandomType(RNG& rng)
    {
        return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL_BUT_8S, 1, 1);
    }
    void saveOutput(const vector<int>& minidx, const vector<int>& maxidx,
                    double minval, double maxval, Mat& dst)
    {
        int i, ndims = (int)minidx.size();
        dst.create(1, ndims*2 + 2, CV_64FC1);

        for( i = 0; i < ndims; i++ )
        {
            dst.at<double>(0,i) = minidx[i];
            dst.at<double>(0,i+ndims) = maxidx[i];
        }
        dst.at<double>(0,ndims*2) = minval;
        dst.at<double>(0,ndims*2+1) = maxval;
    }
    void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        int ndims = src[0].dims;
        vector<int> minidx(ndims), maxidx(ndims);
        double minval=0, maxval=0;
        cv::minMaxIdx(src[0], &minval, &maxval, &minidx[0], &maxidx[0], mask);
        saveOutput(minidx, maxidx, minval, maxval, dst);
    }
    void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
    {
        int ndims=src[0].dims;
        vector<int> minidx(ndims), maxidx(ndims);
        double minval=0, maxval=0;
        cvtest::minMaxLoc(src[0], &minval, &maxval, &minidx, &maxidx, mask);
        saveOutput(minidx, maxidx, minval, maxval, dst);
    }
    double getMaxErr(int)
    {
        return 0;
    }
};


}

typedef Ptr<cvtest::BaseElemWiseOp> ElemWiseOpPtr;
class ElemWiseTest : public ::testing::TestWithParam<ElemWiseOpPtr> {};

TEST_P(ElemWiseTest, accuracy)
{
    ElemWiseOpPtr op = GetParam();

    int testIdx = 0;
    RNG rng((uint64)cvtest::ARITHM_RNG_SEED);
    for( testIdx = 0; testIdx < cvtest::ARITHM_NTESTS; testIdx++ )
    {
        vector<int> size;
        op->getRandomSize(rng, size);
        int type = op->getRandomType(rng);
        int depth = CV_MAT_DEPTH(type);
        bool haveMask = (op->flags & cvtest::BaseElemWiseOp::SUPPORT_MASK) != 0 && rng.uniform(0, 4) == 0;

        double minval=0, maxval=0;
        op->getValueRange(depth, minval, maxval);
        int i, ninputs = op->ninputs;
        vector<Mat> src(ninputs);
        for( i = 0; i < ninputs; i++ )
            src[i] = cvtest::randomMat(rng, size, type, minval, maxval, true);
        Mat dst0, dst, mask;
        if( haveMask )
            mask = cvtest::randomMat(rng, size, CV_8U, 0, 2, true);

        if( (haveMask || ninputs == 0) && !(op->flags & cvtest::BaseElemWiseOp::SCALAR_OUTPUT))
        {
            dst0 = cvtest::randomMat(rng, size, type, minval, maxval, false);
            dst = cvtest::randomMat(rng, size, type, minval, maxval, true);
            cvtest::copy(dst, dst0);
        }
        op->generateScalars(depth, rng);

        op->refop(src, dst0, mask);
        op->op(src, dst, mask);

        double maxErr = op->getMaxErr(depth);
        ASSERT_PRED_FORMAT2(cvtest::MatComparator(maxErr, op->context), dst0, dst) << "\nsrc[0] ~ " <<
            cvtest::MatInfo(!src.empty() ? src[0] : Mat()) << "\ntestCase #" << testIdx << "\n";
    }
}


INSTANTIATE_TEST_CASE_P(Core_Copy, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CopyOp)));
INSTANTIATE_TEST_CASE_P(Core_Set, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SetOp)));
INSTANTIATE_TEST_CASE_P(Core_SetZero, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SetZeroOp)));
INSTANTIATE_TEST_CASE_P(Core_ConvertScale, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::ConvertScaleOp)));
INSTANTIATE_TEST_CASE_P(Core_ConvertScaleAbs, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::ConvertScaleAbsOp)));

INSTANTIATE_TEST_CASE_P(Core_Add, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AddOp)));
INSTANTIATE_TEST_CASE_P(Core_Sub, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SubOp)));
INSTANTIATE_TEST_CASE_P(Core_AddS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AddSOp)));
INSTANTIATE_TEST_CASE_P(Core_SubRS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SubRSOp)));
INSTANTIATE_TEST_CASE_P(Core_ScaleAdd, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::ScaleAddOp)));
INSTANTIATE_TEST_CASE_P(Core_AddWeighted, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AddWeightedOp)));
INSTANTIATE_TEST_CASE_P(Core_AbsDiff, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AbsDiffOp)));


INSTANTIATE_TEST_CASE_P(Core_AbsDiffS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::AbsDiffSOp)));

INSTANTIATE_TEST_CASE_P(Core_And, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicOp('&'))));
INSTANTIATE_TEST_CASE_P(Core_AndS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicSOp('&'))));
INSTANTIATE_TEST_CASE_P(Core_Or, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicOp('|'))));
INSTANTIATE_TEST_CASE_P(Core_OrS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicSOp('|'))));
INSTANTIATE_TEST_CASE_P(Core_Xor, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicOp('^'))));
INSTANTIATE_TEST_CASE_P(Core_XorS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicSOp('^'))));
INSTANTIATE_TEST_CASE_P(Core_Not, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogicSOp('~'))));

INSTANTIATE_TEST_CASE_P(Core_Max, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MaxOp)));
INSTANTIATE_TEST_CASE_P(Core_MaxS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MaxSOp)));
INSTANTIATE_TEST_CASE_P(Core_Min, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MinOp)));
INSTANTIATE_TEST_CASE_P(Core_MinS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MinSOp)));

INSTANTIATE_TEST_CASE_P(Core_Mul, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MulOp)));
INSTANTIATE_TEST_CASE_P(Core_Div, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::DivOp)));
INSTANTIATE_TEST_CASE_P(Core_Recip, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::RecipOp)));

INSTANTIATE_TEST_CASE_P(Core_Cmp, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CmpOp)));
INSTANTIATE_TEST_CASE_P(Core_CmpS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CmpSOp)));

INSTANTIATE_TEST_CASE_P(Core_InRangeS, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::InRangeSOp)));
INSTANTIATE_TEST_CASE_P(Core_InRange, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::InRangeOp)));

INSTANTIATE_TEST_CASE_P(Core_Flip, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::FlipOp)));
INSTANTIATE_TEST_CASE_P(Core_Transpose, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::TransposeOp)));
INSTANTIATE_TEST_CASE_P(Core_SetIdentity, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SetIdentityOp)));

INSTANTIATE_TEST_CASE_P(Core_Exp, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::ExpOp)));
INSTANTIATE_TEST_CASE_P(Core_Log, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::LogOp)));

INSTANTIATE_TEST_CASE_P(Core_CountNonZero, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CountNonZeroOp)));
INSTANTIATE_TEST_CASE_P(Core_Mean, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MeanOp)));
INSTANTIATE_TEST_CASE_P(Core_MeanStdDev, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MeanStdDevOp)));
INSTANTIATE_TEST_CASE_P(Core_Sum, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::SumOp)));
INSTANTIATE_TEST_CASE_P(Core_Norm, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::NormOp)));
INSTANTIATE_TEST_CASE_P(Core_MinMaxLoc, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::MinMaxLocOp)));
INSTANTIATE_TEST_CASE_P(Core_CartToPolarToCart, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new cvtest::CartToPolarToCartOp)));


class CV_ArithmMaskTest : public cvtest::BaseTest
{
public:
    CV_ArithmMaskTest() {}
    ~CV_ArithmMaskTest() {}
protected:
    void run(int)
    {
        try
        {
            RNG& rng = theRNG();
            const int MAX_DIM=3;
            int sizes[MAX_DIM];
            for( int iter = 0; iter < 100; iter++ )
            {
                //ts->printf(cvtest::TS::LOG, ".");

                ts->update_context(this, iter, true);
                int k, dims = rng.uniform(1, MAX_DIM+1), p = 1;
                int depth = rng.uniform(CV_8U, CV_64F+1);
                int cn = rng.uniform(1, 6);
                int type = CV_MAKETYPE(depth, cn);
                int op = rng.uniform(0, 5);
                int depth1 = op <= 1 ? CV_64F : depth;
                for( k = 0; k < dims; k++ )
                {
                    sizes[k] = rng.uniform(1, 30);
                    p *= sizes[k];
                }
                Mat a(dims, sizes, type), a1;
                Mat b(dims, sizes, type), b1;
                Mat mask(dims, sizes, CV_8U);
                Mat mask1;
                Mat c, d;

                rng.fill(a, RNG::UNIFORM, 0, 100);
                rng.fill(b, RNG::UNIFORM, 0, 100);

                // [-2,2) range means that the each generated random number
                // will be one of -2, -1, 0, 1. Saturated to [0,255], it will become
                // 0, 0, 0, 1 => the mask will be filled by ~25%.
                rng.fill(mask, RNG::UNIFORM, -2, 2);

                a.convertTo(a1, depth1);
                b.convertTo(b1, depth1);
                // invert the mask
                compare(mask, 0, mask1, CMP_EQ);
                a1.setTo(0, mask1);
                b1.setTo(0, mask1);

                if( op == 0 )
                {
                    add(a, b, c, mask);
                    add(a1, b1, d);
                }
                else if( op == 1 )
                {
                    subtract(a, b, c, mask);
                    subtract(a1, b1, d);
                }
                else if( op == 2 )
                {
                    bitwise_and(a, b, c, mask);
                    bitwise_and(a1, b1, d);
                }
                else if( op == 3 )
                {
                    bitwise_or(a, b, c, mask);
                    bitwise_or(a1, b1, d);
                }
                else if( op == 4 )
                {
                    bitwise_xor(a, b, c, mask);
                    bitwise_xor(a1, b1, d);
                }
                Mat d1;
                d.convertTo(d1, depth);
                CV_Assert( cvtest::norm(c, d1, CV_C) <= DBL_EPSILON );
            }

            Mat_<uchar> tmpSrc(100,100);
            tmpSrc = 124;
            Mat_<uchar> tmpMask(100,100);
            tmpMask = 255;
            Mat_<uchar> tmpDst(100,100);
            tmpDst = 2;
            tmpSrc.copyTo(tmpDst,tmpMask);
        }
        catch(...)
        {
           ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
        }
    }
};

TEST(Core_ArithmMask, uninitialized) { CV_ArithmMaskTest test; test.safe_run(); }

TEST(Multiply, FloatingPointRounding)
{
    cv::Mat src(1, 1, CV_8UC1, cv::Scalar::all(110)), dst;
    cv::Scalar s(147.286359696927, 1, 1 ,1);

    cv::multiply(src, s, dst, 1, CV_16U);
    // with CV_32F this produce result 16202
    ASSERT_EQ(dst.at<ushort>(0,0), 16201);
}

TEST(Core_Add, AddToColumnWhen3Rows)
{
    cv::Mat m1 = (cv::Mat_<double>(3, 2) << 1, 2, 3, 4, 5, 6);
    m1.col(1) += 10;

    cv::Mat m2 = (cv::Mat_<double>(3, 2) << 1, 12, 3, 14, 5, 16);

    ASSERT_EQ(0, countNonZero(m1 - m2));
}

TEST(Core_Add, AddToColumnWhen4Rows)
{
    cv::Mat m1 = (cv::Mat_<double>(4, 2) << 1, 2, 3, 4, 5, 6, 7, 8);
    m1.col(1) += 10;

    cv::Mat m2 = (cv::Mat_<double>(4, 2) << 1, 12, 3, 14, 5, 16, 7, 18);

    ASSERT_EQ(0, countNonZero(m1 - m2));
}

TEST(Core_round, CvRound)
{
    ASSERT_EQ(2, cvRound(2.0));
    ASSERT_EQ(2, cvRound(2.1));
    ASSERT_EQ(-2, cvRound(-2.1));
    ASSERT_EQ(3, cvRound(2.8));
    ASSERT_EQ(-3, cvRound(-2.8));
    ASSERT_EQ(2, cvRound(2.5));
    ASSERT_EQ(4, cvRound(3.5));
    ASSERT_EQ(-2, cvRound(-2.5));
    ASSERT_EQ(-4, cvRound(-3.5));
}


typedef testing::TestWithParam<Size> Mul1;

TEST_P(Mul1, One)
{
    Size size = GetParam();
    cv::Mat src(size, CV_32FC1, cv::Scalar::all(2)), dst,
            ref_dst(size, CV_32FC1, cv::Scalar::all(6));

    cv::multiply(3, src, dst);

    ASSERT_EQ(0, cvtest::norm(dst, ref_dst, cv::NORM_INF));
}

INSTANTIATE_TEST_CASE_P(Arithm, Mul1, testing::Values(Size(2, 2), Size(1, 1)));

class SubtractOutputMatNotEmpty : public testing::TestWithParam< std::tr1::tuple<cv::Size, perf::MatType, perf::MatDepth, bool> >
{
public:
    cv::Size size;
    int src_type;
    int dst_depth;
    bool fixed;

    void SetUp()
    {
        size = std::tr1::get<0>(GetParam());
        src_type = std::tr1::get<1>(GetParam());
        dst_depth = std::tr1::get<2>(GetParam());
        fixed = std::tr1::get<3>(GetParam());
    }
};

TEST_P(SubtractOutputMatNotEmpty, Mat_Mat)
{
    cv::Mat src1(size, src_type, cv::Scalar::all(16));
    cv::Mat src2(size, src_type, cv::Scalar::all(16));

    cv::Mat dst;

    if (!fixed)
    {
        cv::subtract(src1, src2, dst, cv::noArray(), dst_depth);
    }
    else
    {
        const cv::Mat fixed_dst(size, CV_MAKE_TYPE((dst_depth > 0 ? dst_depth : CV_16S), src1.channels()));
        cv::subtract(src1, src2, fixed_dst, cv::noArray(), dst_depth);
        dst = fixed_dst;
        dst_depth = fixed_dst.depth();
    }

    ASSERT_FALSE(dst.empty());
    ASSERT_EQ(src1.size(), dst.size());
    ASSERT_EQ(dst_depth > 0 ? dst_depth : src1.depth(), dst.depth());
    ASSERT_EQ(0, cv::countNonZero(dst.reshape(1)));
}

TEST_P(SubtractOutputMatNotEmpty, Mat_Mat_WithMask)
{
    cv::Mat src1(size, src_type, cv::Scalar::all(16));
    cv::Mat src2(size, src_type, cv::Scalar::all(16));
    cv::Mat mask(size, CV_8UC1, cv::Scalar::all(255));

    cv::Mat dst;

    if (!fixed)
    {
        cv::subtract(src1, src2, dst, mask, dst_depth);
    }
    else
    {
        const cv::Mat fixed_dst(size, CV_MAKE_TYPE((dst_depth > 0 ? dst_depth : CV_16S), src1.channels()));
        cv::subtract(src1, src2, fixed_dst, mask, dst_depth);
        dst = fixed_dst;
        dst_depth = fixed_dst.depth();
    }

    ASSERT_FALSE(dst.empty());
    ASSERT_EQ(src1.size(), dst.size());
    ASSERT_EQ(dst_depth > 0 ? dst_depth : src1.depth(), dst.depth());
    ASSERT_EQ(0, cv::countNonZero(dst.reshape(1)));
}

TEST_P(SubtractOutputMatNotEmpty, Mat_Mat_Expr)
{
    cv::Mat src1(size, src_type, cv::Scalar::all(16));
    cv::Mat src2(size, src_type, cv::Scalar::all(16));

    cv::Mat dst = src1 - src2;

    ASSERT_FALSE(dst.empty());
    ASSERT_EQ(src1.size(), dst.size());
    ASSERT_EQ(src1.depth(), dst.depth());
    ASSERT_EQ(0, cv::countNonZero(dst.reshape(1)));
}

TEST_P(SubtractOutputMatNotEmpty, Mat_Scalar)
{
    cv::Mat src(size, src_type, cv::Scalar::all(16));

    cv::Mat dst;

    if (!fixed)
    {
        cv::subtract(src, cv::Scalar::all(16), dst, cv::noArray(), dst_depth);
    }
    else
    {
        const cv::Mat fixed_dst(size, CV_MAKE_TYPE((dst_depth > 0 ? dst_depth : CV_16S), src.channels()));
        cv::subtract(src, cv::Scalar::all(16), fixed_dst, cv::noArray(), dst_depth);
        dst = fixed_dst;
        dst_depth = fixed_dst.depth();
    }

    ASSERT_FALSE(dst.empty());
    ASSERT_EQ(src.size(), dst.size());
    ASSERT_EQ(dst_depth > 0 ? dst_depth : src.depth(), dst.depth());
    ASSERT_EQ(0, cv::countNonZero(dst.reshape(1)));
}

TEST_P(SubtractOutputMatNotEmpty, Mat_Scalar_WithMask)
{
    cv::Mat src(size, src_type, cv::Scalar::all(16));
    cv::Mat mask(size, CV_8UC1, cv::Scalar::all(255));

    cv::Mat dst;

    if (!fixed)
    {
        cv::subtract(src, cv::Scalar::all(16), dst, mask, dst_depth);
    }
    else
    {
        const cv::Mat fixed_dst(size, CV_MAKE_TYPE((dst_depth > 0 ? dst_depth : CV_16S), src.channels()));
        cv::subtract(src, cv::Scalar::all(16), fixed_dst, mask, dst_depth);
        dst = fixed_dst;
        dst_depth = fixed_dst.depth();
    }

    ASSERT_FALSE(dst.empty());
    ASSERT_EQ(src.size(), dst.size());
    ASSERT_EQ(dst_depth > 0 ? dst_depth : src.depth(), dst.depth());
    ASSERT_EQ(0, cv::countNonZero(dst.reshape(1)));
}

TEST_P(SubtractOutputMatNotEmpty, Scalar_Mat)
{
    cv::Mat src(size, src_type, cv::Scalar::all(16));

    cv::Mat dst;

    if (!fixed)
    {
        cv::subtract(cv::Scalar::all(16), src, dst, cv::noArray(), dst_depth);
    }
    else
    {
        const cv::Mat fixed_dst(size, CV_MAKE_TYPE((dst_depth > 0 ? dst_depth : CV_16S), src.channels()));
        cv::subtract(cv::Scalar::all(16), src, fixed_dst, cv::noArray(), dst_depth);
        dst = fixed_dst;
        dst_depth = fixed_dst.depth();
    }

    ASSERT_FALSE(dst.empty());
    ASSERT_EQ(src.size(), dst.size());
    ASSERT_EQ(dst_depth > 0 ? dst_depth : src.depth(), dst.depth());
    ASSERT_EQ(0, cv::countNonZero(dst.reshape(1)));
}

TEST_P(SubtractOutputMatNotEmpty, Scalar_Mat_WithMask)
{
    cv::Mat src(size, src_type, cv::Scalar::all(16));
    cv::Mat mask(size, CV_8UC1, cv::Scalar::all(255));

    cv::Mat dst;

    if (!fixed)
    {
        cv::subtract(cv::Scalar::all(16), src, dst, mask, dst_depth);
    }
    else
    {
        const cv::Mat fixed_dst(size, CV_MAKE_TYPE((dst_depth > 0 ? dst_depth : CV_16S), src.channels()));
        cv::subtract(cv::Scalar::all(16), src, fixed_dst, mask, dst_depth);
        dst = fixed_dst;
        dst_depth = fixed_dst.depth();
    }

    ASSERT_FALSE(dst.empty());
    ASSERT_EQ(src.size(), dst.size());
    ASSERT_EQ(dst_depth > 0 ? dst_depth : src.depth(), dst.depth());
    ASSERT_EQ(0, cv::countNonZero(dst.reshape(1)));
}

TEST_P(SubtractOutputMatNotEmpty, Mat_Mat_3d)
{
    int dims[] = {5, size.height, size.width};

    cv::Mat src1(3, dims, src_type, cv::Scalar::all(16));
    cv::Mat src2(3, dims, src_type, cv::Scalar::all(16));

    cv::Mat dst;

    if (!fixed)
    {
        cv::subtract(src1, src2, dst, cv::noArray(), dst_depth);
    }
    else
    {
        const cv::Mat fixed_dst(3, dims, CV_MAKE_TYPE((dst_depth > 0 ? dst_depth : CV_16S), src1.channels()));
        cv::subtract(src1, src2, fixed_dst, cv::noArray(), dst_depth);
        dst = fixed_dst;
        dst_depth = fixed_dst.depth();
    }

    ASSERT_FALSE(dst.empty());
    ASSERT_EQ(src1.dims, dst.dims);
    ASSERT_EQ(src1.size, dst.size);
    ASSERT_EQ(dst_depth > 0 ? dst_depth : src1.depth(), dst.depth());
    ASSERT_EQ(0, cv::countNonZero(dst.reshape(1)));
}

INSTANTIATE_TEST_CASE_P(Arithm, SubtractOutputMatNotEmpty, testing::Combine(
    testing::Values(cv::Size(16, 16), cv::Size(13, 13), cv::Size(16, 13), cv::Size(13, 16)),
    testing::Values(perf::MatType(CV_8UC1), CV_8UC3, CV_8UC4, CV_16SC1, CV_16SC3),
    testing::Values(-1, CV_16S, CV_32S, CV_32F),
    testing::Bool()));


TEST(Core_FindNonZero, singular)
{
    Mat img(10, 10, CV_8U, Scalar::all(0));
    vector<Point> pts, pts2(10);
    findNonZero(img, pts);
    findNonZero(img, pts2);
    ASSERT_TRUE(pts.empty() && pts2.empty());
}

TEST(Core_BoolVector, support)
{
    std::vector<bool> test;
    int i, n = 205;
    int nz = 0;
    test.resize(n);
    for( i = 0; i < n; i++ )
    {
        test[i] = theRNG().uniform(0, 2) != 0;
        nz += (int)test[i];
    }
    ASSERT_EQ( nz, countNonZero(test) );
    ASSERT_FLOAT_EQ((float)nz/n, (float)(mean(test)[0]));
}

/* [<][>][^][v][top][bottom][index][help] */