#include <stdio.h>
#include <time.h>
#include "ap.h"
// disable some irrelevant warnings
#if (AE_COMPILER==AE_MSVC) && !defined(AE_ALL_WARNINGS)
#pragma warning(disable:4100)
#pragma warning(disable:4127)
#pragma warning(disable:4611)
#pragma warning(disable:4702)
#pragma warning(disable:4996)
#endif
#include "alglibinternal.h"
#include "alglibmisc.h"
#include "diffequations.h"
#include "linalg.h"
#include "optimization.h"
#include "solvers.h"
#include "statistics.h"
#include "dataanalysis.h"
#include "specialfunctions.h"
#include "integration.h"
#include "fasttransforms.h"
#include "interpolation.h"

using namespace alglib_impl;






/*************************************************************************
Testing tag sort
*************************************************************************/
ae_bool testtsort(ae_bool silent, ae_state *_state);
ae_bool _pexec_testtsort(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing Nearest Neighbor Search
*************************************************************************/
ae_bool testnearestneighbor(ae_bool silent, ae_state *_state);
ae_bool _pexec_testnearestneighbor(ae_bool silent, ae_state *_state);








ae_bool testhqrnd(ae_bool silent, ae_state *_state);
ae_bool _pexec_testhqrnd(ae_bool silent, ae_state *_state);


/*************************************************************************
Function for test HQRNDContinuous function
*************************************************************************/
ae_bool hqrndcontinuoustest(ae_bool silent, ae_state *_state);


/*************************************************************************
Function for test HQRNDContinuous function
*************************************************************************/
ae_bool hqrnddiscretetest(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testodesolver(ae_bool silent, ae_state *_state);
ae_bool _pexec_testodesolver(ae_bool silent, ae_state *_state);





typedef struct
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t matkind;
    ae_int_t triangle;
    ae_matrix bufa;
    hqrndstate rs;
    rcommstate rcs;
} sparsegenerator;





ae_bool testsparse(ae_bool silent, ae_state *_state);
ae_bool _pexec_testsparse(ae_bool silent, ae_state *_state);


/*************************************************************************
Function for testing basic SKS functional.
Returns True on errors, False on success.

  -- ALGLIB PROJECT --
     Copyright 16.01.1014 by Bochkanov Sergey
*************************************************************************/
ae_bool skstest(ae_state *_state);


/*************************************************************************
Function for testing CRS-specific functionality.
On failure sets ErrorFlag, on success does not touch it.

  -- ALGLIB PROJECT --
     Copyright 30.01.2018 by Bochkanov Sergey
*************************************************************************/
void crstest(ae_bool* errorflag, ae_state *_state);


/*************************************************************************
Function for testing basic functional

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool basicfunctest(ae_state *_state);


/*************************************************************************
Function for testing Level 2 unsymmetric linear algebra functions.
Additionally it tests SparseGet() for several matrix formats.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 20.01.2014 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel2unsymmetric(ae_state *_state);


/*************************************************************************
Function for testing Level 3 unsymmetric linear algebra functions.
Additionally it tests SparseGet() for several matrix formats.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 20.01.2014 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel3unsymmetric(ae_state *_state);


/*************************************************************************
Function for testing Level 2 symmetric linear algebra functions.
Additionally it tests SparseGet() for several matrix formats.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 20.01.2014 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel2symmetric(ae_state *_state);


/*************************************************************************
Function for testing Level 2 symmetric linear algebra functions.
Additionally it tests SparseGet() for several matrix formats.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel3symmetric(ae_state *_state);


/*************************************************************************
Function for testing Level 2 triangular linear algebra functions.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 20.01.2014 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel2triangular(ae_state *_state);


/*************************************************************************
Function for testing basic functional

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool basicfuncrandomtest(ae_state *_state);


/*************************************************************************
Function for testing multyplication matrix with vector

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool linearfunctionstest(ae_state *_state);


/*************************************************************************
Function for testing multyplication for simmetric matrix with vector

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool linearfunctionsstest(ae_state *_state);


/*************************************************************************
Function for testing multyplication sparse matrix with nerrow dense matrix

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool linearfunctionsmmtest(ae_state *_state);


/*************************************************************************
Function for testing multyplication for simmetric sparse matrix with narrow
dense matrix

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool linearfunctionssmmtest(ae_state *_state);


/*************************************************************************
Function for basic test SparseCopy

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool basiccopyfunctest(ae_bool silent, ae_state *_state);


/*************************************************************************
Function for testing SparseCopy

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool copyfunctest(ae_bool silent, ae_state *_state);
void _sparsegenerator_init(void* _p, ae_state *_state, ae_bool make_automatic);
void _sparsegenerator_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic);
void _sparsegenerator_clear(void* _p);
void _sparsegenerator_destroy(void* _p);








ae_bool testablas(ae_bool silent, ae_state *_state);
ae_bool _pexec_testablas(ae_bool silent, ae_state *_state);








ae_bool testcreflections(ae_bool silent, ae_state *_state);
ae_bool _pexec_testcreflections(ae_bool silent, ae_state *_state);








ae_bool testmatgen(ae_bool silent, ae_state *_state);
ae_bool _pexec_testmatgen(ae_bool silent, ae_state *_state);








ae_bool testtrfac(ae_bool silent, ae_state *_state);
ae_bool _pexec_testtrfac(ae_bool silent, ae_state *_state);


/*************************************************************************
Function for testing sparse real Cholesky.
Returns True on errors, False on success.

  -- ALGLIB PROJECT --
     Copyright 16.01.1014 by Bochkanov Sergey
*************************************************************************/
ae_bool sparserealcholeskytest(ae_state *_state);








/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testtrlinsolve(ae_bool silent, ae_state *_state);
ae_bool _pexec_testtrlinsolve(ae_bool silent, ae_state *_state);








/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testsafesolve(ae_bool silent, ae_state *_state);
ae_bool _pexec_testsafesolve(ae_bool silent, ae_state *_state);








ae_bool testrcond(ae_bool silent, ae_state *_state);
ae_bool _pexec_testrcond(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testmatinv(ae_bool silent, ae_state *_state);
ae_bool _pexec_testmatinv(ae_bool silent, ae_state *_state);








ae_bool testhblas(ae_bool silent, ae_state *_state);
ae_bool _pexec_testhblas(ae_bool silent, ae_state *_state);








ae_bool testsblas(ae_bool silent, ae_state *_state);
ae_bool _pexec_testsblas(ae_bool silent, ae_state *_state);








/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testortfac(ae_bool silent, ae_state *_state);
ae_bool _pexec_testortfac(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing
*************************************************************************/
ae_bool testfbls(ae_bool silent, ae_state *_state);
ae_bool _pexec_testfbls(ae_bool silent, ae_state *_state);








ae_bool testcqmodels(ae_bool silent, ae_state *_state);
ae_bool _pexec_testcqmodels(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing bidiagonal SVD decomposition subroutine
*************************************************************************/
ae_bool testbdsvd(ae_bool silent, ae_state *_state);
ae_bool _pexec_testbdsvd(ae_bool silent, ae_state *_state);








ae_bool testblas(ae_bool silent, ae_state *_state);
ae_bool _pexec_testblas(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing SVD decomposition subroutine
*************************************************************************/
ae_bool testsvd(ae_bool silent, ae_state *_state);
ae_bool _pexec_testsvd(ae_bool silent, ae_state *_state);








ae_bool testoptserv(ae_bool silent, ae_state *_state);
ae_bool _pexec_testoptserv(ae_bool silent, ae_state *_state);








ae_bool testsnnls(ae_bool silent, ae_state *_state);
ae_bool _pexec_testsnnls(ae_bool silent, ae_state *_state);








ae_bool testsactivesets(ae_bool silent, ae_state *_state);
ae_bool _pexec_testsactivesets(ae_bool silent, ae_state *_state);








ae_bool testlinmin(ae_bool silent, ae_state *_state);
ae_bool _pexec_testlinmin(ae_bool silent, ae_state *_state);








ae_bool testminlbfgs(ae_bool silent, ae_state *_state);
ae_bool _pexec_testminlbfgs(ae_bool silent, ae_state *_state);








ae_bool testxblas(ae_bool silent, ae_state *_state);
ae_bool _pexec_testxblas(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testdirectdensesolvers(ae_bool silent, ae_state *_state);
ae_bool _pexec_testdirectdensesolvers(ae_bool silent, ae_state *_state);








ae_bool testnormestimator(ae_bool silent, ae_state *_state);
ae_bool _pexec_testnormestimator(ae_bool silent, ae_state *_state);








ae_bool testlinlsqr(ae_bool silent, ae_state *_state);
ae_bool _pexec_testlinlsqr(ae_bool silent, ae_state *_state);








ae_bool testmincg(ae_bool silent, ae_state *_state);
ae_bool _pexec_testmincg(ae_bool silent, ae_state *_state);


/*************************************************************************
Other properties
*************************************************************************/
void testother(ae_bool* err, ae_state *_state);








ae_bool testminbleic(ae_bool silent, ae_state *_state);
ae_bool _pexec_testminbleic(ae_bool silent, ae_state *_state);








ae_bool testminqp(ae_bool silent, ae_state *_state);
ae_bool _pexec_testminqp(ae_bool silent, ae_state *_state);


/*************************************************************************
Function to test: 'MinQPCreate', 'MinQPSetQuadraticTerm', 'MinQPSetBC', 
'MinQPSetOrigin', 'MinQPSetStartingPoint', 'MinQPOptimize', 'MinQPResults'.

Test problem:
    A = diag(aii), aii>0 (random)
    b = 0
    random bounds (either no bounds, one bound, two bounds a<b, two bounds a=b)
    random start point
    dimension - from 1 to 5.
    
Returns True on success, False on failure.
*************************************************************************/
ae_bool simpletest(ae_state *_state);


/*************************************************************************
Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', 'MinQPSetQuadraticTerm',
'MinQPSetOrigin', 'MinQPSetStartingPoint', 'MinQPOptimize', 'MinQPResults'.

Test problem:
    A = positive-definite matrix, obtained by 'SPDMatrixRndCond' function
    b <> 0
    without bounds
    random start point
    dimension - from 1 to 5.
*************************************************************************/
ae_bool functest1(ae_state *_state);


/*************************************************************************
Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', 'MinQPSetQuadraticTerm',
'MinQPSetBC', 'MinQPSetOrigin', 'MinQPSetStartingPoint', 'MinQPOptimize', 
'MinQPResults'.

Test problem:
    A = positive-definite matrix, obtained by 'SPDMatrixRndCond' function
    b <> 0
    boundary constraints
    random start point
    dimension - from 1 to 5.
*************************************************************************/
ae_bool functest2(ae_state *_state);


/*************************************************************************
ConsoleTest.
*************************************************************************/
ae_bool consoletest(ae_state *_state);


/*************************************************************************
This function performs tests specific for QuickQP solver
    
Returns True on failure.
*************************************************************************/
ae_bool quickqptests(ae_state *_state);


/*************************************************************************
This function performs tests specific for BLEIC solver
    
Returns True on error, False on success.
*************************************************************************/
ae_bool bleictests(ae_state *_state);








ae_bool testminbc(ae_bool silent, ae_state *_state);
ae_bool _pexec_testminbc(ae_bool silent, ae_state *_state);








ae_bool testminnlc(ae_bool silent, ae_state *_state);
ae_bool _pexec_testminnlc(ae_bool silent, ae_state *_state);








ae_bool testminns(ae_bool silent, ae_state *_state);
ae_bool _pexec_testminns(ae_bool silent, ae_state *_state);








ae_bool testminlm(ae_bool silent, ae_state *_state);
ae_bool _pexec_testminlm(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing symmetric EVD subroutine
*************************************************************************/
ae_bool testevd(ae_bool silent, ae_state *_state);
ae_bool _pexec_testevd(ae_bool silent, ae_state *_state);








ae_bool testbasestat(ae_bool silent, ae_state *_state);
ae_bool _pexec_testbasestat(ae_bool silent, ae_state *_state);








ae_bool testpca(ae_bool silent, ae_state *_state);
ae_bool _pexec_testpca(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing BDSS operations
*************************************************************************/
ae_bool testbdss(ae_bool silent, ae_state *_state);
ae_bool _pexec_testbdss(ae_bool silent, ae_state *_state);








ae_bool testmlpbase(ae_bool silent, ae_state *_state);
ae_bool _pexec_testmlpbase(ae_bool silent, ae_state *_state);








ae_bool testlda(ae_bool silent, ae_state *_state);
ae_bool _pexec_testlda(ae_bool silent, ae_state *_state);








ae_bool testssa(ae_bool silent, ae_state *_state);
ae_bool _pexec_testssa(ae_bool silent, ae_state *_state);








ae_bool testgammafunc(ae_bool silent, ae_state *_state);
ae_bool _pexec_testgammafunc(ae_bool silent, ae_state *_state);








ae_bool testlinreg(ae_bool silent, ae_state *_state);
ae_bool _pexec_testlinreg(ae_bool silent, ae_state *_state);








ae_bool testfilters(ae_bool silent, ae_state *_state);
ae_bool _pexec_testfilters(ae_bool silent, ae_state *_state);


/*************************************************************************
This function tests SMA(k) filter. It returns True on error.

Additional IsSilent parameter controls detailed error reporting.
*************************************************************************/
ae_bool testsma(ae_bool issilent, ae_state *_state);


/*************************************************************************
This function tests EMA(alpha) filter. It returns True on error.

Additional IsSilent parameter controls detailed error reporting.
*************************************************************************/
ae_bool testema(ae_bool issilent, ae_state *_state);


/*************************************************************************
This function tests LRMA(k) filter. It returns True on error.

Additional IsSilent parameter controls detailed error reporting.
*************************************************************************/
ae_bool testlrma(ae_bool issilent, ae_state *_state);








ae_bool testmcpd(ae_bool silent, ae_state *_state);
ae_bool _pexec_testmcpd(ae_bool silent, ae_state *_state);








ae_bool testmlpe(ae_bool silent, ae_state *_state);
ae_bool _pexec_testmlpe(ae_bool silent, ae_state *_state);








ae_bool testmlptrain(ae_bool silent, ae_state *_state);
ae_bool _pexec_testmlptrain(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing clustering
*************************************************************************/
ae_bool testclustering(ae_bool silent, ae_state *_state);
ae_bool _pexec_testclustering(ae_bool silent, ae_state *_state);








ae_bool testdforest(ae_bool silent, ae_state *_state);
ae_bool _pexec_testdforest(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testgq(ae_bool silent, ae_state *_state);
ae_bool _pexec_testgq(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testgkq(ae_bool silent, ae_state *_state);
ae_bool _pexec_testgkq(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testautogk(ae_bool silent, ae_state *_state);
ae_bool _pexec_testautogk(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testfft(ae_bool silent, ae_state *_state);
ae_bool _pexec_testfft(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testfht(ae_bool silent, ae_state *_state);
ae_bool _pexec_testfht(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testconv(ae_bool silent, ae_state *_state);
ae_bool _pexec_testconv(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testcorr(ae_bool silent, ae_state *_state);
ae_bool _pexec_testcorr(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing IDW interpolation
*************************************************************************/
ae_bool testidwint(ae_bool silent, ae_state *_state);
ae_bool _pexec_testidwint(ae_bool silent, ae_state *_state);








ae_bool testratint(ae_bool silent, ae_state *_state);
ae_bool _pexec_testratint(ae_bool silent, ae_state *_state);








ae_bool testfitsphere(ae_bool silent, ae_state *_state);
ae_bool _pexec_testfitsphere(ae_bool silent, ae_state *_state);








ae_bool testspline1d(ae_bool silent, ae_state *_state);
ae_bool _pexec_testspline1d(ae_bool silent, ae_state *_state);








ae_bool testparametric(ae_bool silent, ae_state *_state);
ae_bool _pexec_testparametric(ae_bool silent, ae_state *_state);








ae_bool testspline3d(ae_bool silence, ae_state *_state);
ae_bool _pexec_testspline3d(ae_bool silence, ae_state *_state);








/*************************************************************************
Unit test
*************************************************************************/
ae_bool testpolint(ae_bool silent, ae_state *_state);
ae_bool _pexec_testpolint(ae_bool silent, ae_state *_state);








ae_bool testlsfit(ae_bool silent, ae_state *_state);
ae_bool _pexec_testlsfit(ae_bool silent, ae_state *_state);








ae_bool testspline2d(ae_bool silent, ae_state *_state);
ae_bool _pexec_testspline2d(ae_bool silent, ae_state *_state);








ae_bool testrbf(ae_bool silent, ae_state *_state);
ae_bool _pexec_testrbf(ae_bool silent, ae_state *_state);


/*************************************************************************
The test  has  to  check, that  algorithm can solve problems of matrix are
degenerate.
    * used model with linear term;
    * points locate in a subspace of dimension less than an original space.

  -- ALGLIB --
     Copyright 13.12.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool sqrdegmatrixrbftest(ae_bool silent, ae_state *_state);


/*************************************************************************
Function for testing basic functionality of RBF module on regular grids with
multi-layer algorithm in 1D.

  -- ALGLIB --
     Copyright 2.03.2012 by Bochkanov Sergey
*************************************************************************/
ae_bool basicmultilayerrbf1dtest(ae_state *_state);








ae_bool testhermite(ae_bool silent, ae_state *_state);
ae_bool _pexec_testhermite(ae_bool silent, ae_state *_state);








ae_bool testlaguerre(ae_bool silent, ae_state *_state);
ae_bool _pexec_testlaguerre(ae_bool silent, ae_state *_state);








ae_bool testlegendre(ae_bool silent, ae_state *_state);
ae_bool _pexec_testlegendre(ae_bool silent, ae_state *_state);








ae_bool testchebyshev(ae_bool silent, ae_state *_state);
ae_bool _pexec_testchebyshev(ae_bool silent, ae_state *_state);








ae_bool testwsr(ae_bool silent, ae_state *_state);
ae_bool _pexec_testwsr(ae_bool silent, ae_state *_state);








ae_bool teststest(ae_bool silent, ae_state *_state);
ae_bool _pexec_teststest(ae_bool silent, ae_state *_state);








ae_bool teststudentttests(ae_bool silent, ae_state *_state);
ae_bool _pexec_teststudentttests(ae_bool silent, ae_state *_state);








ae_bool testmannwhitneyu(ae_bool silent, ae_state *_state);
ae_bool _pexec_testmannwhitneyu(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing Schur decomposition subroutine
*************************************************************************/
ae_bool testschur(ae_bool silent, ae_state *_state);
ae_bool _pexec_testschur(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing bidiagonal SVD decomposition subroutine
*************************************************************************/
ae_bool testspdgevd(ae_bool silent, ae_state *_state);
ae_bool _pexec_testspdgevd(ae_bool silent, ae_state *_state);








ae_bool testinverseupdate(ae_bool silent, ae_state *_state);
ae_bool _pexec_testinverseupdate(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testpolynomialsolver(ae_bool silent, ae_state *_state);
ae_bool _pexec_testpolynomialsolver(ae_bool silent, ae_state *_state);








ae_bool testnleq(ae_bool silent, ae_state *_state);
ae_bool _pexec_testnleq(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testdirectsparsesolvers(ae_bool silent, ae_state *_state);
ae_bool _pexec_testdirectsparsesolvers(ae_bool silent, ae_state *_state);








ae_bool testlincg(ae_bool silent, ae_state *_state);
ae_bool _pexec_testlincg(ae_bool silent, ae_state *_state);





typedef struct
{
    ae_bool bfield;
    double rfield;
    ae_int_t ifield;
    ae_complex cfield;
    ae_vector b1field;
    ae_vector r1field;
    ae_vector i1field;
    ae_vector c1field;
    ae_matrix b2field;
    ae_matrix r2field;
    ae_matrix i2field;
    ae_matrix c2field;
} rec1;


typedef struct
{
    ae_vector b;
    ae_vector i;
    ae_vector r;
} rec4serialization;


typedef struct
{
    ae_complex cval;
    double rval;
    ae_int_t ival;
    ae_bool bval;
    ae_vector i1val;
} poolrec1;


typedef struct
{
    ae_bool bval;
    poolrec1 recval;
    ae_shared_pool pool;
} poolrec2;


typedef struct
{
    ae_int_t val;
} poolsummand;





void rec4serializationalloc(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state);


void rec4serializationserialize(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state);


void rec4serializationunserialize(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state);


ae_bool testalglibbasics(ae_bool silent, ae_state *_state);
ae_bool _pexec_testalglibbasics(ae_bool silent, ae_state *_state);
void _rec1_init(void* _p, ae_state *_state, ae_bool make_automatic);
void _rec1_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic);
void _rec1_clear(void* _p);
void _rec1_destroy(void* _p);
void _rec4serialization_init(void* _p, ae_state *_state, ae_bool make_automatic);
void _rec4serialization_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic);
void _rec4serialization_clear(void* _p);
void _rec4serialization_destroy(void* _p);
void _poolrec1_init(void* _p, ae_state *_state, ae_bool make_automatic);
void _poolrec1_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic);
void _poolrec1_clear(void* _p);
void _poolrec1_destroy(void* _p);
void _poolrec2_init(void* _p, ae_state *_state, ae_bool make_automatic);
void _poolrec2_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic);
void _poolrec2_clear(void* _p);
void _poolrec2_destroy(void* _p);
void _poolsummand_init(void* _p, ae_state *_state, ae_bool make_automatic);
void _poolsummand_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic);
void _poolsummand_clear(void* _p);
void _poolsummand_destroy(void* _p);




static void testtsortunit_unset1di(/* Integer */ ae_vector* a,
     ae_state *_state);
static void testtsortunit_testsortresults(/* Real    */ ae_vector* asorted,
     /* Integer */ ae_vector* p1,
     /* Integer */ ae_vector* p2,
     /* Real    */ ae_vector* aoriginal,
     ae_int_t n,
     ae_bool* waserrors,
     ae_state *_state);





/*************************************************************************
Testing tag sort
*************************************************************************/
ae_bool testtsort(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_int_t n;
    ae_int_t i;
    ae_int_t m;
    ae_int_t offs;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_vector a;
    ae_vector a0;
    ae_vector a1;
    ae_vector a2;
    ae_vector a3;
    ae_vector i1;
    ae_vector i2;
    ae_vector i3;
    ae_vector a4;
    ae_vector pa4;
    ae_vector ar;
    ae_vector ar2;
    ae_vector ai;
    ae_vector p1;
    ae_vector p2;
    ae_vector bufr1;
    ae_vector bufr2;
    ae_vector bufi1;
    ae_bool distinctvals;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&a0, 0, sizeof(a0));
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    memset(&a3, 0, sizeof(a3));
    memset(&i1, 0, sizeof(i1));
    memset(&i2, 0, sizeof(i2));
    memset(&i3, 0, sizeof(i3));
    memset(&a4, 0, sizeof(a4));
    memset(&pa4, 0, sizeof(pa4));
    memset(&ar, 0, sizeof(ar));
    memset(&ar2, 0, sizeof(ar2));
    memset(&ai, 0, sizeof(ai));
    memset(&p1, 0, sizeof(p1));
    memset(&p2, 0, sizeof(p2));
    memset(&bufr1, 0, sizeof(bufr1));
    memset(&bufr2, 0, sizeof(bufr2));
    memset(&bufi1, 0, sizeof(bufi1));
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a3, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&i1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&i2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&i3, 0, DT_INT, _state, ae_true);
    ae_vector_init(&a4, 0, DT_INT, _state, ae_true);
    ae_vector_init(&pa4, 0, DT_INT, _state, ae_true);
    ae_vector_init(&ar, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ar2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ai, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&bufr1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bufr2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bufi1, 0, DT_INT, _state, ae_true);

    waserrors = ae_false;
    maxn = 100;
    passcount = 10;
    
    /*
     * Test tagsort
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Pprobably distinct sort:
             * * generate array of integer random numbers.
             *   Because of birthday paradox, random numbers have to be VERY large
             *   in order to avoid situation when we have distinct values.
             * * sort A0 using TagSort and test sort results
             * * now we can use A0 as reference point and test other functions
             */
            testtsortunit_unset1di(&p1, _state);
            testtsortunit_unset1di(&p2, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&a0, n, _state);
            ae_vector_set_length(&a1, n, _state);
            ae_vector_set_length(&a2, n, _state);
            ae_vector_set_length(&a3, n, _state);
            ae_vector_set_length(&a4, n, _state);
            ae_vector_set_length(&ar, n, _state);
            ae_vector_set_length(&ar2, n, _state);
            ae_vector_set_length(&ai, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(ae_randominteger(100000000, _state));
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                a1.ptr.p_double[i] = a.ptr.p_double[i];
                a2.ptr.p_double[i] = a.ptr.p_double[i];
                a3.ptr.p_double[i] = a.ptr.p_double[i];
                a4.ptr.p_int[i] = ae_round(a.ptr.p_double[i], _state);
                ar.ptr.p_double[i] = (double)(i);
                ar2.ptr.p_double[i] = (double)(i);
                ai.ptr.p_int[i] = i;
            }
            tagsort(&a0, n, &p1, &p2, _state);
            testtsortunit_testsortresults(&a0, &p1, &p2, &a, n, &waserrors, _state);
            distinctvals = ae_true;
            for(i=1; i<=n-1; i++)
            {
                distinctvals = distinctvals&&ae_fp_neq(a0.ptr.p_double[i],a0.ptr.p_double[i-1]);
            }
            if( distinctvals )
            {
                tagsortfasti(&a1, &ai, &bufr1, &bufi1, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    waserrors = (waserrors||ae_fp_neq(a1.ptr.p_double[i],a0.ptr.p_double[i]))||ai.ptr.p_int[i]!=p1.ptr.p_int[i];
                }
                tagsortfastr(&a2, &ar, &bufr1, &bufr2, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    waserrors = (waserrors||ae_fp_neq(a2.ptr.p_double[i],a0.ptr.p_double[i]))||ae_fp_neq(ar.ptr.p_double[i],(double)(p1.ptr.p_int[i]));
                }
                tagsortfast(&a3, &bufr1, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    waserrors = waserrors||ae_fp_neq(a3.ptr.p_double[i],a0.ptr.p_double[i]);
                }
                tagsortmiddleir(&a4, &ar2, 0, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    waserrors = (waserrors||ae_fp_neq((double)(a4.ptr.p_int[i]),a0.ptr.p_double[i]))||ae_fp_neq(ar2.ptr.p_double[i],(double)(p1.ptr.p_int[i]));
                }
            }
            
            /*
             * Non-distinct sort.
             * We test that keys are correctly reordered, but do NOT test order of values.
             */
            testtsortunit_unset1di(&p1, _state);
            testtsortunit_unset1di(&p2, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&a0, n, _state);
            ae_vector_set_length(&a1, n, _state);
            ae_vector_set_length(&a2, n, _state);
            ae_vector_set_length(&a3, n, _state);
            ae_vector_set_length(&a4, n, _state);
            ae_vector_set_length(&ar, n, _state);
            ae_vector_set_length(&ar2, n, _state);
            ae_vector_set_length(&ai, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)((n-i)/2);
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                a1.ptr.p_double[i] = a.ptr.p_double[i];
                a2.ptr.p_double[i] = a.ptr.p_double[i];
                a3.ptr.p_double[i] = a.ptr.p_double[i];
                a4.ptr.p_int[i] = ae_round(a.ptr.p_double[i], _state);
                ar.ptr.p_double[i] = (double)(i);
                ar2.ptr.p_double[i] = (double)(i);
                ai.ptr.p_int[i] = i;
            }
            tagsort(&a0, n, &p1, &p2, _state);
            testtsortunit_testsortresults(&a0, &p1, &p2, &a, n, &waserrors, _state);
            tagsortfasti(&a1, &ai, &bufr1, &bufi1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a1.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            tagsortfastr(&a2, &ar, &bufr1, &bufr2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a2.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            tagsortfast(&a3, &bufr1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a3.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            tagsortmiddleir(&a4, &ar2, 0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq((double)(a4.ptr.p_int[i]),a0.ptr.p_double[i]);
            }
            
            /*
             * 'All same' sort
             * We test that keys are correctly reordered, but do NOT test order of values.
             */
            testtsortunit_unset1di(&p1, _state);
            testtsortunit_unset1di(&p2, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&a0, n, _state);
            ae_vector_set_length(&a1, n, _state);
            ae_vector_set_length(&a2, n, _state);
            ae_vector_set_length(&a3, n, _state);
            ae_vector_set_length(&a4, n, _state);
            ae_vector_set_length(&ar, n, _state);
            ae_vector_set_length(&ar2, n, _state);
            ae_vector_set_length(&ai, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(0);
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                a1.ptr.p_double[i] = a.ptr.p_double[i];
                a2.ptr.p_double[i] = a.ptr.p_double[i];
                a3.ptr.p_double[i] = a.ptr.p_double[i];
                a4.ptr.p_int[i] = ae_round(a.ptr.p_double[i], _state);
                ar.ptr.p_double[i] = (double)(i);
                ar2.ptr.p_double[i] = (double)(i);
                ai.ptr.p_int[i] = i;
            }
            tagsort(&a0, n, &p1, &p2, _state);
            testtsortunit_testsortresults(&a0, &p1, &p2, &a, n, &waserrors, _state);
            tagsortfasti(&a1, &ai, &bufr1, &bufi1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a1.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            tagsortfastr(&a2, &ar, &bufr1, &bufr2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a2.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            tagsortfast(&a3, &bufr1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a3.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            tagsortmiddleir(&a4, &ar2, 0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq((double)(a4.ptr.p_int[i]),a0.ptr.p_double[i]);
            }
            
            /*
             * 0-1 sort
             * We test that keys are correctly reordered, but do NOT test order of values.
             */
            testtsortunit_unset1di(&p1, _state);
            testtsortunit_unset1di(&p2, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&a0, n, _state);
            ae_vector_set_length(&a1, n, _state);
            ae_vector_set_length(&a2, n, _state);
            ae_vector_set_length(&a3, n, _state);
            ae_vector_set_length(&a4, n, _state);
            ae_vector_set_length(&ar, n, _state);
            ae_vector_set_length(&ar2, n, _state);
            ae_vector_set_length(&ai, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                a1.ptr.p_double[i] = a.ptr.p_double[i];
                a2.ptr.p_double[i] = a.ptr.p_double[i];
                a3.ptr.p_double[i] = a.ptr.p_double[i];
                a4.ptr.p_int[i] = ae_round(a.ptr.p_double[i], _state);
                ar.ptr.p_double[i] = (double)(i);
                ar2.ptr.p_double[i] = (double)(i);
                ai.ptr.p_int[i] = i;
            }
            tagsort(&a0, n, &p1, &p2, _state);
            testtsortunit_testsortresults(&a0, &p1, &p2, &a, n, &waserrors, _state);
            tagsortfasti(&a1, &ai, &bufr1, &bufi1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a1.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            tagsortfastr(&a2, &ar, &bufr1, &bufr2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a2.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            tagsortfast(&a3, &bufr1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a3.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            tagsortmiddleir(&a4, &ar2, 0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq((double)(a4.ptr.p_int[i]),a0.ptr.p_double[i]);
            }
            
            /*
             * Special test for TagSortMiddleIR: sorting in the middle gives same results
             * as sorting in the beginning of the array
             */
            m = 3*n;
            offs = ae_randominteger(n, _state);
            ae_vector_set_length(&i1, m, _state);
            ae_vector_set_length(&i2, m, _state);
            ae_vector_set_length(&i3, m, _state);
            ae_vector_set_length(&ar, m, _state);
            ae_vector_set_length(&ar2, m, _state);
            for(i=0; i<=m-1; i++)
            {
                i1.ptr.p_int[i] = ae_randominteger(100000000, _state);
                i2.ptr.p_int[i] = i1.ptr.p_int[i];
                i3.ptr.p_int[i] = i1.ptr.p_int[i];
                ar.ptr.p_double[i] = (double)(i);
                ar2.ptr.p_double[i] = (double)(i);
            }
            for(i=0; i<=n-1; i++)
            {
                i1.ptr.p_int[i] = i1.ptr.p_int[offs+i];
                ar.ptr.p_double[i] = ar.ptr.p_double[offs+i];
            }
            tagsortmiddleir(&i1, &ar, 0, n, _state);
            for(i=1; i<=n-1; i++)
            {
                distinctvals = distinctvals&&i1.ptr.p_int[i]!=i1.ptr.p_int[i-1];
            }
            if( distinctvals )
            {
                tagsortmiddleir(&i2, &ar2, offs, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    waserrors = (waserrors||i2.ptr.p_int[offs+i]!=i1.ptr.p_int[i])||ae_fp_neq(ar2.ptr.p_double[offs+i],ar.ptr.p_double[i]);
                }
            }
        }
    }
    
    /*
     * report
     */
    if( !silent )
    {
        printf("TESTING TAGSORT\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testtsortunit_unset1di(/* Integer */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_int[0] = ae_randominteger(3, _state)-1;
}


static void testtsortunit_testsortresults(/* Real    */ ae_vector* asorted,
     /* Integer */ ae_vector* p1,
     /* Integer */ ae_vector* p2,
     /* Real    */ ae_vector* aoriginal,
     ae_int_t n,
     ae_bool* waserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_vector a2;
    double t;
    ae_vector f;

    ae_frame_make(_state, &_frame_block);
    memset(&a2, 0, sizeof(a2));
    memset(&f, 0, sizeof(f));
    ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&f, 0, DT_INT, _state, ae_true);

    ae_vector_set_length(&a2, n-1+1, _state);
    ae_vector_set_length(&f, n-1+1, _state);
    
    /*
     * is set ordered?
     */
    for(i=0; i<=n-2; i++)
    {
        *waserrors = *waserrors||ae_fp_greater(asorted->ptr.p_double[i],asorted->ptr.p_double[i+1]);
    }
    
    /*
     * P1 correctness
     */
    for(i=0; i<=n-1; i++)
    {
        *waserrors = *waserrors||ae_fp_neq(asorted->ptr.p_double[i],aoriginal->ptr.p_double[p1->ptr.p_int[i]]);
    }
    for(i=0; i<=n-1; i++)
    {
        f.ptr.p_int[i] = 0;
    }
    for(i=0; i<=n-1; i++)
    {
        f.ptr.p_int[p1->ptr.p_int[i]] = f.ptr.p_int[p1->ptr.p_int[i]]+1;
    }
    for(i=0; i<=n-1; i++)
    {
        *waserrors = *waserrors||f.ptr.p_int[i]!=1;
    }
    
    /*
     * P2 correctness
     */
    for(i=0; i<=n-1; i++)
    {
        a2.ptr.p_double[i] = aoriginal->ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        if( p2->ptr.p_int[i]!=i )
        {
            t = a2.ptr.p_double[i];
            a2.ptr.p_double[i] = a2.ptr.p_double[p2->ptr.p_int[i]];
            a2.ptr.p_double[p2->ptr.p_int[i]] = t;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        *waserrors = *waserrors||ae_fp_neq(asorted->ptr.p_double[i],a2.ptr.p_double[i]);
    }
    ae_frame_leave(_state);
}



static ae_bool testnearestneighborunit_kdtresultsdifferent(/* Real    */ ae_matrix* refxy,
     ae_int_t ntotal,
     /* Real    */ ae_matrix* qx,
     /* Real    */ ae_matrix* qxy,
     /* Integer */ ae_vector* qt,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t ny,
     ae_state *_state);
static double testnearestneighborunit_vnorm(/* Real    */ ae_vector* x,
     ae_int_t n,
     ae_int_t normtype,
     ae_state *_state);
static void testnearestneighborunit_testkdtuniform(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t ny,
     ae_int_t normtype,
     ae_bool* kdterrors,
     ae_state *_state);
static void testnearestneighborunit_testkdtreeserialization(ae_bool* err,
     ae_state *_state);
static ae_bool testnearestneighborunit_testspecialcases(ae_state *_state);





/*************************************************************************
Testing Nearest Neighbor Search
*************************************************************************/
ae_bool testnearestneighbor(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_int_t normtype;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t n;
    ae_int_t smalln;
    ae_int_t largen;
    ae_int_t passcount;
    ae_int_t pass;
    ae_bool waserrors;
    ae_bool kdterrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    kdterrors = ae_false;
    passcount = 2;
    smalln = 256;
    largen = 2048;
    ny = 3;
    
    /*
     *
     */
    testnearestneighborunit_testkdtreeserialization(&kdterrors, _state);
    for(pass=1; pass<=passcount; pass++)
    {
        for(normtype=0; normtype<=2; normtype++)
        {
            for(nx=1; nx<=3; nx++)
            {
                
                /*
                 * Test in hypercube
                 */
                ae_matrix_set_length(&xy, largen, nx+ny, _state);
                for(i=0; i<=largen-1; i++)
                {
                    for(j=0; j<=nx+ny-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = 10*ae_randomreal(_state)-5;
                    }
                }
                for(n=1; n<=10; n++)
                {
                    testnearestneighborunit_testkdtuniform(&xy, n, nx, ae_randominteger(ny+1, _state), normtype, &kdterrors, _state);
                }
                testnearestneighborunit_testkdtuniform(&xy, largen, nx, ae_randominteger(ny+1, _state), normtype, &kdterrors, _state);
                
                /*
                 * Test clustered (2*N points, pairs of equal points)
                 */
                ae_matrix_set_length(&xy, 2*smalln, nx+ny, _state);
                for(i=0; i<=smalln-1; i++)
                {
                    for(j=0; j<=nx+ny-1; j++)
                    {
                        xy.ptr.pp_double[2*i+0][j] = 10*ae_randomreal(_state)-5;
                        xy.ptr.pp_double[2*i+1][j] = xy.ptr.pp_double[2*i+0][j];
                    }
                }
                testnearestneighborunit_testkdtuniform(&xy, 2*smalln, nx, ae_randominteger(ny+1, _state), normtype, &kdterrors, _state);
                
                /*
                 * Test degenerate case: all points are same except for one
                 */
                ae_matrix_set_length(&xy, smalln, nx+ny, _state);
                v = ae_randomreal(_state);
                for(i=0; i<=smalln-2; i++)
                {
                    for(j=0; j<=nx+ny-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = v;
                    }
                }
                for(j=0; j<=nx+ny-1; j++)
                {
                    xy.ptr.pp_double[smalln-1][j] = 10*ae_randomreal(_state)-5;
                }
                testnearestneighborunit_testkdtuniform(&xy, smalln, nx, ae_randominteger(ny+1, _state), normtype, &kdterrors, _state);
            }
        }
    }
    kdterrors = kdterrors||testnearestneighborunit_testspecialcases(_state);
    
    /*
     * report
     */
    waserrors = kdterrors;
    if( !silent )
    {
        printf("TESTING NEAREST NEIGHBOR SEARCH\n");
        printf("* KD TREES:                              ");
        if( !kdterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Compare results from different queries:
* X     just X-values
* XY    X-values and Y-values
* XT    X-values and tag values
*************************************************************************/
static ae_bool testnearestneighborunit_kdtresultsdifferent(/* Real    */ ae_matrix* refxy,
     ae_int_t ntotal,
     /* Real    */ ae_matrix* qx,
     /* Real    */ ae_matrix* qxy,
     /* Integer */ ae_vector* qt,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t ny,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_false;
    for(i=0; i<=n-1; i++)
    {
        if( qt->ptr.p_int[i]<0||qt->ptr.p_int[i]>=ntotal )
        {
            result = ae_true;
            return result;
        }
        for(j=0; j<=nx-1; j++)
        {
            result = result||ae_fp_neq(qx->ptr.pp_double[i][j],refxy->ptr.pp_double[qt->ptr.p_int[i]][j]);
            result = result||ae_fp_neq(qxy->ptr.pp_double[i][j],refxy->ptr.pp_double[qt->ptr.p_int[i]][j]);
        }
        for(j=0; j<=ny-1; j++)
        {
            result = result||ae_fp_neq(qxy->ptr.pp_double[i][nx+j],refxy->ptr.pp_double[qt->ptr.p_int[i]][nx+j]);
        }
    }
    return result;
}


/*************************************************************************
Returns norm
*************************************************************************/
static double testnearestneighborunit_vnorm(/* Real    */ ae_vector* x,
     ae_int_t n,
     ae_int_t normtype,
     ae_state *_state)
{
    ae_int_t i;
    double result;


    result = ae_randomreal(_state);
    if( normtype==0 )
    {
        result = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            result = ae_maxreal(result, ae_fabs(x->ptr.p_double[i], _state), _state);
        }
        return result;
    }
    if( normtype==1 )
    {
        result = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            result = result+ae_fabs(x->ptr.p_double[i], _state);
        }
        return result;
    }
    if( normtype==2 )
    {
        result = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            result = result+ae_sqr(x->ptr.p_double[i], _state);
        }
        result = ae_sqrt(result, _state);
        return result;
    }
    return result;
}


/*************************************************************************
Testing Nearest Neighbor Search on uniformly distributed hypercube

NormType: 0, 1, 2
D: space dimension
N: points count
*************************************************************************/
static void testnearestneighborunit_testkdtuniform(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t ny,
     ae_int_t normtype,
     ae_bool* kdterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double errtol;
    ae_vector tags;
    ae_vector ptx;
    ae_vector tmpx;
    ae_vector tmpb;
    kdtree treex;
    kdtree treexy;
    kdtree treext;
    kdtreerequestbuffer bufx;
    kdtreerequestbuffer bufxy;
    kdtreerequestbuffer bufxt;
    ae_matrix qx;
    ae_matrix qxy;
    ae_vector qtags;
    ae_vector qr;
    ae_vector boxmin;
    ae_vector boxmax;
    ae_vector qmin;
    ae_vector qmax;
    double spread;
    ae_int_t kx;
    ae_int_t kxy;
    ae_int_t kt;
    double eps;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t task;
    ae_bool isequal;
    double r;
    ae_int_t q;
    ae_int_t qcount;
    double v;
    ae_bool inthebox;

    ae_frame_make(_state, &_frame_block);
    memset(&tags, 0, sizeof(tags));
    memset(&ptx, 0, sizeof(ptx));
    memset(&tmpx, 0, sizeof(tmpx));
    memset(&tmpb, 0, sizeof(tmpb));
    memset(&treex, 0, sizeof(treex));
    memset(&treexy, 0, sizeof(treexy));
    memset(&treext, 0, sizeof(treext));
    memset(&bufx, 0, sizeof(bufx));
    memset(&bufxy, 0, sizeof(bufxy));
    memset(&bufxt, 0, sizeof(bufxt));
    memset(&qx, 0, sizeof(qx));
    memset(&qxy, 0, sizeof(qxy));
    memset(&qtags, 0, sizeof(qtags));
    memset(&qr, 0, sizeof(qr));
    memset(&boxmin, 0, sizeof(boxmin));
    memset(&boxmax, 0, sizeof(boxmax));
    memset(&qmin, 0, sizeof(qmin));
    memset(&qmax, 0, sizeof(qmax));
    ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
    ae_vector_init(&ptx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpb, 0, DT_BOOL, _state, ae_true);
    _kdtree_init(&treex, _state, ae_true);
    _kdtree_init(&treexy, _state, ae_true);
    _kdtree_init(&treext, _state, ae_true);
    _kdtreerequestbuffer_init(&bufx, _state, ae_true);
    _kdtreerequestbuffer_init(&bufxy, _state, ae_true);
    _kdtreerequestbuffer_init(&bufxt, _state, ae_true);
    ae_matrix_init(&qx, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&qxy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qtags, 0, DT_INT, _state, ae_true);
    ae_vector_init(&qr, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&boxmin, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&boxmax, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qmin, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qmax, 0, DT_REAL, _state, ae_true);

    qcount = 10;
    ae_assert(n>0, "Assertion failed", _state);
    
    /*
     * Tol - roundoff error tolerance (for '>=' comparisons)
     */
    errtol = 100000*ae_machineepsilon;
    
    /*
     * Evaluate bounding box and spread.
     */
    ae_vector_set_length(&boxmin, nx, _state);
    ae_vector_set_length(&boxmax, nx, _state);
    for(j=0; j<=nx-1; j++)
    {
        boxmin.ptr.p_double[j] = xy->ptr.pp_double[0][j];
        boxmax.ptr.p_double[j] = xy->ptr.pp_double[0][j];
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=nx-1; j++)
        {
            boxmin.ptr.p_double[j] = ae_minreal(boxmin.ptr.p_double[j], xy->ptr.pp_double[i][j], _state);
            boxmax.ptr.p_double[j] = ae_maxreal(boxmax.ptr.p_double[j], xy->ptr.pp_double[i][j], _state);
        }
    }
    spread = (double)(0);
    for(j=0; j<=nx-1; j++)
    {
        spread = ae_maxreal(spread, boxmax.ptr.p_double[j]-boxmin.ptr.p_double[j], _state);
    }
    if( ae_fp_eq(spread,(double)(0)) )
    {
        spread = (double)(1);
    }
    
    /*
     * fill tags
     */
    ae_vector_set_length(&tags, n, _state);
    for(i=0; i<=n-1; i++)
    {
        tags.ptr.p_int[i] = i;
    }
    
    /*
     * build trees
     */
    kdtreebuild(xy, n, nx, 0, normtype, &treex, _state);
    kdtreebuild(xy, n, nx, ny, normtype, &treexy, _state);
    kdtreebuildtagged(xy, &tags, n, nx, 0, normtype, &treext, _state);
    
    /*
     * allocate arrays
     */
    ae_vector_set_length(&tmpx, nx, _state);
    ae_vector_set_length(&tmpb, n, _state);
    ae_matrix_set_length(&qx, n, nx, _state);
    ae_matrix_set_length(&qxy, n, nx+ny, _state);
    ae_vector_set_length(&qtags, n, _state);
    ae_vector_set_length(&qr, n, _state);
    ae_vector_set_length(&ptx, nx, _state);
    
    /*
     * test general K-NN queries (with self-matches):
     * * compare results from different trees (must be equal) and
     *   check that correct (value,tag) pairs are returned
     * * test results from XT tree - let R be radius of query result.
     *   then all points not in result must be not closer than R.
     */
    for(q=1; q<=qcount; q++)
    {
        
        /*
         * Select K: 1..N
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            k = 1+ae_randominteger(n, _state);
        }
        else
        {
            k = 1;
        }
        
        /*
         * Select point (either one of the points, or random)
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            i = ae_randominteger(n, _state);
            ae_v_move(&ptx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
        }
        else
        {
            for(i=0; i<=nx-1; i++)
            {
                ptx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * Test functions which use internal buffer:
         * * consistency of results from different queries
         * * points in query are IN the R-sphere (or at the boundary),
         *   and points not in query are outside of the R-sphere (or at the boundary)
         * * distances are correct and are ordered
         */
        kx = kdtreequeryknn(&treex, &ptx, k, ae_true, _state);
        kxy = kdtreequeryknn(&treexy, &ptx, k, ae_true, _state);
        kt = kdtreequeryknn(&treext, &ptx, k, ae_true, _state);
        if( (kx!=k||kxy!=k)||kt!=k )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        unsetrealmatrix(&qx, _state);
        unsetrealmatrix(&qxy, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreequeryresultsxi(&treex, &qx, _state);
        kdtreequeryresultsxyi(&treexy, &qxy, _state);
        kdtreequeryresultstagsi(&treext, &qtags, _state);
        kdtreequeryresultsdistancesi(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        unsetrealmatrix(&qx, _state);
        unsetrealmatrix(&qxy, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreequeryresultsx(&treex, &qx, _state);
        kdtreequeryresultsxy(&treexy, &qxy, _state);
        kdtreequeryresultstags(&treext, &qtags, _state);
        kdtreequeryresultsdistances(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_true;
        }
        r = (double)(0);
        for(i=0; i<=k-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_false;
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &qx.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            r = ae_maxreal(r, testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state), _state);
        }
        for(i=0; i<=n-1; i++)
        {
            if( tmpb.ptr.p_bool[i] )
            {
                ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
                ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                *kdterrors = *kdterrors||ae_fp_less(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1-errtol));
            }
        }
        for(i=0; i<=k-2; i++)
        {
            *kdterrors = *kdterrors||ae_fp_greater(qr.ptr.p_double[i],qr.ptr.p_double[i+1]);
        }
        for(i=0; i<=k-1; i++)
        {
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[qtags.ptr.p_int[i]][0], 1, ae_v_len(0,nx-1));
            *kdterrors = *kdterrors||ae_fp_greater(ae_fabs(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state)-qr.ptr.p_double[i], _state),errtol);
        }
        
        /*
         * Test functions which use external buffer:
         * * create external request buffer, perform buffered request
         * * reset status of internal buffer by performing 1-NN query, it is essential
         *   to test the fact that external buffer is used
         * * check consistency of results from different queries
         * * check that points in query are IN the R-sphere (or at the boundary),
         *   and points not in query are outside of the R-sphere (or at the boundary)
         * * check that distances are correct and are ordered
         */
        kdtreecreaterequestbuffer(&treex, &bufx, _state);
        kdtreecreaterequestbuffer(&treexy, &bufxy, _state);
        kdtreecreaterequestbuffer(&treext, &bufxt, _state);
        kx = kdtreetsqueryknn(&treex, &bufx, &ptx, k, ae_true, _state);
        kxy = kdtreetsqueryknn(&treexy, &bufxy, &ptx, k, ae_true, _state);
        kt = kdtreetsqueryknn(&treext, &bufxt, &ptx, k, ae_true, _state);
        kdtreequeryknn(&treex, &ptx, 1, ae_true, _state);
        kdtreequeryknn(&treexy, &ptx, 1, ae_true, _state);
        kdtreequeryknn(&treext, &ptx, 1, ae_true, _state);
        if( (kx!=k||kxy!=k)||kt!=k )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        unsetrealmatrix(&qx, _state);
        unsetrealmatrix(&qxy, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreetsqueryresultsx(&treex, &bufx, &qx, _state);
        kdtreetsqueryresultsxy(&treexy, &bufxy, &qxy, _state);
        kdtreetsqueryresultstags(&treext, &bufxt, &qtags, _state);
        kdtreetsqueryresultsdistances(&treext, &bufxt, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_true;
        }
        r = (double)(0);
        for(i=0; i<=k-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_false;
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &qx.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            r = ae_maxreal(r, testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state), _state);
        }
        for(i=0; i<=n-1; i++)
        {
            if( tmpb.ptr.p_bool[i] )
            {
                ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
                ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                *kdterrors = *kdterrors||ae_fp_less(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1-errtol));
            }
        }
        for(i=0; i<=k-2; i++)
        {
            *kdterrors = *kdterrors||ae_fp_greater(qr.ptr.p_double[i],qr.ptr.p_double[i+1]);
        }
        for(i=0; i<=k-1; i++)
        {
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[qtags.ptr.p_int[i]][0], 1, ae_v_len(0,nx-1));
            *kdterrors = *kdterrors||ae_fp_greater(ae_fabs(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state)-qr.ptr.p_double[i], _state),errtol);
        }
        
        /*
         * Test reallocation properties: functions must automatically
         * resize array which is too small, but leave unchanged array which is
         * too large.
         */
        if( n>=2 )
        {
            
            /*
             * First step: array is too small, two elements are required
             */
            k = 2;
            kx = kdtreequeryknn(&treex, &ptx, k, ae_true, _state);
            kxy = kdtreequeryknn(&treexy, &ptx, k, ae_true, _state);
            kt = kdtreequeryknn(&treext, &ptx, k, ae_true, _state);
            if( (kx!=k||kxy!=k)||kt!=k )
            {
                *kdterrors = ae_true;
                ae_frame_leave(_state);
                return;
            }
            ae_matrix_set_length(&qx, 1, 1, _state);
            ae_matrix_set_length(&qxy, 1, 1, _state);
            ae_vector_set_length(&qtags, 1, _state);
            ae_vector_set_length(&qr, 1, _state);
            kdtreequeryresultsx(&treex, &qx, _state);
            kdtreequeryresultsxy(&treexy, &qxy, _state);
            kdtreequeryresultstags(&treext, &qtags, _state);
            kdtreequeryresultsdistances(&treext, &qr, _state);
            *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
            
            /*
             * Second step: array is one row larger than needed, so only first
             * row is overwritten. Test it.
             */
            k = 1;
            kx = kdtreequeryknn(&treex, &ptx, k, ae_true, _state);
            kxy = kdtreequeryknn(&treexy, &ptx, k, ae_true, _state);
            kt = kdtreequeryknn(&treext, &ptx, k, ae_true, _state);
            if( (kx!=k||kxy!=k)||kt!=k )
            {
                *kdterrors = ae_true;
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nx-1; i++)
            {
                qx.ptr.pp_double[1][i] = _state->v_nan;
            }
            for(i=0; i<=nx+ny-1; i++)
            {
                qxy.ptr.pp_double[1][i] = _state->v_nan;
            }
            qtags.ptr.p_int[1] = 999;
            qr.ptr.p_double[1] = _state->v_nan;
            kdtreequeryresultsx(&treex, &qx, _state);
            kdtreequeryresultsxy(&treexy, &qxy, _state);
            kdtreequeryresultstags(&treext, &qtags, _state);
            kdtreequeryresultsdistances(&treext, &qr, _state);
            *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
            for(i=0; i<=nx-1; i++)
            {
                *kdterrors = *kdterrors||!ae_isnan(qx.ptr.pp_double[1][i], _state);
            }
            for(i=0; i<=nx+ny-1; i++)
            {
                *kdterrors = *kdterrors||!ae_isnan(qxy.ptr.pp_double[1][i], _state);
            }
            *kdterrors = *kdterrors||!(qtags.ptr.p_int[1]==999);
            *kdterrors = *kdterrors||!ae_isnan(qr.ptr.p_double[1], _state);
        }
        
        /*
         * Test reallocation properties: 'interactive' functions must allocate
         * new array on each call.
         */
        if( n>=2 )
        {
            
            /*
             * On input array is either too small or too large
             */
            for(k=1; k<=2; k++)
            {
                ae_assert(k==1||k==2, "KNN: internal error (unexpected K)!", _state);
                kx = kdtreequeryknn(&treex, &ptx, k, ae_true, _state);
                kxy = kdtreequeryknn(&treexy, &ptx, k, ae_true, _state);
                kt = kdtreequeryknn(&treext, &ptx, k, ae_true, _state);
                if( (kx!=k||kxy!=k)||kt!=k )
                {
                    *kdterrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                ae_matrix_set_length(&qx, 3-k, 3-k, _state);
                ae_matrix_set_length(&qxy, 3-k, 3-k, _state);
                ae_vector_set_length(&qtags, 3-k, _state);
                ae_vector_set_length(&qr, 3-k, _state);
                kdtreequeryresultsxi(&treex, &qx, _state);
                kdtreequeryresultsxyi(&treexy, &qxy, _state);
                kdtreequeryresultstagsi(&treext, &qtags, _state);
                kdtreequeryresultsdistancesi(&treext, &qr, _state);
                *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
                *kdterrors = (*kdterrors||qx.rows!=k)||qx.cols!=nx;
                *kdterrors = (*kdterrors||qxy.rows!=k)||qxy.cols!=nx+ny;
                *kdterrors = *kdterrors||qtags.cnt!=k;
                *kdterrors = *kdterrors||qr.cnt!=k;
            }
        }
    }
    
    /*
     * test general approximate K-NN queries (with self-matches):
     * * compare results from different trees (must be equal) and
     *   check that correct (value,tag) pairs are returned
     * * test results from XT tree - let R be radius of query result.
     *   then all points not in result must be not closer than R/(1+Eps).
     */
    for(q=1; q<=qcount; q++)
    {
        
        /*
         * Select K: 1..N
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            k = 1+ae_randominteger(n, _state);
        }
        else
        {
            k = 1;
        }
        
        /*
         * Select Eps
         */
        eps = 0.5+ae_randomreal(_state);
        
        /*
         * Select point (either one of the points, or random)
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            i = ae_randominteger(n, _state);
            ae_v_move(&ptx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
        }
        else
        {
            for(i=0; i<=nx-1; i++)
            {
                ptx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * Test functions which use internal buffer:
         * * consistency of results from different queries
         * * points in query are IN the R-sphere (or at the boundary),
         *   and points not in query are outside of the R-sphere (or at the boundary)
         * * distances are correct and are ordered
         */
        kx = kdtreequeryaknn(&treex, &ptx, k, ae_true, eps, _state);
        kxy = kdtreequeryaknn(&treexy, &ptx, k, ae_true, eps, _state);
        kt = kdtreequeryaknn(&treext, &ptx, k, ae_true, eps, _state);
        if( (kx!=k||kxy!=k)||kt!=k )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        unsetrealmatrix(&qx, _state);
        unsetrealmatrix(&qxy, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreequeryresultsxi(&treex, &qx, _state);
        kdtreequeryresultsxyi(&treexy, &qxy, _state);
        kdtreequeryresultstagsi(&treext, &qtags, _state);
        kdtreequeryresultsdistancesi(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        unsetrealmatrix(&qx, _state);
        unsetrealmatrix(&qxy, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreequeryresultsx(&treex, &qx, _state);
        kdtreequeryresultsxy(&treexy, &qxy, _state);
        kdtreequeryresultstags(&treext, &qtags, _state);
        kdtreequeryresultsdistances(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_true;
        }
        r = (double)(0);
        for(i=0; i<=k-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_false;
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &qx.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            r = ae_maxreal(r, testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state), _state);
        }
        for(i=0; i<=n-1; i++)
        {
            if( tmpb.ptr.p_bool[i] )
            {
                ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
                ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                *kdterrors = *kdterrors||ae_fp_less(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1-errtol)/(1+eps));
            }
        }
        for(i=0; i<=k-2; i++)
        {
            *kdterrors = *kdterrors||ae_fp_greater(qr.ptr.p_double[i],qr.ptr.p_double[i+1]);
        }
        for(i=0; i<=k-1; i++)
        {
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[qtags.ptr.p_int[i]][0], 1, ae_v_len(0,nx-1));
            *kdterrors = *kdterrors||ae_fp_greater(ae_fabs(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state)-qr.ptr.p_double[i], _state),errtol);
        }
        
        /*
         * Test functions which use external buffer:
         * * create external request buffer, perform buffered request
         * * reset status of internal buffer by performing 1-NN query, it is essential
         *   to test the fact that external buffer is used
         * * check consistency of results from different queries
         * * check that points in query are IN the R-sphere (or at the boundary),
         *   and points not in query are outside of the R-sphere (or at the boundary)
         * * check that distances are correct and are ordered
         */
        kdtreecreaterequestbuffer(&treex, &bufx, _state);
        kdtreecreaterequestbuffer(&treexy, &bufxy, _state);
        kdtreecreaterequestbuffer(&treext, &bufxt, _state);
        kx = kdtreetsqueryaknn(&treex, &bufx, &ptx, k, ae_true, eps, _state);
        kxy = kdtreetsqueryaknn(&treexy, &bufxy, &ptx, k, ae_true, eps, _state);
        kt = kdtreetsqueryaknn(&treext, &bufxt, &ptx, k, ae_true, eps, _state);
        kdtreequeryknn(&treex, &ptx, 1, ae_true, _state);
        kdtreequeryknn(&treexy, &ptx, 1, ae_true, _state);
        kdtreequeryknn(&treext, &ptx, 1, ae_true, _state);
        if( (kx!=k||kxy!=k)||kt!=k )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        unsetrealmatrix(&qx, _state);
        unsetrealmatrix(&qxy, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreetsqueryresultsx(&treex, &bufx, &qx, _state);
        kdtreetsqueryresultsxy(&treexy, &bufxy, &qxy, _state);
        kdtreetsqueryresultstags(&treext, &bufxt, &qtags, _state);
        kdtreetsqueryresultsdistances(&treext, &bufxt, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_true;
        }
        r = (double)(0);
        for(i=0; i<=k-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_false;
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &qx.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            r = ae_maxreal(r, testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state), _state);
        }
        for(i=0; i<=n-1; i++)
        {
            if( tmpb.ptr.p_bool[i] )
            {
                ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
                ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                *kdterrors = *kdterrors||ae_fp_less(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1-errtol)/(1+eps));
            }
        }
        for(i=0; i<=k-2; i++)
        {
            *kdterrors = *kdterrors||ae_fp_greater(qr.ptr.p_double[i],qr.ptr.p_double[i+1]);
        }
        for(i=0; i<=k-1; i++)
        {
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[qtags.ptr.p_int[i]][0], 1, ae_v_len(0,nx-1));
            *kdterrors = *kdterrors||ae_fp_greater(ae_fabs(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state)-qr.ptr.p_double[i], _state),errtol);
        }
    }
    
    /*
     * test general R-NN queries  (with self-matches):
     * * compare results from different trees (must be equal) and
     *   check that correct (value,tag) pairs are returned
     * * test results from XT tree - let R be radius of query result.
     *   then all points not in result must be not closer than R.
     */
    for(q=1; q<=qcount; q++)
    {
        
        /*
         * Select R
         */
        if( ae_fp_greater(ae_randomreal(_state),0.3) )
        {
            r = ae_maxreal(ae_randomreal(_state), ae_machineepsilon, _state);
        }
        else
        {
            r = ae_machineepsilon;
        }
        
        /*
         * Select point (either one of the points, or random)
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            i = ae_randominteger(n, _state);
            ae_v_move(&ptx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
        }
        else
        {
            for(i=0; i<=nx-1; i++)
            {
                ptx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * Test functions which use internal buffer:
         * * consistency of results from different queries
         * * points in query are IN the R-sphere (or at the boundary),
         *   and points not in query are outside of the R-sphere (or at the boundary)
         * * distances are correct and are ordered
         */
        kx = kdtreequeryrnn(&treex, &ptx, r, ae_true, _state);
        kxy = kdtreequeryrnn(&treexy, &ptx, r, ae_true, _state);
        kt = kdtreequeryrnn(&treext, &ptx, r, ae_true, _state);
        if( kxy!=kx||kt!=kx )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        unsetrealmatrix(&qx, _state);
        unsetrealmatrix(&qxy, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreequeryresultsxi(&treex, &qx, _state);
        kdtreequeryresultsxyi(&treexy, &qxy, _state);
        kdtreequeryresultstagsi(&treext, &qtags, _state);
        kdtreequeryresultsdistancesi(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, kx, nx, ny, _state);
        unsetrealmatrix(&qx, _state);
        unsetrealmatrix(&qxy, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreequeryresultsx(&treex, &qx, _state);
        kdtreequeryresultsxy(&treexy, &qxy, _state);
        kdtreequeryresultstags(&treext, &qtags, _state);
        kdtreequeryresultsdistances(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, kx, nx, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_true;
        }
        for(i=0; i<=kx-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_false;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            if( tmpb.ptr.p_bool[i] )
            {
                *kdterrors = *kdterrors||ae_fp_less(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1-errtol));
            }
            else
            {
                *kdterrors = *kdterrors||ae_fp_greater(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1+errtol));
            }
        }
        for(i=0; i<=kx-2; i++)
        {
            *kdterrors = *kdterrors||ae_fp_greater(qr.ptr.p_double[i],qr.ptr.p_double[i+1]);
        }
        
        /*
         * Test functions which use external buffer:
         * * create external request buffer, perform buffered request
         * * reset status of internal buffer by performing 1-NN query, it is essential
         *   to test the fact that external buffer is used
         * * check consistency of results from different queries
         * * check that points in query are IN the R-sphere (or at the boundary),
         *   and points not in query are outside of the R-sphere (or at the boundary)
         * * check that distances are correct and are ordered
         */
        kdtreecreaterequestbuffer(&treex, &bufx, _state);
        kdtreecreaterequestbuffer(&treexy, &bufxy, _state);
        kdtreecreaterequestbuffer(&treext, &bufxt, _state);
        kx = kdtreetsqueryrnn(&treex, &bufx, &ptx, r, ae_true, _state);
        kxy = kdtreetsqueryrnn(&treexy, &bufxy, &ptx, r, ae_true, _state);
        kt = kdtreetsqueryrnn(&treext, &bufxt, &ptx, r, ae_true, _state);
        kdtreequeryknn(&treex, &ptx, 1, ae_true, _state);
        kdtreequeryknn(&treexy, &ptx, 1, ae_true, _state);
        kdtreequeryknn(&treext, &ptx, 1, ae_true, _state);
        if( kxy!=kx||kt!=kx )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        unsetrealmatrix(&qx, _state);
        unsetrealmatrix(&qxy, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreetsqueryresultsx(&treex, &bufx, &qx, _state);
        kdtreetsqueryresultsxy(&treexy, &bufxy, &qxy, _state);
        kdtreetsqueryresultstags(&treext, &bufxt, &qtags, _state);
        kdtreetsqueryresultsdistances(&treext, &bufxt, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, kx, nx, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_true;
        }
        for(i=0; i<=kx-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_false;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            if( tmpb.ptr.p_bool[i] )
            {
                *kdterrors = *kdterrors||ae_fp_less(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1-errtol));
            }
            else
            {
                *kdterrors = *kdterrors||ae_fp_greater(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1+errtol));
            }
        }
        for(i=0; i<=kx-2; i++)
        {
            *kdterrors = *kdterrors||ae_fp_greater(qr.ptr.p_double[i],qr.ptr.p_double[i+1]);
        }
    }
    
    /*
     * test box queries
     */
    ae_vector_set_length(&qmin, nx, _state);
    ae_vector_set_length(&qmax, nx, _state);
    for(q=1; q<=qcount; q++)
    {
        
        /*
         * Test for box exactly equal to one of the points.
         * More than one exactly equal point may be found.
         * Only thread-safe version is tested.
         */
        kdtreecreaterequestbuffer(&treext, &bufxt, _state);
        k = ae_randominteger(n, _state);
        for(j=0; j<=nx-1; j++)
        {
            qmin.ptr.p_double[j] = xy->ptr.pp_double[k][j];
            qmax.ptr.p_double[j] = xy->ptr.pp_double[k][j];
        }
        kx = kdtreetsquerybox(&treext, &bufxt, &qmin, &qmax, _state);
        if( kx<1 )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        unsetrealmatrix(&qx, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreetsqueryresultsx(&treext, &bufxt, &qx, _state);
        kdtreetsqueryresultstags(&treext, &bufxt, &qtags, _state);
        kdtreetsqueryresultsdistances(&treext, &bufxt, &qr, _state);
        for(i=0; i<=kx-1; i++)
        {
            ae_set_error_flag(kdterrors, ae_fp_neq(qr.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testnearestneighborunit.ap:753");
        }
        for(i=0; i<=kx-1; i++)
        {
            for(j=0; j<=nx-1; j++)
            {
                ae_set_error_flag(kdterrors, ae_fp_neq(qx.ptr.pp_double[i][j],xy->ptr.pp_double[k][j]), __FILE__, __LINE__, "testnearestneighborunit.ap:758");
                ae_set_error_flag(kdterrors, ae_fp_neq(qx.ptr.pp_double[i][j],xy->ptr.pp_double[qtags.ptr.p_int[i]][j]), __FILE__, __LINE__, "testnearestneighborunit.ap:759");
            }
        }
        
        /*
         * Test for randomly generated box (thread-safe version)
         */
        for(j=0; j<=nx-1; j++)
        {
            qmin.ptr.p_double[j] = boxmin.ptr.p_double[j]+ae_randomreal(_state)*(boxmax.ptr.p_double[j]-boxmin.ptr.p_double[j]);
            qmax.ptr.p_double[j] = qmin.ptr.p_double[j];
            v = spread*ae_pow((double)(10), -2*ae_randomreal(_state), _state);
            qmin.ptr.p_double[j] = qmin.ptr.p_double[j]-v;
            qmax.ptr.p_double[j] = qmax.ptr.p_double[j]+v;
        }
        kx = kdtreetsquerybox(&treext, &bufxt, &qmin, &qmax, _state);
        unsetrealmatrix(&qx, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreetsqueryresultsx(&treext, &bufxt, &qx, _state);
        kdtreetsqueryresultstags(&treext, &bufxt, &qtags, _state);
        kdtreetsqueryresultsdistances(&treext, &bufxt, &qr, _state);
        for(i=0; i<=kx-1; i++)
        {
            ae_set_error_flag(kdterrors, ae_fp_neq(qr.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testnearestneighborunit.ap:782");
        }
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_false;
        }
        for(i=0; i<=kx-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_true;
        }
        for(i=0; i<=n-1; i++)
        {
            inthebox = ae_true;
            for(j=0; j<=nx-1; j++)
            {
                inthebox = inthebox&&ae_fp_greater_eq(xy->ptr.pp_double[i][j],qmin.ptr.p_double[j]);
                inthebox = inthebox&&ae_fp_less_eq(xy->ptr.pp_double[i][j],qmax.ptr.p_double[j]);
            }
            if( tmpb.ptr.p_bool[i] )
            {
                ae_set_error_flag(kdterrors, !inthebox, __FILE__, __LINE__, "testnearestneighborunit.ap:796");
            }
            else
            {
                ae_set_error_flag(kdterrors, inthebox, __FILE__, __LINE__, "testnearestneighborunit.ap:798");
            }
        }
        
        /*
         * Test for randomly generated box (non-thread-safe version)
         */
        for(j=0; j<=nx-1; j++)
        {
            qmin.ptr.p_double[j] = boxmin.ptr.p_double[j]+ae_randomreal(_state)*(boxmax.ptr.p_double[j]-boxmin.ptr.p_double[j]);
            qmax.ptr.p_double[j] = qmin.ptr.p_double[j];
            v = spread*ae_pow((double)(10), -2*ae_randomreal(_state), _state);
            qmin.ptr.p_double[j] = qmin.ptr.p_double[j]-v;
            qmax.ptr.p_double[j] = qmax.ptr.p_double[j]+v;
        }
        kx = kdtreequerybox(&treext, &qmin, &qmax, _state);
        unsetrealmatrix(&qx, _state);
        unsetintegerarray(&qtags, _state);
        unsetrealarray(&qr, _state);
        kdtreequeryresultsx(&treext, &qx, _state);
        kdtreequeryresultstags(&treext, &qtags, _state);
        kdtreequeryresultsdistances(&treext, &qr, _state);
        for(i=0; i<=kx-1; i++)
        {
            ae_set_error_flag(kdterrors, ae_fp_neq(qr.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testnearestneighborunit.ap:820");
        }
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_false;
        }
        for(i=0; i<=kx-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_true;
        }
        for(i=0; i<=n-1; i++)
        {
            inthebox = ae_true;
            for(j=0; j<=nx-1; j++)
            {
                inthebox = inthebox&&ae_fp_greater_eq(xy->ptr.pp_double[i][j],qmin.ptr.p_double[j]);
                inthebox = inthebox&&ae_fp_less_eq(xy->ptr.pp_double[i][j],qmax.ptr.p_double[j]);
            }
            if( tmpb.ptr.p_bool[i] )
            {
                ae_set_error_flag(kdterrors, !inthebox, __FILE__, __LINE__, "testnearestneighborunit.ap:834");
            }
            else
            {
                ae_set_error_flag(kdterrors, inthebox, __FILE__, __LINE__, "testnearestneighborunit.ap:836");
            }
        }
    }
    
    /*
     * Test self-matching:
     * * self-match - nearest neighbor of each point in XY is the point itself
     * * no self-match - nearest neighbor is NOT the point itself
     */
    if( n>1 )
    {
        
        /*
         * test for N=1 have non-general form, but it is not really needed
         */
        for(task=0; task<=1; task++)
        {
            for(i=0; i<=n-1; i++)
            {
                ae_v_move(&ptx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                kx = kdtreequeryknn(&treex, &ptx, 1, task==0, _state);
                kdtreequeryresultsxi(&treex, &qx, _state);
                if( kx!=1 )
                {
                    *kdterrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                isequal = ae_true;
                for(j=0; j<=nx-1; j++)
                {
                    isequal = isequal&&ae_fp_eq(qx.ptr.pp_double[0][j],ptx.ptr.p_double[j]);
                }
                if( task==0 )
                {
                    *kdterrors = *kdterrors||!isequal;
                }
                else
                {
                    *kdterrors = *kdterrors||isequal;
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Testing serialization of KD trees

This function sets Err to True on errors, but leaves it unchanged on success
*************************************************************************/
static void testnearestneighborunit_testkdtreeserialization(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t normtype;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t q;
    ae_matrix xy;
    ae_vector x;
    ae_vector tags;
    ae_vector qsizes;
    double threshold;
    kdtree tree0;
    kdtree tree1;
    ae_int_t k0;
    ae_int_t k1;
    ae_matrix xy0;
    ae_matrix xy1;
    ae_vector tags0;
    ae_vector tags1;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&x, 0, sizeof(x));
    memset(&tags, 0, sizeof(tags));
    memset(&qsizes, 0, sizeof(qsizes));
    memset(&tree0, 0, sizeof(tree0));
    memset(&tree1, 0, sizeof(tree1));
    memset(&xy0, 0, sizeof(xy0));
    memset(&xy1, 0, sizeof(xy1));
    memset(&tags0, 0, sizeof(tags0));
    memset(&tags1, 0, sizeof(tags1));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
    ae_vector_init(&qsizes, 0, DT_INT, _state, ae_true);
    _kdtree_init(&tree0, _state, ae_true);
    _kdtree_init(&tree1, _state, ae_true);
    ae_matrix_init(&xy0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy1, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tags0, 0, DT_INT, _state, ae_true);
    ae_vector_init(&tags1, 0, DT_INT, _state, ae_true);

    threshold = 100*ae_machineepsilon;
    
    /*
     * different N, NX, NY, NormType
     */
    n = 1;
    while(n<=51)
    {
        
        /*
         * prepare array with query sizes
         */
        ae_vector_set_length(&qsizes, 4, _state);
        qsizes.ptr.p_int[0] = 1;
        qsizes.ptr.p_int[1] = ae_minint(2, n, _state);
        qsizes.ptr.p_int[2] = ae_minint(4, n, _state);
        qsizes.ptr.p_int[3] = n;
        
        /*
         * different NX/NY/NormType
         */
        for(nx=1; nx<=2; nx++)
        {
            for(ny=0; ny<=2; ny++)
            {
                for(normtype=0; normtype<=2; normtype++)
                {
                    
                    /*
                     * Prepare data
                     */
                    ae_matrix_set_length(&xy, n, nx+ny, _state);
                    ae_vector_set_length(&tags, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=nx+ny-1; j++)
                        {
                            xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                        }
                        tags.ptr.p_int[i] = ae_randominteger(100, _state);
                    }
                    
                    /*
                     * Build tree, pass it through serializer
                     */
                    kdtreebuildtagged(&xy, &tags, n, nx, ny, normtype, &tree0, _state);
                    {
                        /*
                         * This code passes data structure through serializers
                         * (serializes it to string and loads back)
                         */
                        ae_serializer _local_serializer;
                        ae_int_t _local_ssize;
                        ae_frame _local_frame_block;
                        ae_dyn_block _local_dynamic_block;
                        
                        ae_frame_make(_state, &_local_frame_block);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_alloc_start(&_local_serializer);
                        kdtreealloc(&_local_serializer, &tree0, _state);
                        _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                        memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                        ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                        ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        kdtreeserialize(&_local_serializer, &tree0, _state);
                        ae_serializer_stop(&_local_serializer, _state);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        kdtreeunserialize(&_local_serializer, &tree1, _state);
                        ae_serializer_stop(&_local_serializer, _state);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_frame_leave(_state);
                    }
                    
                    /*
                     * For each point of XY we make queries with different sizes
                     */
                    ae_vector_set_length(&x, nx, _state);
                    for(k=0; k<=n-1; k++)
                    {
                        for(q=0; q<=qsizes.cnt-1; q++)
                        {
                            ae_v_move(&x.ptr.p_double[0], 1, &xy.ptr.pp_double[k][0], 1, ae_v_len(0,nx-1));
                            k0 = kdtreequeryknn(&tree0, &x, qsizes.ptr.p_int[q], ae_true, _state);
                            k1 = kdtreequeryknn(&tree1, &x, qsizes.ptr.p_int[q], ae_true, _state);
                            if( k0!=k1 )
                            {
                                *err = ae_true;
                                ae_frame_leave(_state);
                                return;
                            }
                            kdtreequeryresultsxy(&tree0, &xy0, _state);
                            kdtreequeryresultsxy(&tree1, &xy1, _state);
                            for(i=0; i<=k0-1; i++)
                            {
                                for(j=0; j<=nx+ny-1; j++)
                                {
                                    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[i][j]-xy1.ptr.pp_double[i][j], _state),threshold) )
                                    {
                                        *err = ae_true;
                                        ae_frame_leave(_state);
                                        return;
                                    }
                                }
                            }
                            kdtreequeryresultstags(&tree0, &tags0, _state);
                            kdtreequeryresultstags(&tree1, &tags1, _state);
                            for(i=0; i<=k0-1; i++)
                            {
                                if( tags0.ptr.p_int[i]!=tags1.ptr.p_int[i] )
                                {
                                    *err = ae_true;
                                    ae_frame_leave(_state);
                                    return;
                                }
                            }
                        }
                    }
                }
            }
        }
        
        /*
         * Next N
         */
        n = n+25;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests different special cases:
* Kd-tree for a zero number of points
* Kd-tree for array with a lot of duplicates (early versions of ALGLIB
  raised stack overflow on such datasets)

It returns True on errors, False on success.
*************************************************************************/
static ae_bool testnearestneighborunit_testspecialcases(ae_state *_state)
{
    ae_frame _frame_block;
    kdtree kdt;
    ae_matrix xy;
    ae_vector tags;
    ae_vector x;
    ae_int_t n;
    ae_int_t nk;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t normtype;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&kdt, 0, sizeof(kdt));
    memset(&xy, 0, sizeof(xy));
    memset(&tags, 0, sizeof(tags));
    memset(&x, 0, sizeof(x));
    _kdtree_init(&kdt, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    for(nx=1; nx<=3; nx++)
    {
        for(ny=0; ny<=3; ny++)
        {
            for(normtype=0; normtype<=2; normtype++)
            {
                
                /*
                 * Build tree
                 */
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    kdtreebuildtagged(&xy, &tags, 0, nx, ny, normtype, &kdt, _state);
                }
                else
                {
                    kdtreebuild(&xy, 0, nx, ny, normtype, &kdt, _state);
                }
                
                /*
                 * Test different queries
                 */
                ae_vector_set_length(&x, nx, _state);
                for(i=0; i<=nx-1; i++)
                {
                    x.ptr.p_double[i] = ae_randomreal(_state);
                }
                result = result||kdtreequeryknn(&kdt, &x, 1, ae_true, _state)>0;
                result = result||kdtreequeryrnn(&kdt, &x, 1.0E6, ae_true, _state)>0;
                result = result||kdtreequeryaknn(&kdt, &x, 1, ae_true, 2.0, _state)>0;
            }
        }
    }
    
    /*
     * Ability to handle array with a lot of duplicates without causing
     * stack overflow.
     *
     * Two situations are handled:
     * * array where ALL N elements are duplicates
     * * array where there are NK distinct elements and N-NK duplicates
     */
    nx = 2;
    ny = 1;
    n = 100000;
    nk = 100;
    v = ae_randomreal(_state);
    ae_matrix_set_length(&xy, n, nx+ny, _state);
    ae_vector_set_length(&x, nx, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=nx+ny-1; j++)
        {
            xy.ptr.pp_double[i][j] = v;
        }
    }
    kdtreebuild(&xy, n, nx, ny, 2, &kdt, _state);
    for(j=0; j<=nx-1; j++)
    {
        x.ptr.p_double[j] = v;
    }
    result = result||kdtreequeryrnn(&kdt, &x, 0.0001, ae_true, _state)!=n;
    for(i=0; i<=nk-1; i++)
    {
        for(j=0; j<=nx+ny-1; j++)
        {
            xy.ptr.pp_double[i][j] = ae_randomreal(_state);
        }
    }
    kdtreebuild(&xy, n, nx, ny, 2, &kdt, _state);
    result = result||kdtreequeryrnn(&kdt, &x, 0.0001, ae_true, _state)<n-nk;
    ae_frame_leave(_state);
    return result;
}



static void testhqrndunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state);
static void testhqrndunit_unsetstate(hqrndstate* state, ae_state *_state);





ae_bool testhqrnd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_int_t samplesize;
    double sigmathreshold;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t i;
    ae_int_t k;
    ae_int_t pass;
    ae_int_t s1;
    ae_int_t s2;
    ae_int_t i1;
    ae_int_t i2;
    double r1;
    double r2;
    ae_vector x;
    ae_vector bins;
    double mean;
    double means;
    double stddev;
    double stddevs;
    double lambdav;
    ae_bool seederrors;
    ae_bool urerrors;
    double ursigmaerr;
    ae_bool uierrors;
    double uisigmaerr;
    ae_bool normerrors;
    double normsigmaerr;
    ae_bool unit2errors;
    ae_bool experrors;
    double expsigmaerr;
    ae_bool discreteerr;
    ae_bool continuouserr;
    hqrndstate state;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&bins, 0, sizeof(bins));
    memset(&state, 0, sizeof(state));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bins, 0, DT_INT, _state, ae_true);
    _hqrndstate_init(&state, _state, ae_true);

    waserrors = ae_false;
    sigmathreshold = (double)(7);
    samplesize = 100000;
    passcount = 50;
    seederrors = ae_false;
    urerrors = ae_false;
    uierrors = ae_false;
    normerrors = ae_false;
    experrors = ae_false;
    unit2errors = ae_false;
    ae_vector_set_length(&x, samplesize-1+1, _state);
    
    /*
     * Test seed errors
     */
    for(pass=1; pass<=passcount; pass++)
    {
        s1 = 1+ae_randominteger(32000, _state);
        s2 = 1+ae_randominteger(32000, _state);
        testhqrndunit_unsetstate(&state, _state);
        hqrndseed(s1, s2, &state, _state);
        i1 = hqrnduniformi(&state, 100, _state);
        testhqrndunit_unsetstate(&state, _state);
        hqrndseed(s1, s2, &state, _state);
        i2 = hqrnduniformi(&state, 100, _state);
        seederrors = seederrors||i1!=i2;
        testhqrndunit_unsetstate(&state, _state);
        hqrndseed(s1, s2, &state, _state);
        r1 = hqrnduniformr(&state, _state);
        testhqrndunit_unsetstate(&state, _state);
        hqrndseed(s1, s2, &state, _state);
        r2 = hqrnduniformr(&state, _state);
        seederrors = seederrors||ae_fp_neq(r1,r2);
    }
    
    /*
     * Test HQRNDRandomize() and real uniform generator
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    ursigmaerr = (double)(0);
    for(i=0; i<=samplesize-1; i++)
    {
        x.ptr.p_double[i] = hqrnduniformr(&state, _state);
    }
    for(i=0; i<=samplesize-1; i++)
    {
        urerrors = (urerrors||ae_fp_less_eq(x.ptr.p_double[i],(double)(0)))||ae_fp_greater_eq(x.ptr.p_double[i],(double)(1));
    }
    testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
    if( ae_fp_neq(means,(double)(0)) )
    {
        ursigmaerr = ae_maxreal(ursigmaerr, ae_fabs((mean-0.5)/means, _state), _state);
    }
    else
    {
        urerrors = ae_true;
    }
    if( ae_fp_neq(stddevs,(double)(0)) )
    {
        ursigmaerr = ae_maxreal(ursigmaerr, ae_fabs((stddev-ae_sqrt((double)1/(double)12, _state))/stddevs, _state), _state);
    }
    else
    {
        urerrors = ae_true;
    }
    urerrors = urerrors||ae_fp_greater(ursigmaerr,sigmathreshold);
    
    /*
     * Test HQRNDRandomize() and integer uniform
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    uisigmaerr = (double)(0);
    for(n=2; n<=10; n++)
    {
        for(i=0; i<=samplesize-1; i++)
        {
            x.ptr.p_double[i] = (double)(hqrnduniformi(&state, n, _state));
        }
        for(i=0; i<=samplesize-1; i++)
        {
            uierrors = (uierrors||ae_fp_less(x.ptr.p_double[i],(double)(0)))||ae_fp_greater_eq(x.ptr.p_double[i],(double)(n));
        }
        testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
        if( ae_fp_neq(means,(double)(0)) )
        {
            uisigmaerr = ae_maxreal(uisigmaerr, ae_fabs((mean-0.5*(n-1))/means, _state), _state);
        }
        else
        {
            uierrors = ae_true;
        }
        if( ae_fp_neq(stddevs,(double)(0)) )
        {
            uisigmaerr = ae_maxreal(uisigmaerr, ae_fabs((stddev-ae_sqrt((ae_sqr((double)(n), _state)-1)/12, _state))/stddevs, _state), _state);
        }
        else
        {
            uierrors = ae_true;
        }
    }
    uierrors = uierrors||ae_fp_greater(uisigmaerr,sigmathreshold);
    
    /*
     * Special 'close-to-limit' test on uniformity of integers
     * (straightforward implementation like 'RND mod N' will return
     *  non-uniform numbers for N=2/3*LIMIT)
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    uisigmaerr = (double)(0);
    n = 1431655708;
    for(i=0; i<=samplesize-1; i++)
    {
        x.ptr.p_double[i] = (double)(hqrnduniformi(&state, n, _state));
    }
    for(i=0; i<=samplesize-1; i++)
    {
        uierrors = (uierrors||ae_fp_less(x.ptr.p_double[i],(double)(0)))||ae_fp_greater_eq(x.ptr.p_double[i],(double)(n));
    }
    testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
    if( ae_fp_neq(means,(double)(0)) )
    {
        uisigmaerr = ae_maxreal(uisigmaerr, ae_fabs((mean-0.5*(n-1))/means, _state), _state);
    }
    else
    {
        uierrors = ae_true;
    }
    if( ae_fp_neq(stddevs,(double)(0)) )
    {
        uisigmaerr = ae_maxreal(uisigmaerr, ae_fabs((stddev-ae_sqrt((ae_sqr((double)(n), _state)-1)/12, _state))/stddevs, _state), _state);
    }
    else
    {
        uierrors = ae_true;
    }
    uierrors = uierrors||ae_fp_greater(uisigmaerr,sigmathreshold);
    
    /*
     * Test normal
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    normsigmaerr = (double)(0);
    i = 0;
    while(i<samplesize)
    {
        hqrndnormal2(&state, &r1, &r2, _state);
        x.ptr.p_double[i] = r1;
        if( i+1<samplesize )
        {
            x.ptr.p_double[i+1] = r2;
        }
        i = i+2;
    }
    testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
    if( ae_fp_neq(means,(double)(0)) )
    {
        normsigmaerr = ae_maxreal(normsigmaerr, ae_fabs((mean-0)/means, _state), _state);
    }
    else
    {
        normerrors = ae_true;
    }
    if( ae_fp_neq(stddevs,(double)(0)) )
    {
        normsigmaerr = ae_maxreal(normsigmaerr, ae_fabs((stddev-1)/stddevs, _state), _state);
    }
    else
    {
        normerrors = ae_true;
    }
    normerrors = normerrors||ae_fp_greater(normsigmaerr,sigmathreshold);
    
    /*
     * Test unit2
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    n = 1000000;
    ae_vector_set_length(&bins, 10, _state);
    for(i=0; i<=bins.cnt-1; i++)
    {
        bins.ptr.p_int[i] = 0;
    }
    for(pass=0; pass<=n-1; pass++)
    {
        hqrndunit2(&state, &r1, &r2, _state);
        ae_set_error_flag(&unit2errors, ae_fp_greater(ae_fabs(r1*r1+r2*r2-1, _state),100*ae_machineepsilon), __FILE__, __LINE__, "testhqrndunit.ap:241");
        k = ae_ifloor((ae_atan2(r1, r2, _state)+ae_pi)/(2*ae_pi)*bins.cnt, _state);
        if( k<0 )
        {
            k = 0;
        }
        if( k>=bins.cnt )
        {
            k = bins.cnt-1;
        }
        bins.ptr.p_int[k] = bins.ptr.p_int[k]+1;
    }
    for(i=0; i<=bins.cnt-1; i++)
    {
        ae_set_error_flag(&unit2errors, ae_fp_less((double)(bins.ptr.p_int[i]),0.9*n/bins.cnt)||ae_fp_greater((double)(bins.ptr.p_int[i]),1.1*n/bins.cnt), __FILE__, __LINE__, "testhqrndunit.ap:250");
    }
    
    /*
     * Test exponential
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    expsigmaerr = (double)(0);
    lambdav = 2+5*ae_randomreal(_state);
    for(i=0; i<=samplesize-1; i++)
    {
        x.ptr.p_double[i] = hqrndexponential(&state, lambdav, _state);
    }
    for(i=0; i<=samplesize-1; i++)
    {
        uierrors = uierrors||ae_fp_less(x.ptr.p_double[i],(double)(0));
    }
    testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
    if( ae_fp_neq(means,(double)(0)) )
    {
        expsigmaerr = ae_maxreal(expsigmaerr, ae_fabs((mean-1.0/lambdav)/means, _state), _state);
    }
    else
    {
        experrors = ae_true;
    }
    if( ae_fp_neq(stddevs,(double)(0)) )
    {
        expsigmaerr = ae_maxreal(expsigmaerr, ae_fabs((stddev-1.0/lambdav)/stddevs, _state), _state);
    }
    else
    {
        experrors = ae_true;
    }
    experrors = experrors||ae_fp_greater(expsigmaerr,sigmathreshold);
    
    /*
     *Discrete/Continuous tests
     */
    discreteerr = hqrnddiscretetest(ae_true, _state);
    continuouserr = hqrndcontinuoustest(ae_true, _state);
    
    /*
     * Final report
     */
    waserrors = ((((((seederrors||urerrors)||uierrors)||normerrors)||unit2errors)||experrors)||discreteerr)||continuouserr;
    if( !silent )
    {
        printf("RNG TEST\n");
        printf("SEED TEST:                               ");
        if( !seederrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("UNIFORM CONTINUOUS:                      ");
        if( !urerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("UNIFORM INTEGER:                         ");
        if( !uierrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("NORMAL:                                  ");
        if( !normerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("UNIT2:                                   ");
        if( !unit2errors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("EXPONENTIAL:                             ");
        if( !experrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("DISCRETE:                                ");
        if( !discreteerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("CONTINUOUS:                              ");
        if( !continuouserr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for test HQRNDContinuous function
*************************************************************************/
ae_bool hqrndcontinuoustest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector sample;
    ae_vector bins;
    ae_vector binbounds;
    ae_int_t nb;
    ae_int_t samplesize;
    hqrndstate state;
    ae_int_t xp;
    ae_int_t i;
    ae_int_t j;
    double v;
    double sigma;
    double sigmamax;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&sample, 0, sizeof(sample));
    memset(&bins, 0, sizeof(bins));
    memset(&binbounds, 0, sizeof(binbounds));
    memset(&state, 0, sizeof(state));
    ae_vector_init(&sample, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bins, 0, DT_INT, _state, ae_true);
    ae_vector_init(&binbounds, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&state, _state, ae_true);

    result = ae_false;
    
    /*
     * Test for sample size equal to 1
     */
    ae_vector_set_length(&sample, 1, _state);
    sample.ptr.p_double[0] = ae_randomreal(_state);
    hqrndrandomize(&state, _state);
    result = result||ae_fp_neq(hqrndcontinuous(&state, &sample, 1, _state),sample.ptr.p_double[0]);
    
    /*
     * Test for larger samples
     */
    xp = 100000;
    sigmamax = 10.0;
    for(samplesize=2; samplesize<=5; samplesize++)
    {
        
        /*
         * 1. Generate random sample with SampleSize points
         * 2. Generate NB=3*(SampleSize-1) bins, with bounds as prescribed by (BinBounds[I],BinBounds[I+1]).
         *    Bin bounds are generated in such a way that value can fall into any bin with same probability
         * 3. Generate many random values
         * 4. Calculate number of values which fall into each bin
         * 5. Bins[I] should have binomial distribution with mean XP/NB and 
         *    variance XP*(1/NB)*(1-1/NB)
         */
        nb = 3*(samplesize-1);
        sigma = ae_sqrt(xp*((double)1/(double)nb)*(1-(double)1/(double)nb), _state);
        ae_vector_set_length(&sample, samplesize, _state);
        sample.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
        for(i=0; i<=samplesize-2; i++)
        {
            sample.ptr.p_double[i+1] = sample.ptr.p_double[i]+0.1+ae_randomreal(_state);
        }
        ae_vector_set_length(&bins, nb, _state);
        ae_vector_set_length(&binbounds, nb+1, _state);
        for(i=0; i<=samplesize-2; i++)
        {
            bins.ptr.p_int[3*i+0] = 0;
            bins.ptr.p_int[3*i+1] = 0;
            bins.ptr.p_int[3*i+2] = 0;
            binbounds.ptr.p_double[3*i+0] = sample.ptr.p_double[i];
            binbounds.ptr.p_double[3*i+1] = sample.ptr.p_double[i]+(sample.ptr.p_double[i+1]-sample.ptr.p_double[i])/3;
            binbounds.ptr.p_double[3*i+2] = sample.ptr.p_double[i]+(sample.ptr.p_double[i+1]-sample.ptr.p_double[i])*2/3;
        }
        binbounds.ptr.p_double[nb] = sample.ptr.p_double[samplesize-1];
        hqrndrandomize(&state, _state);
        for(i=0; i<=xp-1; i++)
        {
            v = hqrndcontinuous(&state, &sample, samplesize, _state);
            for(j=0; j<=nb-1; j++)
            {
                if( ae_fp_greater(v,binbounds.ptr.p_double[j])&&ae_fp_less(v,binbounds.ptr.p_double[j+1]) )
                {
                    bins.ptr.p_int[j] = bins.ptr.p_int[j]+1;
                    break;
                }
            }
        }
        for(i=0; i<=nb-1; i++)
        {
            result = result||ae_fp_greater(ae_fabs(bins.ptr.p_int[i]-(double)xp/(double)nb, _state),sigma*sigmamax);
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for test HQRNDContinuous function
*************************************************************************/
ae_bool hqrnddiscretetest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector sample;
    double sigma;
    double sigmathreshold;
    double tsample;
    double max;
    double min;
    ae_int_t i;
    ae_int_t j;
    ae_int_t s1;
    ae_int_t s2;
    ae_int_t binscount;
    ae_int_t xp;
    ae_vector nn;
    hqrndstate state;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&sample, 0, sizeof(sample));
    memset(&nn, 0, sizeof(nn));
    memset(&state, 0, sizeof(state));
    ae_vector_init(&sample, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&nn, 0, DT_INT, _state, ae_true);
    _hqrndstate_init(&state, _state, ae_true);

    
    /*
     * We test that all values from discrete sample are generated with same probability.
     * To do this, we generate random values many times, then we calculate actual probabilities
     * and compare them with theoretical ones.
     */
    max = (double)(100);
    min = (double)(-100);
    xp = 100000;
    sigmathreshold = 10.0;
    for(binscount=1; binscount<=5; binscount++)
    {
        sigma = ae_sqrt(xp*((double)1/(double)binscount)*(1-(double)1/(double)binscount), _state);
        ae_vector_set_length(&nn, binscount, _state);
        for(i=0; i<=binscount-1; i++)
        {
            nn.ptr.p_int[i] = 0;
        }
        ae_vector_set_length(&sample, binscount, _state);
        sample.ptr.p_double[0] = (max-min)*ae_randomreal(_state)+min;
        for(i=1; i<=binscount-1; i++)
        {
            sample.ptr.p_double[i] = sample.ptr.p_double[i-1]+max*ae_randomreal(_state)+0.001;
        }
        s1 = 1+ae_randominteger(32000, _state);
        s2 = 1+ae_randominteger(32000, _state);
        hqrndseed(s1, s2, &state, _state);
        for(i=0; i<=xp-1; i++)
        {
            tsample = hqrnddiscrete(&state, &sample, binscount, _state);
            for(j=0; j<=binscount-1; j++)
            {
                if( ae_fp_eq(tsample,sample.ptr.p_double[j]) )
                {
                    nn.ptr.p_int[j] = nn.ptr.p_int[j]+1;
                    break;
                }
            }
        }
        for(i=0; i<=binscount-1; i++)
        {
            if( ae_fp_less((double)(nn.ptr.p_int[i]),(double)xp/(double)binscount-sigmathreshold*sigma)||ae_fp_greater((double)(nn.ptr.p_int[i]),(double)xp/(double)binscount+sigmathreshold*sigma) )
            {
                if( !silent )
                {
                    printf("HQRNDDiscreteTest::ErrorReport::\n");
                    printf("nn[%0d]=%0d;\n   xp/BinsCount=%0.5f;\n   C*sigma=%0.5f\n",
                        (int)(i),
                        (int)(nn.ptr.p_int[i]),
                        (double)((double)xp/(double)binscount),
                        (double)(sigmathreshold*sigma));
                    printf("HQRNDDiscreteTest: test is FAILED!\n");
                }
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
        if( !silent )
        {
            printf("HQRNDDiscreteTest: test is OK.\n");
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


static void testhqrndunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state)
{
    ae_int_t i;
    double v1;
    double v2;
    double variance;

    *mean = 0;
    *means = 0;
    *stddev = 0;
    *stddevs = 0;

    *mean = (double)(0);
    *means = (double)(1);
    *stddev = (double)(0);
    *stddevs = (double)(1);
    variance = (double)(0);
    if( n<=1 )
    {
        return;
    }
    
    /*
     * Mean
     */
    for(i=0; i<=n-1; i++)
    {
        *mean = *mean+x->ptr.p_double[i];
    }
    *mean = *mean/n;
    
    /*
     * Variance (using corrected two-pass algorithm)
     */
    if( n!=1 )
    {
        v1 = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v1 = v1+ae_sqr(x->ptr.p_double[i]-(*mean), _state);
        }
        v2 = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v2 = v2+(x->ptr.p_double[i]-(*mean));
        }
        v2 = ae_sqr(v2, _state)/n;
        variance = (v1-v2)/(n-1);
        if( ae_fp_less(variance,(double)(0)) )
        {
            variance = (double)(0);
        }
        *stddev = ae_sqrt(variance, _state);
    }
    
    /*
     * Errors
     */
    *means = *stddev/ae_sqrt((double)(n), _state);
    *stddevs = *stddev*ae_sqrt((double)(2), _state)/ae_sqrt((double)(n-1), _state);
}


/*************************************************************************
Unsets HQRNDState structure
*************************************************************************/
static void testhqrndunit_unsetstate(hqrndstate* state, ae_state *_state)
{


    state->s1 = 0;
    state->s2 = 0;
    state->magicv = 0;
}








/*************************************************************************
Test
*************************************************************************/
ae_bool testodesolver(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t passcount;
    ae_bool curerrors;
    ae_bool rkckerrors;
    ae_bool waserrors;
    ae_vector xtbl;
    ae_matrix ytbl;
    odesolverreport rep;
    ae_vector xg;
    ae_vector y;
    double h;
    double eps;
    ae_int_t solver;
    ae_int_t pass;
    ae_int_t mynfev;
    double v;
    ae_int_t m;
    ae_int_t m2;
    ae_int_t i;
    double err;
    odesolverstate state;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xtbl, 0, sizeof(xtbl));
    memset(&ytbl, 0, sizeof(ytbl));
    memset(&rep, 0, sizeof(rep));
    memset(&xg, 0, sizeof(xg));
    memset(&y, 0, sizeof(y));
    memset(&state, 0, sizeof(state));
    ae_vector_init(&xtbl, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ytbl, 0, 0, DT_REAL, _state, ae_true);
    _odesolverreport_init(&rep, _state, ae_true);
    ae_vector_init(&xg, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _odesolverstate_init(&state, _state, ae_true);

    rkckerrors = ae_false;
    waserrors = ae_false;
    passcount = 10;
    
    /*
     * simple test: just A*sin(x)+B*cos(x)
     */
    ae_assert(passcount>=2, "Assertion failed", _state);
    for(pass=0; pass<=passcount-1; pass++)
    {
        for(solver=0; solver<=0; solver++)
        {
            
            /*
             * prepare
             */
            h = 1.0E-2;
            eps = 1.0E-5;
            if( pass%2==0 )
            {
                eps = -eps;
            }
            ae_vector_set_length(&y, 2, _state);
            for(i=0; i<=1; i++)
            {
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            m = 2+ae_randominteger(10, _state);
            ae_vector_set_length(&xg, m, _state);
            xg.ptr.p_double[0] = (m-1)*ae_randomreal(_state);
            for(i=1; i<=m-1; i++)
            {
                xg.ptr.p_double[i] = xg.ptr.p_double[i-1]+ae_randomreal(_state);
            }
            v = 2*ae_pi/(xg.ptr.p_double[m-1]-xg.ptr.p_double[0]);
            ae_v_muld(&xg.ptr.p_double[0], 1, ae_v_len(0,m-1), v);
            if( ae_fp_greater(ae_randomreal(_state),0.5) )
            {
                ae_v_muld(&xg.ptr.p_double[0], 1, ae_v_len(0,m-1), -1);
            }
            mynfev = 0;
            
            /*
             * choose solver
             */
            if( solver==0 )
            {
                odesolverrkck(&y, 2, &xg, m, eps, h, &state, _state);
            }
            
            /*
             * solve
             */
            while(odesolveriteration(&state, _state))
            {
                state.dy.ptr.p_double[0] = state.y.ptr.p_double[1];
                state.dy.ptr.p_double[1] = -state.y.ptr.p_double[0];
                mynfev = mynfev+1;
            }
            odesolverresults(&state, &m2, &xtbl, &ytbl, &rep, _state);
            
            /*
             * check results
             */
            curerrors = ae_false;
            if( rep.terminationtype<=0 )
            {
                curerrors = ae_true;
            }
            else
            {
                curerrors = curerrors||m2!=m;
                err = (double)(0);
                for(i=0; i<=m-1; i++)
                {
                    err = ae_maxreal(err, ae_fabs(ytbl.ptr.pp_double[i][0]-(y.ptr.p_double[0]*ae_cos(xtbl.ptr.p_double[i]-xtbl.ptr.p_double[0], _state)+y.ptr.p_double[1]*ae_sin(xtbl.ptr.p_double[i]-xtbl.ptr.p_double[0], _state)), _state), _state);
                    err = ae_maxreal(err, ae_fabs(ytbl.ptr.pp_double[i][1]-(-y.ptr.p_double[0]*ae_sin(xtbl.ptr.p_double[i]-xtbl.ptr.p_double[0], _state)+y.ptr.p_double[1]*ae_cos(xtbl.ptr.p_double[i]-xtbl.ptr.p_double[0], _state)), _state), _state);
                }
                curerrors = curerrors||ae_fp_greater(err,10*ae_fabs(eps, _state));
                curerrors = curerrors||mynfev!=rep.nfev;
            }
            if( solver==0 )
            {
                rkckerrors = rkckerrors||curerrors;
            }
        }
    }
    
    /*
     * another test:
     *
     *     y(0)   = 0
     *     dy/dx  = f(x,y)
     *     f(x,y) = 0,   x<1
     *              x-1, x>=1
     *
     * with BOTH absolute and fractional tolerances.
     * Starting from zero will be real challenge for
     * fractional tolerance.
     */
    ae_assert(passcount>=2, "Assertion failed", _state);
    for(pass=0; pass<=passcount-1; pass++)
    {
        h = 1.0E-4;
        eps = 1.0E-4;
        if( pass%2==0 )
        {
            eps = -eps;
        }
        ae_vector_set_length(&y, 1, _state);
        y.ptr.p_double[0] = (double)(0);
        m = 21;
        ae_vector_set_length(&xg, m, _state);
        for(i=0; i<=m-1; i++)
        {
            xg.ptr.p_double[i] = (double)(2*i)/(double)(m-1);
        }
        mynfev = 0;
        odesolverrkck(&y, 1, &xg, m, eps, h, &state, _state);
        while(odesolveriteration(&state, _state))
        {
            state.dy.ptr.p_double[0] = ae_maxreal(state.x-1, (double)(0), _state);
            mynfev = mynfev+1;
        }
        odesolverresults(&state, &m2, &xtbl, &ytbl, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            rkckerrors = ae_true;
        }
        else
        {
            rkckerrors = rkckerrors||m2!=m;
            err = (double)(0);
            for(i=0; i<=m-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(ytbl.ptr.pp_double[i][0]-ae_sqr(ae_maxreal(xg.ptr.p_double[i]-1, (double)(0), _state), _state)/2, _state), _state);
            }
            rkckerrors = rkckerrors||ae_fp_greater(err,ae_fabs(eps, _state));
            rkckerrors = rkckerrors||mynfev!=rep.nfev;
        }
    }
    
    /*
     * end
     */
    waserrors = rkckerrors;
    if( !silent )
    {
        printf("TESTING ODE SOLVER\n");
        printf("* RK CASH-KARP:                           ");
        if( rkckerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testsparseunit_initgenerator(ae_int_t m,
     ae_int_t n,
     ae_int_t matkind,
     ae_int_t triangle,
     sparsegenerator* g,
     ae_state *_state);
static ae_bool testsparseunit_generatenext(sparsegenerator* g,
     /* Real    */ ae_matrix* da,
     sparsematrix* sa,
     ae_state *_state);
static void testsparseunit_createrandom(ae_int_t m,
     ae_int_t n,
     ae_int_t pkind,
     ae_int_t ckind,
     ae_int_t p0,
     ae_int_t p1,
     /* Real    */ ae_matrix* da,
     sparsematrix* sa,
     ae_state *_state);
static ae_bool testsparseunit_enumeratetest(ae_state *_state);
static ae_bool testsparseunit_rewriteexistingtest(ae_state *_state);
static void testsparseunit_testgetrow(ae_bool* err, ae_state *_state);
static ae_bool testsparseunit_testconvertsm(ae_state *_state);
static ae_bool testsparseunit_testgcmatrixtype(ae_state *_state);





ae_bool testsparse(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool basicerrors;
    ae_bool linearerrors;
    ae_bool basicrnderrors;
    ae_bool level2unsymmetricerrors;
    ae_bool level2symmetricerrors;
    ae_bool level2triangularerrors;
    ae_bool level3unsymmetricerrors;
    ae_bool level3symmetricerrors;
    ae_bool linearserrors;
    ae_bool linearmmerrors;
    ae_bool linearsmmerrors;
    ae_bool getrowerrors;
    ae_bool copyerrors;
    ae_bool basiccopyerrors;
    ae_bool enumerateerrors;
    ae_bool rewriteexistingerr;
    ae_bool skserrors;
    ae_bool crserrors;
    ae_bool result;


    getrowerrors = ae_false;
    crserrors = ae_false;
    skserrors = skstest(_state);
    crstest(&crserrors, _state);
    basicerrors = basicfunctest(_state)||testsparseunit_testgcmatrixtype(_state);
    basicrnderrors = basicfuncrandomtest(_state);
    linearerrors = linearfunctionstest(_state);
    level2unsymmetricerrors = testlevel2unsymmetric(_state);
    level2symmetricerrors = testlevel2symmetric(_state);
    level2triangularerrors = testlevel2triangular(_state);
    level3unsymmetricerrors = testlevel3unsymmetric(_state);
    level3symmetricerrors = testlevel3symmetric(_state);
    linearserrors = linearfunctionsstest(_state);
    linearmmerrors = linearfunctionsmmtest(_state);
    linearsmmerrors = linearfunctionssmmtest(_state);
    copyerrors = copyfunctest(ae_true, _state)||testsparseunit_testconvertsm(_state);
    basiccopyerrors = basiccopyfunctest(ae_true, _state);
    enumerateerrors = testsparseunit_enumeratetest(_state);
    rewriteexistingerr = testsparseunit_rewriteexistingtest(_state);
    testsparseunit_testgetrow(&getrowerrors, _state);
    
    /*
     * report
     */
    waserrors = ((((((((((((((((skserrors||crserrors)||getrowerrors)||basicerrors)||linearerrors)||basicrnderrors)||level2unsymmetricerrors)||level2symmetricerrors)||level2triangularerrors)||level3unsymmetricerrors)||level3symmetricerrors)||linearserrors)||linearmmerrors)||linearsmmerrors)||copyerrors)||basiccopyerrors)||enumerateerrors)||rewriteexistingerr;
    if( !silent )
    {
        printf("TESTING SPARSE\n");
        printf("STORAGE FORMAT SPECIFICS:\n");
        printf("* SKS:                                   ");
        if( !skserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* CRS:                                   ");
        if( !crserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("OPERATIONS:\n");
        printf("* GETROW:                                ");
        if( !getrowerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("BLAS:\n");
        printf("* LEVEL 2 GENERAL:                       ");
        if( !level2unsymmetricerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* LEVEL 2 SYMMETRIC:                     ");
        if( !level2symmetricerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* LEVEL 2 TRIANGULAR:                    ");
        if( !level2triangularerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* LEVEL 3 GENERAL:                       ");
        if( !level3unsymmetricerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* LEVEL 3 SYMMETRIC:                     ");
        if( !level3symmetricerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("BASIC TEST:                              ");
        if( !basicerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("COPY TEST:                               ");
        if( !copyerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("BASIC_COPY TEST:                         ");
        if( !basiccopyerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("BASIC_RND TEST:                          ");
        if( !basicrnderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("LINEAR TEST:                             ");
        if( !linearerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("LINEAR TEST FOR SYMMETRIC MATRICES:      ");
        if( !linearserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("LINEAR MxM TEST:                         ");
        if( !linearmmerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("LINEAR MxM TEST FOR SYMMETRIC MATRICES:  ");
        if( !linearsmmerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("ENUMERATE TEST:                          ");
        if( !enumerateerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("REWRITE EXISTING TEST:                   ");
        if( !rewriteexistingerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Function for testing basic SKS functional.
Returns True on errors, False on success.

  -- ALGLIB PROJECT --
     Copyright 16.01.1014 by Bochkanov Sergey
*************************************************************************/
ae_bool skstest(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s0;
    sparsematrix s1;
    sparsematrix s2;
    sparsematrix s3;
    sparsematrix s4;
    sparsematrix s5;
    sparsematrix s6;
    ae_int_t n;
    ae_int_t nz;
    double pnz;
    ae_int_t i;
    ae_int_t j;
    ae_int_t t0;
    ae_int_t t1;
    ae_matrix a;
    ae_matrix wasenumerated;
    ae_vector d;
    ae_vector u;
    hqrndstate rs;
    double v0;
    double v1;
    ae_int_t uppercnt;
    ae_int_t lowercnt;
    ae_int_t bw;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s0, 0, sizeof(s0));
    memset(&s1, 0, sizeof(s1));
    memset(&s2, 0, sizeof(s2));
    memset(&s3, 0, sizeof(s3));
    memset(&s4, 0, sizeof(s4));
    memset(&s5, 0, sizeof(s5));
    memset(&s6, 0, sizeof(s6));
    memset(&a, 0, sizeof(a));
    memset(&wasenumerated, 0, sizeof(wasenumerated));
    memset(&d, 0, sizeof(d));
    memset(&u, 0, sizeof(u));
    memset(&rs, 0, sizeof(rs));
    _sparsematrix_init(&s0, _state, ae_true);
    _sparsematrix_init(&s1, _state, ae_true);
    _sparsematrix_init(&s2, _state, ae_true);
    _sparsematrix_init(&s3, _state, ae_true);
    _sparsematrix_init(&s4, _state, ae_true);
    _sparsematrix_init(&s5, _state, ae_true);
    _sparsematrix_init(&s6, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&wasenumerated, 0, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&d, 0, DT_INT, _state, ae_true);
    ae_vector_init(&u, 0, DT_INT, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    result = ae_false;
    hqrndrandomize(&rs, _state);
    for(n=1; n<=20; n++)
    {
        nz = n*n-n;
        for(;;)
        {
            
            /*
             * Generate N*N matrix where probability of non-diagonal element
             * being non-zero is PNZ. We also generate D and U - subdiagonal
             * and superdiagonal profile sizes.
             *
             * Create matrix with either general SKS or banded SKS constructor function
             */
            if( n>1 )
            {
                pnz = (double)nz/(double)(n*n-n);
            }
            else
            {
                pnz = 1.0;
            }
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&u, n, _state);
            ae_matrix_set_length(&a, n, n, _state);
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                
                /*
                 * Test SparseCreateSKS() functionality
                 */
                for(i=0; i<=n-1; i++)
                {
                    d.ptr.p_int[i] = 0;
                    u.ptr.p_int[i] = 0;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j||ae_fp_less_eq(hqrnduniformr(&rs, _state),pnz) )
                        {
                            a.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                            if( j<i )
                            {
                                d.ptr.p_int[i] = ae_maxint(d.ptr.p_int[i], i-j, _state);
                            }
                            else
                            {
                                u.ptr.p_int[j] = ae_maxint(u.ptr.p_int[j], j-i, _state);
                            }
                        }
                        else
                        {
                            a.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                }
                sparsecreatesks(n, n, &d, &u, &s0, _state);
            }
            else
            {
                
                /*
                 * Test SparseCreateSKSBand() functionality
                 */
                bw = hqrnduniformi(&rs, n+1, _state);
                for(i=0; i<=n-1; i++)
                {
                    d.ptr.p_int[i] = ae_minint(bw, i, _state);
                    u.ptr.p_int[i] = ae_minint(bw, i, _state);
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ae_iabs(i-j, _state)<=bw&&ae_fp_less_eq(hqrnduniformr(&rs, _state),pnz) )
                        {
                            a.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                        }
                        else
                        {
                            a.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                }
                sparsecreatesksband(n, n, bw, &s0, _state);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( ae_fp_neq(a.ptr.pp_double[i][j],(double)(0)) )
                    {
                        if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
                        {
                            ae_set_error_flag(&result, !sparserewriteexisting(&s0, i, j, a.ptr.pp_double[i][j], _state), __FILE__, __LINE__, "testsparseunit.ap:437");
                        }
                        else
                        {
                            sparseset(&s0, i, j, a.ptr.pp_double[i][j], _state);
                        }
                    }
                }
            }
            uppercnt = 0;
            lowercnt = 0;
            for(i=0; i<=n-1; i++)
            {
                uppercnt = uppercnt+u.ptr.p_int[i];
                lowercnt = lowercnt+d.ptr.p_int[i];
            }
            
            /*
             * Try to call SparseRewriteExisting() for out-of-band elements, make sure that it returns False.
             */
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (i>=j&&i-j>d.ptr.p_int[i])||(i<=j&&j-i>u.ptr.p_int[j]) )
                    {
                        ae_set_error_flag(&result, sparserewriteexisting(&s0, i, j, 1.0, _state), __FILE__, __LINE__, "testsparseunit.ap:455");
                    }
                }
            }
            
            /*
             * Convert to several different formats, check their contents with SparseGet().
             */
            sparsecopy(&s0, &s1, _state);
            sparseconverttocrs(&s1, _state);
            sparsecopytocrs(&s0, &s2, _state);
            sparsecopytocrsbuf(&s0, &s3, _state);
            sparsecopytohash(&s0, &s4, _state);
            sparsecopytohashbuf(&s0, &s5, _state);
            sparsecopy(&s0, &s6, _state);
            sparseconverttohash(&s6, _state);
            ae_set_error_flag(&result, sparsegetnrows(&s0, _state)!=n, __FILE__, __LINE__, "testsparseunit.ap:468");
            ae_set_error_flag(&result, sparsegetncols(&s0, _state)!=n, __FILE__, __LINE__, "testsparseunit.ap:469");
            ae_set_error_flag(&result, sparsegetmatrixtype(&s0, _state)!=2, __FILE__, __LINE__, "testsparseunit.ap:470");
            ae_set_error_flag(&result, !sparseissks(&s0, _state), __FILE__, __LINE__, "testsparseunit.ap:471");
            ae_set_error_flag(&result, sparseiscrs(&s0, _state), __FILE__, __LINE__, "testsparseunit.ap:472");
            ae_set_error_flag(&result, sparseishash(&s0, _state), __FILE__, __LINE__, "testsparseunit.ap:473");
            ae_set_error_flag(&result, sparseissks(&s1, _state), __FILE__, __LINE__, "testsparseunit.ap:474");
            ae_set_error_flag(&result, !sparseiscrs(&s1, _state), __FILE__, __LINE__, "testsparseunit.ap:475");
            ae_set_error_flag(&result, sparseishash(&s1, _state), __FILE__, __LINE__, "testsparseunit.ap:476");
            for(i=0; i<=n-1; i++)
            {
                v1 = a.ptr.pp_double[i][i];
                v0 = sparsegetdiagonal(&s0, i, _state);
                ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:481");
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v1 = a.ptr.pp_double[i][j];
                    v0 = sparseget(&s0, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:488");
                    v0 = sparseget(&s1, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:490");
                    v0 = sparseget(&s2, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:492");
                    v0 = sparseget(&s3, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:494");
                    v0 = sparseget(&s4, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:496");
                    v0 = sparseget(&s5, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:498");
                    v0 = sparseget(&s6, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:500");
                }
            }
            
            /*
             * Check enumeration capabilities:
             * * each element returned by SparseEnumerate() is returned only once
             * * each non-zero element of A was enumerated
             */
            ae_matrix_set_length(&wasenumerated, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    wasenumerated.ptr.pp_bool[i][j] = ae_false;
                }
            }
            t0 = 0;
            t1 = 0;
            while(sparseenumerate(&s0, &t0, &t1, &i, &j, &v0, _state))
            {
                ae_set_error_flag(&result, wasenumerated.ptr.pp_bool[i][j], __FILE__, __LINE__, "testsparseunit.ap:516");
                wasenumerated.ptr.pp_bool[i][j] = ae_true;
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( ae_fp_neq(a.ptr.pp_double[i][j],(double)(0)) )
                    {
                        ae_set_error_flag(&result, !wasenumerated.ptr.pp_bool[i][j], __FILE__, __LINE__, "testsparseunit.ap:522");
                    }
                }
            }
            
            /*
             * Check UpperCnt()/LowerCnt()
             */
            ae_set_error_flag(&result, sparsegetuppercount(&s0, _state)!=uppercnt, __FILE__, __LINE__, "testsparseunit.ap:527");
            ae_set_error_flag(&result, sparsegetlowercount(&s0, _state)!=lowercnt, __FILE__, __LINE__, "testsparseunit.ap:528");
            
            /*
             * Check in-place transposition
             */
            sparsecopy(&s0, &s1, _state);
            sparsetransposesks(&s1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v0 = sparseget(&s0, i, j, _state);
                    v1 = sparseget(&s1, j, i, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:540");
                }
            }
            
            /*
             * One more check - matrix is initially created in some other format
             * (CRS or Hash) and converted to SKS later.
             */
            sparsecreate(n, n, 0, &s0, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( ae_fp_neq(a.ptr.pp_double[i][j],(double)(0)) )
                    {
                        sparseset(&s0, i, j, a.ptr.pp_double[i][j], _state);
                    }
                }
            }
            sparsecopy(&s0, &s1, _state);
            sparseconverttosks(&s1, _state);
            sparsecopytosks(&s0, &s2, _state);
            sparsecopytosksbuf(&s0, &s3, _state);
            ae_set_error_flag(&result, !sparseissks(&s1, _state), __FILE__, __LINE__, "testsparseunit.ap:556");
            ae_set_error_flag(&result, sparseiscrs(&s1, _state), __FILE__, __LINE__, "testsparseunit.ap:557");
            ae_set_error_flag(&result, sparseishash(&s1, _state), __FILE__, __LINE__, "testsparseunit.ap:558");
            ae_set_error_flag(&result, !sparseissks(&s2, _state), __FILE__, __LINE__, "testsparseunit.ap:559");
            ae_set_error_flag(&result, sparseiscrs(&s2, _state), __FILE__, __LINE__, "testsparseunit.ap:560");
            ae_set_error_flag(&result, sparseishash(&s2, _state), __FILE__, __LINE__, "testsparseunit.ap:561");
            ae_set_error_flag(&result, !sparseissks(&s3, _state), __FILE__, __LINE__, "testsparseunit.ap:562");
            ae_set_error_flag(&result, sparseiscrs(&s3, _state), __FILE__, __LINE__, "testsparseunit.ap:563");
            ae_set_error_flag(&result, sparseishash(&s3, _state), __FILE__, __LINE__, "testsparseunit.ap:564");
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v1 = a.ptr.pp_double[i][j];
                    v0 = sparseget(&s1, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:570");
                    v0 = sparseget(&s2, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:572");
                    v0 = sparseget(&s3, i, j, _state);
                    ae_set_error_flag(&result, ae_fp_neq(v0,v1), __FILE__, __LINE__, "testsparseunit.ap:574");
                }
            }
            
            /*
             * Increase problem sparcity and try one more time. 
             * Stop after testing NZ=0.
             */
            if( nz==0 )
            {
                break;
            }
            nz = ae_minint(ae_round(nz*0.95, _state), nz-1, _state);
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing CRS-specific functionality.
On failure sets ErrorFlag, on success does not touch it.

  -- ALGLIB PROJECT --
     Copyright 30.01.2018 by Bochkanov Sergey
*************************************************************************/
void crstest(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_int_t nz;
    double pnz;
    hqrndstate rs;
    sparsematrix s0;
    sparsematrix s1;
    sparsematrix s2;
    ae_int_t i;
    ae_int_t j;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&s0, 0, sizeof(s0));
    memset(&s1, 0, sizeof(s1));
    memset(&s2, 0, sizeof(s2));
    _hqrndstate_init(&rs, _state, ae_true);
    _sparsematrix_init(&s0, _state, ae_true);
    _sparsematrix_init(&s1, _state, ae_true);
    _sparsematrix_init(&s2, _state, ae_true);

    hqrndrandomize(&rs, _state);
    for(n=1; n<=10; n++)
    {
        for(m=1; m<=10; m++)
        {
            nz = n*m;
            for(;;)
            {
                
                /*
                 * Generate N*N matrix where probability of non-diagonal element
                 * being non-zero is PNZ. We also generate D and U - subdiagonal
                 * and superdiagonal profile sizes.
                 *
                 * Create matrix with either general SKS or banded SKS constructor function
                 */
                if( n>1 )
                {
                    pnz = (double)nz/(double)(m*n);
                }
                else
                {
                    pnz = 1.0;
                }
                
                /*
                 * Generate random matrix in HASH format, copy it to CRS format, compare with original
                 */
                sparsecreate(m, n, ae_round(nz*hqrnduniformr(&rs, _state), _state), &s0, _state);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ae_fp_less_eq(hqrnduniformr(&rs, _state),pnz) )
                        {
                            sparseset(&s0, i, j, hqrndnormal(&rs, _state), _state);
                        }
                    }
                }
                sparsecopytocrs(&s0, &s1, _state);
                ae_set_error_flag(errorflag, !sparseiscrs(&s1, _state), __FILE__, __LINE__, "testsparseunit.ap:642");
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_neq(sparseget(&s0, i, j, _state),sparseget(&s1, i, j, _state)), __FILE__, __LINE__, "testsparseunit.ap:645");
                    }
                }
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Check transposition
                 */
                sparsecopytocrs(&s1, &s2, _state);
                sparsetransposecrs(&s2, _state);
                ae_set_error_flag(errorflag, !sparseiscrs(&s2, _state), __FILE__, __LINE__, "testsparseunit.ap:654");
                ae_set_error_flag(errorflag, sparsegetnrows(&s1, _state)!=sparsegetncols(&s2, _state), __FILE__, __LINE__, "testsparseunit.ap:655");
                ae_set_error_flag(errorflag, sparsegetncols(&s1, _state)!=sparsegetnrows(&s2, _state), __FILE__, __LINE__, "testsparseunit.ap:656");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_neq(sparseget(&s1, i, j, _state),sparseget(&s2, j, i, _state)), __FILE__, __LINE__, "testsparseunit.ap:661");
                    }
                }
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Increase problem sparcity and try one more time. 
                 * Stop after testing NZ=0.
                 */
                if( nz==0 )
                {
                    break;
                }
                nz = ae_minint(ae_round(nz*0.95, _state), nz-1, _state);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Function for testing basic functional

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool basicfunctest(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t j1;
    ae_int_t uppercnt;
    ae_int_t lowercnt;
    ae_matrix a;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    _sparsematrix_init(&s, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    n = 10;
    m = 10;
    result = ae_false;
    for(i=1; i<=m-1; i++)
    {
        for(j=1; j<=n-1; j++)
        {
            sparsecreate(i, j, 1, &s, _state);
            ae_matrix_set_length(&a, i, j, _state);
            
            /*
             * Checking for Matrix with hash table type
             */
            uppercnt = 0;
            lowercnt = 0;
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    if( j1>i1 )
                    {
                        inc(&uppercnt, _state);
                    }
                    if( j1<i1 )
                    {
                        inc(&lowercnt, _state);
                    }
                    a.ptr.pp_double[i1][j1] = i1+j1+(double)((i+j)*(m+n))/(double)2;
                    a.ptr.pp_double[i1][j1] = a.ptr.pp_double[i1][j1]+1;
                    sparseset(&s, i1, j1, i1+j1+(double)((i+j)*(m+n))/(double)2, _state);
                    sparseadd(&s, i1, j1, (double)(1), _state);
                    if( ae_fp_neq(a.ptr.pp_double[i1][j1],sparseget(&s, i1, j1, _state)) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
            for(i1=0; i1<=ae_minint(i, j, _state)-1; i1++)
            {
                if( ae_fp_neq(a.ptr.pp_double[i1][i1],sparsegetdiagonal(&s, i1, _state)) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            ae_set_error_flag(&result, sparsegetuppercount(&s, _state)!=uppercnt, __FILE__, __LINE__, "testsparseunit.ap:730");
            ae_set_error_flag(&result, sparsegetlowercount(&s, _state)!=lowercnt, __FILE__, __LINE__, "testsparseunit.ap:731");
            
            /*
             * Checking for Matrix with CRS type
             */
            sparseconverttocrs(&s, _state);
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    if( ae_fp_neq(a.ptr.pp_double[i1][j1],sparseget(&s, i1, j1, _state)) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
            for(i1=0; i1<=ae_minint(i, j, _state)-1; i1++)
            {
                if( ae_fp_neq(a.ptr.pp_double[i1][i1],sparsegetdiagonal(&s, i1, _state)) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            ae_set_error_flag(&result, sparsegetuppercount(&s, _state)!=uppercnt, __FILE__, __LINE__, "testsparseunit.ap:750");
            ae_set_error_flag(&result, sparsegetlowercount(&s, _state)!=lowercnt, __FILE__, __LINE__, "testsparseunit.ap:751");
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing Level 2 unsymmetric linear algebra functions.
Additionally it tests SparseGet() for several matrix formats.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 20.01.2014 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel2unsymmetric(ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_vector x0;
    ae_vector x1;
    ae_vector y0;
    ae_vector y1;
    ae_int_t i;
    ae_int_t j;
    ae_matrix a;
    sparsematrix s0;
    sparsematrix sa;
    double eps;
    double v;
    sparsegenerator g;
    hqrndstate rs;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&y0, 0, sizeof(y0));
    memset(&y1, 0, sizeof(y1));
    memset(&a, 0, sizeof(a));
    memset(&s0, 0, sizeof(s0));
    memset(&sa, 0, sizeof(sa));
    memset(&g, 0, sizeof(g));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&s0, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    _sparsegenerator_init(&g, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    eps = 10000*ae_machineepsilon;
    result = ae_false;
    hqrndrandomize(&rs, _state);
    
    /*
     * Test linear algebra functions
     */
    for(m=1; m<=20; m++)
    {
        for(n=1; n<=20; n++)
        {
            testsparseunit_initgenerator(m, n, 0, 0, &g, _state);
            while(testsparseunit_generatenext(&g, &a, &sa, _state))
            {
                
                /*
                 * Convert SA to desired storage format:
                 * * to CRS if M<>N
                 * * with 50% probability to CRS or SKS, if M=N
                 */
                if( m!=n||ae_fp_less(hqrnduniformr(&rs, _state),0.5) )
                {
                    sparsecopytocrs(&sa, &s0, _state);
                }
                else
                {
                    sparsecopytosks(&sa, &s0, _state);
                }
                
                /*
                 * Test SparseGet() for SA and S0 against matrix returned in A
                 */
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(sparseget(&sa, i, j, _state)-a.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:806");
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(sparseget(&s0, i, j, _state)-a.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:807");
                    }
                }
                
                /*
                 * Test SparseMV
                 */
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&x1, n, _state);
                for(j=0; j<=n-1; j++)
                {
                    x0.ptr.p_double[j] = hqrnduniformr(&rs, _state)-0.5;
                    x1.ptr.p_double[j] = x0.ptr.p_double[j];
                }
                sparsemv(&s0, &x0, &y0, _state);
                ae_set_error_flag(&result, y0.cnt<m, __FILE__, __LINE__, "testsparseunit.ap:821");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=m-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y0.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:827");
                }
                
                /*
                 * Test SparseMTV
                 */
                ae_vector_set_length(&x0, m, _state);
                ae_vector_set_length(&x1, m, _state);
                for(j=0; j<=m-1; j++)
                {
                    x0.ptr.p_double[j] = hqrnduniformr(&rs, _state)-0.5;
                    x1.ptr.p_double[j] = x0.ptr.p_double[j];
                }
                sparsemtv(&s0, &x0, &y0, _state);
                ae_set_error_flag(&result, y0.cnt<n, __FILE__, __LINE__, "testsparseunit.ap:841");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(j=0; j<=n-1; j++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[0][j], a.stride, &x1.ptr.p_double[0], 1, ae_v_len(0,m-1));
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y0.ptr.p_double[j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:847");
                }
                
                /*
                 * Test SparseMV2
                 */
                if( m==n )
                {
                    ae_vector_set_length(&x0, n, _state);
                    ae_vector_set_length(&x1, n, _state);
                    for(j=0; j<=n-1; j++)
                    {
                        x0.ptr.p_double[j] = hqrnduniformr(&rs, _state)-0.5;
                        x1.ptr.p_double[j] = x0.ptr.p_double[j];
                    }
                    sparsemv2(&s0, &x0, &y0, &y1, _state);
                    ae_set_error_flag(&result, y0.cnt<n, __FILE__, __LINE__, "testsparseunit.ap:863");
                    ae_set_error_flag(&result, y1.cnt<n, __FILE__, __LINE__, "testsparseunit.ap:864");
                    if( result )
                    {
                        ae_frame_leave(_state);
                        return result;
                    }
                    for(j=0; j<=n-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[j][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y0.ptr.p_double[j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:870");
                        v = ae_v_dotproduct(&a.ptr.pp_double[0][j], a.stride, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y1.ptr.p_double[j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:872");
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing Level 3 unsymmetric linear algebra functions.
Additionally it tests SparseGet() for several matrix formats.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 20.01.2014 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel3unsymmetric(ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t k;
    ae_matrix x0;
    ae_matrix x1;
    ae_matrix y0;
    ae_matrix y1;
    ae_int_t i;
    ae_int_t j;
    ae_matrix a;
    sparsematrix s0;
    sparsematrix sa;
    double eps;
    double v;
    sparsegenerator g;
    hqrndstate rs;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&y0, 0, sizeof(y0));
    memset(&y1, 0, sizeof(y1));
    memset(&a, 0, sizeof(a));
    memset(&s0, 0, sizeof(s0));
    memset(&sa, 0, sizeof(sa));
    memset(&g, 0, sizeof(g));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&x0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&y0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&y1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&s0, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    _sparsegenerator_init(&g, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    eps = 10000*ae_machineepsilon;
    result = ae_false;
    hqrndrandomize(&rs, _state);
    
    /*
     * Test linear algebra functions
     */
    for(m=1; m<=20; m++)
    {
        for(n=1; n<=20; n++)
        {
            testsparseunit_initgenerator(m, n, 0, 0, &g, _state);
            while(testsparseunit_generatenext(&g, &a, &sa, _state))
            {
                
                /*
                 * Choose matrix width K
                 */
                k = 1+hqrnduniformi(&rs, 20, _state);
                
                /*
                 * Convert SA to desired storage format:
                 * * to CRS if M<>N
                 * * with 50% probability to CRS or SKS, if M=N
                 */
                if( m!=n||ae_fp_less(hqrnduniformr(&rs, _state),0.5) )
                {
                    sparsecopytocrs(&sa, &s0, _state);
                }
                else
                {
                    sparsecopytosks(&sa, &s0, _state);
                }
                
                /*
                 * Test SparseGet() for SA and S0 against matrix returned in A
                 */
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ae_set_error_flag(&result, ae_fp_neq(sparseget(&sa, i, j, _state),a.ptr.pp_double[i][j]), __FILE__, __LINE__, "testsparseunit.ap:935");
                        ae_set_error_flag(&result, ae_fp_neq(sparseget(&s0, i, j, _state),a.ptr.pp_double[i][j]), __FILE__, __LINE__, "testsparseunit.ap:936");
                    }
                }
                
                /*
                 * Test SparseMV
                 */
                ae_matrix_set_length(&x0, n, k, _state);
                ae_matrix_set_length(&x1, n, k, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=k-1; j++)
                    {
                        x0.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                        x1.ptr.pp_double[i][j] = x0.ptr.pp_double[i][j];
                    }
                }
                sparsemm(&s0, &x0, k, &y0, _state);
                ae_set_error_flag(&result, y0.rows<m, __FILE__, __LINE__, "testsparseunit.ap:951");
                ae_set_error_flag(&result, y0.cols<k, __FILE__, __LINE__, "testsparseunit.ap:952");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=k-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.pp_double[0][j], x1.stride, ae_v_len(0,n-1));
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y0.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:959");
                    }
                }
                
                /*
                 * Test SparseMTM
                 */
                ae_matrix_set_length(&x0, m, k, _state);
                ae_matrix_set_length(&x1, m, k, _state);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=k-1; j++)
                    {
                        x0.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                        x1.ptr.pp_double[i][j] = x0.ptr.pp_double[i][j];
                    }
                }
                sparsemtm(&s0, &x0, k, &y0, _state);
                ae_set_error_flag(&result, y0.rows<n, __FILE__, __LINE__, "testsparseunit.ap:974");
                ae_set_error_flag(&result, y0.cols<k, __FILE__, __LINE__, "testsparseunit.ap:975");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=k-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[0][i], a.stride, &x1.ptr.pp_double[0][j], x1.stride, ae_v_len(0,m-1));
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y0.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:982");
                    }
                }
                
                /*
                 * Test SparseMM2
                 */
                if( m==n )
                {
                    ae_matrix_set_length(&x0, n, k, _state);
                    ae_matrix_set_length(&x1, n, k, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=k-1; j++)
                        {
                            x0.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                            x1.ptr.pp_double[i][j] = x0.ptr.pp_double[i][j];
                        }
                    }
                    sparsemm2(&s0, &x0, k, &y0, &y1, _state);
                    ae_set_error_flag(&result, y0.rows<n, __FILE__, __LINE__, "testsparseunit.ap:999");
                    ae_set_error_flag(&result, y0.cols<k, __FILE__, __LINE__, "testsparseunit.ap:1000");
                    ae_set_error_flag(&result, y1.rows<n, __FILE__, __LINE__, "testsparseunit.ap:1001");
                    ae_set_error_flag(&result, y1.cols<k, __FILE__, __LINE__, "testsparseunit.ap:1002");
                    if( result )
                    {
                        ae_frame_leave(_state);
                        return result;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=k-1; j++)
                        {
                            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.pp_double[0][j], x1.stride, ae_v_len(0,n-1));
                            ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y0.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1009");
                            v = ae_v_dotproduct(&a.ptr.pp_double[0][i], a.stride, &x1.ptr.pp_double[0][j], x1.stride, ae_v_len(0,n-1));
                            ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y1.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1011");
                        }
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing Level 2 symmetric linear algebra functions.
Additionally it tests SparseGet() for several matrix formats.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 20.01.2014 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel2symmetric(ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_vector x0;
    ae_vector x1;
    ae_vector y0;
    ae_vector y1;
    ae_int_t i;
    ae_int_t j;
    ae_matrix a;
    sparsematrix s0;
    sparsematrix s1;
    sparsematrix sa;
    double eps;
    double v;
    double va;
    double vb;
    sparsegenerator g;
    hqrndstate rs;
    ae_bool isupper;
    ae_int_t triangletype;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&y0, 0, sizeof(y0));
    memset(&y1, 0, sizeof(y1));
    memset(&a, 0, sizeof(a));
    memset(&s0, 0, sizeof(s0));
    memset(&s1, 0, sizeof(s1));
    memset(&sa, 0, sizeof(sa));
    memset(&g, 0, sizeof(g));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&s0, _state, ae_true);
    _sparsematrix_init(&s1, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    _sparsegenerator_init(&g, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    eps = 10000*ae_machineepsilon;
    result = ae_false;
    hqrndrandomize(&rs, _state);
    
    /*
     * Test linear algebra functions
     */
    for(n=1; n<=20; n++)
    {
        for(triangletype=-1; triangletype<=1; triangletype++)
        {
            isupper = ae_fp_greater(hqrnduniformr(&rs, _state),0.5);
            if( triangletype<0 )
            {
                isupper = ae_false;
            }
            if( triangletype>0 )
            {
                isupper = ae_true;
            }
            testsparseunit_initgenerator(n, n, 0, triangletype, &g, _state);
            while(testsparseunit_generatenext(&g, &a, &sa, _state))
            {
                
                /*
                 * Convert SA to desired storage format:
                 * * S0 stores unmodified copy
                 * * S1 stores copy with unmodified triangle corresponding
                 *   to IsUpper and another triangle being spoiled by random
                 *   trash
                 */
                sparsecopytohash(&sa, &s1, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            sparseset(&s1, i, j, hqrnduniformr(&rs, _state), _state);
                        }
                    }
                }
                if( ae_fp_less(hqrnduniformr(&rs, _state),0.5) )
                {
                    sparsecopytocrs(&sa, &s0, _state);
                    sparseconverttocrs(&s1, _state);
                }
                else
                {
                    sparsecopytosks(&sa, &s0, _state);
                    sparseconverttosks(&s1, _state);
                }
                
                /*
                 * Test SparseGet() for SA and S0 against matrix returned in A
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(sparseget(&sa, i, j, _state)-a.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1090");
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(sparseget(&s0, i, j, _state)-a.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1091");
                        ae_set_error_flag(&result, (j<i&&triangletype==1)&&ae_fp_neq(sparseget(&s0, i, j, _state),(double)(0)), __FILE__, __LINE__, "testsparseunit.ap:1092");
                        ae_set_error_flag(&result, (j>i&&triangletype==-1)&&ae_fp_neq(sparseget(&s0, i, j, _state),(double)(0)), __FILE__, __LINE__, "testsparseunit.ap:1093");
                    }
                }
                
                /*
                 * Before we proceed with testing, update empty triangle of A
                 * with its copy from another part of the matrix.
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            a.ptr.pp_double[i][j] = a.ptr.pp_double[j][i];
                        }
                    }
                }
                
                /*
                 * Test SparseSMV
                 */
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&x1, n, _state);
                for(j=0; j<=n-1; j++)
                {
                    x0.ptr.p_double[j] = hqrnduniformr(&rs, _state)-0.5;
                    x1.ptr.p_double[j] = x0.ptr.p_double[j];
                }
                sparsesmv(&s0, isupper, &x0, &y0, _state);
                ae_set_error_flag(&result, y0.cnt<n, __FILE__, __LINE__, "testsparseunit.ap:1116");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y0.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1122");
                }
                sparsesmv(&s1, isupper, &x0, &y1, _state);
                ae_set_error_flag(&result, y1.cnt<n, __FILE__, __LINE__, "testsparseunit.ap:1125");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y1.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1131");
                }
                
                /*
                 * Test SparseVSMV
                 */
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&x1, n, _state);
                for(j=0; j<=n-1; j++)
                {
                    x0.ptr.p_double[j] = hqrnduniformr(&rs, _state)-0.5;
                    x1.ptr.p_double[j] = x0.ptr.p_double[j];
                }
                vb = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        vb = vb+x1.ptr.p_double[i]*a.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                    }
                }
                va = sparsevsmv(&s0, isupper, &x0, _state);
                ae_set_error_flag(&result, ae_fp_greater(ae_fabs(va-vb, _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1149");
                va = sparsevsmv(&s1, isupper, &x0, _state);
                ae_set_error_flag(&result, ae_fp_greater(ae_fabs(va-vb, _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1151");
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing Level 2 symmetric linear algebra functions.
Additionally it tests SparseGet() for several matrix formats.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel3symmetric(ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t k;
    ae_matrix x0;
    ae_matrix x1;
    ae_matrix y0;
    ae_matrix y1;
    ae_int_t i;
    ae_int_t j;
    ae_matrix a;
    sparsematrix s0;
    sparsematrix s1;
    sparsematrix sa;
    double eps;
    double v;
    sparsegenerator g;
    hqrndstate rs;
    ae_bool isupper;
    ae_int_t triangletype;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&y0, 0, sizeof(y0));
    memset(&y1, 0, sizeof(y1));
    memset(&a, 0, sizeof(a));
    memset(&s0, 0, sizeof(s0));
    memset(&s1, 0, sizeof(s1));
    memset(&sa, 0, sizeof(sa));
    memset(&g, 0, sizeof(g));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&x0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&y0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&y1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&s0, _state, ae_true);
    _sparsematrix_init(&s1, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    _sparsegenerator_init(&g, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    eps = 10000*ae_machineepsilon;
    result = ae_false;
    hqrndrandomize(&rs, _state);
    
    /*
     * Test linear algebra functions
     */
    for(n=1; n<=20; n++)
    {
        for(triangletype=-1; triangletype<=1; triangletype++)
        {
            isupper = ae_fp_greater(hqrnduniformr(&rs, _state),0.5);
            if( triangletype<0 )
            {
                isupper = ae_false;
            }
            if( triangletype>0 )
            {
                isupper = ae_true;
            }
            testsparseunit_initgenerator(n, n, 0, triangletype, &g, _state);
            while(testsparseunit_generatenext(&g, &a, &sa, _state))
            {
                
                /*
                 * Choose matrix width K
                 */
                k = 1+hqrnduniformi(&rs, 20, _state);
                
                /*
                 * Convert SA to desired storage format:
                 * * S0 stores unmodified copy
                 * * S1 stores copy with unmodified triangle corresponding
                 *   to IsUpper and another triangle being spoiled by random
                 *   trash
                 */
                sparsecopytohash(&sa, &s1, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            sparseset(&s1, i, j, hqrnduniformr(&rs, _state), _state);
                        }
                    }
                }
                if( ae_fp_less(hqrnduniformr(&rs, _state),0.5) )
                {
                    sparsecopytocrs(&sa, &s0, _state);
                    sparseconverttocrs(&s1, _state);
                }
                else
                {
                    sparsecopytosks(&sa, &s0, _state);
                    sparseconverttosks(&s1, _state);
                }
                
                /*
                 * Test SparseGet() for SA and S0 against matrix returned in A
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(sparseget(&sa, i, j, _state)-a.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1232");
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(sparseget(&s0, i, j, _state)-a.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1233");
                        ae_set_error_flag(&result, (j<i&&triangletype==1)&&ae_fp_neq(sparseget(&s0, i, j, _state),(double)(0)), __FILE__, __LINE__, "testsparseunit.ap:1234");
                        ae_set_error_flag(&result, (j>i&&triangletype==-1)&&ae_fp_neq(sparseget(&s0, i, j, _state),(double)(0)), __FILE__, __LINE__, "testsparseunit.ap:1235");
                    }
                }
                
                /*
                 * Before we proceed with testing, update empty triangle of A
                 * with its copy from another part of the matrix.
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            a.ptr.pp_double[i][j] = a.ptr.pp_double[j][i];
                        }
                    }
                }
                
                /*
                 * Test SparseSMM
                 */
                ae_matrix_set_length(&x0, n, k, _state);
                ae_matrix_set_length(&x1, n, k, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=k-1; j++)
                    {
                        x0.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                        x1.ptr.pp_double[i][j] = x0.ptr.pp_double[i][j];
                    }
                }
                sparsesmm(&s0, isupper, &x0, k, &y0, _state);
                ae_set_error_flag(&result, y0.rows<n, __FILE__, __LINE__, "testsparseunit.ap:1259");
                ae_set_error_flag(&result, y0.cols<k, __FILE__, __LINE__, "testsparseunit.ap:1260");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=k-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.pp_double[0][j], x1.stride, ae_v_len(0,n-1));
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y0.ptr.pp_double[i][j], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1267");
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing Level 2 triangular linear algebra functions.
Returns True on failure.

  -- ALGLIB PROJECT --
     Copyright 20.01.2014 by Bochkanov Sergey
*************************************************************************/
ae_bool testlevel2triangular(ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_vector x0;
    ae_vector x1;
    ae_vector y0;
    ae_vector y1;
    ae_vector ey;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t j1;
    ae_matrix a;
    ae_matrix ea;
    sparsematrix s0;
    sparsematrix s1;
    sparsematrix sa;
    double eps;
    double v;
    sparsegenerator g;
    hqrndstate rs;
    ae_bool isupper;
    ae_bool isunit;
    ae_int_t optype;
    ae_int_t triangletype;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&y0, 0, sizeof(y0));
    memset(&y1, 0, sizeof(y1));
    memset(&ey, 0, sizeof(ey));
    memset(&a, 0, sizeof(a));
    memset(&ea, 0, sizeof(ea));
    memset(&s0, 0, sizeof(s0));
    memset(&s1, 0, sizeof(s1));
    memset(&sa, 0, sizeof(sa));
    memset(&g, 0, sizeof(g));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ey, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ea, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&s0, _state, ae_true);
    _sparsematrix_init(&s1, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    _sparsegenerator_init(&g, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    eps = 10000*ae_machineepsilon;
    result = ae_false;
    hqrndrandomize(&rs, _state);
    
    /*
     * Test sparseTRMV
     */
    for(n=1; n<=20; n++)
    {
        for(triangletype=-1; triangletype<=1; triangletype++)
        {
            isupper = ae_fp_greater(hqrnduniformr(&rs, _state),0.5);
            if( triangletype<0 )
            {
                isupper = ae_false;
            }
            if( triangletype>0 )
            {
                isupper = ae_true;
            }
            testsparseunit_initgenerator(n, n, 0, triangletype, &g, _state);
            while(testsparseunit_generatenext(&g, &a, &sa, _state))
            {
                
                /*
                 * Settings (IsUpper was already set, handle the rest)
                 */
                isunit = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
                optype = hqrnduniformi(&rs, 2, _state);
                
                /*
                 * Convert SA to desired storage format:
                 * * S0 stores unmodified copy
                 * * S1 stores copy with unmodified triangle corresponding
                 *   to IsUpper and another triangle being spoiled by random
                 *   trash
                 */
                sparsecopytohash(&sa, &s1, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            sparseset(&s1, i, j, hqrnduniformr(&rs, _state), _state);
                        }
                    }
                }
                if( ae_fp_less(hqrnduniformr(&rs, _state),0.5) )
                {
                    sparsecopytocrs(&sa, &s0, _state);
                    sparseconverttocrs(&s1, _state);
                }
                else
                {
                    sparsecopytosks(&sa, &s0, _state);
                    sparseconverttosks(&s1, _state);
                }
                
                /*
                 * Generate "effective A"
                 */
                ae_matrix_set_length(&ea, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ea.ptr.pp_double[i][j] = (double)(0);
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j>=i&&isupper)||(j<=i&&!isupper) )
                        {
                            i1 = i;
                            j1 = j;
                            if( optype==1 )
                            {
                                swapi(&i1, &j1, _state);
                            }
                            ea.ptr.pp_double[i1][j1] = a.ptr.pp_double[i][j];
                            if( isunit&&i1==j1 )
                            {
                                ea.ptr.pp_double[i1][j1] = 1.0;
                            }
                        }
                    }
                }
                
                /*
                 * Test SparseTRMV
                 */
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&x1, n, _state);
                for(j=0; j<=n-1; j++)
                {
                    x0.ptr.p_double[j] = hqrnduniformr(&rs, _state)-0.5;
                    x1.ptr.p_double[j] = x0.ptr.p_double[j];
                }
                sparsetrmv(&s0, isupper, isunit, optype, &x0, &y0, _state);
                ae_set_error_flag(&result, y0.cnt<n, __FILE__, __LINE__, "testsparseunit.ap:1386");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&ea.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y0.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1392");
                }
                sparsetrmv(&s0, isupper, isunit, optype, &x0, &y1, _state);
                ae_set_error_flag(&result, y1.cnt<n, __FILE__, __LINE__, "testsparseunit.ap:1395");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&ea.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(v-y1.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1401");
                }
            }
        }
    }
    
    /*
     * Test sparseTRSV
     */
    for(n=1; n<=20; n++)
    {
        for(triangletype=-1; triangletype<=1; triangletype++)
        {
            isupper = ae_fp_greater(hqrnduniformr(&rs, _state),0.5);
            if( triangletype==-1 )
            {
                isupper = ae_false;
            }
            if( triangletype==1 )
            {
                isupper = ae_true;
            }
            testsparseunit_initgenerator(n, n, 1, triangletype, &g, _state);
            while(testsparseunit_generatenext(&g, &a, &sa, _state))
            {
                
                /*
                 * Settings (IsUpper was already set, handle the rest)
                 */
                isunit = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
                optype = hqrnduniformi(&rs, 2, _state);
                
                /*
                 * Convert SA to desired storage format:
                 * * S0 stores unmodified copy
                 * * S1 stores copy with unmodified triangle corresponding
                 *   to IsUpper and another triangle being spoiled by random
                 *   trash
                 */
                sparsecopytohash(&sa, &s1, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            sparseset(&s1, i, j, hqrnduniformr(&rs, _state), _state);
                        }
                    }
                }
                if( ae_fp_less(hqrnduniformr(&rs, _state),0.5) )
                {
                    sparsecopytocrs(&sa, &s0, _state);
                    sparseconverttocrs(&s1, _state);
                }
                else
                {
                    sparsecopytosks(&sa, &s0, _state);
                    sparseconverttosks(&s1, _state);
                }
                
                /*
                 * Generate "effective A" and EY = inv(EA)*x0
                 */
                ae_matrix_set_length(&ea, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ea.ptr.pp_double[i][j] = (double)(0);
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j>=i&&isupper)||(j<=i&&!isupper) )
                        {
                            i1 = i;
                            j1 = j;
                            if( optype==1 )
                            {
                                swapi(&i1, &j1, _state);
                            }
                            ea.ptr.pp_double[i1][j1] = a.ptr.pp_double[i][j];
                            if( isunit&&i1==j1 )
                            {
                                ea.ptr.pp_double[i1][j1] = 1.0;
                            }
                        }
                    }
                }
                ae_vector_set_length(&ey, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    ey.ptr.p_double[i] = hqrnduniformr(&rs, _state)-0.5;
                }
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&x1, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&ea.ptr.pp_double[i][0], 1, &ey.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    x0.ptr.p_double[i] = v;
                    x1.ptr.p_double[i] = v;
                }
                
                /*
                 * Test SparseTRSV
                 */
                sparsetrsv(&s0, isupper, isunit, optype, &x0, _state);
                ae_set_error_flag(&result, x0.cnt<n, __FILE__, __LINE__, "testsparseunit.ap:1485");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(ey.ptr.p_double[i]-x0.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1489");
                }
                sparsetrsv(&s1, isupper, isunit, optype, &x1, _state);
                ae_set_error_flag(&result, x1.cnt<n, __FILE__, __LINE__, "testsparseunit.ap:1491");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(ey.ptr.p_double[i]-x1.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testsparseunit.ap:1495");
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing basic functional

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool basicfuncrandomtest(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t j1;
    ae_matrix a;
    ae_int_t mfigure;
    ae_int_t temp;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    _sparsematrix_init(&s, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    n = 20;
    m = 20;
    mfigure = 10;
    for(i=1; i<=m-1; i++)
    {
        for(j=1; j<=n-1; j++)
        {
            sparsecreate(i, j, 0, &s, _state);
            ae_matrix_set_length(&a, i, j, _state);
            
            /*
             * Checking for Matrix with hash table type
             */
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    temp = 2*ae_randominteger(mfigure, _state)-mfigure;
                    a.ptr.pp_double[i1][j1] = (double)(temp);
                    if( ae_randominteger(2, _state)==0 )
                    {
                        sparseset(&s, i1, j1, (double)(temp), _state);
                        sparseset(&s, i1, j1, (double)(temp), _state);
                    }
                    else
                    {
                        sparseadd(&s, i1, j1, (double)(temp), _state);
                        sparseadd(&s, i1, j1, (double)(0), _state);
                    }
                    if( ae_fp_neq(a.ptr.pp_double[i1][j1],sparseget(&s, i1, j1, _state)) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
            
            /*
             * Nulling all elements
             */
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    if( ae_randominteger(2, _state)==0 )
                    {
                        sparseset(&s, i1, j1, (double)(0), _state);
                    }
                    else
                    {
                        sparseadd(&s, i1, j1, -1*sparseget(&s, i1, j1, _state), _state);
                    }
                }
            }
            
            /*
             * Again initialization of the matrix and check new values
             */
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    temp = 2*ae_randominteger(mfigure, _state)-mfigure;
                    a.ptr.pp_double[i1][j1] = (double)(temp);
                    if( ae_randominteger(2, _state)==0 )
                    {
                        sparseset(&s, i1, j1, (double)(temp), _state);
                    }
                    else
                    {
                        sparseadd(&s, i1, j1, (double)(temp), _state);
                    }
                    if( ae_fp_neq(a.ptr.pp_double[i1][j1],sparseget(&s, i1, j1, _state)) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
            
            /*
             * Checking for Matrix with CRS type
             */
            sparseconverttocrs(&s, _state);
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    if( ae_fp_neq(a.ptr.pp_double[i1][j1],sparseget(&s, i1, j1, _state)) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing multyplication matrix with vector

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool linearfunctionstest(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t j1;
    double lb;
    double rb;
    ae_matrix a;
    ae_vector x0;
    ae_vector x1;
    ae_vector ty;
    ae_vector tyt;
    ae_vector y;
    ae_vector yt;
    ae_vector y0;
    ae_vector yt0;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&ty, 0, sizeof(ty));
    memset(&tyt, 0, sizeof(tyt));
    memset(&y, 0, sizeof(y));
    memset(&yt, 0, sizeof(yt));
    memset(&y0, 0, sizeof(y0));
    memset(&yt0, 0, sizeof(yt0));
    _sparsematrix_init(&s, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ty, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tyt, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yt, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yt0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Accuracy
     */
    eps = 1000*ae_machineepsilon;
    
    /*
     * Size of the matrix (m*n)
     */
    n = 10;
    m = 10;
    
    /*
     * Left and right borders, limiting matrix values
     */
    lb = (double)(-10);
    rb = (double)(10);
    
    /*
     * Test linear algebra functions for:
     * a) sparse matrix converted to CRS from Hash-Table
     * b) sparse matrix initially created as CRS
     */
    for(i=1; i<=m-1; i++)
    {
        for(j=1; j<=n-1; j++)
        {
            
            /*
             * Prepare test problem
             */
            testsparseunit_createrandom(i, j, -1, -1, -1, -1, &a, &s, _state);
            
            /*
             * Initialize temporaries
             */
            ae_vector_set_length(&ty, i, _state);
            ae_vector_set_length(&tyt, j, _state);
            for(i1=0; i1<=i-1; i1++)
            {
                ty.ptr.p_double[i1] = (double)(0);
            }
            for(i1=0; i1<=j-1; i1++)
            {
                tyt.ptr.p_double[i1] = (double)(0);
            }
            ae_vector_set_length(&x0, j, _state);
            ae_vector_set_length(&x1, i, _state);
            for(i1=0; i1<=j-1; i1++)
            {
                x0.ptr.p_double[i1] = (rb-lb)*ae_randomreal(_state)+lb;
            }
            for(i1=0; i1<=i-1; i1++)
            {
                x1.ptr.p_double[i1] = (rb-lb)*ae_randomreal(_state)+lb;
            }
            
            /*
             * Consider two cases: square matrix, and non-square matrix
             */
            if( i!=j )
            {
                
                /*
                 * Searching true result
                 */
                for(i1=0; i1<=i-1; i1++)
                {
                    for(j1=0; j1<=j-1; j1++)
                    {
                        ty.ptr.p_double[i1] = ty.ptr.p_double[i1]+a.ptr.pp_double[i1][j1]*x0.ptr.p_double[j1];
                        tyt.ptr.p_double[j1] = tyt.ptr.p_double[j1]+a.ptr.pp_double[i1][j1]*x1.ptr.p_double[i1];
                    }
                }
                
                /*
                 * Multiplication
                 */
                sparsemv(&s, &x0, &y, _state);
                sparsemtv(&s, &x1, &yt, _state);
                
                /*
                 * Check for MV-result
                 */
                for(i1=0; i1<=i-1; i1++)
                {
                    if( ae_fp_greater_eq(ae_fabs(y.ptr.p_double[i1]-ty.ptr.p_double[i1], _state),eps) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
                
                /*
                 * Check for MTV-result
                 */
                for(i1=0; i1<=j-1; i1++)
                {
                    if( ae_fp_greater_eq(ae_fabs(yt.ptr.p_double[i1]-tyt.ptr.p_double[i1], _state),eps) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
            else
            {
                
                /*
                 * Searching true result
                 */
                for(i1=0; i1<=i-1; i1++)
                {
                    for(j1=0; j1<=j-1; j1++)
                    {
                        ty.ptr.p_double[i1] = ty.ptr.p_double[i1]+a.ptr.pp_double[i1][j1]*x0.ptr.p_double[j1];
                        tyt.ptr.p_double[j1] = tyt.ptr.p_double[j1]+a.ptr.pp_double[i1][j1]*x0.ptr.p_double[i1];
                    }
                }
                sparsemv(&s, &x0, &y, _state);
                sparsemtv(&s, &x0, &yt, _state);
                sparsemv2(&s, &x0, &y0, &yt0, _state);
                
                /*
                 * Check for MV2-result
                 */
                for(i1=0; i1<=i-1; i1++)
                {
                    if( ae_fp_greater_eq(ae_fabs(y0.ptr.p_double[i1]-ty.ptr.p_double[i1], _state),eps)||ae_fp_greater_eq(ae_fabs(yt0.ptr.p_double[i1]-tyt.ptr.p_double[i1], _state),eps) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
                
                /*
                 * Check for MV- and MTV-result by help MV2
                 */
                for(i1=0; i1<=i-1; i1++)
                {
                    if( ae_fp_greater(ae_fabs(y0.ptr.p_double[i1]-y.ptr.p_double[i1], _state),eps)||ae_fp_greater(ae_fabs(yt0.ptr.p_double[i1]-yt.ptr.p_double[i1], _state),eps) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing multyplication for simmetric matrix with vector

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool linearfunctionsstest(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    ae_int_t m;
    ae_int_t i;
    ae_int_t i1;
    ae_int_t j1;
    double lb;
    double rb;
    ae_matrix a;
    ae_vector x0;
    ae_vector x1;
    ae_vector ty;
    ae_vector tyt;
    ae_vector y;
    ae_vector yt;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&ty, 0, sizeof(ty));
    memset(&tyt, 0, sizeof(tyt));
    memset(&y, 0, sizeof(y));
    memset(&yt, 0, sizeof(yt));
    _sparsematrix_init(&s, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ty, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tyt, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yt, 0, DT_REAL, _state, ae_true);

    
    /*
     *Accuracy
     */
    eps = 1000*ae_machineepsilon;
    
    /*
     * Size of the matrix (m*m)
     */
    m = 10;
    
    /*
     * Left and right borders, limiting matrix values
     */
    lb = (double)(-10);
    rb = (double)(10);
    
    /*
     * Test linear algebra functions for:
     * a) sparse matrix converted to CRS from Hash-Table
     * b) sparse matrix initially created as CRS
     */
    for(i=1; i<=m-1; i++)
    {
        
        /*
         * Prepare test problem
         */
        testsparseunit_createrandom(i, i, -1, -1, -1, -1, &a, &s, _state);
        
        /*
         * Initialize temporaries
         */
        ae_vector_set_length(&ty, i, _state);
        ae_vector_set_length(&tyt, i, _state);
        ae_vector_set_length(&x0, i, _state);
        ae_vector_set_length(&x1, i, _state);
        for(i1=0; i1<=i-1; i1++)
        {
            ty.ptr.p_double[i1] = (double)(0);
            tyt.ptr.p_double[i1] = (double)(0);
            x0.ptr.p_double[i1] = (rb-lb)*ae_randomreal(_state)+lb;
            x1.ptr.p_double[i1] = (rb-lb)*ae_randomreal(_state)+lb;
        }
        
        /*
         * Searching true result for upper and lower triangles
         * of the matrix
         */
        for(i1=0; i1<=i-1; i1++)
        {
            for(j1=i1; j1<=i-1; j1++)
            {
                ty.ptr.p_double[i1] = ty.ptr.p_double[i1]+a.ptr.pp_double[i1][j1]*x0.ptr.p_double[j1];
                if( i1!=j1 )
                {
                    ty.ptr.p_double[j1] = ty.ptr.p_double[j1]+a.ptr.pp_double[i1][j1]*x0.ptr.p_double[i1];
                }
            }
        }
        for(i1=0; i1<=i-1; i1++)
        {
            for(j1=0; j1<=i1; j1++)
            {
                tyt.ptr.p_double[i1] = tyt.ptr.p_double[i1]+a.ptr.pp_double[i1][j1]*x1.ptr.p_double[j1];
                if( i1!=j1 )
                {
                    tyt.ptr.p_double[j1] = tyt.ptr.p_double[j1]+a.ptr.pp_double[i1][j1]*x1.ptr.p_double[i1];
                }
            }
        }
        
        /*
         * Multiplication
         */
        sparsesmv(&s, ae_true, &x0, &y, _state);
        sparsesmv(&s, ae_false, &x1, &yt, _state);
        
        /*
         * Check for SMV-result
         */
        for(i1=0; i1<=i-1; i1++)
        {
            if( ae_fp_greater_eq(ae_fabs(y.ptr.p_double[i1]-ty.ptr.p_double[i1], _state),eps)||ae_fp_greater_eq(ae_fabs(yt.ptr.p_double[i1]-tyt.ptr.p_double[i1], _state),eps) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing multyplication sparse matrix with nerrow dense matrix

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool linearfunctionsmmtest(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    ae_int_t n;
    ae_int_t m;
    ae_int_t kmax;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t i1;
    ae_int_t j1;
    ae_int_t k1;
    double lb;
    double rb;
    ae_matrix a;
    ae_matrix x0;
    ae_matrix x1;
    ae_matrix ty;
    ae_matrix tyt;
    ae_matrix y;
    ae_matrix yt;
    ae_matrix y0;
    ae_matrix yt0;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&ty, 0, sizeof(ty));
    memset(&tyt, 0, sizeof(tyt));
    memset(&y, 0, sizeof(y));
    memset(&yt, 0, sizeof(yt));
    memset(&y0, 0, sizeof(y0));
    memset(&yt0, 0, sizeof(yt0));
    _sparsematrix_init(&s, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ty, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&tyt, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&y, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&yt, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&y0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&yt0, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Accuracy
     */
    eps = 1000*ae_machineepsilon;
    
    /*
     * Size of the matrix (m*n)
     */
    n = 32;
    m = 32;
    kmax = 32;
    
    /*
     * Left and right borders, limiting matrix values
     */
    lb = (double)(-10);
    rb = (double)(10);
    
    /*
     * Test linear algebra functions for:
     * a) sparse matrix converted to CRS from Hash-Table
     * b) sparse matrix initially created as CRS
     */
    for(i=1; i<=m-1; i++)
    {
        for(j=1; j<=n-1; j++)
        {
            
            /*
             * Prepare test problem
             */
            testsparseunit_createrandom(i, j, -1, -1, -1, -1, &a, &s, _state);
            ae_matrix_set_length(&x0, j, kmax, _state);
            ae_matrix_set_length(&x1, i, kmax, _state);
            for(i1=0; i1<=j-1; i1++)
            {
                for(j1=0; j1<=kmax-1; j1++)
                {
                    x0.ptr.pp_double[i1][j1] = (rb-lb)*ae_randomreal(_state)+lb;
                }
            }
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=kmax-1; j1++)
                {
                    x1.ptr.pp_double[i1][j1] = (rb-lb)*ae_randomreal(_state)+lb;
                }
            }
            ae_matrix_set_length(&ty, i, kmax, _state);
            ae_matrix_set_length(&tyt, j, kmax, _state);
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=kmax-1; j1++)
                {
                    ty.ptr.pp_double[i1][j1] = (double)(0);
                }
            }
            for(i1=0; i1<=j-1; i1++)
            {
                for(j1=0; j1<=kmax-1; j1++)
                {
                    tyt.ptr.pp_double[i1][j1] = (double)(0);
                }
            }
            if( i!=j )
            {
                for(i1=0; i1<=i-1; i1++)
                {
                    for(k1=0; k1<=kmax-1; k1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            ty.ptr.pp_double[i1][k1] = ty.ptr.pp_double[i1][k1]+a.ptr.pp_double[i1][j1]*x0.ptr.pp_double[j1][k1];
                            tyt.ptr.pp_double[j1][k1] = tyt.ptr.pp_double[j1][k1]+a.ptr.pp_double[i1][j1]*x1.ptr.pp_double[i1][k1];
                        }
                    }
                }
            }
            else
            {
                for(i1=0; i1<=i-1; i1++)
                {
                    for(k1=0; k1<=kmax-1; k1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            ty.ptr.pp_double[i1][k1] = ty.ptr.pp_double[i1][k1]+a.ptr.pp_double[i1][j1]*x0.ptr.pp_double[j1][k1];
                            tyt.ptr.pp_double[j1][k1] = tyt.ptr.pp_double[j1][k1]+a.ptr.pp_double[i1][j1]*x0.ptr.pp_double[i1][k1];
                        }
                    }
                }
            }
            for(k=1; k<=kmax; k++)
            {
                
                /*
                 * Consider two cases: square matrix, and non-square matrix
                 */
                if( i!=j )
                {
                    
                    /*
                     * Multiplication
                     */
                    sparsemm(&s, &x0, k, &y, _state);
                    sparsemtm(&s, &x1, k, &yt, _state);
                    
                    /*
                     * Check for MM-result
                     */
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=k-1; j1++)
                        {
                            if( ae_fp_greater_eq(ae_fabs(y.ptr.pp_double[i1][j1]-ty.ptr.pp_double[i1][j1], _state),eps) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                    
                    /*
                     * Check for MTM-result
                     */
                    for(i1=0; i1<=j-1; i1++)
                    {
                        for(j1=0; j1<=k-1; j1++)
                        {
                            if( ae_fp_greater_eq(ae_fabs(yt.ptr.pp_double[i1][j1]-tyt.ptr.pp_double[i1][j1], _state),eps) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
                else
                {
                    sparsemm(&s, &x0, k, &y, _state);
                    sparsemtm(&s, &x0, k, &yt, _state);
                    sparsemm2(&s, &x0, k, &y0, &yt0, _state);
                    
                    /*
                     * Check for MM2-result
                     */
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=k-1; j1++)
                        {
                            if( ae_fp_greater_eq(ae_fabs(y0.ptr.pp_double[i1][j1]-ty.ptr.pp_double[i1][j1], _state),eps)||ae_fp_greater_eq(ae_fabs(yt0.ptr.pp_double[i1][j1]-tyt.ptr.pp_double[i1][j1], _state),eps) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                    
                    /*
                     * Check for MV- and MTM-result by help MV2
                     */
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=k-1; j1++)
                        {
                            if( ae_fp_greater(ae_fabs(y0.ptr.pp_double[i1][j1]-y.ptr.pp_double[i1][j1], _state),eps)||ae_fp_greater(ae_fabs(yt0.ptr.pp_double[i1][j1]-yt.ptr.pp_double[i1][j1], _state),eps) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing multyplication for simmetric sparse matrix with narrow
dense matrix

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool linearfunctionssmmtest(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    ae_int_t m;
    ae_int_t k;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t j1;
    ae_int_t k1;
    double lb;
    double rb;
    ae_matrix a;
    ae_matrix x0;
    ae_matrix x1;
    ae_matrix ty;
    ae_matrix tyt;
    ae_matrix y;
    ae_matrix yt;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&ty, 0, sizeof(ty));
    memset(&tyt, 0, sizeof(tyt));
    memset(&y, 0, sizeof(y));
    memset(&yt, 0, sizeof(yt));
    _sparsematrix_init(&s, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ty, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&tyt, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&y, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&yt, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Accuracy
     */
    eps = 1000*ae_machineepsilon;
    
    /*
     * Size of the matrix (m*m)
     */
    m = 32;
    k = 32;
    
    /*
     * Left and right borders, limiting matrix values
     */
    lb = (double)(-10);
    rb = (double)(10);
    
    /*
     * Test linear algebra functions for:
     * a) sparse matrix converted to CRS from Hash-Table
     * b) sparse matrix initially created as CRS
     */
    for(i=1; i<=m-1; i++)
    {
        for(j=1; j<=k-1; j++)
        {
            
            /*
             * Prepare test problem
             */
            testsparseunit_createrandom(i, i, -1, -1, -1, -1, &a, &s, _state);
            
            /*
             * Initialize temporaries
             */
            ae_matrix_set_length(&ty, i, j, _state);
            ae_matrix_set_length(&tyt, i, j, _state);
            ae_matrix_set_length(&x0, i, j, _state);
            ae_matrix_set_length(&x1, i, j, _state);
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    ty.ptr.pp_double[i1][j1] = (double)(0);
                    tyt.ptr.pp_double[i1][j1] = (double)(0);
                    x0.ptr.pp_double[i1][j1] = (rb-lb)*ae_randomreal(_state)+lb;
                    x1.ptr.pp_double[i1][j1] = (rb-lb)*ae_randomreal(_state)+lb;
                }
            }
            
            /*
             * Searching true result for upper and lower triangles
             * of the matrix
             */
            for(k1=0; k1<=j-1; k1++)
            {
                for(i1=0; i1<=i-1; i1++)
                {
                    for(j1=i1; j1<=i-1; j1++)
                    {
                        ty.ptr.pp_double[i1][k1] = ty.ptr.pp_double[i1][k1]+a.ptr.pp_double[i1][j1]*x0.ptr.pp_double[j1][k1];
                        if( i1!=j1 )
                        {
                            ty.ptr.pp_double[j1][k1] = ty.ptr.pp_double[j1][k1]+a.ptr.pp_double[i1][j1]*x0.ptr.pp_double[i1][k1];
                        }
                    }
                }
            }
            for(k1=0; k1<=j-1; k1++)
            {
                for(i1=0; i1<=i-1; i1++)
                {
                    for(j1=0; j1<=i1; j1++)
                    {
                        tyt.ptr.pp_double[i1][k1] = tyt.ptr.pp_double[i1][k1]+a.ptr.pp_double[i1][j1]*x1.ptr.pp_double[j1][k1];
                        if( i1!=j1 )
                        {
                            tyt.ptr.pp_double[j1][k1] = tyt.ptr.pp_double[j1][k1]+a.ptr.pp_double[i1][j1]*x1.ptr.pp_double[i1][k1];
                        }
                    }
                }
            }
            
            /*
             * Multiplication
             */
            sparsesmm(&s, ae_true, &x0, j, &y, _state);
            sparsesmm(&s, ae_false, &x1, j, &yt, _state);
            
            /*
             * Check for SMM-result
             */
            for(k1=0; k1<=j-1; k1++)
            {
                for(i1=0; i1<=i-1; i1++)
                {
                    if( ae_fp_greater_eq(ae_fabs(y.ptr.pp_double[i1][k1]-ty.ptr.pp_double[i1][k1], _state),eps)||ae_fp_greater_eq(ae_fabs(yt.ptr.pp_double[i1][k1]-tyt.ptr.pp_double[i1][k1], _state),eps) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for basic test SparseCopy

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool basiccopyfunctest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    sparsematrix ss;
    sparsematrix sss;
    ae_int_t n;
    ae_int_t m;
    ae_vector ner;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t j1;
    ae_matrix a;
    double a0;
    double a1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&ss, 0, sizeof(ss));
    memset(&sss, 0, sizeof(sss));
    memset(&ner, 0, sizeof(ner));
    memset(&a, 0, sizeof(a));
    _sparsematrix_init(&s, _state, ae_true);
    _sparsematrix_init(&ss, _state, ae_true);
    _sparsematrix_init(&sss, _state, ae_true);
    ae_vector_init(&ner, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    n = 30;
    m = 30;
    for(i=1; i<=m-1; i++)
    {
        for(j=1; j<=n-1; j++)
        {
            sparsecreate(i, j, 1, &s, _state);
            ae_matrix_set_length(&a, i, j, _state);
            ae_vector_set_length(&ner, i, _state);
            for(i1=0; i1<=i-1; i1++)
            {
                if( i1<=j-3 )
                {
                    ner.ptr.p_int[i1] = 2;
                }
                else
                {
                    if( j-3<i1&&i1<=j-2 )
                    {
                        ner.ptr.p_int[i1] = 1;
                    }
                    else
                    {
                        ner.ptr.p_int[i1] = 0;
                    }
                }
            }
            sparsecreatecrs(i, j, &ner, &sss, _state);
            
            /*
             * Checking for Matrix with hash table type
             */
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    if( j1>i1&&j1<=i1+2 )
                    {
                        a.ptr.pp_double[i1][j1] = (double)(i1+j1+1);
                        sparseset(&s, i1, j1, a.ptr.pp_double[i1][j1], _state);
                        sparseadd(&s, i1, j1, (double)(0), _state);
                        sparseset(&sss, i1, j1, a.ptr.pp_double[i1][j1], _state);
                    }
                    else
                    {
                        a.ptr.pp_double[i1][j1] = (double)(0);
                        sparseset(&s, i1, j1, a.ptr.pp_double[i1][j1], _state);
                        sparseadd(&s, i1, j1, (double)(0), _state);
                    }
                    
                    /*
                     * Check for SparseCreate
                     */
                    sparsecopy(&s, &ss, _state);
                    a0 = sparseget(&s, i1, j1, _state);
                    a1 = sparseget(&ss, i1, j1, _state);
                    if( ae_fp_neq(a0,a1) )
                    {
                        if( !silent )
                        {
                            printf("BasicCopyFuncTest::Report::SparseGet\n");
                            printf("S::[%0d,%0d]=%0.5f\n",
                                (int)(i1),
                                (int)(j1),
                                (double)(a0));
                            printf("SS::[%0d,%0d]=%0.5f\n",
                                (int)(i1),
                                (int)(j1),
                                (double)(a1));
                            printf("          TEST FAILED.\n");
                        }
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
            
            /*
             * Check for SparseCreateCRS
             */
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    sparsecopy(&sss, &ss, _state);
                    a0 = sparseget(&sss, i1, j1, _state);
                    a1 = sparseget(&ss, i1, j1, _state);
                    if( ae_fp_neq(a0,a1) )
                    {
                        if( !silent )
                        {
                            printf("BasicCopyFuncTest::Report::SparseGet\n");
                            printf("S::[%0d,%0d]=%0.5f\n",
                                (int)(i1),
                                (int)(j1),
                                (double)(a0));
                            printf("SS::[%0d,%0d]=%0.5f\n",
                                (int)(i1),
                                (int)(j1),
                                (double)(a1));
                            printf("          TEST FAILED.\n");
                        }
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
            
            /*
             * Check for Matrix with CRS type
             */
            sparseconverttocrs(&s, _state);
            sparsecopy(&s, &ss, _state);
            for(i1=0; i1<=i-1; i1++)
            {
                for(j1=0; j1<=j-1; j1++)
                {
                    a0 = sparseget(&s, i1, j1, _state);
                    a1 = sparseget(&ss, i1, j1, _state);
                    if( ae_fp_neq(a0,a1) )
                    {
                        if( !silent )
                        {
                            printf("BasicCopyFuncTest::Report::SparseGet\n");
                            printf("S::[%0d,%0d]=%0.5f\n",
                                (int)(i1),
                                (int)(j1),
                                (double)(a0));
                            printf("SS::[%0d,%0d]=%0.5f\n",
                                (int)(i1),
                                (int)(j1),
                                (double)(a1));
                            printf("          TEST FAILED.\n");
                        }
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
    }
    if( !silent )
    {
        printf("          TEST IS PASSED.\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing SparseCopy

  -- ALGLIB PROJECT --
     Copyright 14.10.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool copyfunctest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    sparsematrix ss;
    ae_int_t n;
    ae_int_t m;
    ae_int_t mtype;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t j1;
    double lb;
    double rb;
    ae_matrix a;
    ae_vector x0;
    ae_vector x1;
    ae_vector ty;
    ae_vector tyt;
    ae_vector y;
    ae_vector yt;
    ae_vector y0;
    ae_vector yt0;
    ae_vector cpy;
    ae_vector cpyt;
    ae_vector cpy0;
    ae_vector cpyt0;
    double eps;
    double a0;
    double a1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&ss, 0, sizeof(ss));
    memset(&a, 0, sizeof(a));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&ty, 0, sizeof(ty));
    memset(&tyt, 0, sizeof(tyt));
    memset(&y, 0, sizeof(y));
    memset(&yt, 0, sizeof(yt));
    memset(&y0, 0, sizeof(y0));
    memset(&yt0, 0, sizeof(yt0));
    memset(&cpy, 0, sizeof(cpy));
    memset(&cpyt, 0, sizeof(cpyt));
    memset(&cpy0, 0, sizeof(cpy0));
    memset(&cpyt0, 0, sizeof(cpyt0));
    _sparsematrix_init(&s, _state, ae_true);
    _sparsematrix_init(&ss, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ty, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tyt, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yt, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yt0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cpy, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cpyt, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cpy0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cpyt0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Accuracy
     */
    eps = 1000*ae_machineepsilon;
    
    /*
     * Size of the matrix (m*n)
     */
    n = 30;
    m = 30;
    
    /*
     * Left and right borders, limiting matrix values
     */
    lb = (double)(-10);
    rb = (double)(10);
    
    /*
     * Test linear algebra functions for:
     * a) sparse matrix converted to CRS from Hash-Table
     * b) sparse matrix initially created as CRS
     */
    for(i=1; i<=m-1; i++)
    {
        for(j=1; j<=n-1; j++)
        {
            for(mtype=0; mtype<=1; mtype++)
            {
                
                /*
                 * Prepare test problem
                 */
                testsparseunit_createrandom(i, j, -1, mtype, -1, -1, &a, &s, _state);
                sparsecopy(&s, &ss, _state);
                
                /*
                 * Initialize temporaries
                 */
                ae_vector_set_length(&ty, i, _state);
                ae_vector_set_length(&tyt, j, _state);
                for(i1=0; i1<=i-1; i1++)
                {
                    ty.ptr.p_double[i1] = (double)(0);
                }
                for(i1=0; i1<=j-1; i1++)
                {
                    tyt.ptr.p_double[i1] = (double)(0);
                }
                ae_vector_set_length(&x0, j, _state);
                ae_vector_set_length(&x1, i, _state);
                for(i1=0; i1<=j-1; i1++)
                {
                    x0.ptr.p_double[i1] = (rb-lb)*ae_randomreal(_state)+lb;
                }
                for(i1=0; i1<=i-1; i1++)
                {
                    x1.ptr.p_double[i1] = (rb-lb)*ae_randomreal(_state)+lb;
                }
                
                /*
                 * Consider two cases: square matrix, and non-square matrix
                 */
                if( i!=j )
                {
                    
                    /*
                     * Searching true result
                     */
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            ty.ptr.p_double[i1] = ty.ptr.p_double[i1]+a.ptr.pp_double[i1][j1]*x0.ptr.p_double[j1];
                            tyt.ptr.p_double[j1] = tyt.ptr.p_double[j1]+a.ptr.pp_double[i1][j1]*x1.ptr.p_double[i1];
                        }
                    }
                    
                    /*
                     * Multiplication
                     */
                    sparsemv(&s, &x0, &y, _state);
                    sparsemtv(&s, &x1, &yt, _state);
                    sparsemv(&ss, &x0, &cpy, _state);
                    sparsemtv(&ss, &x1, &cpyt, _state);
                    
                    /*
                     * Check for MV-result
                     */
                    for(i1=0; i1<=i-1; i1++)
                    {
                        if( (ae_fp_greater_eq(ae_fabs(y.ptr.p_double[i1]-ty.ptr.p_double[i1], _state),eps)||ae_fp_greater_eq(ae_fabs(cpy.ptr.p_double[i1]-ty.ptr.p_double[i1], _state),eps))||ae_fp_neq(cpy.ptr.p_double[i1]-y.ptr.p_double[i1],(double)(0)) )
                        {
                            if( !silent )
                            {
                                printf("CopyFuncTest::Report::RES_MV\n");
                                printf("Y[%0d]=%0.5f; tY[%0d]=%0.5f\n",
                                    (int)(i1),
                                    (double)(y.ptr.p_double[i1]),
                                    (int)(i1),
                                    (double)(ty.ptr.p_double[i1]));
                                printf("cpY[%0d]=%0.5f;\n",
                                    (int)(i1),
                                    (double)(cpy.ptr.p_double[i1]));
                                printf("          TEST FAILED.\n");
                            }
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                    
                    /*
                     * Check for MTV-result
                     */
                    for(i1=0; i1<=j-1; i1++)
                    {
                        if( (ae_fp_greater_eq(ae_fabs(yt.ptr.p_double[i1]-tyt.ptr.p_double[i1], _state),eps)||ae_fp_greater_eq(ae_fabs(cpyt.ptr.p_double[i1]-tyt.ptr.p_double[i1], _state),eps))||ae_fp_neq(cpyt.ptr.p_double[i1]-yt.ptr.p_double[i1],(double)(0)) )
                        {
                            if( !silent )
                            {
                                printf("CopyFuncTest::Report::RES_MTV\n");
                                printf("Yt[%0d]=%0.5f; tYt[%0d]=%0.5f\n",
                                    (int)(i1),
                                    (double)(yt.ptr.p_double[i1]),
                                    (int)(i1),
                                    (double)(tyt.ptr.p_double[i1]));
                                printf("cpYt[%0d]=%0.5f;\n",
                                    (int)(i1),
                                    (double)(cpyt.ptr.p_double[i1]));
                                printf("          TEST FAILED.\n");
                            }
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                    sparsecopy(&s, &ss, _state);
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            a0 = sparseget(&s, i1, j1, _state);
                            a1 = sparseget(&ss, i1, j1, _state);
                            if( ae_fp_neq(a0,a1) )
                            {
                                if( !silent )
                                {
                                    printf("CopyFuncTest::Report::SparseGet\n");
                                    printf("S::[%0d,%0d]=%0.5f\n",
                                        (int)(i1),
                                        (int)(j1),
                                        (double)(a0));
                                    printf("SS::[%0d,%0d]=%0.5f\n",
                                        (int)(i1),
                                        (int)(j1),
                                        (double)(a1));
                                    printf("          TEST FAILED.\n");
                                }
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
                else
                {
                    
                    /*
                     * Searching true result
                     */
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            ty.ptr.p_double[i1] = ty.ptr.p_double[i1]+a.ptr.pp_double[i1][j1]*x0.ptr.p_double[j1];
                            tyt.ptr.p_double[j1] = tyt.ptr.p_double[j1]+a.ptr.pp_double[i1][j1]*x0.ptr.p_double[i1];
                        }
                    }
                    
                    /*
                     * Multiplication
                     */
                    sparsemv(&s, &x0, &y, _state);
                    sparsemtv(&s, &x0, &yt, _state);
                    sparsemv2(&s, &x0, &y0, &yt0, _state);
                    sparsemv(&ss, &x0, &cpy, _state);
                    sparsemtv(&ss, &x0, &cpyt, _state);
                    sparsemv2(&ss, &x0, &cpy0, &cpyt0, _state);
                    
                    /*
                     * Check for MV2-result
                     */
                    for(i1=0; i1<=i-1; i1++)
                    {
                        if( ((((ae_fp_greater_eq(ae_fabs(y0.ptr.p_double[i1]-ty.ptr.p_double[i1], _state),eps)||ae_fp_greater_eq(ae_fabs(yt0.ptr.p_double[i1]-tyt.ptr.p_double[i1], _state),eps))||ae_fp_greater_eq(ae_fabs(cpy0.ptr.p_double[i1]-ty.ptr.p_double[i1], _state),eps))||ae_fp_greater_eq(ae_fabs(cpyt0.ptr.p_double[i1]-tyt.ptr.p_double[i1], _state),eps))||ae_fp_neq(cpy0.ptr.p_double[i1]-y0.ptr.p_double[i1],(double)(0)))||ae_fp_neq(cpyt0.ptr.p_double[i1]-yt0.ptr.p_double[i1],(double)(0)) )
                        {
                            if( !silent )
                            {
                                printf("CopyFuncTest::Report::RES_MV2\n");
                                printf("Y0[%0d]=%0.5f; tY[%0d]=%0.5f\n",
                                    (int)(i1),
                                    (double)(y0.ptr.p_double[i1]),
                                    (int)(i1),
                                    (double)(ty.ptr.p_double[i1]));
                                printf("Yt0[%0d]=%0.5f; tYt[%0d]=%0.5f\n",
                                    (int)(i1),
                                    (double)(yt0.ptr.p_double[i1]),
                                    (int)(i1),
                                    (double)(tyt.ptr.p_double[i1]));
                                printf("cpY0[%0d]=%0.5f;\n",
                                    (int)(i1),
                                    (double)(cpy0.ptr.p_double[i1]));
                                printf("cpYt0[%0d]=%0.5f;\n",
                                    (int)(i1),
                                    (double)(cpyt0.ptr.p_double[i1]));
                                printf("          TEST FAILED.\n");
                            }
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                    
                    /*
                     * Check for MV- and MTV-result by help MV2
                     */
                    for(i1=0; i1<=i-1; i1++)
                    {
                        if( ((ae_fp_greater(ae_fabs(y0.ptr.p_double[i1]-y.ptr.p_double[i1], _state),eps)||ae_fp_greater(ae_fabs(yt0.ptr.p_double[i1]-yt.ptr.p_double[i1], _state),eps))||ae_fp_greater(ae_fabs(cpy0.ptr.p_double[i1]-cpy.ptr.p_double[i1], _state),eps))||ae_fp_greater(ae_fabs(cpyt0.ptr.p_double[i1]-cpyt.ptr.p_double[i1], _state),eps) )
                        {
                            if( !silent )
                            {
                                printf("CopyFuncTest::Report::RES_MV_MVT\n");
                                printf("Y0[%0d]=%0.5f; Y[%0d]=%0.5f\n",
                                    (int)(i1),
                                    (double)(y0.ptr.p_double[i1]),
                                    (int)(i1),
                                    (double)(y.ptr.p_double[i1]));
                                printf("Yt0[%0d]=%0.5f; Yt[%0d]=%0.5f\n",
                                    (int)(i1),
                                    (double)(yt0.ptr.p_double[i1]),
                                    (int)(i1),
                                    (double)(yt.ptr.p_double[i1]));
                                printf("cpY0[%0d]=%0.5f; cpY[%0d]=%0.5f\n",
                                    (int)(i1),
                                    (double)(cpy0.ptr.p_double[i1]),
                                    (int)(i1),
                                    (double)(cpy.ptr.p_double[i1]));
                                printf("cpYt0[%0d]=%0.5f; cpYt[%0d]=%0.5f\n",
                                    (int)(i1),
                                    (double)(cpyt0.ptr.p_double[i1]),
                                    (int)(i1),
                                    (double)(cpyt.ptr.p_double[i1]));
                                printf("          TEST FAILED.\n");
                            }
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                    sparsecopy(&s, &ss, _state);
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            a0 = sparseget(&s, i1, j1, _state);
                            a1 = sparseget(&ss, i1, j1, _state);
                            if( ae_fp_neq(a0,a1) )
                            {
                                if( !silent )
                                {
                                    printf("CopyFuncTest::Report::SparseGet\n");
                                    printf("S::[%0d,%0d]=%0.5f\n",
                                        (int)(i1),
                                        (int)(j1),
                                        (double)(a0));
                                    printf("SS::[%0d,%0d]=%0.5f\n",
                                        (int)(i1),
                                        (int)(j1),
                                        (double)(a1));
                                    printf("          TEST FAILED.\n");
                                }
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
            }
        }
    }
    if( !silent )
    {
        printf("          TEST IS PASSED.\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function initializes sparse matrix generator, which is used to generate
a set of matrices with sequentially increasing sparsity.

PARAMETERS:
    M, N        -   matrix size. If M=0, then matrix is square N*N.
                    N and M must be small enough to store N*M dense matrix.
    MatKind     -   matrix properties:
                    * 0     -   general sparse (no structure)
                    * 1     -   general sparse, but diagonal is always present and non-zero
                    * 2     -   diagonally dominant, SPD
    Triangle    -   triangle being returned:
                    * +1    -   upper triangle
                    * -1    -   lower triangle
                    *  0    -   full matrix is returned
                    
OUTPUT PARAMETERS:
    G           -   generator
    A           -   matrix A in dense format
    SA          -   matrix A in sparse format (hash-table storage)
*************************************************************************/
static void testsparseunit_initgenerator(ae_int_t m,
     ae_int_t n,
     ae_int_t matkind,
     ae_int_t triangle,
     sparsegenerator* g,
     ae_state *_state)
{

    _sparsegenerator_clear(g);

    g->n = n;
    g->m = m;
    g->matkind = matkind;
    g->triangle = triangle;
    hqrndrandomize(&g->rs, _state);
    ae_vector_set_length(&g->rcs.ia, 5+1, _state);
    ae_vector_set_length(&g->rcs.ra, 1+1, _state);
    g->rcs.stage = -1;
}


static ae_bool testsparseunit_generatenext(sparsegenerator* g,
     /* Real    */ ae_matrix* da,
     sparsematrix* sa,
     ae_state *_state)
{
    ae_int_t n;
    ae_int_t m;
    ae_int_t nz;
    ae_int_t nzd;
    double pnz;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_bool result;

    ae_matrix_clear(da);
    _sparsematrix_clear(sa);

    
    /*
     * Reverse communication preparations
     * I know it looks ugly, but it works the same way
     * anywhere from C++ to Python.
     *
     * This code initializes locals by:
     * * random values determined during code
     *   generation - on first subroutine call
     * * values from previous call - on subsequent calls
     */
    if( g->rcs.stage>=0 )
    {
        n = g->rcs.ia.ptr.p_int[0];
        m = g->rcs.ia.ptr.p_int[1];
        nz = g->rcs.ia.ptr.p_int[2];
        nzd = g->rcs.ia.ptr.p_int[3];
        i = g->rcs.ia.ptr.p_int[4];
        j = g->rcs.ia.ptr.p_int[5];
        pnz = g->rcs.ra.ptr.p_double[0];
        v = g->rcs.ra.ptr.p_double[1];
    }
    else
    {
        n = 359;
        m = -58;
        nz = -919;
        nzd = -909;
        i = 81;
        j = 255;
        pnz = 74;
        v = -788;
    }
    if( g->rcs.stage==0 )
    {
        goto lbl_0;
    }
    if( g->rcs.stage==1 )
    {
        goto lbl_1;
    }
    
    /*
     * Routine body
     */
    n = g->n;
    if( g->m==0 )
    {
        m = n;
    }
    else
    {
        m = g->m;
    }
    ae_assert(m>0&&n>0, "GenerateNext: incorrect N/M", _state);
    
    /*
     * Generate general sparse matrix
     */
    if( g->matkind!=0 )
    {
        goto lbl_2;
    }
    nz = n*m;
lbl_4:
    if( ae_false )
    {
        goto lbl_5;
    }
    
    /*
     * Generate dense N*N matrix where probability of element
     * being non-zero is PNZ.
     */
    pnz = (double)nz/(double)(n*m);
    ae_matrix_set_length(&g->bufa, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_less_eq(hqrnduniformr(&g->rs, _state),pnz) )
            {
                g->bufa.ptr.pp_double[i][j] = hqrnduniformr(&g->rs, _state)-0.5;
            }
            else
            {
                g->bufa.ptr.pp_double[i][j] = 0.0;
            }
        }
    }
    
    /*
     * Output matrix and RComm
     */
    ae_matrix_set_length(da, m, n, _state);
    sparsecreate(m, n, ae_round(pnz*m*n, _state), sa, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (j<=i&&g->triangle<=0)||(j>=i&&g->triangle>=0) )
            {
                da->ptr.pp_double[i][j] = g->bufa.ptr.pp_double[i][j];
                sparseset(sa, i, j, g->bufa.ptr.pp_double[i][j], _state);
            }
            else
            {
                da->ptr.pp_double[i][j] = 0.0;
            }
        }
    }
    g->rcs.stage = 0;
    goto lbl_rcomm;
lbl_0:
    
    /*
     * Increase problem sparcity and try one more time. 
     * Stop after testing NZ=0.
     */
    if( nz==0 )
    {
        goto lbl_5;
    }
    nz = nz/2;
    goto lbl_4;
lbl_5:
    result = ae_false;
    return result;
lbl_2:
    
    /*
     * Generate general sparse matrix with non-zero diagonal
     */
    if( g->matkind!=1 )
    {
        goto lbl_6;
    }
    ae_assert(n==m, "GenerateNext: non-square matrix for MatKind=1", _state);
    nz = n*n-n;
lbl_8:
    if( ae_false )
    {
        goto lbl_9;
    }
    
    /*
     * Generate dense N*N matrix where probability of non-diagonal element
     * being non-zero is PNZ.
     */
    if( n>1 )
    {
        pnz = (double)nz/(double)(n*n-n);
    }
    else
    {
        pnz = (double)(1);
    }
    ae_matrix_set_length(&g->bufa, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( i==j )
            {
                do
                {
                    g->bufa.ptr.pp_double[i][i] = hqrnduniformr(&g->rs, _state)-0.5;
                }
                while(ae_fp_eq(g->bufa.ptr.pp_double[i][i],(double)(0)));
                g->bufa.ptr.pp_double[i][i] = g->bufa.ptr.pp_double[i][i]+1.5*ae_sign(g->bufa.ptr.pp_double[i][i], _state);
                continue;
            }
            if( ae_fp_less_eq(hqrnduniformr(&g->rs, _state),pnz) )
            {
                g->bufa.ptr.pp_double[i][j] = hqrnduniformr(&g->rs, _state)-0.5;
            }
            else
            {
                g->bufa.ptr.pp_double[i][j] = 0.0;
            }
        }
    }
    
    /*
     * Output matrix and RComm
     */
    ae_matrix_set_length(da, n, n, _state);
    sparsecreate(n, n, ae_round(pnz*(n*n-n)+n, _state), sa, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (j<=i&&g->triangle<=0)||(j>=i&&g->triangle>=0) )
            {
                da->ptr.pp_double[i][j] = g->bufa.ptr.pp_double[i][j];
                sparseset(sa, i, j, g->bufa.ptr.pp_double[i][j], _state);
            }
            else
            {
                da->ptr.pp_double[i][j] = 0.0;
            }
        }
    }
    g->rcs.stage = 1;
    goto lbl_rcomm;
lbl_1:
    
    /*
     * Increase problem sparcity and try one more time. 
     * Stop after testing NZ=0.
     */
    if( nz==0 )
    {
        goto lbl_9;
    }
    nz = nz/2;
    goto lbl_8;
lbl_9:
    result = ae_false;
    return result;
lbl_6:
    ae_assert(ae_false, "Assertion failed", _state);
    result = ae_false;
    return result;
    
    /*
     * Saving state
     */
lbl_rcomm:
    result = ae_true;
    g->rcs.ia.ptr.p_int[0] = n;
    g->rcs.ia.ptr.p_int[1] = m;
    g->rcs.ia.ptr.p_int[2] = nz;
    g->rcs.ia.ptr.p_int[3] = nzd;
    g->rcs.ia.ptr.p_int[4] = i;
    g->rcs.ia.ptr.p_int[5] = j;
    g->rcs.ra.ptr.p_double[0] = pnz;
    g->rcs.ra.ptr.p_double[1] = v;
    return result;
}


/*************************************************************************
This function creates random sparse matrix with some prescribed pattern.

INPUT PARAMETERS:
    M       -   number of rows
    N       -   number of columns
    PKind   -   sparsity pattern:
                *-1 = pattern is chosen at random as well as P0/P1
                * 0 = matrix with up to P0 non-zero elements at random locations
                      (however, actual number of non-zero elements can be
                      less than P0, and in fact can be zero)
                * 1 = band matrix with P0 non-zero elements below diagonal
                      and P1 non-zero element above diagonal
                * 2 = matrix with random number of contiguous non-zero 
                      elements in the each row
    CKind   -   creation type:
                *-1 = CKind is chosen at random
                * 0 = matrix is created in Hash-Table format and converted
                      to CRS representation
                * 1 = matrix is created in CRS format

OUTPUT PARAMETERS:
    DA      -   dense representation of A, array[M,N]
    SA      -   sparse representation of A, in CRS format

  -- ALGLIB PROJECT --
     Copyright 31.10.2011 by Bochkanov Sergey
*************************************************************************/
static void testsparseunit_createrandom(ae_int_t m,
     ae_int_t n,
     ae_int_t pkind,
     ae_int_t ckind,
     ae_int_t p0,
     ae_int_t p1,
     /* Real    */ ae_matrix* da,
     sparsematrix* sa,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxpkind;
    ae_int_t maxckind;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    ae_vector c0;
    ae_vector c1;
    ae_vector rowsizes;

    ae_frame_make(_state, &_frame_block);
    memset(&c0, 0, sizeof(c0));
    memset(&c1, 0, sizeof(c1));
    memset(&rowsizes, 0, sizeof(rowsizes));
    ae_matrix_clear(da);
    _sparsematrix_clear(sa);
    ae_vector_init(&c0, 0, DT_INT, _state, ae_true);
    ae_vector_init(&c1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&rowsizes, 0, DT_INT, _state, ae_true);

    maxpkind = 2;
    maxckind = 1;
    ae_assert(m>=1, "CreateRandom: incorrect parameters", _state);
    ae_assert(n>=1, "CreateRandom: incorrect parameters", _state);
    ae_assert(pkind>=-1&&pkind<=maxpkind, "CreateRandom: incorrect parameters", _state);
    ae_assert(ckind>=-1&&ckind<=maxckind, "CreateRandom: incorrect parameters", _state);
    if( pkind==-1 )
    {
        pkind = ae_randominteger(maxpkind+1, _state);
        if( pkind==0 )
        {
            p0 = ae_randominteger(m*n, _state);
        }
        if( pkind==1 )
        {
            p0 = ae_randominteger(ae_minint(m, n, _state), _state);
            p1 = ae_randominteger(ae_minint(m, n, _state), _state);
        }
    }
    if( ckind==-1 )
    {
        ckind = ae_randominteger(maxckind+1, _state);
    }
    if( pkind==0 )
    {
        
        /*
         * Matrix with elements at random locations
         */
        ae_matrix_set_length(da, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                da->ptr.pp_double[i][j] = (double)(0);
            }
        }
        if( ckind==0 )
        {
            
            /*
             * Create matrix in Hash format, convert to CRS
             */
            sparsecreate(m, n, 1, sa, _state);
            for(k=0; k<=p0-1; k++)
            {
                i = ae_randominteger(m, _state);
                j = ae_randominteger(n, _state);
                v = (double)(ae_randominteger(17, _state)-8)/(double)8;
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    da->ptr.pp_double[i][j] = v;
                    sparseset(sa, i, j, v, _state);
                }
                else
                {
                    da->ptr.pp_double[i][j] = da->ptr.pp_double[i][j]+v;
                    sparseadd(sa, i, j, v, _state);
                }
            }
            sparseconverttocrs(sa, _state);
            ae_frame_leave(_state);
            return;
        }
        if( ckind==1 )
        {
            
            /*
             * Create matrix in CRS format
             */
            for(k=0; k<=p0-1; k++)
            {
                i = ae_randominteger(m, _state);
                j = ae_randominteger(n, _state);
                v = (double)(ae_randominteger(17, _state)-8)/(double)8;
                da->ptr.pp_double[i][j] = v;
            }
            ae_vector_set_length(&rowsizes, m, _state);
            for(i=0; i<=m-1; i++)
            {
                rowsizes.ptr.p_int[i] = 0;
                for(j=0; j<=n-1; j++)
                {
                    if( ae_fp_neq(da->ptr.pp_double[i][j],(double)(0)) )
                    {
                        rowsizes.ptr.p_int[i] = rowsizes.ptr.p_int[i]+1;
                    }
                }
            }
            sparsecreatecrs(m, n, &rowsizes, sa, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( ae_fp_neq(da->ptr.pp_double[i][j],(double)(0)) )
                    {
                        sparseset(sa, i, j, da->ptr.pp_double[i][j], _state);
                    }
                }
            }
            ae_frame_leave(_state);
            return;
        }
        ae_assert(ae_false, "CreateRandom: internal error", _state);
    }
    if( pkind==1 )
    {
        
        /*
         * Band matrix
         */
        ae_matrix_set_length(da, m, n, _state);
        ae_vector_set_length(&rowsizes, m, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                da->ptr.pp_double[i][j] = (double)(0);
            }
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=ae_maxint(i-p0, 0, _state); j<=ae_minint(i+p1, n-1, _state); j++)
            {
                do
                {
                    da->ptr.pp_double[i][j] = (double)(ae_randominteger(17, _state)-8)/(double)8;
                }
                while(ae_fp_eq(da->ptr.pp_double[i][j],(double)(0)));
            }
            rowsizes.ptr.p_int[i] = ae_maxint(ae_minint(i+p1, n-1, _state)-ae_maxint(i-p0, 0, _state)+1, 0, _state);
        }
        if( ckind==0 )
        {
            sparsecreate(m, n, 1, sa, _state);
        }
        if( ckind==1 )
        {
            sparsecreatecrs(m, n, &rowsizes, sa, _state);
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( ae_fp_neq(da->ptr.pp_double[i][j],(double)(0)) )
                {
                    sparseset(sa, i, j, da->ptr.pp_double[i][j], _state);
                }
            }
        }
        sparseconverttocrs(sa, _state);
        ae_frame_leave(_state);
        return;
    }
    if( pkind==2 )
    {
        
        /*
         * Matrix with one contiguous sequence of non-zero elements per row
         */
        ae_matrix_set_length(da, m, n, _state);
        ae_vector_set_length(&rowsizes, m, _state);
        ae_vector_set_length(&c0, m, _state);
        ae_vector_set_length(&c1, m, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                da->ptr.pp_double[i][j] = (double)(0);
            }
        }
        for(i=0; i<=m-1; i++)
        {
            c0.ptr.p_int[i] = ae_randominteger(n, _state);
            c1.ptr.p_int[i] = c0.ptr.p_int[i]+ae_randominteger(n-c0.ptr.p_int[i]+1, _state);
            rowsizes.ptr.p_int[i] = c1.ptr.p_int[i]-c0.ptr.p_int[i];
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=c0.ptr.p_int[i]; j<=c1.ptr.p_int[i]-1; j++)
            {
                do
                {
                    da->ptr.pp_double[i][j] = (double)(ae_randominteger(17, _state)-8)/(double)8;
                }
                while(ae_fp_eq(da->ptr.pp_double[i][j],(double)(0)));
            }
        }
        if( ckind==0 )
        {
            sparsecreate(m, n, 1, sa, _state);
        }
        if( ckind==1 )
        {
            sparsecreatecrs(m, n, &rowsizes, sa, _state);
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( ae_fp_neq(da->ptr.pp_double[i][j],(double)(0)) )
                {
                    sparseset(sa, i, j, da->ptr.pp_double[i][j], _state);
                }
            }
        }
        sparseconverttocrs(sa, _state);
        ae_frame_leave(_state);
        return;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function does test for SparseEnumerate function.

  -- ALGLIB PROJECT --
     Copyright 14.03.2012 by Bochkanov Sergey
*************************************************************************/
static ae_bool testsparseunit_enumeratetest(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix spa;
    ae_matrix a;
    ae_matrix ta;
    ae_int_t m;
    ae_int_t n;
    double r;
    double v;
    ae_int_t ne;
    ae_int_t t0;
    ae_int_t t1;
    ae_int_t counter;
    ae_int_t c;
    ae_int_t hashcrs;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&spa, 0, sizeof(spa));
    memset(&a, 0, sizeof(a));
    memset(&ta, 0, sizeof(ta));
    _sparsematrix_init(&spa, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ta, 0, 0, DT_BOOL, _state, ae_true);

    r = 10.5;
    for(m=1; m<=30; m++)
    {
        for(n=1; n<=30; n++)
        {
            ne = 0;
            
            /*
             * Create matrix with non-zero elements inside the region:
             * 0<=I<S.M and 0<=J<S.N
             */
            ae_matrix_set_length(&a, m, n, _state);
            ae_matrix_set_length(&ta, m, n, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = (double)(0);
                    ta.ptr.pp_bool[i][j] = ae_false;
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c = ae_randominteger(2, _state);
                    if( c==0 )
                    {
                        a.ptr.pp_double[i][j] = (double)(0);
                    }
                    else
                    {
                        a.ptr.pp_double[i][j] = r*(2*ae_randomreal(_state)-1);
                        
                        /*
                         * Number of non-zero elements
                         */
                        ne = ne+1;
                    }
                }
            }
            for(hashcrs=0; hashcrs<=1; hashcrs++)
            {
                sparsecreate(m, n, m*n, &spa, _state);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        sparseset(&spa, i, j, a.ptr.pp_double[i][j], _state);
                    }
                }
                if( hashcrs==1 )
                {
                    sparseconverttocrs(&spa, _state);
                }
                t0 = 0;
                t1 = 0;
                counter = 0;
                while(sparseenumerate(&spa, &t0, &t1, &i, &j, &v, _state))
                {
                    ta.ptr.pp_bool[i][j] = ae_true;
                    counter = counter+1;
                    if( ae_fp_neq(v,a.ptr.pp_double[i][j]) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
                
                /*
                 * Check that all non-zero elements was enumerated
                 */
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ta.ptr.pp_bool[i][j]&&ae_fp_eq(a.ptr.pp_double[i][j],(double)(0)) )
                        {
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                }
                if( ne!=counter )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function does test for SparseRewriteExisting function.

  -- ALGLIB PROJECT --
     Copyright 14.03.2012 by Bochkanov Sergey
*************************************************************************/
static ae_bool testsparseunit_rewriteexistingtest(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix spa;
    double spaval;
    ae_matrix a;
    ae_matrix ta;
    ae_int_t m;
    ae_int_t n;
    ae_int_t c;
    ae_int_t ne;
    ae_int_t nr;
    double r;
    double v;
    ae_int_t hashcrs;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&spa, 0, sizeof(spa));
    memset(&a, 0, sizeof(a));
    memset(&ta, 0, sizeof(ta));
    _sparsematrix_init(&spa, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ta, 0, 0, DT_BOOL, _state, ae_true);

    r = 20.0;
    for(m=1; m<=30; m++)
    {
        for(n=1; n<=30; n++)
        {
            ae_matrix_set_length(&a, m, n, _state);
            ae_matrix_set_length(&ta, m, n, _state);
            for(hashcrs=0; hashcrs<=1; hashcrs++)
            {
                v = r*(2*ae_randomreal(_state)-1);
                
                /*
                 * Creating and filling of the matrix
                 */
                ne = 0;
                sparsecreate(m, n, m*n, &spa, _state);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        c = ae_randominteger(2, _state);
                        if( c==0 )
                        {
                            a.ptr.pp_double[i][j] = (double)(0);
                        }
                        if( c==1 )
                        {
                            do
                            {
                                a.ptr.pp_double[i][j] = r*(2*ae_randomreal(_state)-1);
                            }
                            while(ae_fp_eq(a.ptr.pp_double[i][j],(double)(0)));
                            sparseset(&spa, i, j, a.ptr.pp_double[i][j], _state);
                            ne = ne+1;
                        }
                        ta.ptr.pp_bool[i][j] = ae_false;
                    }
                }
                if( hashcrs==1 )
                {
                    sparseconverttocrs(&spa, _state);
                }
                
                /*
                 * Rewrite some elements
                 */
                nr = 0;
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        c = ae_randominteger(2, _state);
                        if( c==1 )
                        {
                            ta.ptr.pp_bool[i][j] = sparserewriteexisting(&spa, i, j, v, _state);
                            if( ta.ptr.pp_bool[i][j] )
                            {
                                a.ptr.pp_double[i][j] = v;
                                nr = nr+1;
                            }
                        }
                    }
                }
                
                /*
                 * Now we have to be sure, that all changes had made correctly
                 */
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ta.ptr.pp_bool[i][j] )
                        {
                            spaval = sparseget(&spa, i, j, _state);
                            nr = nr-1;
                            if( ae_fp_neq(spaval,v)||ae_fp_neq(spaval,a.ptr.pp_double[i][j]) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
                if( nr!=0 )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                
                /*
                 * Rewrite all elements
                 */
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ta.ptr.pp_bool[i][j] = sparserewriteexisting(&spa, i, j, v, _state);
                        if( ta.ptr.pp_bool[i][j] )
                        {
                            a.ptr.pp_double[i][j] = v;
                        }
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ta.ptr.pp_bool[i][j] )
                        {
                            ne = ne-1;
                        }
                    }
                }
                if( ne!=0 )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        spaval = sparseget(&spa, i, j, _state);
                        if( ta.ptr.pp_bool[i][j] )
                        {
                            if( ae_fp_neq(spaval,v)||ae_fp_neq(spaval,a.ptr.pp_double[i][j]) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                        else
                        {
                            if( ae_fp_neq(spaval,(double)(0))||ae_fp_neq(spaval,a.ptr.pp_double[i][j]) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Test  for  SparseGetRow/GetCompressedRow  function.   It  creates  random
dense and sparse matrices;  then  get every  row from  sparse matrix  and
compares it with every row in dense matrix.

On failure sets error flag, on success leaves it unchanged.

  -- ALGLIB PROJECT --
     Copyright 23.07.2012 by Bochkanov Sergey
*************************************************************************/
static void testsparseunit_testgetrow(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    ae_matrix a;
    ae_int_t m;
    ae_int_t n;
    ae_int_t msize;
    ae_int_t nsize;
    ae_int_t nz;
    ae_vector vals;
    ae_vector mrow;
    ae_vector colidx;
    ae_vector wasreturned;
    ae_int_t mtype;
    ae_int_t i;
    ae_int_t j;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    memset(&vals, 0, sizeof(vals));
    memset(&mrow, 0, sizeof(mrow));
    memset(&colidx, 0, sizeof(colidx));
    memset(&wasreturned, 0, sizeof(wasreturned));
    _sparsematrix_init(&s, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vals, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&mrow, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&colidx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&wasreturned, 0, DT_BOOL, _state, ae_true);

    msize = 15;
    nsize = 15;
    for(mtype=1; mtype<=2; mtype++)
    {
        for(m=1; m<=msize; m++)
        {
            for(n=1; n<=nsize; n++)
            {
                
                /*
                 * Skip nonrectangular SKS matrices - not supported
                 */
                if( mtype==2&&m!=n )
                {
                    continue;
                }
                
                /*
                 * Create "reference" and sparse matrices
                 */
                ae_matrix_set_length(&a, m, n, _state);
                sparsecreate(m, n, 1, &s, _state);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ae_randominteger(5, _state)==3 )
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            sparseset(&s, i, j, a.ptr.pp_double[i][j], _state);
                        }
                        else
                        {
                            a.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                }
                
                /*
                 * Choose matrix type to test
                 */
                if( mtype==1 )
                {
                    sparseconverttocrs(&s, _state);
                }
                else
                {
                    sparseconverttosks(&s, _state);
                }
                
                /*
                 * Test SparseGetRow()
                 */
                for(i=0; i<=m-1; i++)
                {
                    sparsegetrow(&s, i, &mrow, _state);
                    for(j=0; j<=n-1; j++)
                    {
                        if( ae_fp_neq(mrow.ptr.p_double[j],a.ptr.pp_double[i][j])||ae_fp_neq(mrow.ptr.p_double[j],sparseget(&s, i, j, _state)) )
                        {
                            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testsparseunit.ap:3013");
                            ae_frame_leave(_state);
                            return;
                        }
                    }
                }
                
                /*
                 * Test SparseGetCompressedRow()
                 */
                ae_vector_set_length(&wasreturned, n, _state);
                for(i=0; i<=m-1; i++)
                {
                    sparsegetcompressedrow(&s, i, &colidx, &vals, &nz, _state);
                    if( nz<0||nz>n )
                    {
                        ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testsparseunit.ap:3027");
                        ae_frame_leave(_state);
                        return;
                    }
                    for(j=0; j<=n-1; j++)
                    {
                        wasreturned.ptr.p_bool[j] = ae_false;
                    }
                    for(j=0; j<=nz-1; j++)
                    {
                        if( colidx.ptr.p_int[j]<0||colidx.ptr.p_int[j]>n )
                        {
                            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testsparseunit.ap:3036");
                            ae_frame_leave(_state);
                            return;
                        }
                        ae_set_error_flag(err, j>0&&colidx.ptr.p_int[j]<=colidx.ptr.p_int[j-1], __FILE__, __LINE__, "testsparseunit.ap:3039");
                        ae_set_error_flag(err, ae_fp_neq(vals.ptr.p_double[j],a.ptr.pp_double[i][colidx.ptr.p_int[j]])||ae_fp_neq(vals.ptr.p_double[j],sparseget(&s, i, colidx.ptr.p_int[j], _state)), __FILE__, __LINE__, "testsparseunit.ap:3040");
                        wasreturned.ptr.p_bool[colidx.ptr.p_int[j]] = ae_true;
                    }
                    for(j=0; j<=n-1; j++)
                    {
                        ae_set_error_flag(err, ae_fp_neq(a.ptr.pp_double[i][j],(double)(0))&&!wasreturned.ptr.p_bool[j], __FILE__, __LINE__, "testsparseunit.ap:3044");
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test for SparseConvert functions(isn't tested ConvertToCRS function). The
function  create random  dense and sparse  matrices  in CRS  format. Then
convert  sparse matrix  to some  format  by CONVERT_TO/COPY_TO  functions,
then it does  some modification in matrices and compares that marices are
identical.

NOTE:
    Result of the function assigned to variable CopyErrors in unit test.

  -- ALGLIB PROJECT --
     Copyright 23.07.2012 by Bochkanov Sergey
*************************************************************************/
static ae_bool testsparseunit_testconvertsm(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    sparsematrix cs;
    ae_matrix a;
    ae_int_t m;
    ae_int_t n;
    ae_int_t msize;
    ae_int_t nsize;
    ae_vector ner;
    double tmp;
    ae_int_t i;
    ae_int_t j;
    ae_int_t vartf;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&cs, 0, sizeof(cs));
    memset(&a, 0, sizeof(a));
    memset(&ner, 0, sizeof(ner));
    _sparsematrix_init(&s, _state, ae_true);
    _sparsematrix_init(&cs, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ner, 0, DT_INT, _state, ae_true);

    msize = 15;
    nsize = 15;
    for(m=1; m<=msize; m++)
    {
        for(n=1; n<=nsize; n++)
        {
            for(vartf=0; vartf<=2; vartf++)
            {
                ae_matrix_set_length(&a, m, n, _state);
                ae_vector_set_length(&ner, m, _state);
                for(i=0; i<=m-1; i++)
                {
                    ner.ptr.p_int[i] = 0;
                    for(j=0; j<=n-1; j++)
                    {
                        if( ae_randominteger(5, _state)==3 )
                        {
                            ner.ptr.p_int[i] = ner.ptr.p_int[i]+1;
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                        else
                        {
                            a.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                }
                
                /*
                 * Create sparse matrix
                 */
                sparsecreatecrs(m, n, &ner, &s, _state);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ae_fp_neq(a.ptr.pp_double[i][j],(double)(0)) )
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            sparseset(&s, i, j, a.ptr.pp_double[i][j], _state);
                        }
                    }
                }
                
                /*
                 * Set matrix type(we have to be sure that all formats
                 * converted correctly)
                 */
                i = ae_randominteger(2, _state);
                if( i==0 )
                {
                    sparseconverttohash(&s, _state);
                }
                if( i==1 )
                {
                    sparseconverttocrs(&s, _state);
                }
                
                /*
                 * Start test
                 */
                if( vartf==0 )
                {
                    sparseconverttohash(&s, _state);
                    sparsecopy(&s, &cs, _state);
                }
                if( vartf==1 )
                {
                    sparsecopytohash(&s, &cs, _state);
                }
                if( vartf==2 )
                {
                    sparsecopytocrs(&s, &cs, _state);
                }
                
                /*
                 * Change some elements in row
                 */
                if( vartf!=2 )
                {
                    for(i=0; i<=m-1; i++)
                    {
                        tmp = 2*ae_randomreal(_state)-1;
                        j = ae_randominteger(n, _state);
                        a.ptr.pp_double[i][j] = tmp;
                        sparseset(&cs, i, j, tmp, _state);
                        tmp = 2*ae_randomreal(_state)-1;
                        j = ae_randominteger(n, _state);
                        a.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]+tmp;
                        sparseadd(&cs, i, j, tmp, _state);
                    }
                }
                
                /*
                 * Check that A is identical to S
                 */
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ae_fp_neq(a.ptr.pp_double[i][j],sparseget(&cs, i, j, _state)) )
                        {
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Test for  check/get  type functions.  The function  create sparse matrix,
converts it to desired type then check this type.

NOTE:
    Result of the function assigned to variable BasicErrors in unit test.

  -- ALGLIB PROJECT --
     Copyright 23.07.2012 by Bochkanov Sergey
*************************************************************************/
static ae_bool testsparseunit_testgcmatrixtype(ae_state *_state)
{
    ae_frame _frame_block;
    sparsematrix s;
    sparsematrix cs;
    ae_int_t m;
    ae_int_t n;
    ae_int_t msize;
    ae_int_t nsize;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&cs, 0, sizeof(cs));
    _sparsematrix_init(&s, _state, ae_true);
    _sparsematrix_init(&cs, _state, ae_true);

    msize = 5;
    nsize = 5;
    for(m=1; m<=msize; m++)
    {
        for(n=1; n<=nsize; n++)
        {
            sparsecreate(m, n, 1, &s, _state);
            sparseconverttocrs(&s, _state);
            if( (sparseishash(&s, _state)||!sparseiscrs(&s, _state))||sparsegetmatrixtype(&s, _state)!=1 )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            sparseconverttohash(&s, _state);
            if( (!sparseishash(&s, _state)||sparseiscrs(&s, _state))||sparsegetmatrixtype(&s, _state)!=0 )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            sparsecopytocrs(&s, &cs, _state);
            if( (sparseishash(&cs, _state)||!sparseiscrs(&cs, _state))||sparsegetmatrixtype(&cs, _state)!=1 )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            sparsecopytohash(&cs, &s, _state);
            if( (!sparseishash(&s, _state)||sparseiscrs(&s, _state))||sparsegetmatrixtype(&s, _state)!=0 )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


void _sparsegenerator_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    sparsegenerator *p = (sparsegenerator*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_init(&p->bufa, 0, 0, DT_REAL, _state, make_automatic);
    _hqrndstate_init(&p->rs, _state, make_automatic);
    _rcommstate_init(&p->rcs, _state, make_automatic);
}


void _sparsegenerator_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
{
    sparsegenerator *dst = (sparsegenerator*)_dst;
    sparsegenerator *src = (sparsegenerator*)_src;
    dst->n = src->n;
    dst->m = src->m;
    dst->matkind = src->matkind;
    dst->triangle = src->triangle;
    ae_matrix_init_copy(&dst->bufa, &src->bufa, _state, make_automatic);
    _hqrndstate_init_copy(&dst->rs, &src->rs, _state, make_automatic);
    _rcommstate_init_copy(&dst->rcs, &src->rcs, _state, make_automatic);
}


void _sparsegenerator_clear(void* _p)
{
    sparsegenerator *p = (sparsegenerator*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_clear(&p->bufa);
    _hqrndstate_clear(&p->rs);
    _rcommstate_clear(&p->rcs);
}


void _sparsegenerator_destroy(void* _p)
{
    sparsegenerator *p = (sparsegenerator*)_p;
    ae_touch_ptr((void*)p);
    ae_matrix_destroy(&p->bufa);
    _hqrndstate_destroy(&p->rs);
    _rcommstate_destroy(&p->rcs);
}



static ae_bool testablasunit_testtrsm(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testsyrk(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testgemm(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testtrans(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testrank1(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testgemv(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static void testablasunit_testsymv(ae_int_t minn,
     ae_int_t maxn,
     ae_bool* errorflag,
     ae_state *_state);
static void testablasunit_testtrsv(ae_int_t minn,
     ae_int_t maxn,
     ae_bool* errorflag,
     ae_state *_state);
static void testablasunit_spectest(ae_bool* errorflag, ae_state *_state);
static ae_bool testablasunit_testcopy(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static void testablasunit_testreflections(ae_bool* errorflag,
     ae_state *_state);
static void testablasunit_refcmatrixrighttrsm(ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Complex */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state);
static void testablasunit_refcmatrixlefttrsm(ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Complex */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state);
static void testablasunit_refrmatrixrighttrsm(ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Real    */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state);
static void testablasunit_refrmatrixlefttrsm(ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Real    */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state);
static ae_bool testablasunit_internalcmatrixtrinverse(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static ae_bool testablasunit_internalrmatrixtrinverse(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static void testablasunit_refcmatrixherk(ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Complex */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     double beta,
     /* Complex */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_bool isupper,
     ae_state *_state);
static void testablasunit_refrmatrixsyrk(ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     double beta,
     /* Real    */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_bool isupper,
     ae_state *_state);
static void testablasunit_refcmatrixgemm(ae_int_t m,
     ae_int_t n,
     ae_int_t k,
     ae_complex alpha,
     /* Complex */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     /* Complex */ ae_matrix* b,
     ae_int_t ib,
     ae_int_t jb,
     ae_int_t optypeb,
     ae_complex beta,
     /* Complex */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_state *_state);
static void testablasunit_refrmatrixgemm(ae_int_t m,
     ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     /* Real    */ ae_matrix* b,
     ae_int_t ib,
     ae_int_t jb,
     ae_int_t optypeb,
     double beta,
     /* Real    */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_state *_state);
static void testablasunit_refrmatrixsymv(ae_int_t n,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_bool isupper,
     /* Real    */ ae_vector* x,
     ae_int_t ix,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t iy,
     ae_state *_state);
static double testablasunit_refrmatrixsyvmv(ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_bool isupper,
     /* Real    */ ae_vector* x,
     ae_int_t ix,
     ae_state *_state);





ae_bool testablas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n0;
    ae_int_t n1;
    ae_bool trsmerrors;
    ae_bool syrkerrors;
    ae_bool gemmerrors;
    ae_bool transerrors;
    ae_bool rank1errors;
    ae_bool gemverrors;
    ae_bool symverrors;
    ae_bool trsverrors;
    ae_bool reflerrors;
    ae_bool copyerrors;
    ae_bool specerrors;
    ae_bool waserrors;
    ae_matrix ra;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ra, 0, sizeof(ra));
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);

    trsmerrors = ae_false;
    syrkerrors = ae_false;
    gemmerrors = ae_false;
    transerrors = ae_false;
    rank1errors = ae_false;
    gemverrors = ae_false;
    symverrors = ae_false;
    trsverrors = ae_false;
    reflerrors = ae_false;
    copyerrors = ae_false;
    waserrors = ae_false;
    specerrors = ae_false;
    testablasunit_spectest(&specerrors, _state);
    trsmerrors = trsmerrors||testablasunit_testtrsm(1, 3*matrixtilesizea(_state)+1, _state);
    syrkerrors = syrkerrors||testablasunit_testsyrk(1, 3*matrixtilesizea(_state)+1, _state);
    gemmerrors = gemmerrors||testablasunit_testgemm(1, 3*matrixtilesizea(_state)+1, _state);
    transerrors = transerrors||testablasunit_testtrans(1, 3*matrixtilesizea(_state)+1, _state);
    rank1errors = rank1errors||testablasunit_testrank1(1, 3*matrixtilesizea(_state)+1, _state);
    gemverrors = gemverrors||testablasunit_testgemv(1, 3*matrixtilesizea(_state)+1, _state);
    copyerrors = copyerrors||testablasunit_testcopy(1, 3*matrixtilesizea(_state)+1, _state);
    testablasunit_testsymv(1, 3*matrixtilesizea(_state)+1, &symverrors, _state);
    testablasunit_testtrsv(1, 3*matrixtilesizea(_state)+1, &trsverrors, _state);
    testablasunit_testreflections(&reflerrors, _state);
    n0 = 6*matrixtilesizeb(_state);
    n1 = 6*matrixtilesizeb(_state);
    gemmerrors = gemmerrors||testablasunit_testgemm(n0, n1, _state);
    trsmerrors = trsmerrors||testablasunit_testtrsm(n0, n1, _state);
    syrkerrors = syrkerrors||testablasunit_testsyrk(n0, n1, _state);
    
    /*
     * report
     */
    waserrors = (((((((((trsmerrors||syrkerrors)||gemmerrors)||transerrors)||rank1errors)||gemverrors)||symverrors)||trsverrors)||reflerrors)||copyerrors)||specerrors;
    if( !silent )
    {
        printf("TESTING ABLAS\n");
        printf("LEVEL 3 FUNCTIONS:\n");
        printf("* TRSM                                   ");
        if( trsmerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SYRK                                   ");
        if( syrkerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* GEMM                                   ");
        if( gemmerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LEVEL 2 FUNCTIONS:\n");
        printf("* TRANS                                  ");
        if( transerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* RANK1                                  ");
        if( rank1errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* GEMV                                   ");
        if( gemverrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SYMV/SYVMV                             ");
        if( symverrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* TRSV                                   ");
        if( trsverrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* REFL                                   ");
        if( reflerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COPY                                   ");
        if( copyerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("SPECIAL TESTS                            ");
        if( specerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
?Matrix????TRSM tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testtrsm(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t optype;
    ae_int_t uppertype;
    ae_int_t unittype;
    ae_int_t xoffsi;
    ae_int_t xoffsj;
    ae_int_t aoffsitype;
    ae_int_t aoffsjtype;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_matrix refra;
    ae_matrix refrxl;
    ae_matrix refrxr;
    ae_matrix refca;
    ae_matrix refcxl;
    ae_matrix refcxr;
    ae_matrix ra;
    ae_matrix ca;
    ae_matrix rxr1;
    ae_matrix rxl1;
    ae_matrix cxr1;
    ae_matrix cxl1;
    ae_matrix rxr2;
    ae_matrix rxl2;
    ae_matrix cxr2;
    ae_matrix cxl2;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&refra, 0, sizeof(refra));
    memset(&refrxl, 0, sizeof(refrxl));
    memset(&refrxr, 0, sizeof(refrxr));
    memset(&refca, 0, sizeof(refca));
    memset(&refcxl, 0, sizeof(refcxl));
    memset(&refcxr, 0, sizeof(refcxr));
    memset(&ra, 0, sizeof(ra));
    memset(&ca, 0, sizeof(ca));
    memset(&rxr1, 0, sizeof(rxr1));
    memset(&rxl1, 0, sizeof(rxl1));
    memset(&cxr1, 0, sizeof(cxr1));
    memset(&cxl1, 0, sizeof(cxl1));
    memset(&rxr2, 0, sizeof(rxr2));
    memset(&rxl2, 0, sizeof(rxl2));
    memset(&cxr2, 0, sizeof(cxr2));
    memset(&cxl2, 0, sizeof(cxl2));
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrxl, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrxr, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcxl, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcxr, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&rxr1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rxl1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cxr1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cxl1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&rxr2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rxl2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cxr2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cxl2, 0, 0, DT_COMPLEX, _state, ae_true);

    threshold = ae_sqr((double)(maxn), _state)*100*ae_machineepsilon;
    result = ae_false;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize RefRA/RefCA by random matrices whose upper
         * and lower triangle submatrices are non-degenerate
         * well-conditioned matrices.
         *
         * Matrix size is 2Mx2M (four copies of same MxM matrix
         * to test different offsets)
         */
        ae_matrix_set_length(&refra, 2*m, 2*m, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refra.ptr.pp_double[i][j] = 0.2*ae_randomreal(_state)-0.1;
            }
        }
        for(i=0; i<=m-1; i++)
        {
            refra.ptr.pp_double[i][i] = (2*ae_randominteger(1, _state)-1)*(2*m+ae_randomreal(_state));
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refra.ptr.pp_double[i+m][j] = refra.ptr.pp_double[i][j];
                refra.ptr.pp_double[i][j+m] = refra.ptr.pp_double[i][j];
                refra.ptr.pp_double[i+m][j+m] = refra.ptr.pp_double[i][j];
            }
        }
        ae_matrix_set_length(&refca, 2*m, 2*m, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refca.ptr.pp_complex[i][j].x = 0.2*ae_randomreal(_state)-0.1;
                refca.ptr.pp_complex[i][j].y = 0.2*ae_randomreal(_state)-0.1;
            }
        }
        for(i=0; i<=m-1; i++)
        {
            refca.ptr.pp_complex[i][i].x = (2*ae_randominteger(2, _state)-1)*(2*m+ae_randomreal(_state));
            refca.ptr.pp_complex[i][i].y = (2*ae_randominteger(2, _state)-1)*(2*m+ae_randomreal(_state));
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refca.ptr.pp_complex[i+m][j] = refca.ptr.pp_complex[i][j];
                refca.ptr.pp_complex[i][j+m] = refca.ptr.pp_complex[i][j];
                refca.ptr.pp_complex[i+m][j+m] = refca.ptr.pp_complex[i][j];
            }
        }
        
        /*
         * Generate random XL/XR.
         *
         * XR is NxM matrix (matrix for 'Right' subroutines)
         * XL is MxN matrix (matrix for 'Left' subroutines)
         */
        ae_matrix_set_length(&refrxr, n, m, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refrxr.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        ae_matrix_set_length(&refrxl, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                refrxl.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        ae_matrix_set_length(&refcxr, n, m, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refcxr.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcxr.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        ae_matrix_set_length(&refcxl, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                refcxl.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcxl.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * test different types of operations, offsets, and so on...
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        ae_matrix_set_length(&ra, 2*m, 2*m, _state);
        ae_matrix_set_length(&rxr1, n, m, _state);
        ae_matrix_set_length(&rxr2, n, m, _state);
        ae_matrix_set_length(&rxl1, m, n, _state);
        ae_matrix_set_length(&rxl2, m, n, _state);
        ae_matrix_set_length(&ca, 2*m, 2*m, _state);
        ae_matrix_set_length(&cxr1, n, m, _state);
        ae_matrix_set_length(&cxr2, n, m, _state);
        ae_matrix_set_length(&cxl1, m, n, _state);
        ae_matrix_set_length(&cxl2, m, n, _state);
        optype = ae_randominteger(3, _state);
        uppertype = ae_randominteger(2, _state);
        unittype = ae_randominteger(2, _state);
        xoffsi = ae_randominteger(2, _state);
        xoffsj = ae_randominteger(2, _state);
        aoffsitype = ae_randominteger(2, _state);
        aoffsjtype = ae_randominteger(2, _state);
        aoffsi = m*aoffsitype;
        aoffsj = m*aoffsjtype;
        
        /*
         * copy A, XR, XL (fill unused parts with random garbage)
         */
        for(i=0; i<=2*m-1; i++)
        {
            for(j=0; j<=2*m-1; j++)
            {
                if( ((i>=aoffsi&&i<aoffsi+m)&&j>=aoffsj)&&j<aoffsj+m )
                {
                    ca.ptr.pp_complex[i][j] = refca.ptr.pp_complex[i][j];
                    ra.ptr.pp_double[i][j] = refra.ptr.pp_double[i][j];
                }
                else
                {
                    ca.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    ra.ptr.pp_double[i][j] = ae_randomreal(_state);
                }
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                if( i>=xoffsi&&j>=xoffsj )
                {
                    cxr1.ptr.pp_complex[i][j] = refcxr.ptr.pp_complex[i][j];
                    cxr2.ptr.pp_complex[i][j] = refcxr.ptr.pp_complex[i][j];
                    rxr1.ptr.pp_double[i][j] = refrxr.ptr.pp_double[i][j];
                    rxr2.ptr.pp_double[i][j] = refrxr.ptr.pp_double[i][j];
                }
                else
                {
                    cxr1.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    cxr2.ptr.pp_complex[i][j] = cxr1.ptr.pp_complex[i][j];
                    rxr1.ptr.pp_double[i][j] = ae_randomreal(_state);
                    rxr2.ptr.pp_double[i][j] = rxr1.ptr.pp_double[i][j];
                }
            }
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i>=xoffsi&&j>=xoffsj )
                {
                    cxl1.ptr.pp_complex[i][j] = refcxl.ptr.pp_complex[i][j];
                    cxl2.ptr.pp_complex[i][j] = refcxl.ptr.pp_complex[i][j];
                    rxl1.ptr.pp_double[i][j] = refrxl.ptr.pp_double[i][j];
                    rxl2.ptr.pp_double[i][j] = refrxl.ptr.pp_double[i][j];
                }
                else
                {
                    cxl1.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    cxl2.ptr.pp_complex[i][j] = cxl1.ptr.pp_complex[i][j];
                    rxl1.ptr.pp_double[i][j] = ae_randomreal(_state);
                    rxl2.ptr.pp_double[i][j] = rxl1.ptr.pp_double[i][j];
                }
            }
        }
        
        /*
         * Test CXR
         */
        cmatrixrighttrsm(n-xoffsi, m-xoffsj, &ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &cxr1, xoffsi, xoffsj, _state);
        testablasunit_refcmatrixrighttrsm(n-xoffsi, m-xoffsj, &ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &cxr2, xoffsi, xoffsj, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(cxr1.ptr.pp_complex[i][j],cxr2.ptr.pp_complex[i][j]), _state),threshold);
            }
        }
        
        /*
         * Test CXL
         */
        cmatrixlefttrsm(m-xoffsi, n-xoffsj, &ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &cxl1, xoffsi, xoffsj, _state);
        testablasunit_refcmatrixlefttrsm(m-xoffsi, n-xoffsj, &ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &cxl2, xoffsi, xoffsj, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(cxl1.ptr.pp_complex[i][j],cxl2.ptr.pp_complex[i][j]), _state),threshold);
            }
        }
        if( optype<2 )
        {
            
            /*
             * Test RXR
             */
            rmatrixrighttrsm(n-xoffsi, m-xoffsj, &ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &rxr1, xoffsi, xoffsj, _state);
            testablasunit_refrmatrixrighttrsm(n-xoffsi, m-xoffsj, &ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &rxr2, xoffsi, xoffsj, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    result = result||ae_fp_greater(ae_fabs(rxr1.ptr.pp_double[i][j]-rxr2.ptr.pp_double[i][j], _state),threshold);
                }
            }
            
            /*
             * Test RXL
             */
            rmatrixlefttrsm(m-xoffsi, n-xoffsj, &ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &rxl1, xoffsi, xoffsj, _state);
            testablasunit_refrmatrixlefttrsm(m-xoffsi, n-xoffsj, &ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &rxl2, xoffsi, xoffsj, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    result = result||ae_fp_greater(ae_fabs(rxl1.ptr.pp_double[i][j]-rxl2.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
SYRK tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testsyrk(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t k;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t uppertype;
    ae_int_t xoffsi;
    ae_int_t xoffsj;
    ae_int_t aoffsitype;
    ae_int_t aoffsjtype;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t alphatype;
    ae_int_t betatype;
    ae_matrix refra;
    ae_matrix refrc;
    ae_matrix refca;
    ae_matrix refcc;
    double alpha;
    double beta;
    ae_matrix ra1;
    ae_matrix ra2;
    ae_matrix ca1;
    ae_matrix ca2;
    ae_matrix rc;
    ae_matrix rct;
    ae_matrix cc;
    ae_matrix cct;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&refra, 0, sizeof(refra));
    memset(&refrc, 0, sizeof(refrc));
    memset(&refca, 0, sizeof(refca));
    memset(&refcc, 0, sizeof(refcc));
    memset(&ra1, 0, sizeof(ra1));
    memset(&ra2, 0, sizeof(ra2));
    memset(&ca1, 0, sizeof(ca1));
    memset(&ca2, 0, sizeof(ca2));
    memset(&rc, 0, sizeof(rc));
    memset(&rct, 0, sizeof(rct));
    memset(&cc, 0, sizeof(cc));
    memset(&cct, 0, sizeof(cct));
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrc, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcc, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ra1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ra2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ca2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&rc, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rct, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cc, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cct, 0, 0, DT_COMPLEX, _state, ae_true);

    threshold = maxn*100*ae_machineepsilon;
    result = ae_false;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        k = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            k = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize RefRA/RefCA by random Hermitian matrices,
         * RefRC/RefCC by random matrices
         *
         * RA/CA size is 2Nx2N (four copies of same NxN matrix
         * to test different offsets)
         */
        ae_matrix_set_length(&refra, 2*n, 2*n, _state);
        ae_matrix_set_length(&refca, 2*n, 2*n, _state);
        for(i=0; i<=n-1; i++)
        {
            refra.ptr.pp_double[i][i] = 2*ae_randomreal(_state)-1;
            refca.ptr.pp_complex[i][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
            for(j=i+1; j<=n-1; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                refra.ptr.pp_double[j][i] = refra.ptr.pp_double[i][j];
                refca.ptr.pp_complex[j][i] = ae_c_conj(refca.ptr.pp_complex[i][j], _state);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                refra.ptr.pp_double[i+n][j] = refra.ptr.pp_double[i][j];
                refra.ptr.pp_double[i][j+n] = refra.ptr.pp_double[i][j];
                refra.ptr.pp_double[i+n][j+n] = refra.ptr.pp_double[i][j];
                refca.ptr.pp_complex[i+n][j] = refca.ptr.pp_complex[i][j];
                refca.ptr.pp_complex[i][j+n] = refca.ptr.pp_complex[i][j];
                refca.ptr.pp_complex[i+n][j+n] = refca.ptr.pp_complex[i][j];
            }
        }
        ae_matrix_set_length(&refrc, n, k, _state);
        ae_matrix_set_length(&refcc, n, k, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=k-1; j++)
            {
                refrc.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refcc.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcc.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * test different types of operations, offsets, and so on...
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        ae_matrix_set_length(&ra1, 2*n, 2*n, _state);
        ae_matrix_set_length(&ra2, 2*n, 2*n, _state);
        ae_matrix_set_length(&ca1, 2*n, 2*n, _state);
        ae_matrix_set_length(&ca2, 2*n, 2*n, _state);
        ae_matrix_set_length(&rc, n, k, _state);
        ae_matrix_set_length(&rct, k, n, _state);
        ae_matrix_set_length(&cc, n, k, _state);
        ae_matrix_set_length(&cct, k, n, _state);
        uppertype = ae_randominteger(2, _state);
        xoffsi = ae_randominteger(2, _state);
        xoffsj = ae_randominteger(2, _state);
        aoffsitype = ae_randominteger(2, _state);
        aoffsjtype = ae_randominteger(2, _state);
        alphatype = ae_randominteger(2, _state);
        betatype = ae_randominteger(2, _state);
        aoffsi = n*aoffsitype;
        aoffsj = n*aoffsjtype;
        alpha = alphatype*(2*ae_randomreal(_state)-1);
        beta = betatype*(2*ae_randomreal(_state)-1);
        
        /*
         * copy A, C (fill unused parts with random garbage)
         */
        for(i=0; i<=2*n-1; i++)
        {
            for(j=0; j<=2*n-1; j++)
            {
                if( ((i>=aoffsi&&i<aoffsi+n)&&j>=aoffsj)&&j<aoffsj+n )
                {
                    ca1.ptr.pp_complex[i][j] = refca.ptr.pp_complex[i][j];
                    ca2.ptr.pp_complex[i][j] = refca.ptr.pp_complex[i][j];
                    ra1.ptr.pp_double[i][j] = refra.ptr.pp_double[i][j];
                    ra2.ptr.pp_double[i][j] = refra.ptr.pp_double[i][j];
                }
                else
                {
                    ca1.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    ca2.ptr.pp_complex[i][j] = ca1.ptr.pp_complex[i][j];
                    ra1.ptr.pp_double[i][j] = ae_randomreal(_state);
                    ra2.ptr.pp_double[i][j] = ra1.ptr.pp_double[i][j];
                }
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=k-1; j++)
            {
                if( i>=xoffsi&&j>=xoffsj )
                {
                    rc.ptr.pp_double[i][j] = refrc.ptr.pp_double[i][j];
                    rct.ptr.pp_double[j][i] = refrc.ptr.pp_double[i][j];
                    cc.ptr.pp_complex[i][j] = refcc.ptr.pp_complex[i][j];
                    cct.ptr.pp_complex[j][i] = refcc.ptr.pp_complex[i][j];
                }
                else
                {
                    rc.ptr.pp_double[i][j] = ae_randomreal(_state);
                    rct.ptr.pp_double[j][i] = rc.ptr.pp_double[i][j];
                    cc.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    cct.ptr.pp_complex[j][i] = cct.ptr.pp_complex[j][i];
                }
            }
        }
        
        /*
         * Test complex
         * Only one of transform types is selected and tested
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            cmatrixherk(n-xoffsi, k-xoffsj, alpha, &cc, xoffsi, xoffsj, 0, beta, &ca1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refcmatrixherk(n-xoffsi, k-xoffsj, alpha, &cc, xoffsi, xoffsj, 0, beta, &ca2, aoffsi, aoffsj, uppertype==0, _state);
        }
        else
        {
            cmatrixherk(n-xoffsi, k-xoffsj, alpha, &cct, xoffsj, xoffsi, 2, beta, &ca1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refcmatrixherk(n-xoffsi, k-xoffsj, alpha, &cct, xoffsj, xoffsi, 2, beta, &ca2, aoffsi, aoffsj, uppertype==0, _state);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(ca1.ptr.pp_complex[i][j],ca2.ptr.pp_complex[i][j]), _state),threshold);
            }
        }
        
        /*
         * Test old version of HERK (named SYRK)
         * Only one of transform types is selected and tested
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            cmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &cc, xoffsi, xoffsj, 0, beta, &ca1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refcmatrixherk(n-xoffsi, k-xoffsj, alpha, &cc, xoffsi, xoffsj, 0, beta, &ca2, aoffsi, aoffsj, uppertype==0, _state);
        }
        else
        {
            cmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &cct, xoffsj, xoffsi, 2, beta, &ca1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refcmatrixherk(n-xoffsi, k-xoffsj, alpha, &cct, xoffsj, xoffsi, 2, beta, &ca2, aoffsi, aoffsj, uppertype==0, _state);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(ca1.ptr.pp_complex[i][j],ca2.ptr.pp_complex[i][j]), _state),threshold);
            }
        }
        
        /*
         * Test real
         * Only one of transform types is selected and tested
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            rmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &rc, xoffsi, xoffsj, 0, beta, &ra1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refrmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &rc, xoffsi, xoffsj, 0, beta, &ra2, aoffsi, aoffsj, uppertype==0, _state);
        }
        else
        {
            rmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &rct, xoffsj, xoffsi, 1, beta, &ra1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refrmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &rct, xoffsj, xoffsi, 1, beta, &ra2, aoffsi, aoffsj, uppertype==0, _state);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                result = result||ae_fp_greater(ae_fabs(ra1.ptr.pp_double[i][j]-ra2.ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
GEMM tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testgemm(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t k;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t aoptype;
    ae_int_t aoptyper;
    ae_int_t boffsi;
    ae_int_t boffsj;
    ae_int_t boptype;
    ae_int_t boptyper;
    ae_int_t coffsi;
    ae_int_t coffsj;
    ae_matrix refra;
    ae_matrix refrb;
    ae_matrix refrc;
    ae_matrix refca;
    ae_matrix refcb;
    ae_matrix refcc;
    double alphar;
    double betar;
    ae_complex alphac;
    ae_complex betac;
    ae_matrix rc1;
    ae_matrix rc2;
    ae_matrix cc1;
    ae_matrix cc2;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&refra, 0, sizeof(refra));
    memset(&refrb, 0, sizeof(refrb));
    memset(&refrc, 0, sizeof(refrc));
    memset(&refca, 0, sizeof(refca));
    memset(&refcb, 0, sizeof(refcb));
    memset(&refcc, 0, sizeof(refcc));
    memset(&rc1, 0, sizeof(rc1));
    memset(&rc2, 0, sizeof(rc2));
    memset(&cc1, 0, sizeof(cc1));
    memset(&cc2, 0, sizeof(cc2));
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrb, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrc, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcb, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcc, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&rc1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rc2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cc1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cc2, 0, 0, DT_COMPLEX, _state, ae_true);

    threshold = maxn*100*ae_machineepsilon;
    result = ae_false;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N/K in [1,MX] such that max(M,N,K)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        k = 1+ae_randominteger(mx, _state);
        i = ae_randominteger(3, _state);
        if( i==0 )
        {
            m = mx;
        }
        if( i==1 )
        {
            n = mx;
        }
        if( i==2 )
        {
            k = mx;
        }
        
        /*
         * Initialize A/B/C by random matrices with size (MaxN+1)*(MaxN+1)
         */
        ae_matrix_set_length(&refra, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refrb, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refrc, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refca, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refcb, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refcc, maxn+1, maxn+1, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refrb.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refrc.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                refcb.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcb.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                refcc.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcc.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * test different types of operations, offsets, and so on...
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        ae_matrix_set_length(&rc1, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&rc2, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&cc1, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&cc2, maxn+1, maxn+1, _state);
        aoffsi = ae_randominteger(2, _state);
        aoffsj = ae_randominteger(2, _state);
        aoptype = ae_randominteger(3, _state);
        aoptyper = ae_randominteger(2, _state);
        boffsi = ae_randominteger(2, _state);
        boffsj = ae_randominteger(2, _state);
        boptype = ae_randominteger(3, _state);
        boptyper = ae_randominteger(2, _state);
        coffsi = ae_randominteger(2, _state);
        coffsj = ae_randominteger(2, _state);
        alphar = ae_randominteger(2, _state)*(2*ae_randomreal(_state)-1);
        betar = ae_randominteger(2, _state)*(2*ae_randomreal(_state)-1);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            alphac.x = 2*ae_randomreal(_state)-1;
            alphac.y = 2*ae_randomreal(_state)-1;
        }
        else
        {
            alphac = ae_complex_from_i(0);
        }
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            betac.x = 2*ae_randomreal(_state)-1;
            betac.y = 2*ae_randomreal(_state)-1;
        }
        else
        {
            betac = ae_complex_from_i(0);
        }
        
        /*
         * copy C
         */
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                rc1.ptr.pp_double[i][j] = refrc.ptr.pp_double[i][j];
                rc2.ptr.pp_double[i][j] = refrc.ptr.pp_double[i][j];
                cc1.ptr.pp_complex[i][j] = refcc.ptr.pp_complex[i][j];
                cc2.ptr.pp_complex[i][j] = refcc.ptr.pp_complex[i][j];
            }
        }
        
        /*
         * Test complex
         */
        cmatrixgemm(m, n, k, alphac, &refca, aoffsi, aoffsj, aoptype, &refcb, boffsi, boffsj, boptype, betac, &cc1, coffsi, coffsj, _state);
        testablasunit_refcmatrixgemm(m, n, k, alphac, &refca, aoffsi, aoffsj, aoptype, &refcb, boffsi, boffsj, boptype, betac, &cc2, coffsi, coffsj, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(cc1.ptr.pp_complex[i][j],cc2.ptr.pp_complex[i][j]), _state),threshold);
            }
        }
        
        /*
         * Test real
         */
        rmatrixgemm(m, n, k, alphar, &refra, aoffsi, aoffsj, aoptyper, &refrb, boffsi, boffsj, boptyper, betar, &rc1, coffsi, coffsj, _state);
        testablasunit_refrmatrixgemm(m, n, k, alphar, &refra, aoffsi, aoffsj, aoptyper, &refrb, boffsi, boffsj, boptyper, betar, &rc2, coffsi, coffsj, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                result = result||ae_fp_greater(ae_fabs(rc1.ptr.pp_double[i][j]-rc2.ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
transpose tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testtrans(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t boffsi;
    ae_int_t boffsj;
    double v1;
    double v2;
    double threshold;
    ae_matrix refra;
    ae_matrix refrb;
    ae_matrix refca;
    ae_matrix refcb;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&refra, 0, sizeof(refra));
    memset(&refrb, 0, sizeof(refrb));
    memset(&refca, 0, sizeof(refca));
    memset(&refcb, 0, sizeof(refcb));
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrb, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcb, 0, 0, DT_COMPLEX, _state, ae_true);

    result = ae_false;
    threshold = 1000*ae_machineepsilon;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         * Generate random V1 and V2 which are used to fill
         * RefRB/RefCB with control values.
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_randominteger(2, _state)==0 )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        v1 = ae_randomreal(_state);
        v2 = ae_randomreal(_state);
        
        /*
         * Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
         * Fill B with control values
         */
        ae_matrix_set_length(&refra, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refrb, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refca, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refcb, maxn+1, maxn+1, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                refrb.ptr.pp_double[i][j] = i*v1+j*v2;
                refcb.ptr.pp_complex[i][j] = ae_complex_from_d(i*v1+j*v2);
            }
        }
        
        /*
         * test different offsets (zero or one)
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        aoffsi = ae_randominteger(2, _state);
        aoffsj = ae_randominteger(2, _state);
        boffsi = ae_randominteger(2, _state);
        boffsj = ae_randominteger(2, _state);
        rmatrixtranspose(m, n, &refra, aoffsi, aoffsj, &refrb, boffsi, boffsj, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                if( ((i<boffsi||i>=boffsi+n)||j<boffsj)||j>=boffsj+m )
                {
                    result = result||ae_fp_greater(ae_fabs(refrb.ptr.pp_double[i][j]-(v1*i+v2*j), _state),threshold);
                }
                else
                {
                    result = result||ae_fp_greater(ae_fabs(refrb.ptr.pp_double[i][j]-refra.ptr.pp_double[aoffsi+j-boffsj][aoffsj+i-boffsi], _state),threshold);
                }
            }
        }
        cmatrixtranspose(m, n, &refca, aoffsi, aoffsj, &refcb, boffsi, boffsj, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                if( ((i<boffsi||i>=boffsi+n)||j<boffsj)||j>=boffsj+m )
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub_d(refcb.ptr.pp_complex[i][j],v1*i+v2*j), _state),threshold);
                }
                else
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub(refcb.ptr.pp_complex[i][j],refca.ptr.pp_complex[aoffsi+j-boffsj][aoffsj+i-boffsi]), _state),threshold);
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
rank-1tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testrank1(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t uoffs;
    ae_int_t voffs;
    double threshold;
    double ralpha;
    ae_matrix refra;
    ae_matrix refrb;
    ae_matrix refca;
    ae_matrix refcb;
    ae_vector ru;
    ae_vector rv;
    ae_vector cu;
    ae_vector cv;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&refra, 0, sizeof(refra));
    memset(&refrb, 0, sizeof(refrb));
    memset(&refca, 0, sizeof(refca));
    memset(&refcb, 0, sizeof(refcb));
    memset(&ru, 0, sizeof(ru));
    memset(&rv, 0, sizeof(rv));
    memset(&cu, 0, sizeof(cu));
    memset(&cv, 0, sizeof(cv));
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrb, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcb, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&ru, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cu, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cv, 0, DT_COMPLEX, _state, ae_true);

    result = ae_false;
    threshold = 1000*ae_machineepsilon;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_randominteger(2, _state)==0 )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
         * Fill B with control values
         */
        ae_matrix_set_length(&refra, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&refrb, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&refca, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&refcb, maxn+maxn, maxn+maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                refrb.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refcb.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcb.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        ae_vector_set_length(&ru, 2*m, _state);
        ae_vector_set_length(&cu, 2*m, _state);
        for(i=0; i<=2*m-1; i++)
        {
            ru.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            cu.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            cu.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        }
        ae_vector_set_length(&rv, 2*n, _state);
        ae_vector_set_length(&cv, 2*n, _state);
        for(i=0; i<=2*n-1; i++)
        {
            rv.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            cv.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            cv.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        }
        
        /*
         * Generate random offsets of all operands and random coefficients.
         */
        aoffsi = ae_randominteger(maxn, _state);
        aoffsj = ae_randominteger(maxn, _state);
        uoffs = ae_randominteger(m, _state);
        voffs = ae_randominteger(n, _state);
        ralpha = ae_randomreal(_state)-0.5;
        
        /*
         * Test CMatrixRank1() and deprecated RMatrixRank1()
         */
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                refca.ptr.pp_complex[i][j] = refcb.ptr.pp_complex[i][j];
            }
        }
        cmatrixrank1(m, n, &refca, aoffsi, aoffsj, &cu, uoffs, &cv, voffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                if( ((i<aoffsi||i>=aoffsi+m)||j<aoffsj)||j>=aoffsj+n )
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub(refca.ptr.pp_complex[i][j],refcb.ptr.pp_complex[i][j]), _state),threshold);
                }
                else
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub(refca.ptr.pp_complex[i][j],ae_c_add(refcb.ptr.pp_complex[i][j],ae_c_mul(cu.ptr.p_complex[i-aoffsi+uoffs],cv.ptr.p_complex[j-aoffsj+voffs]))), _state),threshold);
                }
            }
        }
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                refra.ptr.pp_double[i][j] = refrb.ptr.pp_double[i][j];
            }
        }
        rmatrixrank1(m, n, &refra, aoffsi, aoffsj, &ru, uoffs, &rv, voffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                if( ((i<aoffsi||i>=aoffsi+m)||j<aoffsj)||j>=aoffsj+n )
                {
                    result = result||ae_fp_greater(ae_fabs(refra.ptr.pp_double[i][j]-refrb.ptr.pp_double[i][j], _state),threshold);
                }
                else
                {
                    result = result||ae_fp_greater(ae_fabs(refra.ptr.pp_double[i][j]-(refrb.ptr.pp_double[i][j]+ru.ptr.p_double[i-aoffsi+uoffs]*rv.ptr.p_double[j-aoffsj+voffs]), _state),threshold);
                }
            }
        }
        
        /*
         * Test modern RMatrixGER()
         */
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                refra.ptr.pp_double[i][j] = refrb.ptr.pp_double[i][j];
            }
        }
        rmatrixger(m, n, &refra, aoffsi, aoffsj, ralpha, &ru, uoffs, &rv, voffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                if( ((i<aoffsi||i>=aoffsi+m)||j<aoffsj)||j>=aoffsj+n )
                {
                    ae_set_error_flag(&result, ae_fp_neq(ae_fabs(refra.ptr.pp_double[i][j]-refrb.ptr.pp_double[i][j], _state),0.0), __FILE__, __LINE__, "testablasunit.ap:1132");
                }
                else
                {
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(refra.ptr.pp_double[i][j]-(refrb.ptr.pp_double[i][j]+ralpha*ru.ptr.p_double[i-aoffsi+uoffs]*rv.ptr.p_double[j-aoffsj+voffs]), _state),threshold), __FILE__, __LINE__, "testablasunit.ap:1134");
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
MV tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testgemv(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t xoffs;
    ae_int_t yoffs;
    ae_int_t opca;
    ae_int_t opra;
    double threshold;
    double ralpha;
    double rbeta;
    double rv1;
    double rv2;
    ae_complex cv1;
    ae_complex cv2;
    ae_matrix refra;
    ae_matrix refca;
    ae_vector rx;
    ae_vector ry;
    ae_vector cx;
    ae_vector cy;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&refra, 0, sizeof(refra));
    memset(&refca, 0, sizeof(refca));
    memset(&rx, 0, sizeof(rx));
    memset(&ry, 0, sizeof(ry));
    memset(&cx, 0, sizeof(cx));
    memset(&cy, 0, sizeof(cy));
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&rx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ry, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cx, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cy, 0, DT_COMPLEX, _state, ae_true);

    result = ae_false;
    threshold = 1000*ae_machineepsilon;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_randominteger(2, _state)==0 )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
         * Initialize X by random vector with size (MaxN+MaxN)
         * Fill Y by control values
         */
        ae_matrix_set_length(&refra, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&refca, maxn+maxn, maxn+maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        ae_vector_set_length(&rx, 2*maxn, _state);
        ae_vector_set_length(&cx, 2*maxn, _state);
        ae_vector_set_length(&ry, 2*maxn, _state);
        ae_vector_set_length(&cy, 2*maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            rx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            cx.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            cx.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        }
        
        /*
         * Select random offsets and operations.
         *
         * To avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        aoffsi = ae_randominteger(maxn, _state);
        aoffsj = ae_randominteger(maxn, _state);
        xoffs = ae_randominteger(maxn, _state);
        yoffs = ae_randominteger(maxn, _state);
        opca = ae_randominteger(3, _state);
        opra = ae_randominteger(2, _state);
        ralpha = (ae_randomreal(_state)-0.5)*ae_randominteger(2, _state);
        rbeta = (ae_randomreal(_state)-0.5)*ae_randominteger(2, _state);
        
        /*
         * Test CMatrixMV and deprecated RMatrixMV
         */
        for(i=0; i<=2*maxn-1; i++)
        {
            cy.ptr.p_complex[i] = ae_complex_from_i(i);
        }
        cmatrixmv(m, n, &refca, aoffsi, aoffsj, opca, &cx, xoffs, &cy, yoffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            if( i<yoffs||i>=yoffs+m )
            {
                result = result||ae_c_neq_d(cy.ptr.p_complex[i],(double)(i));
            }
            else
            {
                cv1 = cy.ptr.p_complex[i];
                cv2 = ae_complex_from_d(0.0);
                if( opca==0 )
                {
                    cv2 = ae_v_cdotproduct(&refca.ptr.pp_complex[aoffsi+i-yoffs][aoffsj], 1, "N", &cx.ptr.p_complex[xoffs], 1, "N", ae_v_len(aoffsj,aoffsj+n-1));
                }
                if( opca==1 )
                {
                    cv2 = ae_v_cdotproduct(&refca.ptr.pp_complex[aoffsi][aoffsj+i-yoffs], refca.stride, "N", &cx.ptr.p_complex[xoffs], 1, "N", ae_v_len(aoffsi,aoffsi+n-1));
                }
                if( opca==2 )
                {
                    cv2 = ae_v_cdotproduct(&refca.ptr.pp_complex[aoffsi][aoffsj+i-yoffs], refca.stride, "Conj", &cx.ptr.p_complex[xoffs], 1, "N", ae_v_len(aoffsi,aoffsi+n-1));
                }
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(cv1,cv2), _state),threshold);
            }
        }
        for(i=0; i<=2*maxn-1; i++)
        {
            ry.ptr.p_double[i] = (double)(i);
        }
        rmatrixmv(m, n, &refra, aoffsi, aoffsj, opra, &rx, xoffs, &ry, yoffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            if( i<yoffs||i>=yoffs+m )
            {
                result = result||ae_fp_neq(ry.ptr.p_double[i],(double)(i));
            }
            else
            {
                rv1 = ry.ptr.p_double[i];
                rv2 = (double)(0);
                if( opra==0 )
                {
                    rv2 = ae_v_dotproduct(&refra.ptr.pp_double[aoffsi+i-yoffs][aoffsj], 1, &rx.ptr.p_double[xoffs], 1, ae_v_len(aoffsj,aoffsj+n-1));
                }
                if( opra==1 )
                {
                    rv2 = ae_v_dotproduct(&refra.ptr.pp_double[aoffsi][aoffsj+i-yoffs], refra.stride, &rx.ptr.p_double[xoffs], 1, ae_v_len(aoffsi,aoffsi+n-1));
                }
                result = result||ae_fp_greater(ae_fabs(rv1-rv2, _state),threshold);
            }
        }
        
        /*
         * Test modern RMatrixGEMV()
         */
        for(i=0; i<=2*maxn-1; i++)
        {
            ry.ptr.p_double[i] = (double)(i);
        }
        rmatrixgemv(m, n, ralpha, &refra, aoffsi, aoffsj, opra, &rx, xoffs, rbeta, &ry, yoffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            if( i<yoffs||i>=yoffs+m )
            {
                ae_set_error_flag(&result, ae_fp_neq(ry.ptr.p_double[i],(double)(i)), __FILE__, __LINE__, "testablasunit.ap:1282");
            }
            else
            {
                rv1 = ry.ptr.p_double[i];
                rv2 = (double)(0);
                if( opra==0 )
                {
                    rv2 = ae_v_dotproduct(&refra.ptr.pp_double[aoffsi+i-yoffs][aoffsj], 1, &rx.ptr.p_double[xoffs], 1, ae_v_len(aoffsj,aoffsj+n-1));
                }
                if( opra==1 )
                {
                    rv2 = ae_v_dotproduct(&refra.ptr.pp_double[aoffsi][aoffsj+i-yoffs], refra.stride, &rx.ptr.p_double[xoffs], 1, ae_v_len(aoffsi,aoffsi+n-1));
                }
                rv2 = rbeta*i+ralpha*rv2;
                ae_set_error_flag(&result, ae_fp_greater(ae_fabs(rv1-rv2, _state),threshold), __FILE__, __LINE__, "testablasunit.ap:1292");
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
SYMV/SYVMV tests

Sets error flag on failure, ignores on success.
*************************************************************************/
static void testablasunit_testsymv(ae_int_t minn,
     ae_int_t maxn,
     ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t xoffs;
    ae_int_t yoffs;
    ae_bool isuppera;
    double threshold;
    double ralpha;
    double rbeta;
    double rv1;
    double rv2;
    ae_matrix refra;
    ae_vector rx;
    ae_vector ry;
    ae_vector rz;

    ae_frame_make(_state, &_frame_block);
    memset(&refra, 0, sizeof(refra));
    memset(&rx, 0, sizeof(rx));
    memset(&ry, 0, sizeof(ry));
    memset(&rz, 0, sizeof(rz));
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ry, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rz, 0, DT_REAL, _state, ae_true);

    threshold = 1000*ae_machineepsilon;
    for(n=minn; n<=maxn; n++)
    {
        
        /*
         * Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
         * Initialize X by random vector with size (MaxN+MaxN)
         * Fill Y by control values
         */
        ae_matrix_set_length(&refra, maxn+maxn, maxn+maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        ae_vector_set_length(&rx, 2*maxn, _state);
        ae_vector_set_length(&ry, 2*maxn, _state);
        ae_vector_set_length(&rz, 2*maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            rx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        
        /*
         * Select random offsets and operations.
         *
         * To avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        aoffsi = ae_randominteger(maxn, _state);
        aoffsj = ae_randominteger(maxn, _state);
        xoffs = ae_randominteger(maxn, _state);
        yoffs = ae_randominteger(maxn, _state);
        isuppera = ae_fp_greater(ae_randomreal(_state),0.5);
        ralpha = (ae_randomreal(_state)-0.5)*ae_randominteger(2, _state);
        rbeta = (ae_randomreal(_state)-0.5)*ae_randominteger(2, _state);
        
        /*
         * Test RMatrixSYMV()
         */
        for(i=0; i<=2*maxn-1; i++)
        {
            ry.ptr.p_double[i] = (double)(i);
            rz.ptr.p_double[i] = (double)(i);
        }
        rmatrixsymv(n, ralpha, &refra, aoffsi, aoffsj, isuppera, &rx, xoffs, rbeta, &ry, yoffs, _state);
        testablasunit_refrmatrixsymv(n, ralpha, &refra, aoffsi, aoffsj, isuppera, &rx, xoffs, rbeta, &rz, yoffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            if( i<yoffs||i>=yoffs+n )
            {
                ae_set_error_flag(errorflag, ae_fp_neq(ry.ptr.p_double[i],(double)(i)), __FILE__, __LINE__, "testablasunit.ap:1380");
            }
            else
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(ry.ptr.p_double[i]-rz.ptr.p_double[i], _state),threshold*maxreal3(ae_fabs(ry.ptr.p_double[i], _state), ae_fabs(rz.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testablasunit.ap:1382");
            }
        }
        
        /*
         * Test RMatrixSYVMV()
         */
        rv1 = rmatrixsyvmv(n, &refra, aoffsi, aoffsj, isuppera, &rx, xoffs, &ry, _state);
        rv2 = testablasunit_refrmatrixsyvmv(n, &refra, aoffsi, aoffsj, isuppera, &rx, xoffs, _state);
        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(rv1-rv2, _state),threshold*maxreal3(ae_fabs(rv1, _state), ae_fabs(rv2, _state), 1.0, _state)), __FILE__, __LINE__, "testablasunit.ap:1396");
    }
    ae_frame_leave(_state);
}


/*************************************************************************
TRSV tests

Sets error flag on failure, ignores on success.
*************************************************************************/
static void testablasunit_testtrsv(ae_int_t minn,
     ae_int_t maxn,
     ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t xoffs;
    ae_bool isuppera;
    ae_bool isunita;
    ae_int_t opa;
    double threshold;
    ae_matrix refra;
    ae_matrix ea;
    ae_vector rx;
    ae_vector ry;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&refra, 0, sizeof(refra));
    memset(&ea, 0, sizeof(ea));
    memset(&rx, 0, sizeof(rx));
    memset(&ry, 0, sizeof(ry));
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ea, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ry, 0, DT_REAL, _state, ae_true);

    for(n=minn; n<=maxn; n++)
    {
        
        /*
         * Decreased threshold because of ill-conditioning of randomly generated matrices
         */
        threshold = 1.0E-6*(1+n);
        
        /*
         * Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
         * Initialize X by sparse random vector with size (MaxN+MaxN) (sparsity is important to test some branches of algorithm)
         * Fill Y by control values
         */
        ae_matrix_set_length(&refra, maxn+maxn, maxn+maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        ae_vector_set_length(&rx, 2*maxn, _state);
        ae_vector_set_length(&ry, 2*maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            rx.ptr.p_double[i] = (2*ae_randomreal(_state)-1)*ae_randominteger(3, _state);
        }
        
        /*
         * Select random offsets and operations.
         *
         * To avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         *
         * NOTE: in order to improve conditioning properties we add 10*Identity
         *       to purported diagonal of A
         */
        aoffsi = ae_randominteger(maxn, _state);
        aoffsj = ae_randominteger(maxn, _state);
        xoffs = ae_randominteger(maxn, _state);
        isuppera = ae_fp_greater(ae_randomreal(_state),0.5);
        isunita = ae_fp_greater(ae_randomreal(_state),0.5);
        opa = ae_randominteger(2, _state);
        for(i=0; i<=n-1; i++)
        {
            refra.ptr.pp_double[aoffsi+i][aoffsj+i] = refra.ptr.pp_double[aoffsi+i][aoffsj+i]+10;
        }
        
        /*
         * Test RMatrixTRSV():
         * * check that elements of RX beyond [XOffs,XOffs+N-1] are unchanged
         * * calculate RX:=TRSV(RX)
         * * extract effective A from RefRA to EA=array[N,N]
         * * compare product EA*RX, compare with copy of RX stored in RY
         */
        for(i=0; i<=2*maxn-1; i++)
        {
            ry.ptr.p_double[i] = rx.ptr.p_double[i];
        }
        rmatrixtrsv(n, &refra, aoffsi, aoffsj, isuppera, isunita, opa, &rx, xoffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            if( i<xoffs||i>xoffs+n-1 )
            {
                ae_set_error_flag(errorflag, ae_fp_neq(rx.ptr.p_double[i],ry.ptr.p_double[i]), __FILE__, __LINE__, "testablasunit.ap:1480");
            }
        }
        ae_matrix_set_length(&ea, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = refra.ptr.pp_double[aoffsi+i][aoffsj+j];
                if( (j<i&&isuppera)||(j>i&&!isuppera) )
                {
                    v = (double)(0);
                }
                if( isunita&&i==j )
                {
                    v = (double)(1);
                }
                if( opa==0 )
                {
                    ea.ptr.pp_double[i][j] = v;
                }
                else
                {
                    ea.ptr.pp_double[j][i] = v;
                }
            }
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&ea.ptr.pp_double[i][0], 1, &rx.ptr.p_double[xoffs], 1, ae_v_len(0,n-1));
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(v-ry.ptr.p_double[xoffs+i], _state),threshold), __FILE__, __LINE__, "testablasunit.ap:1503");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Special test.
On failure sets error flag, on success does not change it.
*************************************************************************/
static void testablasunit_spectest(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix emptyr2;
    ae_matrix emptyc2;
    ae_matrix outputr2;
    ae_matrix outputc2;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t pass;

    ae_frame_make(_state, &_frame_block);
    memset(&emptyr2, 0, sizeof(emptyr2));
    memset(&emptyc2, 0, sizeof(emptyc2));
    memset(&outputr2, 0, sizeof(outputr2));
    memset(&outputc2, 0, sizeof(outputc2));
    ae_matrix_init(&emptyr2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&emptyc2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&outputr2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&outputc2, 0, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * Test that SYRK, GEMM and TRSM does not reference empty argument at all.
     *
     * In order to perform this test we pass empty (unallocated) matrix
     * with large offset; incorrect implementation will crash on such data.
     */
    n = 128+ae_randominteger(65, _state)-32;
    ae_matrix_set_length(&outputr2, n, n, _state);
    ae_matrix_set_length(&outputc2, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            outputr2.ptr.pp_double[i][j] = (double)(0);
            outputc2.ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
    for(pass=0; pass<=10; pass++)
    {
        rmatrixgemm(n, n, 0, 1.0, &emptyr2, 35345, 23453, ae_randominteger(2, _state), &emptyr2, 74764, 26845, ae_randominteger(2, _state), 1.0+ae_randominteger(2, _state), &outputr2, 0, 0, _state);
        rmatrixgemm(n, n, n, 0.0, &emptyr2, 35345, 23453, ae_randominteger(2, _state), &emptyr2, 74764, 26845, ae_randominteger(2, _state), 1.0+ae_randominteger(2, _state), &outputr2, 0, 0, _state);
        cmatrixgemm(n, n, 0, ae_complex_from_d(1.0), &emptyc2, 35345, 23453, ae_randominteger(3, _state), &emptyc2, 74764, 26845, ae_randominteger(3, _state), ae_complex_from_d(1.0+ae_randominteger(2, _state)), &outputc2, 0, 0, _state);
        cmatrixgemm(n, n, n, ae_complex_from_d(0.0), &emptyc2, 35345, 23453, ae_randominteger(3, _state), &emptyc2, 74764, 26845, ae_randominteger(3, _state), ae_complex_from_d(1.0+ae_randominteger(2, _state)), &outputc2, 0, 0, _state);
        rmatrixsyrk(n, 0, 1.0, &emptyr2, 54674, 34657, 2*ae_randominteger(2, _state), 1.0+ae_randominteger(2, _state), &outputr2, 0, 0, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        rmatrixsyrk(n, n, 0.0, &emptyr2, 54674, 34657, 2*ae_randominteger(2, _state), 1.0+ae_randominteger(2, _state), &outputr2, 0, 0, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        cmatrixherk(n, 0, 1.0, &emptyc2, 54674, 34657, 2*ae_randominteger(2, _state), 1.0+ae_randominteger(2, _state), &outputc2, 0, 0, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        cmatrixherk(n, n, 0.0, &emptyc2, 54674, 34657, 2*ae_randominteger(2, _state), 1.0+ae_randominteger(2, _state), &outputc2, 0, 0, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        rmatrixrighttrsm(0, 0, &emptyr2, 63463, 36345, ae_fp_greater(ae_randomreal(_state),0.5), ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(2, _state), &outputr2, 0, 0, _state);
        rmatrixlefttrsm(0, 0, &emptyr2, 63463, 36345, ae_fp_greater(ae_randomreal(_state),0.5), ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(2, _state), &outputr2, 0, 0, _state);
        cmatrixrighttrsm(0, 0, &emptyc2, 63463, 36345, ae_fp_greater(ae_randomreal(_state),0.5), ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(3, _state), &outputc2, 0, 0, _state);
        cmatrixlefttrsm(0, 0, &emptyc2, 63463, 36345, ae_fp_greater(ae_randomreal(_state),0.5), ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(3, _state), &outputc2, 0, 0, _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
COPY tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testcopy(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t boffsi;
    ae_int_t boffsj;
    double threshold;
    ae_matrix ra;
    ae_matrix rb;
    ae_matrix ca;
    ae_matrix cb;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ra, 0, sizeof(ra));
    memset(&rb, 0, sizeof(rb));
    memset(&ca, 0, sizeof(ca));
    memset(&cb, 0, sizeof(cb));
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rb, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cb, 0, 0, DT_COMPLEX, _state, ae_true);

    result = ae_false;
    threshold = 1000*ae_machineepsilon;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_randominteger(2, _state)==0 )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
         * Initialize X by random vector with size (MaxN+MaxN)
         * Fill Y by control values
         */
        ae_matrix_set_length(&ra, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&ca, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&rb, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&cb, maxn+maxn, maxn+maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                rb.ptr.pp_double[i][j] = (double)(1+2*i+3*j);
                cb.ptr.pp_complex[i][j] = ae_complex_from_i(1+2*i+3*j);
            }
        }
        
        /*
         * test different offsets (zero or one)
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        aoffsi = ae_randominteger(maxn, _state);
        aoffsj = ae_randominteger(maxn, _state);
        boffsi = ae_randominteger(maxn, _state);
        boffsj = ae_randominteger(maxn, _state);
        cmatrixcopy(m, n, &ca, aoffsi, aoffsj, &cb, boffsi, boffsj, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                if( ((i<boffsi||i>=boffsi+m)||j<boffsj)||j>=boffsj+n )
                {
                    result = result||ae_c_neq_d(cb.ptr.pp_complex[i][j],(double)(1+2*i+3*j));
                }
                else
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[aoffsi+i-boffsi][aoffsj+j-boffsj],cb.ptr.pp_complex[i][j]), _state),threshold);
                }
            }
        }
        rmatrixcopy(m, n, &ra, aoffsi, aoffsj, &rb, boffsi, boffsj, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                if( ((i<boffsi||i>=boffsi+m)||j<boffsj)||j>=boffsj+n )
                {
                    result = result||ae_fp_neq(rb.ptr.pp_double[i][j],(double)(1+2*i+3*j));
                }
                else
                {
                    result = result||ae_fp_greater(ae_fabs(ra.ptr.pp_double[aoffsi+i-boffsi][aoffsj+j-boffsj]-rb.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


static void testablasunit_testreflections(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t m;
    ae_int_t maxmn;
    ae_vector x;
    ae_vector v;
    ae_vector work;
    ae_matrix h;
    ae_matrix a;
    ae_matrix b;
    ae_matrix c;
    double tmp;
    double beta;
    double tau;
    double err;
    double mer;
    double mel;
    double meg;
    ae_int_t pass;
    ae_int_t passcount;
    double threshold;
    ae_int_t tasktype;
    double xscale;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&v, 0, sizeof(v));
    memset(&work, 0, sizeof(work));
    memset(&h, 0, sizeof(h));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&c, 0, sizeof(c));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&v, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&h, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);

    passcount = 10;
    threshold = 100*ae_machineepsilon;
    mer = (double)(0);
    mel = (double)(0);
    meg = (double)(0);
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=10; n++)
        {
            for(m=1; m<=10; m++)
            {
                
                /*
                 * Task
                 */
                n = 1+ae_randominteger(10, _state);
                m = 1+ae_randominteger(10, _state);
                maxmn = ae_maxint(m, n, _state);
                
                /*
                 * Initialize
                 */
                ae_vector_set_length(&x, maxmn+1, _state);
                ae_vector_set_length(&v, maxmn+1, _state);
                ae_vector_set_length(&work, maxmn+1, _state);
                ae_matrix_set_length(&h, maxmn+1, maxmn+1, _state);
                ae_matrix_set_length(&a, maxmn+1, maxmn+1, _state);
                ae_matrix_set_length(&b, maxmn+1, maxmn+1, _state);
                ae_matrix_set_length(&c, maxmn+1, maxmn+1, _state);
                
                /*
                 * GenerateReflection, three tasks are possible:
                 * * random X
                 * * zero X
                 * * non-zero X[1], all other are zeros
                 * * random X, near underflow scale
                 * * random X, near overflow scale
                 */
                for(tasktype=0; tasktype<=4; tasktype++)
                {
                    xscale = (double)(1);
                    if( tasktype==0 )
                    {
                        for(i=1; i<=n; i++)
                        {
                            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    if( tasktype==1 )
                    {
                        for(i=1; i<=n; i++)
                        {
                            x.ptr.p_double[i] = (double)(0);
                        }
                    }
                    if( tasktype==2 )
                    {
                        x.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                        for(i=2; i<=n; i++)
                        {
                            x.ptr.p_double[i] = (double)(0);
                        }
                    }
                    if( tasktype==3 )
                    {
                        for(i=1; i<=n; i++)
                        {
                            x.ptr.p_double[i] = (ae_randominteger(21, _state)-10)*ae_minrealnumber;
                        }
                        xscale = 10*ae_minrealnumber;
                    }
                    if( tasktype==4 )
                    {
                        for(i=1; i<=n; i++)
                        {
                            x.ptr.p_double[i] = (2*ae_randomreal(_state)-1)*ae_maxrealnumber;
                        }
                        xscale = ae_maxrealnumber;
                    }
                    ae_v_move(&v.ptr.p_double[1], 1, &x.ptr.p_double[1], 1, ae_v_len(1,n));
                    generatereflection(&v, n, &tau, _state);
                    beta = v.ptr.p_double[1];
                    v.ptr.p_double[1] = (double)(1);
                    for(i=1; i<=n; i++)
                    {
                        for(j=1; j<=n; j++)
                        {
                            if( i==j )
                            {
                                h.ptr.pp_double[i][j] = 1-tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                            }
                            else
                            {
                                h.ptr.pp_double[i][j] = -tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                            }
                        }
                    }
                    err = (double)(0);
                    for(i=1; i<=n; i++)
                    {
                        tmp = ae_v_dotproduct(&h.ptr.pp_double[i][1], 1, &x.ptr.p_double[1], 1, ae_v_len(1,n));
                        if( i==1 )
                        {
                            err = ae_maxreal(err, ae_fabs(tmp-beta, _state), _state);
                        }
                        else
                        {
                            err = ae_maxreal(err, ae_fabs(tmp, _state), _state);
                        }
                    }
                    meg = ae_maxreal(meg, err/xscale, _state);
                }
                
                /*
                 * ApplyReflectionFromTheLeft
                 */
                for(i=1; i<=m; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    v.ptr.p_double[i] = x.ptr.p_double[i];
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        b.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                    }
                }
                generatereflection(&v, m, &tau, _state);
                beta = v.ptr.p_double[1];
                v.ptr.p_double[1] = (double)(1);
                applyreflectionfromtheleft(&b, tau, &v, 1, m, 1, n, &work, _state);
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=m; j++)
                    {
                        if( i==j )
                        {
                            h.ptr.pp_double[i][j] = 1-tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                        }
                        else
                        {
                            h.ptr.pp_double[i][j] = -tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                        }
                    }
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        tmp = ae_v_dotproduct(&h.ptr.pp_double[i][1], 1, &a.ptr.pp_double[1][j], a.stride, ae_v_len(1,m));
                        c.ptr.pp_double[i][j] = tmp;
                    }
                }
                err = (double)(0);
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        err = ae_maxreal(err, ae_fabs(b.ptr.pp_double[i][j]-c.ptr.pp_double[i][j], _state), _state);
                    }
                }
                mel = ae_maxreal(mel, err, _state);
                
                /*
                 * ApplyReflectionFromTheRight
                 */
                for(i=1; i<=n; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    v.ptr.p_double[i] = x.ptr.p_double[i];
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        b.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                    }
                }
                generatereflection(&v, n, &tau, _state);
                beta = v.ptr.p_double[1];
                v.ptr.p_double[1] = (double)(1);
                applyreflectionfromtheright(&b, tau, &v, 1, m, 1, n, &work, _state);
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        if( i==j )
                        {
                            h.ptr.pp_double[i][j] = 1-tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                        }
                        else
                        {
                            h.ptr.pp_double[i][j] = -tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                        }
                    }
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        tmp = ae_v_dotproduct(&a.ptr.pp_double[i][1], 1, &h.ptr.pp_double[1][j], h.stride, ae_v_len(1,n));
                        c.ptr.pp_double[i][j] = tmp;
                    }
                }
                err = (double)(0);
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        err = ae_maxreal(err, ae_fabs(b.ptr.pp_double[i][j]-c.ptr.pp_double[i][j], _state), _state);
                    }
                }
                mer = ae_maxreal(mer, err, _state);
            }
        }
    }
    
    /*
     * Overflow crash test
     */
    ae_vector_set_length(&x, 10+1, _state);
    ae_vector_set_length(&v, 10+1, _state);
    for(i=1; i<=10; i++)
    {
        v.ptr.p_double[i] = ae_maxrealnumber*0.01*(2*ae_randomreal(_state)-1);
    }
    generatereflection(&v, 10, &tau, _state);
    
    /*
     * Result
     */
    ae_set_error_flag(errorflag, (ae_fp_greater(meg,threshold)||ae_fp_greater(mel,threshold))||ae_fp_greater(mer,threshold), __FILE__, __LINE__, "testablasunit.ap:1888");
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation

  -- ALGLIB routine --
     15.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refcmatrixrighttrsm(ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Complex */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix a2;
    ae_vector tx;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;
    ae_bool rupper;

    ae_frame_make(_state, &_frame_block);
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    memset(&tx, 0, sizeof(tx));
    ae_matrix_init(&a1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tx, 0, DT_COMPLEX, _state, ae_true);

    if( n*m==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_matrix_set_length(&a1, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
    if( isupper )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                a1.ptr.pp_complex[i][j] = a->ptr.pp_complex[i1+i][j1+j];
            }
        }
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                a1.ptr.pp_complex[i][j] = a->ptr.pp_complex[i1+i][j1+j];
            }
        }
    }
    rupper = isupper;
    if( isunit )
    {
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.pp_complex[i][i] = ae_complex_from_i(1);
        }
    }
    ae_matrix_set_length(&a2, n, n, _state);
    if( optype==0 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_complex[i][j] = a1.ptr.pp_complex[i][j];
            }
        }
    }
    if( optype==1 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_complex[i][j] = a1.ptr.pp_complex[j][i];
            }
        }
        rupper = !rupper;
    }
    if( optype==2 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_complex[i][j] = ae_c_conj(a1.ptr.pp_complex[j][i], _state);
            }
        }
        rupper = !rupper;
    }
    testablasunit_internalcmatrixtrinverse(&a2, n, rupper, ae_false, _state);
    ae_vector_set_length(&tx, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_cmove(&tx.ptr.p_complex[0], 1, &x->ptr.pp_complex[i2+i][j2], 1, "N", ae_v_len(0,n-1));
        for(j=0; j<=n-1; j++)
        {
            vc = ae_v_cdotproduct(&tx.ptr.p_complex[0], 1, "N", &a2.ptr.pp_complex[0][j], a2.stride, "N", ae_v_len(0,n-1));
            x->ptr.pp_complex[i2+i][j2+j] = vc;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation

  -- ALGLIB routine --
     15.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refcmatrixlefttrsm(ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Complex */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix a2;
    ae_vector tx;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;
    ae_bool rupper;

    ae_frame_make(_state, &_frame_block);
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    memset(&tx, 0, sizeof(tx));
    ae_matrix_init(&a1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tx, 0, DT_COMPLEX, _state, ae_true);

    if( n*m==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_matrix_set_length(&a1, m, m, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            a1.ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
    if( isupper )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=i; j<=m-1; j++)
            {
                a1.ptr.pp_complex[i][j] = a->ptr.pp_complex[i1+i][j1+j];
            }
        }
    }
    else
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                a1.ptr.pp_complex[i][j] = a->ptr.pp_complex[i1+i][j1+j];
            }
        }
    }
    rupper = isupper;
    if( isunit )
    {
        for(i=0; i<=m-1; i++)
        {
            a1.ptr.pp_complex[i][i] = ae_complex_from_i(1);
        }
    }
    ae_matrix_set_length(&a2, m, m, _state);
    if( optype==0 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_complex[i][j] = a1.ptr.pp_complex[i][j];
            }
        }
    }
    if( optype==1 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_complex[i][j] = a1.ptr.pp_complex[j][i];
            }
        }
        rupper = !rupper;
    }
    if( optype==2 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_complex[i][j] = ae_c_conj(a1.ptr.pp_complex[j][i], _state);
            }
        }
        rupper = !rupper;
    }
    testablasunit_internalcmatrixtrinverse(&a2, m, rupper, ae_false, _state);
    ae_vector_set_length(&tx, m, _state);
    for(j=0; j<=n-1; j++)
    {
        ae_v_cmove(&tx.ptr.p_complex[0], 1, &x->ptr.pp_complex[i2][j2+j], x->stride, "N", ae_v_len(0,m-1));
        for(i=0; i<=m-1; i++)
        {
            vc = ae_v_cdotproduct(&a2.ptr.pp_complex[i][0], 1, "N", &tx.ptr.p_complex[0], 1, "N", ae_v_len(0,m-1));
            x->ptr.pp_complex[i2+i][j2+j] = vc;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation

  -- ALGLIB routine --
     15.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refrmatrixrighttrsm(ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Real    */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix a2;
    ae_vector tx;
    ae_int_t i;
    ae_int_t j;
    double vr;
    ae_bool rupper;

    ae_frame_make(_state, &_frame_block);
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    memset(&tx, 0, sizeof(tx));
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    if( n*m==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_matrix_set_length(&a1, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_double[i][j] = (double)(0);
        }
    }
    if( isupper )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                a1.ptr.pp_double[i][j] = a->ptr.pp_double[i1+i][j1+j];
            }
        }
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                a1.ptr.pp_double[i][j] = a->ptr.pp_double[i1+i][j1+j];
            }
        }
    }
    rupper = isupper;
    if( isunit )
    {
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.pp_double[i][i] = (double)(1);
        }
    }
    ae_matrix_set_length(&a2, n, n, _state);
    if( optype==0 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
            }
        }
    }
    if( optype==1 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_double[i][j] = a1.ptr.pp_double[j][i];
            }
        }
        rupper = !rupper;
    }
    testablasunit_internalrmatrixtrinverse(&a2, n, rupper, ae_false, _state);
    ae_vector_set_length(&tx, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_move(&tx.ptr.p_double[0], 1, &x->ptr.pp_double[i2+i][j2], 1, ae_v_len(0,n-1));
        for(j=0; j<=n-1; j++)
        {
            vr = ae_v_dotproduct(&tx.ptr.p_double[0], 1, &a2.ptr.pp_double[0][j], a2.stride, ae_v_len(0,n-1));
            x->ptr.pp_double[i2+i][j2+j] = vr;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation

  -- ALGLIB routine --
     15.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refrmatrixlefttrsm(ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Real    */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix a2;
    ae_vector tx;
    ae_int_t i;
    ae_int_t j;
    double vr;
    ae_bool rupper;

    ae_frame_make(_state, &_frame_block);
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    memset(&tx, 0, sizeof(tx));
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    if( n*m==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_matrix_set_length(&a1, m, m, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            a1.ptr.pp_double[i][j] = (double)(0);
        }
    }
    if( isupper )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=i; j<=m-1; j++)
            {
                a1.ptr.pp_double[i][j] = a->ptr.pp_double[i1+i][j1+j];
            }
        }
    }
    else
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                a1.ptr.pp_double[i][j] = a->ptr.pp_double[i1+i][j1+j];
            }
        }
    }
    rupper = isupper;
    if( isunit )
    {
        for(i=0; i<=m-1; i++)
        {
            a1.ptr.pp_double[i][i] = (double)(1);
        }
    }
    ae_matrix_set_length(&a2, m, m, _state);
    if( optype==0 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
            }
        }
    }
    if( optype==1 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_double[i][j] = a1.ptr.pp_double[j][i];
            }
        }
        rupper = !rupper;
    }
    testablasunit_internalrmatrixtrinverse(&a2, m, rupper, ae_false, _state);
    ae_vector_set_length(&tx, m, _state);
    for(j=0; j<=n-1; j++)
    {
        ae_v_move(&tx.ptr.p_double[0], 1, &x->ptr.pp_double[i2][j2+j], x->stride, ae_v_len(0,m-1));
        for(i=0; i<=m-1; i++)
        {
            vr = ae_v_dotproduct(&a2.ptr.pp_double[i][0], 1, &tx.ptr.p_double[0], 1, ae_v_len(0,m-1));
            x->ptr.pp_double[i2+i][j2+j] = vr;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Internal subroutine.
Triangular matrix inversion

  -- LAPACK routine (version 3.0) --
     Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
     Courant Institute, Argonne National Lab, and Rice University
     February 29, 1992
*************************************************************************/
static ae_bool testablasunit_internalcmatrixtrinverse(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_complex ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&t, 0, sizeof(t));
    ae_vector_init(&t, 0, DT_COMPLEX, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_c_eq_d(a->ptr.pp_complex[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_complex[j][j] = ae_c_d_div(1,a->ptr.pp_complex[j][j]);
                ajj = ae_c_neg(a->ptr.pp_complex[j][j]);
            }
            else
            {
                ajj = ae_complex_from_i(-1);
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_cmove(&t.ptr.p_complex[0], 1, &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i+1<j )
                    {
                        v = ae_v_cdotproduct(&a->ptr.pp_complex[i][i+1], 1, "N", &t.ptr.p_complex[i+1], 1, "N", ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = ae_complex_from_i(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,ae_c_mul(a->ptr.pp_complex[i][i],t.ptr.p_complex[i]));
                    }
                    else
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,t.ptr.p_complex[i]);
                    }
                }
                ae_v_cmulc(&a->ptr.pp_complex[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_c_eq_d(a->ptr.pp_complex[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_complex[j][j] = ae_c_d_div(1,a->ptr.pp_complex[j][j]);
                ajj = ae_c_neg(a->ptr.pp_complex[j][j]);
            }
            else
            {
                ajj = ae_complex_from_i(-1);
            }
            if( j+1<n )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_cmove(&t.ptr.p_complex[j+1], 1, &a->ptr.pp_complex[j+1][j], a->stride, "N", ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_cdotproduct(&a->ptr.pp_complex[i][j+1], 1, "N", &t.ptr.p_complex[j+1], 1, "N", ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = ae_complex_from_i(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,ae_c_mul(a->ptr.pp_complex[i][i],t.ptr.p_complex[i]));
                    }
                    else
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,t.ptr.p_complex[i]);
                    }
                }
                ae_v_cmulc(&a->ptr.pp_complex[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Internal subroutine.
Triangular matrix inversion

  -- LAPACK routine (version 3.0) --
     Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
     Courant Institute, Argonne National Lab, and Rice University
     February 29, 1992
*************************************************************************/
static ae_bool testablasunit_internalrmatrixtrinverse(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    double v;
    double ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&t, 0, sizeof(t));
    ae_vector_init(&t, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = (double)(-1);
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_move(&t.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i<j-1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][i+1], 1, &t.ptr.p_double[i+1], 1, ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = (double)(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = (double)(-1);
            }
            if( j<n-1 )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_move(&t.ptr.p_double[j+1], 1, &a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &t.ptr.p_double[j+1], 1, ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = (double)(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference SYRK subroutine.

  -- ALGLIB routine --
     16.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refcmatrixherk(ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Complex */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     double beta,
     /* Complex */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_bool isupper,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ae;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;

    ae_frame_make(_state, &_frame_block);
    memset(&ae, 0, sizeof(ae));
    ae_matrix_init(&ae, 0, 0, DT_COMPLEX, _state, ae_true);

    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (isupper&&j>=i)||(!isupper&&j<=i) )
            {
                if( ae_fp_eq(beta,(double)(0)) )
                {
                    c->ptr.pp_complex[i+ic][j+jc] = ae_complex_from_i(0);
                }
                else
                {
                    c->ptr.pp_complex[i+ic][j+jc] = ae_c_mul_d(c->ptr.pp_complex[i+ic][j+jc],beta);
                }
            }
        }
    }
    if( ae_fp_eq(alpha,(double)(0)) )
    {
        ae_frame_leave(_state);
        return;
    }
    if( n*k>0 )
    {
        ae_matrix_set_length(&ae, n, k, _state);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            if( optypea==0 )
            {
                ae.ptr.pp_complex[i][j] = a->ptr.pp_complex[ia+i][ja+j];
            }
            if( optypea==2 )
            {
                ae.ptr.pp_complex[i][j] = ae_c_conj(a->ptr.pp_complex[ia+j][ja+i], _state);
            }
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            vc = ae_complex_from_i(0);
            if( k>0 )
            {
                vc = ae_v_cdotproduct(&ae.ptr.pp_complex[i][0], 1, "N", &ae.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,k-1));
            }
            vc = ae_c_mul_d(vc,alpha);
            if( isupper&&j>=i )
            {
                c->ptr.pp_complex[ic+i][jc+j] = ae_c_add(vc,c->ptr.pp_complex[ic+i][jc+j]);
            }
            if( !isupper&&j<=i )
            {
                c->ptr.pp_complex[ic+i][jc+j] = ae_c_add(vc,c->ptr.pp_complex[ic+i][jc+j]);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference SYRK subroutine.

  -- ALGLIB routine --
     16.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refrmatrixsyrk(ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     double beta,
     /* Real    */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_bool isupper,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ae;
    ae_int_t i;
    ae_int_t j;
    double vr;

    ae_frame_make(_state, &_frame_block);
    memset(&ae, 0, sizeof(ae));
    ae_matrix_init(&ae, 0, 0, DT_REAL, _state, ae_true);

    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (isupper&&j>=i)||(!isupper&&j<=i) )
            {
                if( ae_fp_eq(beta,(double)(0)) )
                {
                    c->ptr.pp_double[i+ic][j+jc] = (double)(0);
                }
                else
                {
                    c->ptr.pp_double[i+ic][j+jc] = c->ptr.pp_double[i+ic][j+jc]*beta;
                }
            }
        }
    }
    if( ae_fp_eq(alpha,(double)(0)) )
    {
        ae_frame_leave(_state);
        return;
    }
    if( n*k>0 )
    {
        ae_matrix_set_length(&ae, n, k, _state);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            if( optypea==0 )
            {
                ae.ptr.pp_double[i][j] = a->ptr.pp_double[ia+i][ja+j];
            }
            if( optypea==1 )
            {
                ae.ptr.pp_double[i][j] = a->ptr.pp_double[ia+j][ja+i];
            }
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            vr = (double)(0);
            if( k>0 )
            {
                vr = ae_v_dotproduct(&ae.ptr.pp_double[i][0], 1, &ae.ptr.pp_double[j][0], 1, ae_v_len(0,k-1));
            }
            vr = alpha*vr;
            if( isupper&&j>=i )
            {
                c->ptr.pp_double[ic+i][jc+j] = vr+c->ptr.pp_double[ic+i][jc+j];
            }
            if( !isupper&&j<=i )
            {
                c->ptr.pp_double[ic+i][jc+j] = vr+c->ptr.pp_double[ic+i][jc+j];
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference GEMM,
ALGLIB subroutine
*************************************************************************/
static void testablasunit_refcmatrixgemm(ae_int_t m,
     ae_int_t n,
     ae_int_t k,
     ae_complex alpha,
     /* Complex */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     /* Complex */ ae_matrix* b,
     ae_int_t ib,
     ae_int_t jb,
     ae_int_t optypeb,
     ae_complex beta,
     /* Complex */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ae;
    ae_matrix be;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;

    ae_frame_make(_state, &_frame_block);
    memset(&ae, 0, sizeof(ae));
    memset(&be, 0, sizeof(be));
    ae_matrix_init(&ae, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&be, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_matrix_set_length(&ae, m, k, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            if( optypea==0 )
            {
                ae.ptr.pp_complex[i][j] = a->ptr.pp_complex[ia+i][ja+j];
            }
            if( optypea==1 )
            {
                ae.ptr.pp_complex[i][j] = a->ptr.pp_complex[ia+j][ja+i];
            }
            if( optypea==2 )
            {
                ae.ptr.pp_complex[i][j] = ae_c_conj(a->ptr.pp_complex[ia+j][ja+i], _state);
            }
        }
    }
    ae_matrix_set_length(&be, k, n, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( optypeb==0 )
            {
                be.ptr.pp_complex[i][j] = b->ptr.pp_complex[ib+i][jb+j];
            }
            if( optypeb==1 )
            {
                be.ptr.pp_complex[i][j] = b->ptr.pp_complex[ib+j][jb+i];
            }
            if( optypeb==2 )
            {
                be.ptr.pp_complex[i][j] = ae_c_conj(b->ptr.pp_complex[ib+j][jb+i], _state);
            }
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            vc = ae_v_cdotproduct(&ae.ptr.pp_complex[i][0], 1, "N", &be.ptr.pp_complex[0][j], be.stride, "N", ae_v_len(0,k-1));
            vc = ae_c_mul(alpha,vc);
            if( ae_c_neq_d(beta,(double)(0)) )
            {
                vc = ae_c_add(vc,ae_c_mul(beta,c->ptr.pp_complex[ic+i][jc+j]));
            }
            c->ptr.pp_complex[ic+i][jc+j] = vc;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference GEMM,
ALGLIB subroutine
*************************************************************************/
static void testablasunit_refrmatrixgemm(ae_int_t m,
     ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     /* Real    */ ae_matrix* b,
     ae_int_t ib,
     ae_int_t jb,
     ae_int_t optypeb,
     double beta,
     /* Real    */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ae;
    ae_matrix be;
    ae_int_t i;
    ae_int_t j;
    double vc;

    ae_frame_make(_state, &_frame_block);
    memset(&ae, 0, sizeof(ae));
    memset(&be, 0, sizeof(be));
    ae_matrix_init(&ae, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&be, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&ae, m, k, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            if( optypea==0 )
            {
                ae.ptr.pp_double[i][j] = a->ptr.pp_double[ia+i][ja+j];
            }
            if( optypea==1 )
            {
                ae.ptr.pp_double[i][j] = a->ptr.pp_double[ia+j][ja+i];
            }
        }
    }
    ae_matrix_set_length(&be, k, n, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( optypeb==0 )
            {
                be.ptr.pp_double[i][j] = b->ptr.pp_double[ib+i][jb+j];
            }
            if( optypeb==1 )
            {
                be.ptr.pp_double[i][j] = b->ptr.pp_double[ib+j][jb+i];
            }
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            vc = ae_v_dotproduct(&ae.ptr.pp_double[i][0], 1, &be.ptr.pp_double[0][j], be.stride, ae_v_len(0,k-1));
            vc = alpha*vc;
            if( ae_fp_neq(beta,(double)(0)) )
            {
                vc = vc+beta*c->ptr.pp_double[ic+i][jc+j];
            }
            c->ptr.pp_double[ic+i][jc+j] = vc;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference SYMV,
ALGLIB subroutine
*************************************************************************/
static void testablasunit_refrmatrixsymv(ae_int_t n,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_bool isupper,
     /* Real    */ ae_vector* x,
     ae_int_t ix,
     double beta,
     /* Real    */ ae_vector* y,
     ae_int_t iy,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_matrix b;

    ae_frame_make(_state, &_frame_block);
    memset(&b, 0, sizeof(b));
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Convert problem to traditional dense GEMV
     */
    ae_matrix_set_length(&b, n, n, _state);
    if( isupper )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                b.ptr.pp_double[i][j] = a->ptr.pp_double[ia+i][ja+j];
                b.ptr.pp_double[j][i] = a->ptr.pp_double[ia+i][ja+j];
            }
        }
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                b.ptr.pp_double[i][j] = a->ptr.pp_double[ia+i][ja+j];
                b.ptr.pp_double[j][i] = a->ptr.pp_double[ia+i][ja+j];
            }
        }
    }
    
    /*
     * Calculate result
     */
    for(i=0; i<=n-1; i++)
    {
        v = beta*y->ptr.p_double[iy+i];
        for(j=0; j<=n-1; j++)
        {
            v = v+alpha*b.ptr.pp_double[i][j]*x->ptr.p_double[ix+j];
        }
        y->ptr.p_double[iy+i] = v;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference SYVMV,
ALGLIB subroutine
*************************************************************************/
static double testablasunit_refrmatrixsyvmv(ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_bool isupper,
     /* Real    */ ae_vector* x,
     ae_int_t ix,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_matrix b;
    double result;

    ae_frame_make(_state, &_frame_block);
    memset(&b, 0, sizeof(b));
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Convert problem to traditional dense GEMV
     */
    ae_matrix_set_length(&b, n, n, _state);
    if( isupper )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                b.ptr.pp_double[i][j] = a->ptr.pp_double[ia+i][ja+j];
                b.ptr.pp_double[j][i] = a->ptr.pp_double[ia+i][ja+j];
            }
        }
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                b.ptr.pp_double[i][j] = a->ptr.pp_double[ia+i][ja+j];
                b.ptr.pp_double[j][i] = a->ptr.pp_double[ia+i][ja+j];
            }
        }
    }
    
    /*
     * Calculate result
     */
    result = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            result = result+x->ptr.p_double[ix+i]*b.ptr.pp_double[i][j]*x->ptr.p_double[ix+j];
        }
    }
    ae_frame_leave(_state);
    return result;
}








ae_bool testcreflections(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t m;
    ae_int_t maxmn;
    ae_vector x;
    ae_vector v;
    ae_vector work;
    ae_matrix h;
    ae_matrix a;
    ae_matrix b;
    ae_matrix c;
    ae_complex tmp;
    ae_complex beta;
    ae_complex tau;
    double err;
    double mer;
    double mel;
    double meg;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool waserrors;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&v, 0, sizeof(v));
    memset(&work, 0, sizeof(work));
    memset(&h, 0, sizeof(h));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&c, 0, sizeof(c));
    ae_vector_init(&x, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&v, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&work, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&h, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_COMPLEX, _state, ae_true);

    threshold = 1000*ae_machineepsilon;
    passcount = 1000;
    mer = (double)(0);
    mel = (double)(0);
    meg = (double)(0);
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Task
         */
        n = 1+ae_randominteger(10, _state);
        m = 1+ae_randominteger(10, _state);
        maxmn = ae_maxint(m, n, _state);
        
        /*
         * Initialize
         */
        ae_vector_set_length(&x, maxmn+1, _state);
        ae_vector_set_length(&v, maxmn+1, _state);
        ae_vector_set_length(&work, maxmn+1, _state);
        ae_matrix_set_length(&h, maxmn+1, maxmn+1, _state);
        ae_matrix_set_length(&a, maxmn+1, maxmn+1, _state);
        ae_matrix_set_length(&b, maxmn+1, maxmn+1, _state);
        ae_matrix_set_length(&c, maxmn+1, maxmn+1, _state);
        
        /*
         * GenerateReflection
         */
        for(i=1; i<=n; i++)
        {
            x.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            x.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            v.ptr.p_complex[i] = x.ptr.p_complex[i];
        }
        complexgeneratereflection(&v, n, &tau, _state);
        beta = v.ptr.p_complex[1];
        v.ptr.p_complex[1] = ae_complex_from_i(1);
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                if( i==j )
                {
                    h.ptr.pp_complex[i][j] = ae_c_d_sub(1,ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
                else
                {
                    h.ptr.pp_complex[i][j] = ae_c_neg(ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
            }
        }
        err = (double)(0);
        for(i=1; i<=n; i++)
        {
            tmp = ae_v_cdotproduct(&h.ptr.pp_complex[1][i], h.stride, "Conj", &x.ptr.p_complex[1], 1, "N", ae_v_len(1,n));
            if( i==1 )
            {
                err = ae_maxreal(err, ae_c_abs(ae_c_sub(tmp,beta), _state), _state);
            }
            else
            {
                err = ae_maxreal(err, ae_c_abs(tmp, _state), _state);
            }
        }
        err = ae_maxreal(err, ae_fabs(beta.y, _state), _state);
        meg = ae_maxreal(meg, err, _state);
        
        /*
         * ApplyReflectionFromTheLeft
         */
        for(i=1; i<=m; i++)
        {
            x.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            x.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            v.ptr.p_complex[i] = x.ptr.p_complex[i];
        }
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                b.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
            }
        }
        complexgeneratereflection(&v, m, &tau, _state);
        beta = v.ptr.p_complex[1];
        v.ptr.p_complex[1] = ae_complex_from_i(1);
        complexapplyreflectionfromtheleft(&b, tau, &v, 1, m, 1, n, &work, _state);
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=m; j++)
            {
                if( i==j )
                {
                    h.ptr.pp_complex[i][j] = ae_c_d_sub(1,ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
                else
                {
                    h.ptr.pp_complex[i][j] = ae_c_neg(ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
            }
        }
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                tmp = ae_v_cdotproduct(&h.ptr.pp_complex[i][1], 1, "N", &a.ptr.pp_complex[1][j], a.stride, "N", ae_v_len(1,m));
                c.ptr.pp_complex[i][j] = tmp;
            }
        }
        err = (double)(0);
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                err = ae_maxreal(err, ae_c_abs(ae_c_sub(b.ptr.pp_complex[i][j],c.ptr.pp_complex[i][j]), _state), _state);
            }
        }
        mel = ae_maxreal(mel, err, _state);
        
        /*
         * ApplyReflectionFromTheRight
         */
        for(i=1; i<=n; i++)
        {
            x.ptr.p_complex[i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
            v.ptr.p_complex[i] = x.ptr.p_complex[i];
        }
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                a.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                b.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
            }
        }
        complexgeneratereflection(&v, n, &tau, _state);
        beta = v.ptr.p_complex[1];
        v.ptr.p_complex[1] = ae_complex_from_i(1);
        complexapplyreflectionfromtheright(&b, tau, &v, 1, m, 1, n, &work, _state);
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                if( i==j )
                {
                    h.ptr.pp_complex[i][j] = ae_c_d_sub(1,ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
                else
                {
                    h.ptr.pp_complex[i][j] = ae_c_neg(ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
            }
        }
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                tmp = ae_v_cdotproduct(&a.ptr.pp_complex[i][1], 1, "N", &h.ptr.pp_complex[1][j], h.stride, "N", ae_v_len(1,n));
                c.ptr.pp_complex[i][j] = tmp;
            }
        }
        err = (double)(0);
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                err = ae_maxreal(err, ae_c_abs(ae_c_sub(b.ptr.pp_complex[i][j],c.ptr.pp_complex[i][j]), _state), _state);
            }
        }
        mer = ae_maxreal(mer, err, _state);
    }
    
    /*
     * Overflow crash test
     */
    ae_vector_set_length(&x, 10+1, _state);
    ae_vector_set_length(&v, 10+1, _state);
    for(i=1; i<=10; i++)
    {
        v.ptr.p_complex[i] = ae_complex_from_d(ae_maxrealnumber*0.01*(2*ae_randomreal(_state)-1));
    }
    complexgeneratereflection(&v, 10, &tau, _state);
    
    /*
     * report
     */
    waserrors = (ae_fp_greater(meg,threshold)||ae_fp_greater(mel,threshold))||ae_fp_greater(mer,threshold);
    if( !silent )
    {
        printf("TESTING COMPLEX REFLECTIONS\n");
        printf("Generate error:                          %5.3e\n",
            (double)(meg));
        printf("Apply(L) error:                          %5.3e\n",
            (double)(mel));
        printf("Apply(R) error:                          %5.3e\n",
            (double)(mer));
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        printf("Overflow crash test:                     PASSED\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static ae_int_t testmatgenunit_maxsvditerations = 60;
static void testmatgenunit_unset2d(/* Real    */ ae_matrix* a,
     ae_state *_state);
static void testmatgenunit_unset2dc(/* Complex */ ae_matrix* a,
     ae_state *_state);
static ae_bool testmatgenunit_isspd(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_state *_state);
static ae_bool testmatgenunit_ishpd(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static ae_bool testmatgenunit_testeult(ae_state *_state);
static double testmatgenunit_svdcond(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static ae_bool testmatgenunit_obsoletesvddecomposition(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* v,
     ae_state *_state);
static double testmatgenunit_extsign(double a, double b, ae_state *_state);
static double testmatgenunit_mymax(double a, double b, ae_state *_state);
static double testmatgenunit_pythag(double a, double b, ae_state *_state);





ae_bool testmatgen(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix b;
    ae_matrix u;
    ae_matrix v;
    ae_matrix ca;
    ae_matrix cb;
    ae_matrix r1;
    ae_matrix r2;
    ae_matrix c1;
    ae_matrix c2;
    ae_vector w;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool waserrors;
    double cond;
    double threshold;
    double vt;
    ae_complex ct;
    double minw;
    double maxw;
    ae_bool serr;
    ae_bool herr;
    ae_bool spderr;
    ae_bool hpderr;
    ae_bool rerr;
    ae_bool cerr;
    ae_bool eulerr;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&u, 0, sizeof(u));
    memset(&v, 0, sizeof(v));
    memset(&ca, 0, sizeof(ca));
    memset(&cb, 0, sizeof(cb));
    memset(&r1, 0, sizeof(r1));
    memset(&r2, 0, sizeof(r2));
    memset(&c1, 0, sizeof(c1));
    memset(&c2, 0, sizeof(c2));
    memset(&w, 0, sizeof(w));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cb, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&r1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&c2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    rerr = ae_false;
    cerr = ae_false;
    serr = ae_false;
    herr = ae_false;
    spderr = ae_false;
    hpderr = ae_false;
    eulerr = ae_false;
    waserrors = ae_false;
    maxn = 20;
    passcount = 15;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * Testing orthogonal
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            ae_matrix_set_length(&r1, n-1+1, 2*n-1+1, _state);
            ae_matrix_set_length(&r2, 2*n-1+1, n-1+1, _state);
            ae_matrix_set_length(&c1, n-1+1, 2*n-1+1, _state);
            ae_matrix_set_length(&c2, 2*n-1+1, n-1+1, _state);
            
            /*
             * Random orthogonal, real
             */
            testmatgenunit_unset2d(&a, _state);
            testmatgenunit_unset2d(&b, _state);
            rmatrixrndorthogonal(n, &a, _state);
            rmatrixrndorthogonal(n, &b, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    vt = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &a.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    vt = ae_v_dotproduct(&b.ptr.pp_double[i][0], 1, &b.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    if( n>=2 )
                    {
                        rerr = rerr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
            
            /*
             * Random orthogonal, complex
             */
            testmatgenunit_unset2dc(&ca, _state);
            testmatgenunit_unset2dc(&cb, _state);
            cmatrixrndorthogonal(n, &ca, _state);
            cmatrixrndorthogonal(n, &cb, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    ct = ae_v_cdotproduct(&ca.ptr.pp_complex[i][0], 1, "N", &ca.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    ct = ae_v_cdotproduct(&cb.ptr.pp_complex[i][0], 1, "N", &cb.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    if( n>=2 )
                    {
                        cerr = cerr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                    }
                }
            }
            
            /*
             * From the right real tests:
             * 1. E*Q is orthogonal
             * 2. Q1<>Q2 (routine result is changing)
             * 3. (E E)'*Q = (Q' Q')' (correct handling of non-square matrices)
             */
            testmatgenunit_unset2d(&a, _state);
            testmatgenunit_unset2d(&b, _state);
            ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
            ae_matrix_set_length(&b, n-1+1, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = (double)(0);
                    b.ptr.pp_double[i][j] = (double)(0);
                }
                a.ptr.pp_double[i][i] = (double)(1);
                b.ptr.pp_double[i][i] = (double)(1);
            }
            rmatrixrndorthogonalfromtheright(&a, n, n, _state);
            rmatrixrndorthogonalfromtheright(&b, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    vt = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &a.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    vt = ae_v_dotproduct(&b.ptr.pp_double[i][0], 1, &b.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    if( n>=2 )
                    {
                        rerr = rerr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    r2.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    r2.ptr.pp_double[i+n][j] = r2.ptr.pp_double[i][j];
                }
            }
            rmatrixrndorthogonalfromtheright(&r2, 2*n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rerr = rerr||ae_fp_greater(ae_fabs(r2.ptr.pp_double[i+n][j]-r2.ptr.pp_double[i][j], _state),threshold);
                }
            }
            
            /*
             * From the left real tests:
             * 1. Q*E is orthogonal
             * 2. Q1<>Q2 (routine result is changing)
             * 3. Q*(E E) = (Q Q) (correct handling of non-square matrices)
             */
            testmatgenunit_unset2d(&a, _state);
            testmatgenunit_unset2d(&b, _state);
            ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
            ae_matrix_set_length(&b, n-1+1, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = (double)(0);
                    b.ptr.pp_double[i][j] = (double)(0);
                }
                a.ptr.pp_double[i][i] = (double)(1);
                b.ptr.pp_double[i][i] = (double)(1);
            }
            rmatrixrndorthogonalfromtheleft(&a, n, n, _state);
            rmatrixrndorthogonalfromtheleft(&b, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    vt = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &a.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    vt = ae_v_dotproduct(&b.ptr.pp_double[i][0], 1, &b.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    if( n>=2 )
                    {
                        rerr = rerr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    r1.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    r1.ptr.pp_double[i][j+n] = r1.ptr.pp_double[i][j];
                }
            }
            rmatrixrndorthogonalfromtheleft(&r1, n, 2*n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rerr = rerr||ae_fp_greater(ae_fabs(r1.ptr.pp_double[i][j]-r1.ptr.pp_double[i][j+n], _state),threshold);
                }
            }
            
            /*
             * From the right complex tests:
             * 1. E*Q is orthogonal
             * 2. Q1<>Q2 (routine result is changing)
             * 3. (E E)'*Q = (Q' Q')' (correct handling of non-square matrices)
             */
            testmatgenunit_unset2dc(&ca, _state);
            testmatgenunit_unset2dc(&cb, _state);
            ae_matrix_set_length(&ca, n-1+1, n-1+1, _state);
            ae_matrix_set_length(&cb, n-1+1, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                    cb.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                }
                ca.ptr.pp_complex[i][i] = ae_complex_from_i(1);
                cb.ptr.pp_complex[i][i] = ae_complex_from_i(1);
            }
            cmatrixrndorthogonalfromtheright(&ca, n, n, _state);
            cmatrixrndorthogonalfromtheright(&cb, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    ct = ae_v_cdotproduct(&ca.ptr.pp_complex[i][0], 1, "N", &ca.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    ct = ae_v_cdotproduct(&cb.ptr.pp_complex[i][0], 1, "N", &cb.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    cerr = cerr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c2.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                    c2.ptr.pp_complex[i+n][j] = c2.ptr.pp_complex[i][j];
                }
            }
            cmatrixrndorthogonalfromtheright(&c2, 2*n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub(c2.ptr.pp_complex[i+n][j],c2.ptr.pp_complex[i][j]), _state),threshold);
                }
            }
            
            /*
             * From the left complex tests:
             * 1. Q*E is orthogonal
             * 2. Q1<>Q2 (routine result is changing)
             * 3. Q*(E E) = (Q Q) (correct handling of non-square matrices)
             */
            testmatgenunit_unset2dc(&ca, _state);
            testmatgenunit_unset2dc(&cb, _state);
            ae_matrix_set_length(&ca, n-1+1, n-1+1, _state);
            ae_matrix_set_length(&cb, n-1+1, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                    cb.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                }
                ca.ptr.pp_complex[i][i] = ae_complex_from_i(1);
                cb.ptr.pp_complex[i][i] = ae_complex_from_i(1);
            }
            cmatrixrndorthogonalfromtheleft(&ca, n, n, _state);
            cmatrixrndorthogonalfromtheleft(&cb, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    ct = ae_v_cdotproduct(&ca.ptr.pp_complex[i][0], 1, "N", &ca.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    ct = ae_v_cdotproduct(&cb.ptr.pp_complex[i][0], 1, "N", &cb.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    cerr = cerr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c1.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                    c1.ptr.pp_complex[i][j+n] = c1.ptr.pp_complex[i][j];
                }
            }
            cmatrixrndorthogonalfromtheleft(&c1, n, 2*n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub(c1.ptr.pp_complex[i][j],c1.ptr.pp_complex[i][j+n]), _state),threshold);
                }
            }
        }
    }
    
    /*
     * Testing GCond
     */
    for(n=2; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * real test
             */
            testmatgenunit_unset2d(&a, _state);
            cond = ae_exp(ae_log((double)(1000), _state)*ae_randomreal(_state), _state);
            rmatrixrndcond(n, cond, &a, _state);
            ae_matrix_set_length(&b, n+1, n+1, _state);
            for(i=1; i<=n; i++)
            {
                for(j=1; j<=n; j++)
                {
                    b.ptr.pp_double[i][j] = a.ptr.pp_double[i-1][j-1];
                }
            }
            if( testmatgenunit_obsoletesvddecomposition(&b, n, n, &w, &v, _state) )
            {
                maxw = w.ptr.p_double[1];
                minw = w.ptr.p_double[1];
                for(i=2; i<=n; i++)
                {
                    if( ae_fp_greater(w.ptr.p_double[i],maxw) )
                    {
                        maxw = w.ptr.p_double[i];
                    }
                    if( ae_fp_less(w.ptr.p_double[i],minw) )
                    {
                        minw = w.ptr.p_double[i];
                    }
                }
                vt = maxw/minw/cond;
                if( ae_fp_greater(ae_fabs(ae_log(vt, _state), _state),ae_log(1+threshold, _state)) )
                {
                    rerr = ae_true;
                }
            }
        }
    }
    
    /*
     * Symmetric/SPD
     * N = 2 .. 30
     */
    for(n=2; n<=maxn; n++)
    {
        
        /*
         * SPD matrices
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Generate A
             */
            testmatgenunit_unset2d(&a, _state);
            cond = ae_exp(ae_log((double)(1000), _state)*ae_randomreal(_state), _state);
            spdmatrixrndcond(n, cond, &a, _state);
            
            /*
             * test condition number
             */
            spderr = spderr||ae_fp_greater(testmatgenunit_svdcond(&a, n, _state)/cond-1,threshold);
            
            /*
             * test SPD
             */
            spderr = spderr||!testmatgenunit_isspd(&a, n, ae_true, _state);
            
            /*
             * test that A is symmetic
             */
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    spderr = spderr||ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-a.ptr.pp_double[j][i], _state),threshold);
                }
            }
            
            /*
             * test for difference between A and B (subsequent matrix)
             */
            testmatgenunit_unset2d(&b, _state);
            spdmatrixrndcond(n, cond, &b, _state);
            if( n>=2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        spderr = spderr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
        }
        
        /*
         * HPD matrices
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Generate A
             */
            testmatgenunit_unset2dc(&ca, _state);
            cond = ae_exp(ae_log((double)(1000), _state)*ae_randomreal(_state), _state);
            hpdmatrixrndcond(n, cond, &ca, _state);
            
            /*
             * test HPD
             */
            hpderr = hpderr||!testmatgenunit_ishpd(&ca, n, _state);
            
            /*
             * test that A is Hermitian
             */
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    hpderr = hpderr||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[i][j],ae_c_conj(ca.ptr.pp_complex[j][i], _state)), _state),threshold);
                }
            }
            
            /*
             * test for difference between A and B (subsequent matrix)
             */
            testmatgenunit_unset2dc(&cb, _state);
            hpdmatrixrndcond(n, cond, &cb, _state);
            if( n>=2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        hpderr = hpderr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                    }
                }
            }
        }
        
        /*
         * Symmetric matrices
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * test condition number
             */
            testmatgenunit_unset2d(&a, _state);
            cond = ae_exp(ae_log((double)(1000), _state)*ae_randomreal(_state), _state);
            smatrixrndcond(n, cond, &a, _state);
            serr = serr||ae_fp_greater(testmatgenunit_svdcond(&a, n, _state)/cond-1,threshold);
            
            /*
             * test for difference between A and B
             */
            testmatgenunit_unset2d(&b, _state);
            smatrixrndcond(n, cond, &b, _state);
            if( n>=2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        serr = serr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
        }
        
        /*
         * Hermitian matrices
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Generate A
             */
            testmatgenunit_unset2dc(&ca, _state);
            cond = ae_exp(ae_log((double)(1000), _state)*ae_randomreal(_state), _state);
            hmatrixrndcond(n, cond, &ca, _state);
            
            /*
             * test that A is Hermitian
             */
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    herr = herr||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[i][j],ae_c_conj(ca.ptr.pp_complex[j][i], _state)), _state),threshold);
                }
            }
            
            /*
             * test for difference between A and B (subsequent matrix)
             */
            testmatgenunit_unset2dc(&cb, _state);
            hmatrixrndcond(n, cond, &cb, _state);
            if( n>=2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        herr = herr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                    }
                }
            }
        }
    }
    
    /*
     * Test for symmetric matrices
     */
    eulerr = testmatgenunit_testeult(_state);
    
    /*
     * report
     */
    waserrors = (((((rerr||cerr)||serr)||spderr)||herr)||hpderr)||eulerr;
    if( !silent )
    {
        printf("TESTING MATRIX GENERATOR\n");
        printf("REAL TEST:                               ");
        if( !rerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("COMPLEX TEST:                            ");
        if( !cerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SYMMETRIC TEST:                          ");
        if( !serr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("HERMITIAN TEST:                          ");
        if( !herr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SPD TEST:                                ");
        if( !spderr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("HPD TEST:                                ");
        if( !hpderr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("TEST FOR SYMMETRIC MATRICES:             ");
        if( !eulerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testmatgenunit_unset2d(/* Real    */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_double[0][0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testmatgenunit_unset2dc(/* Complex */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Test whether matrix is SPD
*************************************************************************/
static ae_bool testmatgenunit_isspd(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix _a;
    ae_int_t i;
    ae_int_t j;
    double ajj;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&_a, 0, sizeof(_a));
    ae_matrix_init_copy(&_a, a, _state, ae_true);
    a = &_a;

    
    /*
     *     Test the input parameters.
     */
    ae_assert(n>=0, "Error in SMatrixCholesky: incorrect function arguments", _state);
    
    /*
     *     Quick return if possible
     */
    result = ae_true;
    if( n<=0 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( isupper )
    {
        
        /*
         * Compute the Cholesky factorization A = U'*U.
         */
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Compute U(J,J) and test for non-positive-definiteness.
             */
            v = ae_v_dotproduct(&a->ptr.pp_double[0][j], a->stride, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
            ajj = a->ptr.pp_double[j][j]-v;
            if( ae_fp_less_eq(ajj,(double)(0)) )
            {
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            ajj = ae_sqrt(ajj, _state);
            a->ptr.pp_double[j][j] = ajj;
            
            /*
             * Compute elements J+1:N of row J.
             */
            if( j<n-1 )
            {
                for(i=j+1; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a->ptr.pp_double[0][i], a->stride, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
                    a->ptr.pp_double[j][i] = a->ptr.pp_double[j][i]-v;
                }
                v = 1/ajj;
                ae_v_muld(&a->ptr.pp_double[j][j+1], 1, ae_v_len(j+1,n-1), v);
            }
        }
    }
    else
    {
        
        /*
         * Compute the Cholesky factorization A = L*L'.
         */
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Compute L(J,J) and test for non-positive-definiteness.
             */
            v = ae_v_dotproduct(&a->ptr.pp_double[j][0], 1, &a->ptr.pp_double[j][0], 1, ae_v_len(0,j-1));
            ajj = a->ptr.pp_double[j][j]-v;
            if( ae_fp_less_eq(ajj,(double)(0)) )
            {
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            ajj = ae_sqrt(ajj, _state);
            a->ptr.pp_double[j][j] = ajj;
            
            /*
             * Compute elements J+1:N of column J.
             */
            if( j<n-1 )
            {
                for(i=j+1; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &a->ptr.pp_double[j][0], 1, ae_v_len(0,j-1));
                    a->ptr.pp_double[i][j] = a->ptr.pp_double[i][j]-v;
                }
                v = 1/ajj;
                ae_v_muld(&a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1), v);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Tests whether A is HPD
*************************************************************************/
static ae_bool testmatgenunit_ishpd(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix _a;
    ae_int_t j;
    double ajj;
    ae_complex v;
    double r;
    ae_vector t;
    ae_vector t2;
    ae_vector t3;
    ae_int_t i;
    ae_matrix a1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&_a, 0, sizeof(_a));
    memset(&t, 0, sizeof(t));
    memset(&t2, 0, sizeof(t2));
    memset(&t3, 0, sizeof(t3));
    memset(&a1, 0, sizeof(a1));
    ae_matrix_init_copy(&_a, a, _state, ae_true);
    a = &_a;
    ae_vector_init(&t, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&t2, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&t3, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_vector_set_length(&t, n-1+1, _state);
    ae_vector_set_length(&t2, n-1+1, _state);
    ae_vector_set_length(&t3, n-1+1, _state);
    result = ae_true;
    
    /*
     * Compute the Cholesky factorization A = U'*U.
     */
    for(j=0; j<=n-1; j++)
    {
        
        /*
         * Compute U(J,J) and test for non-positive-definiteness.
         */
        v = ae_v_cdotproduct(&a->ptr.pp_complex[0][j], a->stride, "Conj", &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,j-1));
        ajj = ae_c_sub(a->ptr.pp_complex[j][j],v).x;
        if( ae_fp_less_eq(ajj,(double)(0)) )
        {
            a->ptr.pp_complex[j][j] = ae_complex_from_d(ajj);
            result = ae_false;
            ae_frame_leave(_state);
            return result;
        }
        ajj = ae_sqrt(ajj, _state);
        a->ptr.pp_complex[j][j] = ae_complex_from_d(ajj);
        
        /*
         * Compute elements J+1:N-1 of row J.
         */
        if( j<n-1 )
        {
            ae_v_cmove(&t2.ptr.p_complex[0], 1, &a->ptr.pp_complex[0][j], a->stride, "Conj", ae_v_len(0,j-1));
            ae_v_cmove(&t3.ptr.p_complex[j+1], 1, &a->ptr.pp_complex[j][j+1], 1, "N", ae_v_len(j+1,n-1));
            for(i=j+1; i<=n-1; i++)
            {
                v = ae_v_cdotproduct(&a->ptr.pp_complex[0][i], a->stride, "N", &t2.ptr.p_complex[0], 1, "N", ae_v_len(0,j-1));
                t3.ptr.p_complex[i] = ae_c_sub(t3.ptr.p_complex[i],v);
            }
            ae_v_cmove(&a->ptr.pp_complex[j][j+1], 1, &t3.ptr.p_complex[j+1], 1, "N", ae_v_len(j+1,n-1));
            r = 1/ajj;
            ae_v_cmuld(&a->ptr.pp_complex[j][j+1], 1, ae_v_len(j+1,n-1), r);
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The function check, that upper triangle from symmetric matrix is equal to
lower triangle.
*************************************************************************/
static ae_bool testmatgenunit_testeult(ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix b;
    double c;
    double range;
    double eps;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);

    eps = 2*ae_machineepsilon;
    range = 100*(2*ae_randomreal(_state)-1);
    for(n=1; n<=15; n++)
    {
        c = 900*ae_randomreal(_state)+100;
        
        /*
         * Generate symmetric matrix and check it
         */
        smatrixrndcond(n, c, &a, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-a.ptr.pp_double[j][i], _state),eps) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
        spdmatrixrndcond(n, c, &a, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-a.ptr.pp_double[j][i], _state),eps) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
        hmatrixrndcond(n, c, &b, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( ae_fp_greater(ae_fabs(b.ptr.pp_complex[i][j].x-b.ptr.pp_complex[j][i].x, _state),eps)||ae_fp_greater(ae_fabs(b.ptr.pp_complex[i][j].y+b.ptr.pp_complex[j][i].y, _state),eps) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
        hpdmatrixrndcond(n, c, &b, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( ae_fp_greater(ae_fabs(b.ptr.pp_complex[i][j].x-b.ptr.pp_complex[j][i].x, _state),eps)||ae_fp_greater(ae_fabs(b.ptr.pp_complex[i][j].y+b.ptr.pp_complex[j][i].y, _state),eps) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
        
        /*
         * Prepare symmetric matrix with real values
         */
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = range*(2*ae_randomreal(_state)-1);
            }
        }
        for(i=0; i<=n-2; i++)
        {
            for(j=i+1; j<=n-1; j++)
            {
                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
            }
        }
        smatrixrndmultiply(&a, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-a.ptr.pp_double[j][i], _state),eps) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
        
        /*
         * Prepare symmetric matrix with complex values
         */
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                b.ptr.pp_complex[i][j].x = range*(2*ae_randomreal(_state)-1);
                if( i!=j )
                {
                    b.ptr.pp_complex[i][j].y = range*(2*ae_randomreal(_state)-1);
                }
                else
                {
                    b.ptr.pp_complex[i][j].y = (double)(0);
                }
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=i+1; j<=n-1; j++)
            {
                b.ptr.pp_complex[i][j].x = b.ptr.pp_complex[j][i].x;
                b.ptr.pp_complex[i][j].y = -b.ptr.pp_complex[j][i].y;
            }
        }
        hmatrixrndmultiply(&b, n, _state);
        for(i=0; i<=n-1; i++)
        {
            b.ptr.pp_complex[i][i].y = (double)(0);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( ae_fp_greater(ae_fabs(b.ptr.pp_complex[i][j].x-b.ptr.pp_complex[j][i].x, _state),eps)||ae_fp_greater(ae_fabs(b.ptr.pp_complex[i][j].y+b.ptr.pp_complex[j][i].y, _state),eps) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
SVD condition number
*************************************************************************/
static double testmatgenunit_svdcond(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix v;
    ae_vector w;
    ae_int_t i;
    ae_int_t j;
    double minw;
    double maxw;
    double result;

    ae_frame_make(_state, &_frame_block);
    memset(&a1, 0, sizeof(a1));
    memset(&v, 0, sizeof(v));
    memset(&w, 0, sizeof(w));
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&a1, n+1, n+1, _state);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a1.ptr.pp_double[i][j] = a->ptr.pp_double[i-1][j-1];
        }
    }
    if( !testmatgenunit_obsoletesvddecomposition(&a1, n, n, &w, &v, _state) )
    {
        result = (double)(0);
        ae_frame_leave(_state);
        return result;
    }
    minw = w.ptr.p_double[1];
    maxw = w.ptr.p_double[1];
    for(i=2; i<=n; i++)
    {
        if( ae_fp_less(w.ptr.p_double[i],minw) )
        {
            minw = w.ptr.p_double[i];
        }
        if( ae_fp_greater(w.ptr.p_double[i],maxw) )
        {
            maxw = w.ptr.p_double[i];
        }
    }
    result = maxw/minw;
    ae_frame_leave(_state);
    return result;
}


static ae_bool testmatgenunit_obsoletesvddecomposition(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* v,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t nm;
    ae_int_t minmn;
    ae_int_t l;
    ae_int_t k;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t its;
    ae_int_t i;
    double z;
    double y;
    double x;
    double vscale;
    double s;
    double h;
    double g;
    double f;
    double c;
    double anorm;
    ae_vector rv1;
    ae_bool flag;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&rv1, 0, sizeof(rv1));
    ae_vector_clear(w);
    ae_matrix_clear(v);
    ae_vector_init(&rv1, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&rv1, n+1, _state);
    ae_vector_set_length(w, n+1, _state);
    ae_matrix_set_length(v, n+1, n+1, _state);
    result = ae_true;
    if( m<n )
    {
        minmn = m;
    }
    else
    {
        minmn = n;
    }
    g = 0.0;
    vscale = 0.0;
    anorm = 0.0;
    l = n;
    for(i=1; i<=n; i++)
    {
        l = i+1;
        rv1.ptr.p_double[i] = vscale*g;
        g = (double)(0);
        s = (double)(0);
        vscale = (double)(0);
        if( i<=m )
        {
            for(k=i; k<=m; k++)
            {
                vscale = vscale+ae_fabs(a->ptr.pp_double[k][i], _state);
            }
            if( ae_fp_neq(vscale,0.0) )
            {
                for(k=i; k<=m; k++)
                {
                    a->ptr.pp_double[k][i] = a->ptr.pp_double[k][i]/vscale;
                    s = s+a->ptr.pp_double[k][i]*a->ptr.pp_double[k][i];
                }
                f = a->ptr.pp_double[i][i];
                g = -testmatgenunit_extsign(ae_sqrt(s, _state), f, _state);
                h = f*g-s;
                a->ptr.pp_double[i][i] = f-g;
                if( i!=n )
                {
                    for(j=l; j<=n; j++)
                    {
                        s = 0.0;
                        for(k=i; k<=m; k++)
                        {
                            s = s+a->ptr.pp_double[k][i]*a->ptr.pp_double[k][j];
                        }
                        f = s/h;
                        for(k=i; k<=m; k++)
                        {
                            a->ptr.pp_double[k][j] = a->ptr.pp_double[k][j]+f*a->ptr.pp_double[k][i];
                        }
                    }
                }
                for(k=i; k<=m; k++)
                {
                    a->ptr.pp_double[k][i] = vscale*a->ptr.pp_double[k][i];
                }
            }
        }
        w->ptr.p_double[i] = vscale*g;
        g = 0.0;
        s = 0.0;
        vscale = 0.0;
        if( i<=m&&i!=n )
        {
            for(k=l; k<=n; k++)
            {
                vscale = vscale+ae_fabs(a->ptr.pp_double[i][k], _state);
            }
            if( ae_fp_neq(vscale,0.0) )
            {
                for(k=l; k<=n; k++)
                {
                    a->ptr.pp_double[i][k] = a->ptr.pp_double[i][k]/vscale;
                    s = s+a->ptr.pp_double[i][k]*a->ptr.pp_double[i][k];
                }
                f = a->ptr.pp_double[i][l];
                g = -testmatgenunit_extsign(ae_sqrt(s, _state), f, _state);
                h = f*g-s;
                a->ptr.pp_double[i][l] = f-g;
                for(k=l; k<=n; k++)
                {
                    rv1.ptr.p_double[k] = a->ptr.pp_double[i][k]/h;
                }
                if( i!=m )
                {
                    for(j=l; j<=m; j++)
                    {
                        s = 0.0;
                        for(k=l; k<=n; k++)
                        {
                            s = s+a->ptr.pp_double[j][k]*a->ptr.pp_double[i][k];
                        }
                        for(k=l; k<=n; k++)
                        {
                            a->ptr.pp_double[j][k] = a->ptr.pp_double[j][k]+s*rv1.ptr.p_double[k];
                        }
                    }
                }
                for(k=l; k<=n; k++)
                {
                    a->ptr.pp_double[i][k] = vscale*a->ptr.pp_double[i][k];
                }
            }
        }
        anorm = testmatgenunit_mymax(anorm, ae_fabs(w->ptr.p_double[i], _state)+ae_fabs(rv1.ptr.p_double[i], _state), _state);
    }
    for(i=n; i>=1; i--)
    {
        if( i<n )
        {
            if( ae_fp_neq(g,0.0) )
            {
                for(j=l; j<=n; j++)
                {
                    v->ptr.pp_double[j][i] = a->ptr.pp_double[i][j]/a->ptr.pp_double[i][l]/g;
                }
                for(j=l; j<=n; j++)
                {
                    s = 0.0;
                    for(k=l; k<=n; k++)
                    {
                        s = s+a->ptr.pp_double[i][k]*v->ptr.pp_double[k][j];
                    }
                    for(k=l; k<=n; k++)
                    {
                        v->ptr.pp_double[k][j] = v->ptr.pp_double[k][j]+s*v->ptr.pp_double[k][i];
                    }
                }
            }
            for(j=l; j<=n; j++)
            {
                v->ptr.pp_double[i][j] = 0.0;
                v->ptr.pp_double[j][i] = 0.0;
            }
        }
        v->ptr.pp_double[i][i] = 1.0;
        g = rv1.ptr.p_double[i];
        l = i;
    }
    for(i=minmn; i>=1; i--)
    {
        l = i+1;
        g = w->ptr.p_double[i];
        if( i<n )
        {
            for(j=l; j<=n; j++)
            {
                a->ptr.pp_double[i][j] = 0.0;
            }
        }
        if( ae_fp_neq(g,0.0) )
        {
            g = 1.0/g;
            if( i!=n )
            {
                for(j=l; j<=n; j++)
                {
                    s = 0.0;
                    for(k=l; k<=m; k++)
                    {
                        s = s+a->ptr.pp_double[k][i]*a->ptr.pp_double[k][j];
                    }
                    f = s/a->ptr.pp_double[i][i]*g;
                    for(k=i; k<=m; k++)
                    {
                        a->ptr.pp_double[k][j] = a->ptr.pp_double[k][j]+f*a->ptr.pp_double[k][i];
                    }
                }
            }
            for(j=i; j<=m; j++)
            {
                a->ptr.pp_double[j][i] = a->ptr.pp_double[j][i]*g;
            }
        }
        else
        {
            for(j=i; j<=m; j++)
            {
                a->ptr.pp_double[j][i] = 0.0;
            }
        }
        a->ptr.pp_double[i][i] = a->ptr.pp_double[i][i]+1.0;
    }
    nm = 0;
    for(k=n; k>=1; k--)
    {
        for(its=1; its<=testmatgenunit_maxsvditerations; its++)
        {
            flag = ae_true;
            for(l=k; l>=1; l--)
            {
                nm = l-1;
                if( ae_fp_eq(ae_fabs(rv1.ptr.p_double[l], _state)+anorm,anorm) )
                {
                    flag = ae_false;
                    break;
                }
                if( ae_fp_eq(ae_fabs(w->ptr.p_double[nm], _state)+anorm,anorm) )
                {
                    break;
                }
            }
            if( flag )
            {
                c = 0.0;
                s = 1.0;
                for(i=l; i<=k; i++)
                {
                    f = s*rv1.ptr.p_double[i];
                    if( ae_fp_neq(ae_fabs(f, _state)+anorm,anorm) )
                    {
                        g = w->ptr.p_double[i];
                        h = testmatgenunit_pythag(f, g, _state);
                        w->ptr.p_double[i] = h;
                        h = 1.0/h;
                        c = g*h;
                        s = -f*h;
                        for(j=1; j<=m; j++)
                        {
                            y = a->ptr.pp_double[j][nm];
                            z = a->ptr.pp_double[j][i];
                            a->ptr.pp_double[j][nm] = y*c+z*s;
                            a->ptr.pp_double[j][i] = -y*s+z*c;
                        }
                    }
                }
            }
            z = w->ptr.p_double[k];
            if( l==k )
            {
                if( ae_fp_less(z,0.0) )
                {
                    w->ptr.p_double[k] = -z;
                    for(j=1; j<=n; j++)
                    {
                        v->ptr.pp_double[j][k] = -v->ptr.pp_double[j][k];
                    }
                }
                break;
            }
            if( its==testmatgenunit_maxsvditerations )
            {
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            x = w->ptr.p_double[l];
            nm = k-1;
            y = w->ptr.p_double[nm];
            g = rv1.ptr.p_double[nm];
            h = rv1.ptr.p_double[k];
            f = ((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
            g = testmatgenunit_pythag(f, (double)(1), _state);
            f = ((x-z)*(x+z)+h*(y/(f+testmatgenunit_extsign(g, f, _state))-h))/x;
            c = 1.0;
            s = 1.0;
            for(j=l; j<=nm; j++)
            {
                i = j+1;
                g = rv1.ptr.p_double[i];
                y = w->ptr.p_double[i];
                h = s*g;
                g = c*g;
                z = testmatgenunit_pythag(f, h, _state);
                rv1.ptr.p_double[j] = z;
                c = f/z;
                s = h/z;
                f = x*c+g*s;
                g = -x*s+g*c;
                h = y*s;
                y = y*c;
                for(jj=1; jj<=n; jj++)
                {
                    x = v->ptr.pp_double[jj][j];
                    z = v->ptr.pp_double[jj][i];
                    v->ptr.pp_double[jj][j] = x*c+z*s;
                    v->ptr.pp_double[jj][i] = -x*s+z*c;
                }
                z = testmatgenunit_pythag(f, h, _state);
                w->ptr.p_double[j] = z;
                if( ae_fp_neq(z,0.0) )
                {
                    z = 1.0/z;
                    c = f*z;
                    s = h*z;
                }
                f = c*g+s*y;
                x = -s*g+c*y;
                for(jj=1; jj<=m; jj++)
                {
                    y = a->ptr.pp_double[jj][j];
                    z = a->ptr.pp_double[jj][i];
                    a->ptr.pp_double[jj][j] = y*c+z*s;
                    a->ptr.pp_double[jj][i] = -y*s+z*c;
                }
            }
            rv1.ptr.p_double[l] = 0.0;
            rv1.ptr.p_double[k] = f;
            w->ptr.p_double[k] = x;
        }
    }
    ae_frame_leave(_state);
    return result;
}


static double testmatgenunit_extsign(double a, double b, ae_state *_state)
{
    double result;


    if( ae_fp_greater_eq(b,(double)(0)) )
    {
        result = ae_fabs(a, _state);
    }
    else
    {
        result = -ae_fabs(a, _state);
    }
    return result;
}


static double testmatgenunit_mymax(double a, double b, ae_state *_state)
{
    double result;


    if( ae_fp_greater(a,b) )
    {
        result = a;
    }
    else
    {
        result = b;
    }
    return result;
}


static double testmatgenunit_pythag(double a, double b, ae_state *_state)
{
    double result;


    if( ae_fp_less(ae_fabs(a, _state),ae_fabs(b, _state)) )
    {
        result = ae_fabs(b, _state)*ae_sqrt(1+ae_sqr(a/b, _state), _state);
    }
    else
    {
        result = ae_fabs(a, _state)*ae_sqrt(1+ae_sqr(b/a, _state), _state);
    }
    return result;
}



static void testtrfacunit_testcluproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* err,
     ae_bool* properr,
     ae_state *_state);
static void testtrfacunit_testrluproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* err,
     ae_bool* properr,
     ae_state *_state);
static void testtrfacunit_testdensecholeskyupdates(ae_bool* spdupderrorflag,
     ae_state *_state);





ae_bool testtrfac(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ra;
    ae_matrix ral;
    ae_matrix rau;
    ae_matrix ca;
    ae_matrix cal;
    ae_matrix cau;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t maxmn;
    ae_int_t largemn;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;
    double vr;
    ae_bool waserrors;
    ae_bool dspderr;
    ae_bool sspderr;
    ae_bool hpderr;
    ae_bool rerr;
    ae_bool cerr;
    ae_bool properr;
    ae_bool dspdupderr;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ra, 0, sizeof(ra));
    memset(&ral, 0, sizeof(ral));
    memset(&rau, 0, sizeof(rau));
    memset(&ca, 0, sizeof(ca));
    memset(&cal, 0, sizeof(cal));
    memset(&cau, 0, sizeof(cau));
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ral, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rau, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cal, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cau, 0, 0, DT_COMPLEX, _state, ae_true);

    rerr = ae_false;
    dspderr = ae_false;
    sspderr = ae_false;
    cerr = ae_false;
    hpderr = ae_false;
    properr = ae_false;
    dspdupderr = ae_false;
    waserrors = ae_false;
    maxmn = 4*matrixtilesizea(_state)+1;
    largemn = 256;
    threshold = 1000*ae_machineepsilon*maxmn;
    
    /*
     * Sparse Cholesky
     */
    sspderr = sparserealcholeskytest(_state);
    
    /*
     * Cholesky updates
     */
    testtrfacunit_testdensecholeskyupdates(&dspdupderr, _state);
    
    /*
     * test LU:
     * * first, test on small-scale matrices
     * * then, perform several large-scale tests
     */
    for(mx=1; mx<=maxmn; mx++)
    {
        
        /*
         * Initialize N/M, both are <=MX,
         * at least one of them is exactly equal to MX
         */
        n = 1+ae_randominteger(mx, _state);
        m = 1+ae_randominteger(mx, _state);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            n = mx;
        }
        else
        {
            m = mx;
        }
        
        /*
         * First, test on zero matrix
         */
        ae_matrix_set_length(&ra, m, n, _state);
        ae_matrix_set_length(&ca, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ra.ptr.pp_double[i][j] = (double)(0);
                ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
        }
        testtrfacunit_testcluproblem(&ca, m, n, threshold, &cerr, &properr, _state);
        testtrfacunit_testrluproblem(&ra, m, n, threshold, &rerr, &properr, _state);
        
        /*
         * Second, random matrix with moderate condition number
         */
        ae_matrix_set_length(&ra, m, n, _state);
        ae_matrix_set_length(&ca, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ra.ptr.pp_double[i][j] = (double)(0);
                ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
        }
        for(i=0; i<=ae_minint(m, n, _state)-1; i++)
        {
            ra.ptr.pp_double[i][i] = 1+10*ae_randomreal(_state);
            ca.ptr.pp_complex[i][i] = ae_complex_from_d(1+10*ae_randomreal(_state));
        }
        cmatrixrndorthogonalfromtheleft(&ca, m, n, _state);
        cmatrixrndorthogonalfromtheright(&ca, m, n, _state);
        rmatrixrndorthogonalfromtheleft(&ra, m, n, _state);
        rmatrixrndorthogonalfromtheright(&ra, m, n, _state);
        testtrfacunit_testcluproblem(&ca, m, n, threshold, &cerr, &properr, _state);
        testtrfacunit_testrluproblem(&ra, m, n, threshold, &rerr, &properr, _state);
    }
    for(m=largemn-1; m<=largemn+1; m++)
    {
        for(n=largemn-1; n<=largemn+1; n++)
        {
            
            /*
             * Random matrix with moderate condition number
             */
            ae_matrix_set_length(&ra, m, n, _state);
            ae_matrix_set_length(&ca, m, n, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ra.ptr.pp_double[i][j] = (double)(0);
                    ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                }
            }
            for(i=0; i<=ae_minint(m, n, _state)-1; i++)
            {
                ra.ptr.pp_double[i][i] = 1+10*ae_randomreal(_state);
                ca.ptr.pp_complex[i][i] = ae_complex_from_d(1+10*ae_randomreal(_state));
            }
            cmatrixrndorthogonalfromtheleft(&ca, m, n, _state);
            cmatrixrndorthogonalfromtheright(&ca, m, n, _state);
            rmatrixrndorthogonalfromtheleft(&ra, m, n, _state);
            rmatrixrndorthogonalfromtheright(&ra, m, n, _state);
            testtrfacunit_testcluproblem(&ca, m, n, threshold, &cerr, &properr, _state);
            testtrfacunit_testrluproblem(&ra, m, n, threshold, &rerr, &properr, _state);
        }
    }
    
    /*
     * Test Cholesky
     */
    for(n=1; n<=maxmn; n++)
    {
        
        /*
         * Load CA (HPD matrix with low condition number),
         *      CAL and CAU - its lower and upper triangles
         */
        hpdmatrixrndcond(n, 1+50*ae_randomreal(_state), &ca, _state);
        ae_matrix_set_length(&cal, n, n, _state);
        ae_matrix_set_length(&cau, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                cal.ptr.pp_complex[i][j] = ae_complex_from_i(i);
                cau.ptr.pp_complex[i][j] = ae_complex_from_i(j);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            ae_v_cmove(&cal.ptr.pp_complex[i][0], 1, &ca.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,i));
            ae_v_cmove(&cau.ptr.pp_complex[i][i], 1, &ca.ptr.pp_complex[i][i], 1, "N", ae_v_len(i,n-1));
        }
        
        /*
         * Test HPDMatrixCholesky:
         * 1. it must leave upper (lower) part unchanged
         * 2. max(A-L*L^H) must be small
         */
        if( hpdmatrixcholesky(&cal, n, ae_false, _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j>i )
                    {
                        hpderr = hpderr||ae_c_neq_d(cal.ptr.pp_complex[i][j],(double)(i));
                    }
                    else
                    {
                        vc = ae_v_cdotproduct(&cal.ptr.pp_complex[i][0], 1, "N", &cal.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,j));
                        hpderr = hpderr||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[i][j],vc), _state),threshold);
                    }
                }
            }
        }
        else
        {
            hpderr = ae_true;
        }
        if( hpdmatrixcholesky(&cau, n, ae_true, _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j<i )
                    {
                        hpderr = hpderr||ae_c_neq_d(cau.ptr.pp_complex[i][j],(double)(j));
                    }
                    else
                    {
                        vc = ae_v_cdotproduct(&cau.ptr.pp_complex[0][i], cau.stride, "Conj", &cau.ptr.pp_complex[0][j], cau.stride, "N", ae_v_len(0,i));
                        hpderr = hpderr||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[i][j],vc), _state),threshold);
                    }
                }
            }
        }
        else
        {
            hpderr = ae_true;
        }
        
        /*
         * Load RA (SPD matrix with low condition number),
         *      RAL and RAU - its lower and upper triangles
         *
         * Test SPDMatrixCholesky:
         * 1. it must leave upper (lower) part unchanged
         * 2. max(A-L*L^H) must be small
         *
         * After testing SPDMatrixCholesky() we compare results
         * returned by SparseCholeskyX() against ones returned
         * by SPDMatrixCholesky().
         */
        spdmatrixrndcond(n, 1+50*ae_randomreal(_state), &ra, _state);
        ae_matrix_set_length(&ral, n, n, _state);
        ae_matrix_set_length(&rau, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ral.ptr.pp_double[i][j] = (double)(i);
                rau.ptr.pp_double[i][j] = (double)(j);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            ae_v_move(&ral.ptr.pp_double[i][0], 1, &ra.ptr.pp_double[i][0], 1, ae_v_len(0,i));
            ae_v_move(&rau.ptr.pp_double[i][i], 1, &ra.ptr.pp_double[i][i], 1, ae_v_len(i,n-1));
        }
        if( spdmatrixcholesky(&ral, n, ae_false, _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j>i )
                    {
                        dspderr = dspderr||ae_fp_neq(ral.ptr.pp_double[i][j],(double)(i));
                    }
                    else
                    {
                        vr = ae_v_dotproduct(&ral.ptr.pp_double[i][0], 1, &ral.ptr.pp_double[j][0], 1, ae_v_len(0,j));
                        dspderr = dspderr||ae_fp_greater(ae_fabs(ra.ptr.pp_double[i][j]-vr, _state),threshold);
                    }
                }
            }
        }
        else
        {
            dspderr = ae_true;
        }
        if( spdmatrixcholesky(&rau, n, ae_true, _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j<i )
                    {
                        dspderr = dspderr||ae_fp_neq(rau.ptr.pp_double[i][j],(double)(j));
                    }
                    else
                    {
                        vr = ae_v_dotproduct(&rau.ptr.pp_double[0][i], rau.stride, &rau.ptr.pp_double[0][j], rau.stride, ae_v_len(0,i));
                        dspderr = dspderr||ae_fp_greater(ae_fabs(ra.ptr.pp_double[i][j]-vr, _state),threshold);
                    }
                }
            }
        }
        else
        {
            dspderr = ae_true;
        }
        
        /*
         * Check algorithms on negative definite matrices -
         * correct error code must be returned.
         */
        ae_matrix_set_length(&ra, n, n, _state);
        ae_matrix_set_length(&ca, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ra.ptr.pp_double[i][j] = 0.0;
                ca.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
            }
            ra.ptr.pp_double[i][i] = 1.0;
            ca.ptr.pp_complex[i][i] = ae_complex_from_d(1.0);
        }
        ra.ptr.pp_double[n/2][n/2] = -1.0;
        ca.ptr.pp_complex[n/2][n/2] = ae_complex_from_d(-1.0);
        ae_set_error_flag(&dspderr, spdmatrixcholesky(&ra, n, ae_fp_greater(ae_randomreal(_state),0.5), _state), __FILE__, __LINE__, "testtrfacunit.ap:499");
        ae_set_error_flag(&hpderr, hpdmatrixcholesky(&ca, n, ae_fp_greater(ae_randomreal(_state),0.5), _state), __FILE__, __LINE__, "testtrfacunit.ap:500");
    }
    
    /*
     * report
     */
    waserrors = (((((rerr||dspderr)||sspderr)||cerr)||hpderr)||properr)||dspdupderr;
    if( !silent )
    {
        printf("TESTING TRIANGULAR FACTORIZATIONS\n");
        printf("* REAL:                                  ");
        if( rerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SPD (dense)                            ");
        if( dspderr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SPD (sparse)                           ");
        if( sspderr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX:                               ");
        if( cerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* HPD:                                   ");
        if( hpderr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* OTHER PROPERTIES:                      ");
        if( properr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TESTING UPDATED FACTORIZATIONS\n");
        printf("* SPD (dense)                            ");
        if( dspdupderr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing sparse real Cholesky.
Returns True on errors, False on success.

  -- ALGLIB PROJECT --
     Copyright 16.01.1014 by Bochkanov Sergey
*************************************************************************/
ae_bool sparserealcholeskytest(ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t nz;
    double pnz;
    ae_matrix a;
    ae_matrix a1;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    ae_int_t t0;
    ae_int_t t1;
    ae_bool isupper;
    double offscale;
    double tol;
    sparsematrix sa;
    sparsematrix sa1;
    sparsematrix sc;
    sparsebuffers sbuf;
    ae_vector p0;
    ae_vector p1;
    ae_vector b1;
    ae_int_t cfmt;
    ae_int_t cord;
    hqrndstate rs;
    ae_int_t maxfmt;
    ae_int_t maxord;
    ae_int_t minord;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&a1, 0, sizeof(a1));
    memset(&sa, 0, sizeof(sa));
    memset(&sa1, 0, sizeof(sa1));
    memset(&sc, 0, sizeof(sc));
    memset(&sbuf, 0, sizeof(sbuf));
    memset(&p0, 0, sizeof(p0));
    memset(&p1, 0, sizeof(p1));
    memset(&b1, 0, sizeof(b1));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    _sparsematrix_init(&sa1, _state, ae_true);
    _sparsematrix_init(&sc, _state, ae_true);
    _sparsebuffers_init(&sbuf, _state, ae_true);
    ae_vector_init(&p0, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&b1, 0, DT_BOOL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    result = ae_false;
    hqrndrandomize(&rs, _state);
    
    /*
     * Settings
     */
    maxfmt = 2;
    maxord = 0;
    minord = -2;
    offscale = 1.0E-3;
    tol = 1.0E-8;
    
    /*
     * SparseCholeskyX test: performed for matrices
     * of all sizes in 1..20 and all sparcity percentages.
     */
    for(n=1; n<=20; n++)
    {
        nz = n*n-n;
        for(;;)
        {
            
            /*
             * Generate symmetric N*N matrix where probability of non-diagonal element
             * being non-zero is PNZ. Off-diagonal elements are set to very
             * small values, so positive definiteness is guaranteed.
             */
            if( n>1 )
            {
                pnz = (double)nz/(double)(n*n-n);
            }
            else
            {
                pnz = 1.0;
            }
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=i; j++)
                {
                    if( i==j )
                    {
                        a.ptr.pp_double[i][i] = 1+hqrnduniformr(&rs, _state);
                        continue;
                    }
                    if( ae_fp_less_eq(hqrnduniformr(&rs, _state),pnz) )
                    {
                        a.ptr.pp_double[i][j] = offscale*(hqrnduniformr(&rs, _state)-0.5);
                        a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                    }
                    else
                    {
                        a.ptr.pp_double[i][j] = 0.0;
                        a.ptr.pp_double[j][i] = 0.0;
                    }
                }
            }
            
            /*
             * Problem statement
             */
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            cfmt = ae_randominteger(maxfmt+1, _state);
            cord = ae_randominteger(maxord+1-minord, _state)+minord;
            
            /*
             * Create matrix is hash-based storage format, convert it to random storage format.
             */
            sparsecreate(n, n, 0, &sa, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j<=i&&!isupper)||(j>=i&&isupper) )
                    {
                        sparseset(&sa, i, j, a.ptr.pp_double[i][j], _state);
                    }
                }
            }
            sparseconvertto(&sa, hqrnduniformi(&rs, maxfmt+1, _state), _state);
            
            /*
             * Perform sparse Cholesky and make several tests:
             * * correctness of P0 and P1 (they are correct permutations and one is inverse of another)
             * * format of SC matches CFmt
             * * SC has correct size (exactly N*N)
             * * check that correct triangle is returned
             */
            if( !sparsecholeskyx(&sa, n, isupper, &p0, &p1, cord, ae_randominteger(3, _state), cfmt, &sbuf, &sc, _state) )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testtrfacunit.ap:669");
                ae_frame_leave(_state);
                return result;
            }
            ae_set_error_flag(&result, p0.cnt<n, __FILE__, __LINE__, "testtrfacunit.ap:672");
            ae_set_error_flag(&result, p1.cnt<n, __FILE__, __LINE__, "testtrfacunit.ap:673");
            if( result )
            {
                ae_frame_leave(_state);
                return result;
            }
            ae_vector_set_length(&b1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b1.ptr.p_bool[i] = ae_false;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(&result, p0.ptr.p_int[i]<0, __FILE__, __LINE__, "testtrfacunit.ap:682");
                ae_set_error_flag(&result, p1.ptr.p_int[i]<0, __FILE__, __LINE__, "testtrfacunit.ap:683");
                ae_set_error_flag(&result, p0.ptr.p_int[i]>=n, __FILE__, __LINE__, "testtrfacunit.ap:684");
                ae_set_error_flag(&result, p1.ptr.p_int[i]>=n, __FILE__, __LINE__, "testtrfacunit.ap:685");
                if( result )
                {
                    ae_frame_leave(_state);
                    return result;
                }
                ae_set_error_flag(&result, b1.ptr.p_bool[p0.ptr.p_int[i]], __FILE__, __LINE__, "testtrfacunit.ap:690");
                b1.ptr.p_bool[p0.ptr.p_int[i]] = ae_true;
                ae_set_error_flag(&result, p1.ptr.p_int[p0.ptr.p_int[i]]!=i, __FILE__, __LINE__, "testtrfacunit.ap:694");
            }
            ae_set_error_flag(&result, sparsegetmatrixtype(&sc, _state)!=cfmt, __FILE__, __LINE__, "testtrfacunit.ap:696");
            ae_set_error_flag(&result, sparsegetncols(&sc, _state)!=n, __FILE__, __LINE__, "testtrfacunit.ap:697");
            ae_set_error_flag(&result, sparsegetnrows(&sc, _state)!=n, __FILE__, __LINE__, "testtrfacunit.ap:698");
            t0 = 0;
            t1 = 0;
            while(sparseenumerate(&sc, &t0, &t1, &i, &j, &v, _state))
            {
                ae_set_error_flag(&result, j<i&&isupper, __FILE__, __LINE__, "testtrfacunit.ap:703");
                ae_set_error_flag(&result, j>i&&!isupper, __FILE__, __LINE__, "testtrfacunit.ap:704");
            }
            
            /*
             * Now, test correctness of Cholesky decomposition itself.
             * We calculate U'*U (or L*L') and check at against permutation
             * of A given by P0.
             *
             * NOTE: we expect that only one triangle of SC is filled,
             *       and another one is exactly zero.
             */
            if( isupper )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(k=0; k<=n-1; k++)
                        {
                            v = v+sparseget(&sc, k, j, _state)*sparseget(&sc, k, i, _state);
                        }
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(a.ptr.pp_double[p0.ptr.p_int[i]][p0.ptr.p_int[j]]-v, _state),tol), __FILE__, __LINE__, "testtrfacunit.ap:723");
                    }
                }
            }
            else
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(k=0; k<=n-1; k++)
                        {
                            v = v+sparseget(&sc, j, k, _state)*sparseget(&sc, i, k, _state);
                        }
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(a.ptr.pp_double[p0.ptr.p_int[i]][p0.ptr.p_int[j]]-v, _state),tol), __FILE__, __LINE__, "testtrfacunit.ap:734");
                    }
                }
            }
            
            /*
             * Increase problem sparcity and try one more time. 
             * Stop after testing NZ=0.
             */
            if( nz==0 )
            {
                break;
            }
            nz = nz/2;
        }
    }
    
    /*
     * SparseCholeskySkyline test: performed for matrices
     * of all sizes in 1..20 and all sparcity percentages.
     */
    for(n=1; n<=20; n++)
    {
        nz = n*n-n;
        for(;;)
        {
            
            /*
             * Choose IsUpper - main triangle to work with.
             *
             * Generate A - symmetric N*N matrix where probability of non-diagonal
             * element being non-zero is PNZ. Off-diagonal elements are set to
             * very small values, so positive definiteness is guaranteed. Full matrix
             * is generated.
             *
             * Additionally, we create A1 - same as A, but one of the triangles is
             * asymmetrically spoiled. If IsUpper is True, we spoil lower one, or vice versa.
             */
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            if( n>1 )
            {
                pnz = (double)nz/(double)(n*n-n);
            }
            else
            {
                pnz = 1.0;
            }
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=i; j++)
                {
                    if( i==j )
                    {
                        a.ptr.pp_double[i][i] = 1+hqrnduniformr(&rs, _state);
                        continue;
                    }
                    if( ae_fp_less_eq(hqrnduniformr(&rs, _state),pnz) )
                    {
                        a.ptr.pp_double[i][j] = offscale*(hqrnduniformr(&rs, _state)-0.5);
                        a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                    }
                    else
                    {
                        a.ptr.pp_double[i][j] = 0.0;
                        a.ptr.pp_double[j][i] = 0.0;
                    }
                }
            }
            ae_matrix_set_length(&a1, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j<=i&&!isupper)||(j>=i&&isupper) )
                    {
                        
                        /*
                         * Copy one triangle
                         */
                        a1.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                    }
                    else
                    {
                        
                        /*
                         * Form another sparse pattern in different triangle.
                         */
                        if( ae_fp_less_eq(hqrnduniformr(&rs, _state),pnz) )
                        {
                            a1.ptr.pp_double[i][j] = offscale*(hqrnduniformr(&rs, _state)-0.5);
                        }
                        else
                        {
                            a1.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                }
            }
            
            /*
             * Create copies of A and A1 in hash-based storage format.
             * Only one triangle of A is copied, but A1 is copied fully.
             * Convert them to SKS
             */
            sparsecreate(n, n, 0, &sa, _state);
            sparsecreate(n, n, 0, &sa1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j<=i&&!isupper)||(j>=i&&isupper) )
                    {
                        sparseset(&sa, i, j, a.ptr.pp_double[i][j], _state);
                    }
                    sparseset(&sa1, i, j, a1.ptr.pp_double[i][j], _state);
                }
            }
            sparseconverttosks(&sa, _state);
            sparseconverttosks(&sa1, _state);
            
            /*
             * Call SparseCholeskySkyline() for SA and make several tests:
             * * check that it is still SKS
             * * check that it has correct size (exactly N*N)
             * * check that correct triangle is returned (and another one is unchanged - zero)
             * * check that it is correct Cholesky decomposition.
             *   We calculate U'*U (or L*L') and check at against A. We expect
             *   that only one triangle of SA is filled, and another one is
             *   exactly zero.
             */
            if( !sparsecholeskyskyline(&sa, n, isupper, _state) )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testtrfacunit.ap:846");
                ae_frame_leave(_state);
                return result;
            }
            ae_set_error_flag(&result, !sparseissks(&sa, _state), __FILE__, __LINE__, "testtrfacunit.ap:849");
            ae_set_error_flag(&result, sparsegetncols(&sa, _state)!=n, __FILE__, __LINE__, "testtrfacunit.ap:850");
            ae_set_error_flag(&result, sparsegetnrows(&sa, _state)!=n, __FILE__, __LINE__, "testtrfacunit.ap:851");
            t0 = 0;
            t1 = 0;
            while(sparseenumerate(&sa, &t0, &t1, &i, &j, &v, _state))
            {
                ae_set_error_flag(&result, j<i&&isupper, __FILE__, __LINE__, "testtrfacunit.ap:856");
                ae_set_error_flag(&result, j>i&&!isupper, __FILE__, __LINE__, "testtrfacunit.ap:857");
            }
            if( isupper )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(k=0; k<=n-1; k++)
                        {
                            v = v+sparseget(&sa, k, j, _state)*sparseget(&sa, k, i, _state);
                        }
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-v, _state),tol), __FILE__, __LINE__, "testtrfacunit.ap:867");
                    }
                }
            }
            else
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(k=0; k<=n-1; k++)
                        {
                            v = v+sparseget(&sa, j, k, _state)*sparseget(&sa, i, k, _state);
                        }
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-v, _state),tol), __FILE__, __LINE__, "testtrfacunit.ap:878");
                    }
                }
            }
            
            /*
             * Call SparseCholeskySkyline() for SA1 and make several tests:
             * * check that it is still SKS
             * * check that it has correct size (exactly N*N)
             * * check that factorized triangle matches contents of SA,
             *   and another triangle was unchanged (matches contents of A1).
             */
            if( !sparsecholeskyskyline(&sa1, n, isupper, _state) )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testtrfacunit.ap:891");
                ae_frame_leave(_state);
                return result;
            }
            ae_set_error_flag(&result, !sparseissks(&sa1, _state), __FILE__, __LINE__, "testtrfacunit.ap:894");
            ae_set_error_flag(&result, sparsegetncols(&sa1, _state)!=n, __FILE__, __LINE__, "testtrfacunit.ap:895");
            ae_set_error_flag(&result, sparsegetnrows(&sa1, _state)!=n, __FILE__, __LINE__, "testtrfacunit.ap:896");
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j<=i&&!isupper)||(j>=i&&isupper) )
                    {
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(sparseget(&sa1, i, j, _state)-sparseget(&sa, i, j, _state), _state),10*ae_machineepsilon), __FILE__, __LINE__, "testtrfacunit.ap:900");
                    }
                    else
                    {
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(sparseget(&sa1, i, j, _state)-a1.ptr.pp_double[i][j], _state),10*ae_machineepsilon), __FILE__, __LINE__, "testtrfacunit.ap:902");
                    }
                }
            }
            
            /*
             * Increase problem sparcity and try one more time. 
             * Stop after testing NZ=0.
             */
            if( nz==0 )
            {
                break;
            }
            nz = nz/2;
        }
    }
    ae_frame_leave(_state);
    return result;
}


static void testtrfacunit_testcluproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* err,
     ae_bool* properr,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ca;
    ae_matrix cl;
    ae_matrix cu;
    ae_matrix ca2;
    ae_vector ct;
    ae_int_t i;
    ae_int_t j;
    ae_int_t minmn;
    ae_complex v;
    ae_vector p;

    ae_frame_make(_state, &_frame_block);
    memset(&ca, 0, sizeof(ca));
    memset(&cl, 0, sizeof(cl));
    memset(&cu, 0, sizeof(cu));
    memset(&ca2, 0, sizeof(ca2));
    memset(&ct, 0, sizeof(ct));
    memset(&p, 0, sizeof(p));
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cl, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cu, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ca2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&ct, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);

    minmn = ae_minint(m, n, _state);
    
    /*
     * PLU test
     */
    ae_matrix_set_length(&ca, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_cmove(&ca.ptr.pp_complex[i][0], 1, &a->ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
    }
    cmatrixplu(&ca, m, n, &p, _state);
    for(i=0; i<=minmn-1; i++)
    {
        if( p.ptr.p_int[i]<i||p.ptr.p_int[i]>=m )
        {
            *properr = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_matrix_set_length(&cl, m, minmn, _state);
    for(j=0; j<=minmn-1; j++)
    {
        for(i=0; i<=j-1; i++)
        {
            cl.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
        }
        cl.ptr.pp_complex[j][j] = ae_complex_from_d(1.0);
        for(i=j+1; i<=m-1; i++)
        {
            cl.ptr.pp_complex[i][j] = ca.ptr.pp_complex[i][j];
        }
    }
    ae_matrix_set_length(&cu, minmn, n, _state);
    for(i=0; i<=minmn-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            cu.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
        }
        for(j=i; j<=n-1; j++)
        {
            cu.ptr.pp_complex[i][j] = ca.ptr.pp_complex[i][j];
        }
    }
    ae_matrix_set_length(&ca2, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&cl.ptr.pp_complex[i][0], 1, "N", &cu.ptr.pp_complex[0][j], cu.stride, "N", ae_v_len(0,minmn-1));
            ca2.ptr.pp_complex[i][j] = v;
        }
    }
    ae_vector_set_length(&ct, n, _state);
    for(i=minmn-1; i>=0; i--)
    {
        if( i!=p.ptr.p_int[i] )
        {
            ae_v_cmove(&ct.ptr.p_complex[0], 1, &ca2.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
            ae_v_cmove(&ca2.ptr.pp_complex[i][0], 1, &ca2.ptr.pp_complex[p.ptr.p_int[i]][0], 1, "N", ae_v_len(0,n-1));
            ae_v_cmove(&ca2.ptr.pp_complex[p.ptr.p_int[i]][0], 1, &ct.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *err = *err||ae_fp_greater(ae_c_abs(ae_c_sub(a->ptr.pp_complex[i][j],ca2.ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    
    /*
     * LUP test
     */
    ae_matrix_set_length(&ca, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_cmove(&ca.ptr.pp_complex[i][0], 1, &a->ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
    }
    cmatrixlup(&ca, m, n, &p, _state);
    for(i=0; i<=minmn-1; i++)
    {
        if( p.ptr.p_int[i]<i||p.ptr.p_int[i]>=n )
        {
            *properr = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_matrix_set_length(&cl, m, minmn, _state);
    for(j=0; j<=minmn-1; j++)
    {
        for(i=0; i<=j-1; i++)
        {
            cl.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
        }
        for(i=j; i<=m-1; i++)
        {
            cl.ptr.pp_complex[i][j] = ca.ptr.pp_complex[i][j];
        }
    }
    ae_matrix_set_length(&cu, minmn, n, _state);
    for(i=0; i<=minmn-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            cu.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
        }
        cu.ptr.pp_complex[i][i] = ae_complex_from_d(1.0);
        for(j=i+1; j<=n-1; j++)
        {
            cu.ptr.pp_complex[i][j] = ca.ptr.pp_complex[i][j];
        }
    }
    ae_matrix_set_length(&ca2, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&cl.ptr.pp_complex[i][0], 1, "N", &cu.ptr.pp_complex[0][j], cu.stride, "N", ae_v_len(0,minmn-1));
            ca2.ptr.pp_complex[i][j] = v;
        }
    }
    ae_vector_set_length(&ct, m, _state);
    for(i=minmn-1; i>=0; i--)
    {
        if( i!=p.ptr.p_int[i] )
        {
            ae_v_cmove(&ct.ptr.p_complex[0], 1, &ca2.ptr.pp_complex[0][i], ca2.stride, "N", ae_v_len(0,m-1));
            ae_v_cmove(&ca2.ptr.pp_complex[0][i], ca2.stride, &ca2.ptr.pp_complex[0][p.ptr.p_int[i]], ca2.stride, "N", ae_v_len(0,m-1));
            ae_v_cmove(&ca2.ptr.pp_complex[0][p.ptr.p_int[i]], ca2.stride, &ct.ptr.p_complex[0], 1, "N", ae_v_len(0,m-1));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *err = *err||ae_fp_greater(ae_c_abs(ae_c_sub(a->ptr.pp_complex[i][j],ca2.ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


static void testtrfacunit_testrluproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* err,
     ae_bool* properr,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ca;
    ae_matrix cl;
    ae_matrix cu;
    ae_matrix ca2;
    ae_vector ct;
    ae_int_t i;
    ae_int_t j;
    ae_int_t minmn;
    double v;
    ae_vector p;

    ae_frame_make(_state, &_frame_block);
    memset(&ca, 0, sizeof(ca));
    memset(&cl, 0, sizeof(cl));
    memset(&cu, 0, sizeof(cu));
    memset(&ca2, 0, sizeof(ca2));
    memset(&ct, 0, sizeof(ct));
    memset(&p, 0, sizeof(p));
    ae_matrix_init(&ca, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cl, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cu, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);

    minmn = ae_minint(m, n, _state);
    
    /*
     * PLU test
     */
    ae_matrix_set_length(&ca, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_move(&ca.ptr.pp_double[i][0], 1, &a->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
    }
    rmatrixplu(&ca, m, n, &p, _state);
    for(i=0; i<=minmn-1; i++)
    {
        if( p.ptr.p_int[i]<i||p.ptr.p_int[i]>=m )
        {
            *properr = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_matrix_set_length(&cl, m, minmn, _state);
    for(j=0; j<=minmn-1; j++)
    {
        for(i=0; i<=j-1; i++)
        {
            cl.ptr.pp_double[i][j] = 0.0;
        }
        cl.ptr.pp_double[j][j] = 1.0;
        for(i=j+1; i<=m-1; i++)
        {
            cl.ptr.pp_double[i][j] = ca.ptr.pp_double[i][j];
        }
    }
    ae_matrix_set_length(&cu, minmn, n, _state);
    for(i=0; i<=minmn-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            cu.ptr.pp_double[i][j] = 0.0;
        }
        for(j=i; j<=n-1; j++)
        {
            cu.ptr.pp_double[i][j] = ca.ptr.pp_double[i][j];
        }
    }
    ae_matrix_set_length(&ca2, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&cl.ptr.pp_double[i][0], 1, &cu.ptr.pp_double[0][j], cu.stride, ae_v_len(0,minmn-1));
            ca2.ptr.pp_double[i][j] = v;
        }
    }
    ae_vector_set_length(&ct, n, _state);
    for(i=minmn-1; i>=0; i--)
    {
        if( i!=p.ptr.p_int[i] )
        {
            ae_v_move(&ct.ptr.p_double[0], 1, &ca2.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            ae_v_move(&ca2.ptr.pp_double[i][0], 1, &ca2.ptr.pp_double[p.ptr.p_int[i]][0], 1, ae_v_len(0,n-1));
            ae_v_move(&ca2.ptr.pp_double[p.ptr.p_int[i]][0], 1, &ct.ptr.p_double[0], 1, ae_v_len(0,n-1));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *err = *err||ae_fp_greater(ae_fabs(a->ptr.pp_double[i][j]-ca2.ptr.pp_double[i][j], _state),threshold);
        }
    }
    
    /*
     * LUP test
     */
    ae_matrix_set_length(&ca, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_move(&ca.ptr.pp_double[i][0], 1, &a->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
    }
    rmatrixlup(&ca, m, n, &p, _state);
    for(i=0; i<=minmn-1; i++)
    {
        if( p.ptr.p_int[i]<i||p.ptr.p_int[i]>=n )
        {
            *properr = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_matrix_set_length(&cl, m, minmn, _state);
    for(j=0; j<=minmn-1; j++)
    {
        for(i=0; i<=j-1; i++)
        {
            cl.ptr.pp_double[i][j] = 0.0;
        }
        for(i=j; i<=m-1; i++)
        {
            cl.ptr.pp_double[i][j] = ca.ptr.pp_double[i][j];
        }
    }
    ae_matrix_set_length(&cu, minmn, n, _state);
    for(i=0; i<=minmn-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            cu.ptr.pp_double[i][j] = 0.0;
        }
        cu.ptr.pp_double[i][i] = 1.0;
        for(j=i+1; j<=n-1; j++)
        {
            cu.ptr.pp_double[i][j] = ca.ptr.pp_double[i][j];
        }
    }
    ae_matrix_set_length(&ca2, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&cl.ptr.pp_double[i][0], 1, &cu.ptr.pp_double[0][j], cu.stride, ae_v_len(0,minmn-1));
            ca2.ptr.pp_double[i][j] = v;
        }
    }
    ae_vector_set_length(&ct, m, _state);
    for(i=minmn-1; i>=0; i--)
    {
        if( i!=p.ptr.p_int[i] )
        {
            ae_v_move(&ct.ptr.p_double[0], 1, &ca2.ptr.pp_double[0][i], ca2.stride, ae_v_len(0,m-1));
            ae_v_move(&ca2.ptr.pp_double[0][i], ca2.stride, &ca2.ptr.pp_double[0][p.ptr.p_int[i]], ca2.stride, ae_v_len(0,m-1));
            ae_v_move(&ca2.ptr.pp_double[0][p.ptr.p_int[i]], ca2.stride, &ct.ptr.p_double[0], 1, ae_v_len(0,m-1));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *err = *err||ae_fp_greater(ae_fabs(a->ptr.pp_double[i][j]-ca2.ptr.pp_double[i][j], _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Function for testing dense Cholesky updates
Sets error flag to True on errors, does not change it on success.

  -- ALGLIB PROJECT --
     Copyright 16.01.1014 by Bochkanov Sergey
*************************************************************************/
static void testtrfacunit_testdensecholeskyupdates(ae_bool* spdupderrorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    double pfix;
    ae_matrix a0;
    ae_matrix a1;
    ae_vector u;
    ae_vector fix;
    ae_int_t i;
    ae_int_t j;
    ae_bool isupper;
    double tol;
    ae_vector bufr;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&a0, 0, sizeof(a0));
    memset(&a1, 0, sizeof(a1));
    memset(&u, 0, sizeof(u));
    memset(&fix, 0, sizeof(fix));
    memset(&bufr, 0, sizeof(bufr));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&a0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&u, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&fix, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&bufr, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Settings
     */
    tol = 1.0E-8;
    
    /*
     * Test rank-1 updates
     *
     * For each matrix size in 1..30 select sparse update vector with probability of element
     * being non-zero equal to 1/2.
     */
    for(n=1; n<=30; n++)
    {
        
        /*
         * Generate two matrices A0=A1, fill one triangle with SPD matrix,
         * another one with trash. Prepare vector U.
         */
        isupper = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
        spdmatrixrndcond(n, 1.0E4, &a0, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( (j<i&&isupper)||(j>i&&!isupper) )
                {
                    a0.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                }
            }
        }
        ae_matrix_set_length(&a1, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a1.ptr.pp_double[i][j] = a0.ptr.pp_double[i][j];
            }
        }
        ae_vector_set_length(&u, n, _state);
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_less_eq(hqrnduniformr(&rs, _state),0.5) )
            {
                u.ptr.p_double[i] = hqrnduniformr(&rs, _state)-0.5;
            }
            else
            {
                u.ptr.p_double[i] = (double)(0);
            }
        }
        
        /*
         * Factorize and compare:
         * * A0 is factorized as follows: first with full Cholesky, then
         *   we call SPDMatrixCholeskyUpdateAdd1
         * * A1 is transformed explicitly before factorization with full Cholesky
         *
         * We randomly test either SPDMatrixCholeskyUpdateFix() or its
         * buffered version, SPDMatrixCholeskyUpdateFixBuf()
         */
        ae_set_error_flag(spdupderrorflag, !spdmatrixcholesky(&a0, n, isupper, _state), __FILE__, __LINE__, "testtrfacunit.ap:985");
        if( *spdupderrorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        if( ae_fp_less(hqrnduniformr(&rs, _state),0.5) )
        {
            spdmatrixcholeskyupdateadd1(&a0, n, isupper, &u, _state);
        }
        else
        {
            spdmatrixcholeskyupdateadd1buf(&a0, n, isupper, &u, &bufr, _state);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( (j>=i&&isupper)||(j<=i&&!isupper) )
                {
                    a1.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j]+u.ptr.p_double[i]*u.ptr.p_double[j];
                }
            }
        }
        ae_set_error_flag(spdupderrorflag, !spdmatrixcholesky(&a1, n, isupper, _state), __FILE__, __LINE__, "testtrfacunit.ap:996");
        if( *spdupderrorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ae_set_error_flag(spdupderrorflag, ae_fp_greater(ae_fabs(a0.ptr.pp_double[i][j]-a1.ptr.pp_double[i][j], _state),tol), __FILE__, __LINE__, "testtrfacunit.ap:1001");
            }
        }
    }
    
    /*
     * Test variable fixing functions.
     *
     * For each matrix size in 1..30 select PFix - probability of each variable being fixed,
     * and perform test.
     */
    for(n=1; n<=30; n++)
    {
        
        /*
         * Generate two matrices A0=A1, fill one triangle with SPD matrix,
         * another one with trash. Prepare vector Fix.
         */
        pfix = (double)hqrnduniformi(&rs, n+1, _state)/(double)n;
        isupper = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
        spdmatrixrndcond(n, 1.0E4, &a0, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( (j<i&&isupper)||(j>i&&!isupper) )
                {
                    a0.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                }
            }
        }
        ae_matrix_set_length(&a1, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a1.ptr.pp_double[i][j] = a0.ptr.pp_double[i][j];
            }
        }
        ae_vector_set_length(&fix, n, _state);
        for(i=0; i<=n-1; i++)
        {
            fix.ptr.p_bool[i] = ae_fp_less_eq(hqrnduniformr(&rs, _state),pfix);
        }
        
        /*
         * Factorize and compare:
         * * A0 is factorized as follows: first with full Cholesky, then
         *   variables are fixed with SPDMatrixCholeskyUpdateFix
         * * A1 is fixed explicitly before factorization with full Cholesky
         *
         * We randomly test either SPDMatrixCholeskyUpdateFix() or its
         * buffered version, SPDMatrixCholeskyUpdateFixBuf()
         */
        ae_set_error_flag(spdupderrorflag, !spdmatrixcholesky(&a0, n, isupper, _state), __FILE__, __LINE__, "testtrfacunit.ap:1040");
        if( *spdupderrorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        if( ae_fp_less(hqrnduniformr(&rs, _state),0.5) )
        {
            spdmatrixcholeskyupdatefixbuf(&a0, n, isupper, &fix, &bufr, _state);
        }
        else
        {
            spdmatrixcholeskyupdatefix(&a0, n, isupper, &fix, _state);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( (j>=i&&isupper)||(j<=i&&!isupper) )
                {
                    if( fix.ptr.p_bool[i]||fix.ptr.p_bool[j] )
                    {
                        if( i==j )
                        {
                            a1.ptr.pp_double[i][j] = (double)(1);
                        }
                        else
                        {
                            a1.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                }
            }
        }
        ae_set_error_flag(spdupderrorflag, !spdmatrixcholesky(&a1, n, isupper, _state), __FILE__, __LINE__, "testtrfacunit.ap:1057");
        if( *spdupderrorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ae_set_error_flag(spdupderrorflag, ae_fp_greater(ae_fabs(a0.ptr.pp_double[i][j]-a1.ptr.pp_double[i][j], _state),tol), __FILE__, __LINE__, "testtrfacunit.ap:1062");
            }
        }
    }
    ae_frame_leave(_state);
}








/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testtrlinsolve(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxmn;
    ae_int_t passcount;
    double threshold;
    ae_matrix aeffective;
    ae_matrix aparam;
    ae_vector xe;
    ae_vector b;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t cnts;
    ae_int_t cntu;
    ae_int_t cntt;
    ae_int_t cntm;
    ae_bool waserrors;
    ae_bool isupper;
    ae_bool istrans;
    ae_bool isunit;
    double v;
    double s;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&aeffective, 0, sizeof(aeffective));
    memset(&aparam, 0, sizeof(aparam));
    memset(&xe, 0, sizeof(xe));
    memset(&b, 0, sizeof(b));
    ae_matrix_init(&aeffective, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&aparam, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    maxmn = 15;
    passcount = 15;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * Different problems
     */
    for(n=1; n<=maxmn; n++)
    {
        ae_matrix_set_length(&aeffective, n-1+1, n-1+1, _state);
        ae_matrix_set_length(&aparam, n-1+1, n-1+1, _state);
        ae_vector_set_length(&xe, n-1+1, _state);
        ae_vector_set_length(&b, n-1+1, _state);
        for(pass=1; pass<=passcount; pass++)
        {
            for(cnts=0; cnts<=1; cnts++)
            {
                for(cntu=0; cntu<=1; cntu++)
                {
                    for(cntt=0; cntt<=1; cntt++)
                    {
                        for(cntm=0; cntm<=2; cntm++)
                        {
                            isupper = cnts==0;
                            isunit = cntu==0;
                            istrans = cntt==0;
                            
                            /*
                             * Skip meaningless combinations of parameters:
                             * (matrix is singular) AND (matrix is unit diagonal)
                             */
                            if( cntm==2&&isunit )
                            {
                                continue;
                            }
                            
                            /*
                             * Clear matrices
                             */
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    aeffective.ptr.pp_double[i][j] = (double)(0);
                                    aparam.ptr.pp_double[i][j] = (double)(0);
                                }
                            }
                            
                            /*
                             * Prepare matrices
                             */
                            if( isupper )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i; j<=n-1; j++)
                                    {
                                        aeffective.ptr.pp_double[i][j] = 0.9*(2*ae_randomreal(_state)-1);
                                        aparam.ptr.pp_double[i][j] = aeffective.ptr.pp_double[i][j];
                                    }
                                    aeffective.ptr.pp_double[i][i] = (2*ae_randominteger(2, _state)-1)*(0.8+ae_randomreal(_state));
                                    aparam.ptr.pp_double[i][i] = aeffective.ptr.pp_double[i][i];
                                }
                            }
                            else
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=i; j++)
                                    {
                                        aeffective.ptr.pp_double[i][j] = 0.9*(2*ae_randomreal(_state)-1);
                                        aparam.ptr.pp_double[i][j] = aeffective.ptr.pp_double[i][j];
                                    }
                                    aeffective.ptr.pp_double[i][i] = (2*ae_randominteger(2, _state)-1)*(0.8+ae_randomreal(_state));
                                    aparam.ptr.pp_double[i][i] = aeffective.ptr.pp_double[i][i];
                                }
                            }
                            if( isunit )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    aeffective.ptr.pp_double[i][i] = (double)(1);
                                    aparam.ptr.pp_double[i][i] = (double)(0);
                                }
                            }
                            if( istrans )
                            {
                                if( isupper )
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=i+1; j<=n-1; j++)
                                        {
                                            aeffective.ptr.pp_double[j][i] = aeffective.ptr.pp_double[i][j];
                                            aeffective.ptr.pp_double[i][j] = (double)(0);
                                        }
                                    }
                                }
                                else
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=i+1; j<=n-1; j++)
                                        {
                                            aeffective.ptr.pp_double[i][j] = aeffective.ptr.pp_double[j][i];
                                            aeffective.ptr.pp_double[j][i] = (double)(0);
                                        }
                                    }
                                }
                            }
                            
                            /*
                             * Prepare task, solve, compare
                             */
                            for(i=0; i<=n-1; i++)
                            {
                                xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                v = ae_v_dotproduct(&aeffective.ptr.pp_double[i][0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
                                b.ptr.p_double[i] = v;
                            }
                            rmatrixtrsafesolve(&aparam, n, &b, &s, isupper, istrans, isunit, _state);
                            ae_v_muld(&xe.ptr.p_double[0], 1, ae_v_len(0,n-1), s);
                            ae_v_sub(&xe.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            v = ae_v_dotproduct(&xe.ptr.p_double[0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            v = ae_sqrt(v, _state);
                            waserrors = waserrors||ae_fp_greater(v,threshold);
                        }
                    }
                }
            }
        }
    }
    
    /*
     * report
     */
    if( !silent )
    {
        printf("TESTING RMatrixTRSafeSolve\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testsafesolveunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testsafesolveunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);





/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testsafesolve(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxmn;
    double threshold;
    ae_bool rerrors;
    ae_bool cerrors;
    ae_bool waserrors;
    ae_bool isupper;
    ae_int_t trans;
    ae_bool isunit;
    double scalea;
    double growth;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t j1;
    ae_int_t j2;
    ae_complex cv;
    ae_matrix ca;
    ae_matrix cea;
    ae_matrix ctmpa;
    ae_vector cxs;
    ae_vector cxe;
    double rv;
    ae_matrix ra;
    ae_matrix rea;
    ae_matrix rtmpa;
    ae_vector rxs;
    ae_vector rxe;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ca, 0, sizeof(ca));
    memset(&cea, 0, sizeof(cea));
    memset(&ctmpa, 0, sizeof(ctmpa));
    memset(&cxs, 0, sizeof(cxs));
    memset(&cxe, 0, sizeof(cxe));
    memset(&ra, 0, sizeof(ra));
    memset(&rea, 0, sizeof(rea));
    memset(&rtmpa, 0, sizeof(rtmpa));
    memset(&rxs, 0, sizeof(rxs));
    memset(&rxe, 0, sizeof(rxe));
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cea, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ctmpa, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cxs, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cxe, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rea, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rtmpa, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rxs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rxe, 0, DT_REAL, _state, ae_true);

    maxmn = 30;
    threshold = 100000*ae_machineepsilon;
    rerrors = ae_false;
    cerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Different problems: general tests
     */
    for(n=1; n<=maxmn; n++)
    {
        
        /*
         * test complex solver with well-conditioned matrix:
         * 1. generate A: fill off-diagonal elements with small values,
         *    diagonal elements are filled with larger values
         * 2. generate 'effective' A
         * 3. prepare task (exact X is stored in CXE, right part - in CXS),
         *    solve and compare CXS and CXE
         */
        isupper = ae_fp_greater(ae_randomreal(_state),0.5);
        trans = ae_randominteger(3, _state);
        isunit = ae_fp_greater(ae_randomreal(_state),0.5);
        scalea = ae_randomreal(_state)+0.5;
        ae_matrix_set_length(&ca, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    ca.ptr.pp_complex[i][j].x = (2*ae_randominteger(2, _state)-1)*(5+ae_randomreal(_state));
                    ca.ptr.pp_complex[i][j].y = (2*ae_randominteger(2, _state)-1)*(5+ae_randomreal(_state));
                }
                else
                {
                    ca.ptr.pp_complex[i][j].x = 0.2*ae_randomreal(_state)-0.1;
                    ca.ptr.pp_complex[i][j].y = 0.2*ae_randomreal(_state)-0.1;
                }
            }
        }
        testsafesolveunit_cmatrixmakeacopy(&ca, n, n, &ctmpa, _state);
        for(i=0; i<=n-1; i++)
        {
            if( isupper )
            {
                j1 = 0;
                j2 = i-1;
            }
            else
            {
                j1 = i+1;
                j2 = n-1;
            }
            for(j=j1; j<=j2; j++)
            {
                ctmpa.ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
            if( isunit )
            {
                ctmpa.ptr.pp_complex[i][i] = ae_complex_from_i(1);
            }
        }
        ae_matrix_set_length(&cea, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            if( trans==0 )
            {
                ae_v_cmoved(&cea.ptr.pp_complex[i][0], 1, &ctmpa.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1), scalea);
            }
            if( trans==1 )
            {
                ae_v_cmoved(&cea.ptr.pp_complex[0][i], cea.stride, &ctmpa.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1), scalea);
            }
            if( trans==2 )
            {
                ae_v_cmoved(&cea.ptr.pp_complex[0][i], cea.stride, &ctmpa.ptr.pp_complex[i][0], 1, "Conj", ae_v_len(0,n-1), scalea);
            }
        }
        ae_vector_set_length(&cxe, n, _state);
        for(i=0; i<=n-1; i++)
        {
            cxe.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            cxe.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        }
        ae_vector_set_length(&cxs, n, _state);
        for(i=0; i<=n-1; i++)
        {
            cv = ae_v_cdotproduct(&cea.ptr.pp_complex[i][0], 1, "N", &cxe.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
            cxs.ptr.p_complex[i] = cv;
        }
        if( cmatrixscaledtrsafesolve(&ca, scalea, n, &cxs, isupper, trans, isunit, ae_sqrt(ae_maxrealnumber, _state), _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                cerrors = cerrors||ae_fp_greater(ae_c_abs(ae_c_sub(cxs.ptr.p_complex[i],cxe.ptr.p_complex[i]), _state),threshold);
            }
        }
        else
        {
            cerrors = ae_true;
        }
        
        /*
         * same with real
         */
        isupper = ae_fp_greater(ae_randomreal(_state),0.5);
        trans = ae_randominteger(2, _state);
        isunit = ae_fp_greater(ae_randomreal(_state),0.5);
        scalea = ae_randomreal(_state)+0.5;
        ae_matrix_set_length(&ra, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    ra.ptr.pp_double[i][j] = (2*ae_randominteger(2, _state)-1)*(5+ae_randomreal(_state));
                }
                else
                {
                    ra.ptr.pp_double[i][j] = 0.2*ae_randomreal(_state)-0.1;
                }
            }
        }
        testsafesolveunit_rmatrixmakeacopy(&ra, n, n, &rtmpa, _state);
        for(i=0; i<=n-1; i++)
        {
            if( isupper )
            {
                j1 = 0;
                j2 = i-1;
            }
            else
            {
                j1 = i+1;
                j2 = n-1;
            }
            for(j=j1; j<=j2; j++)
            {
                rtmpa.ptr.pp_double[i][j] = (double)(0);
            }
            if( isunit )
            {
                rtmpa.ptr.pp_double[i][i] = (double)(1);
            }
        }
        ae_matrix_set_length(&rea, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            if( trans==0 )
            {
                ae_v_moved(&rea.ptr.pp_double[i][0], 1, &rtmpa.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), scalea);
            }
            if( trans==1 )
            {
                ae_v_moved(&rea.ptr.pp_double[0][i], rea.stride, &rtmpa.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), scalea);
            }
        }
        ae_vector_set_length(&rxe, n, _state);
        for(i=0; i<=n-1; i++)
        {
            rxe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        ae_vector_set_length(&rxs, n, _state);
        for(i=0; i<=n-1; i++)
        {
            rv = ae_v_dotproduct(&rea.ptr.pp_double[i][0], 1, &rxe.ptr.p_double[0], 1, ae_v_len(0,n-1));
            rxs.ptr.p_double[i] = rv;
        }
        if( rmatrixscaledtrsafesolve(&ra, scalea, n, &rxs, isupper, trans, isunit, ae_sqrt(ae_maxrealnumber, _state), _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                rerrors = rerrors||ae_fp_greater(ae_fabs(rxs.ptr.p_double[i]-rxe.ptr.p_double[i], _state),threshold);
            }
        }
        else
        {
            rerrors = ae_true;
        }
    }
    
    /*
     * Special test with diagonal ill-conditioned matrix:
     * * ability to solve it when resulting growth is less than threshold
     * * ability to stop solve when resulting growth is greater than threshold
     *
     * A = diag(1, 1/growth)
     * b = (1, 0.5)
     */
    n = 2;
    growth = (double)(10);
    ae_matrix_set_length(&ca, n, n, _state);
    ca.ptr.pp_complex[0][0] = ae_complex_from_i(1);
    ca.ptr.pp_complex[0][1] = ae_complex_from_i(0);
    ca.ptr.pp_complex[1][0] = ae_complex_from_i(0);
    ca.ptr.pp_complex[1][1] = ae_complex_from_d(1/growth);
    ae_vector_set_length(&cxs, n, _state);
    cxs.ptr.p_complex[0] = ae_complex_from_d(1.0);
    cxs.ptr.p_complex[1] = ae_complex_from_d(0.5);
    cerrors = cerrors||!cmatrixscaledtrsafesolve(&ca, 1.0, n, &cxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(3, _state), ae_false, 1.05*ae_maxreal(ae_c_abs(cxs.ptr.p_complex[1], _state)*growth, 1.0, _state), _state);
    cerrors = cerrors||!cmatrixscaledtrsafesolve(&ca, 1.0, n, &cxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(3, _state), ae_false, 0.95*ae_maxreal(ae_c_abs(cxs.ptr.p_complex[1], _state)*growth, 1.0, _state), _state);
    ae_matrix_set_length(&ra, n, n, _state);
    ra.ptr.pp_double[0][0] = (double)(1);
    ra.ptr.pp_double[0][1] = (double)(0);
    ra.ptr.pp_double[1][0] = (double)(0);
    ra.ptr.pp_double[1][1] = 1/growth;
    ae_vector_set_length(&rxs, n, _state);
    rxs.ptr.p_double[0] = 1.0;
    rxs.ptr.p_double[1] = 0.5;
    rerrors = rerrors||!rmatrixscaledtrsafesolve(&ra, 1.0, n, &rxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(2, _state), ae_false, 1.05*ae_maxreal(ae_fabs(rxs.ptr.p_double[1], _state)*growth, 1.0, _state), _state);
    rerrors = rerrors||!rmatrixscaledtrsafesolve(&ra, 1.0, n, &rxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(2, _state), ae_false, 0.95*ae_maxreal(ae_fabs(rxs.ptr.p_double[1], _state)*growth, 1.0, _state), _state);
    
    /*
     * Special test with diagonal degenerate matrix:
     * * ability to solve it when resulting growth is less than threshold
     * * ability to stop solve when resulting growth is greater than threshold
     *
     * A = diag(1, 0)
     * b = (1, 0.5)
     */
    n = 2;
    ae_matrix_set_length(&ca, n, n, _state);
    ca.ptr.pp_complex[0][0] = ae_complex_from_i(1);
    ca.ptr.pp_complex[0][1] = ae_complex_from_i(0);
    ca.ptr.pp_complex[1][0] = ae_complex_from_i(0);
    ca.ptr.pp_complex[1][1] = ae_complex_from_i(0);
    ae_vector_set_length(&cxs, n, _state);
    cxs.ptr.p_complex[0] = ae_complex_from_d(1.0);
    cxs.ptr.p_complex[1] = ae_complex_from_d(0.5);
    cerrors = cerrors||cmatrixscaledtrsafesolve(&ca, 1.0, n, &cxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(3, _state), ae_false, ae_sqrt(ae_maxrealnumber, _state), _state);
    ae_matrix_set_length(&ra, n, n, _state);
    ra.ptr.pp_double[0][0] = (double)(1);
    ra.ptr.pp_double[0][1] = (double)(0);
    ra.ptr.pp_double[1][0] = (double)(0);
    ra.ptr.pp_double[1][1] = (double)(0);
    ae_vector_set_length(&rxs, n, _state);
    rxs.ptr.p_double[0] = 1.0;
    rxs.ptr.p_double[1] = 0.5;
    rerrors = rerrors||rmatrixscaledtrsafesolve(&ra, 1.0, n, &rxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(2, _state), ae_false, ae_sqrt(ae_maxrealnumber, _state), _state);
    
    /*
     * report
     */
    waserrors = rerrors||cerrors;
    if( !silent )
    {
        printf("TESTING SAFE TR SOLVER\n");
        printf("REAL:                                    ");
        if( !rerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("COMPLEX:                                 ");
        if( !cerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testsafesolveunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Copy
*************************************************************************/
static void testsafesolveunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}



static double testrcondunit_threshold50 = 0.25;
static double testrcondunit_threshold90 = 0.10;
static void testrcondunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testrcondunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testrcondunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testrcondunit_rmatrixgenzero(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state);
static ae_bool testrcondunit_rmatrixinvmattr(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static ae_bool testrcondunit_rmatrixinvmatlu(/* Real    */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state);
static ae_bool testrcondunit_rmatrixinvmat(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static void testrcondunit_rmatrixrefrcond(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double* rc1,
     double* rcinf,
     ae_state *_state);
static void testrcondunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);
static void testrcondunit_cmatrixgenzero(/* Complex */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state);
static ae_bool testrcondunit_cmatrixinvmattr(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static ae_bool testrcondunit_cmatrixinvmatlu(/* Complex */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state);
static ae_bool testrcondunit_cmatrixinvmat(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static void testrcondunit_cmatrixrefrcond(/* Complex */ ae_matrix* a,
     ae_int_t n,
     double* rc1,
     double* rcinf,
     ae_state *_state);
static ae_bool testrcondunit_testrmatrixtrrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testcmatrixtrrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testrmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testspdmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testcmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testhpdmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);





ae_bool testrcond(ae_bool silent, ae_state *_state)
{
    ae_int_t maxn;
    ae_int_t passcount;
    ae_bool waserrors;
    ae_bool rtrerr;
    ae_bool ctrerr;
    ae_bool rerr;
    ae_bool cerr;
    ae_bool spderr;
    ae_bool hpderr;
    ae_bool result;


    maxn = 10;
    passcount = 100;
    
    /*
     * report
     */
    rtrerr = !testrcondunit_testrmatrixtrrcond(maxn, passcount, _state);
    ctrerr = !testrcondunit_testcmatrixtrrcond(maxn, passcount, _state);
    rerr = !testrcondunit_testrmatrixrcond(maxn, passcount, _state);
    cerr = !testrcondunit_testcmatrixrcond(maxn, passcount, _state);
    spderr = !testrcondunit_testspdmatrixrcond(maxn, passcount, _state);
    hpderr = !testrcondunit_testhpdmatrixrcond(maxn, passcount, _state);
    waserrors = ((((rtrerr||ctrerr)||rerr)||cerr)||spderr)||hpderr;
    if( !silent )
    {
        printf("TESTING RCOND\n");
        printf("REAL TRIANGULAR:                         ");
        if( !rtrerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("COMPLEX TRIANGULAR:                      ");
        if( !ctrerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("REAL:                                    ");
        if( !rerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SPD:                                     ");
        if( !spderr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("HPD:                                     ");
        if( !hpderr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("COMPLEX:                                 ");
        if( !cerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testrcondunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testrcondunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_double[i][j] = (double)(1+2*i+3*j);
            }
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testrcondunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_i(1+2*i+3*j);
            }
        }
    }
}


/*************************************************************************
Generate matrix with given condition number C (2-norm)
*************************************************************************/
static void testrcondunit_rmatrixgenzero(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    ae_matrix_set_length(a0, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a0->ptr.pp_double[i][j] = (double)(0);
        }
    }
}


/*************************************************************************
triangular inverse
*************************************************************************/
static ae_bool testrcondunit_rmatrixinvmattr(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    double v;
    double ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&t, 0, sizeof(t));
    ae_vector_init(&t, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = (double)(-1);
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_move(&t.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i<j-1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][i+1], 1, &t.ptr.p_double[i+1], 1, ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = (double)(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = (double)(-1);
            }
            if( j<n-1 )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_move(&t.ptr.p_double[j+1], 1, &a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &t.ptr.p_double[j+1], 1, ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = (double)(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
LU inverse
*************************************************************************/
static ae_bool testrcondunit_rmatrixinvmatlu(/* Real    */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector work;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jp;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&work, 0, sizeof(work));
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    
    /*
     * Quick return if possible
     */
    if( n==0 )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_vector_set_length(&work, n-1+1, _state);
    
    /*
     * Form inv(U)
     */
    if( !testrcondunit_rmatrixinvmattr(a, n, ae_true, ae_false, _state) )
    {
        result = ae_false;
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Solve the equation inv(A)*L = inv(U) for inv(A).
     */
    for(j=n-1; j>=0; j--)
    {
        
        /*
         * Copy current column of L to WORK and replace with zeros.
         */
        for(i=j+1; i<=n-1; i++)
        {
            work.ptr.p_double[i] = a->ptr.pp_double[i][j];
            a->ptr.pp_double[i][j] = (double)(0);
        }
        
        /*
         * Compute current column of inv(A).
         */
        if( j<n-1 )
        {
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &work.ptr.p_double[j+1], 1, ae_v_len(j+1,n-1));
                a->ptr.pp_double[i][j] = a->ptr.pp_double[i][j]-v;
            }
        }
    }
    
    /*
     * Apply column interchanges.
     */
    for(j=n-2; j>=0; j--)
    {
        jp = pivots->ptr.p_int[j];
        if( jp!=j )
        {
            ae_v_move(&work.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
            ae_v_move(&a->ptr.pp_double[0][j], a->stride, &a->ptr.pp_double[0][jp], a->stride, ae_v_len(0,n-1));
            ae_v_move(&a->ptr.pp_double[0][jp], a->stride, &work.ptr.p_double[0], 1, ae_v_len(0,n-1));
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Matrix inverse
*************************************************************************/
static ae_bool testrcondunit_rmatrixinvmat(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector pivots;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&pivots, 0, sizeof(pivots));
    ae_vector_init(&pivots, 0, DT_INT, _state, ae_true);

    rmatrixlu(a, n, n, &pivots, _state);
    result = testrcondunit_rmatrixinvmatlu(a, &pivots, n, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
reference RCond
*************************************************************************/
static void testrcondunit_rmatrixrefrcond(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double* rc1,
     double* rcinf,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix inva;
    double nrm1a;
    double nrminfa;
    double nrm1inva;
    double nrminfinva;
    double v;
    ae_int_t k;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&inva, 0, sizeof(inva));
    *rc1 = 0;
    *rcinf = 0;
    ae_matrix_init(&inva, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * inv A
     */
    testrcondunit_rmatrixmakeacopy(a, n, n, &inva, _state);
    if( !testrcondunit_rmatrixinvmat(&inva, n, _state) )
    {
        *rc1 = (double)(0);
        *rcinf = (double)(0);
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * norm A
     */
    nrm1a = (double)(0);
    nrminfa = (double)(0);
    for(k=0; k<=n-1; k++)
    {
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_fabs(a->ptr.pp_double[i][k], _state);
        }
        nrm1a = ae_maxreal(nrm1a, v, _state);
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_fabs(a->ptr.pp_double[k][i], _state);
        }
        nrminfa = ae_maxreal(nrminfa, v, _state);
    }
    
    /*
     * norm inv A
     */
    nrm1inva = (double)(0);
    nrminfinva = (double)(0);
    for(k=0; k<=n-1; k++)
    {
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_fabs(inva.ptr.pp_double[i][k], _state);
        }
        nrm1inva = ae_maxreal(nrm1inva, v, _state);
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_fabs(inva.ptr.pp_double[k][i], _state);
        }
        nrminfinva = ae_maxreal(nrminfinva, v, _state);
    }
    
    /*
     * result
     */
    *rc1 = nrm1inva*nrm1a;
    *rcinf = nrminfinva*nrminfa;
    ae_frame_leave(_state);
}


/*************************************************************************
Copy
*************************************************************************/
static void testrcondunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}


/*************************************************************************
Generate matrix with given condition number C (2-norm)
*************************************************************************/
static void testrcondunit_cmatrixgenzero(/* Complex */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    ae_matrix_set_length(a0, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a0->ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
}


/*************************************************************************
triangular inverse
*************************************************************************/
static ae_bool testrcondunit_cmatrixinvmattr(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_complex ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&t, 0, sizeof(t));
    ae_vector_init(&t, 0, DT_COMPLEX, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_c_eq_d(a->ptr.pp_complex[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_complex[j][j] = ae_c_d_div(1,a->ptr.pp_complex[j][j]);
                ajj = ae_c_neg(a->ptr.pp_complex[j][j]);
            }
            else
            {
                ajj = ae_complex_from_i(-1);
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_cmove(&t.ptr.p_complex[0], 1, &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i<j-1 )
                    {
                        v = ae_v_cdotproduct(&a->ptr.pp_complex[i][i+1], 1, "N", &t.ptr.p_complex[i+1], 1, "N", ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = ae_complex_from_i(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,ae_c_mul(a->ptr.pp_complex[i][i],t.ptr.p_complex[i]));
                    }
                    else
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,t.ptr.p_complex[i]);
                    }
                }
                ae_v_cmulc(&a->ptr.pp_complex[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_c_eq_d(a->ptr.pp_complex[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_complex[j][j] = ae_c_d_div(1,a->ptr.pp_complex[j][j]);
                ajj = ae_c_neg(a->ptr.pp_complex[j][j]);
            }
            else
            {
                ajj = ae_complex_from_i(-1);
            }
            if( j<n-1 )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_cmove(&t.ptr.p_complex[j+1], 1, &a->ptr.pp_complex[j+1][j], a->stride, "N", ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_cdotproduct(&a->ptr.pp_complex[i][j+1], 1, "N", &t.ptr.p_complex[j+1], 1, "N", ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = ae_complex_from_i(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,ae_c_mul(a->ptr.pp_complex[i][i],t.ptr.p_complex[i]));
                    }
                    else
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,t.ptr.p_complex[i]);
                    }
                }
                ae_v_cmulc(&a->ptr.pp_complex[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
LU inverse
*************************************************************************/
static ae_bool testrcondunit_cmatrixinvmatlu(/* Complex */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector work;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jp;
    ae_complex v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&work, 0, sizeof(work));
    ae_vector_init(&work, 0, DT_COMPLEX, _state, ae_true);

    result = ae_true;
    
    /*
     * Quick return if possible
     */
    if( n==0 )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_vector_set_length(&work, n-1+1, _state);
    
    /*
     * Form inv(U)
     */
    if( !testrcondunit_cmatrixinvmattr(a, n, ae_true, ae_false, _state) )
    {
        result = ae_false;
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Solve the equation inv(A)*L = inv(U) for inv(A).
     */
    for(j=n-1; j>=0; j--)
    {
        
        /*
         * Copy current column of L to WORK and replace with zeros.
         */
        for(i=j+1; i<=n-1; i++)
        {
            work.ptr.p_complex[i] = a->ptr.pp_complex[i][j];
            a->ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
        
        /*
         * Compute current column of inv(A).
         */
        if( j<n-1 )
        {
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_cdotproduct(&a->ptr.pp_complex[i][j+1], 1, "N", &work.ptr.p_complex[j+1], 1, "N", ae_v_len(j+1,n-1));
                a->ptr.pp_complex[i][j] = ae_c_sub(a->ptr.pp_complex[i][j],v);
            }
        }
    }
    
    /*
     * Apply column interchanges.
     */
    for(j=n-2; j>=0; j--)
    {
        jp = pivots->ptr.p_int[j];
        if( jp!=j )
        {
            ae_v_cmove(&work.ptr.p_complex[0], 1, &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,n-1));
            ae_v_cmove(&a->ptr.pp_complex[0][j], a->stride, &a->ptr.pp_complex[0][jp], a->stride, "N", ae_v_len(0,n-1));
            ae_v_cmove(&a->ptr.pp_complex[0][jp], a->stride, &work.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Matrix inverse
*************************************************************************/
static ae_bool testrcondunit_cmatrixinvmat(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector pivots;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&pivots, 0, sizeof(pivots));
    ae_vector_init(&pivots, 0, DT_INT, _state, ae_true);

    cmatrixlu(a, n, n, &pivots, _state);
    result = testrcondunit_cmatrixinvmatlu(a, &pivots, n, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
reference RCond
*************************************************************************/
static void testrcondunit_cmatrixrefrcond(/* Complex */ ae_matrix* a,
     ae_int_t n,
     double* rc1,
     double* rcinf,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix inva;
    double nrm1a;
    double nrminfa;
    double nrm1inva;
    double nrminfinva;
    double v;
    ae_int_t k;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&inva, 0, sizeof(inva));
    *rc1 = 0;
    *rcinf = 0;
    ae_matrix_init(&inva, 0, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * inv A
     */
    testrcondunit_cmatrixmakeacopy(a, n, n, &inva, _state);
    if( !testrcondunit_cmatrixinvmat(&inva, n, _state) )
    {
        *rc1 = (double)(0);
        *rcinf = (double)(0);
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * norm A
     */
    nrm1a = (double)(0);
    nrminfa = (double)(0);
    for(k=0; k<=n-1; k++)
    {
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_c_abs(a->ptr.pp_complex[i][k], _state);
        }
        nrm1a = ae_maxreal(nrm1a, v, _state);
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_c_abs(a->ptr.pp_complex[k][i], _state);
        }
        nrminfa = ae_maxreal(nrminfa, v, _state);
    }
    
    /*
     * norm inv A
     */
    nrm1inva = (double)(0);
    nrminfinva = (double)(0);
    for(k=0; k<=n-1; k++)
    {
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_c_abs(inva.ptr.pp_complex[i][k], _state);
        }
        nrm1inva = ae_maxreal(nrm1inva, v, _state);
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_c_abs(inva.ptr.pp_complex[k][i], _state);
        }
        nrminfinva = ae_maxreal(nrminfinva, v, _state);
    }
    
    /*
     * result
     */
    *rc1 = nrm1inva*nrm1a;
    *rcinf = nrminfinva*nrminfa;
    ae_frame_leave(_state);
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testrmatrixtrrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix ea;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j1;
    ae_int_t j2;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool isupper;
    ae_bool isunit;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&ea, 0, sizeof(ea));
    memset(&p, 0, sizeof(p));
    memset(&q50, 0, sizeof(q50));
    memset(&q90, 0, sizeof(q90));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ea, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 2, _state);
    ae_vector_set_length(&q90, 2, _state);
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * special test for zero matrix
         */
        testrcondunit_rmatrixgenzero(&a, n, _state);
        errspec = errspec||ae_fp_neq(rmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
        errspec = errspec||ae_fp_neq(rmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=1; i++)
        {
            q50.ptr.p_double[i] = (double)(0);
            q90.ptr.p_double[i] = (double)(0);
        }
        for(pass=1; pass<=passcount; pass++)
        {
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            isunit = ae_fp_greater(ae_randomreal(_state),0.5);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = ae_randomreal(_state)-0.5;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 1+ae_randomreal(_state);
            }
            testrcondunit_rmatrixmakeacopy(&a, n, n, &ea, _state);
            for(i=0; i<=n-1; i++)
            {
                if( isupper )
                {
                    j1 = 0;
                    j2 = i-1;
                }
                else
                {
                    j1 = i+1;
                    j2 = n-1;
                }
                for(j=j1; j<=j2; j++)
                {
                    ea.ptr.pp_double[i][j] = (double)(0);
                }
                if( isunit )
                {
                    ea.ptr.pp_double[i][i] = (double)(1);
                }
            }
            testrcondunit_rmatrixrefrcond(&ea, n, &erc1, &ercinf, _state);
            
            /*
             * 1-norm
             */
            v = 1/rmatrixtrrcond1(&a, n, isupper, isunit, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Inf-norm
             */
            v = 1/rmatrixtrrcondinf(&a, n, isupper, isunit, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
        }
        for(i=0; i<=1; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            a.ptr.pp_double[0][0] = (double)(1);
            a.ptr.pp_double[n-1][n-1] = (double)(1);
            errspec = errspec||ae_fp_neq(rmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
            errspec = errspec||ae_fp_neq(rmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = (double)(1);
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_double[i][i] = 0.1*ae_maxrealnumber;
            errspec = errspec||ae_fp_neq(rmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
            errspec = errspec||ae_fp_neq(rmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testcmatrixtrrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix ea;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j1;
    ae_int_t j2;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool isupper;
    ae_bool isunit;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&ea, 0, sizeof(ea));
    memset(&p, 0, sizeof(p));
    memset(&q50, 0, sizeof(q50));
    memset(&q90, 0, sizeof(q90));
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ea, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 2, _state);
    ae_vector_set_length(&q90, 2, _state);
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * special test for zero matrix
         */
        testrcondunit_cmatrixgenzero(&a, n, _state);
        errspec = errspec||ae_fp_neq(cmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
        errspec = errspec||ae_fp_neq(cmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=1; i++)
        {
            q50.ptr.p_double[i] = (double)(0);
            q90.ptr.p_double[i] = (double)(0);
        }
        for(pass=1; pass<=passcount; pass++)
        {
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            isunit = ae_fp_greater(ae_randomreal(_state),0.5);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j].x = ae_randomreal(_state)-0.5;
                    a.ptr.pp_complex[i][j].y = ae_randomreal(_state)-0.5;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_complex[i][i].x = 1+ae_randomreal(_state);
                a.ptr.pp_complex[i][i].y = 1+ae_randomreal(_state);
            }
            testrcondunit_cmatrixmakeacopy(&a, n, n, &ea, _state);
            for(i=0; i<=n-1; i++)
            {
                if( isupper )
                {
                    j1 = 0;
                    j2 = i-1;
                }
                else
                {
                    j1 = i+1;
                    j2 = n-1;
                }
                for(j=j1; j<=j2; j++)
                {
                    ea.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                }
                if( isunit )
                {
                    ea.ptr.pp_complex[i][i] = ae_complex_from_i(1);
                }
            }
            testrcondunit_cmatrixrefrcond(&ea, n, &erc1, &ercinf, _state);
            
            /*
             * 1-norm
             */
            v = 1/cmatrixtrrcond1(&a, n, isupper, isunit, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Inf-norm
             */
            v = 1/cmatrixtrrcondinf(&a, n, isupper, isunit, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
        }
        for(i=0; i<=1; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            a.ptr.pp_complex[0][0] = ae_complex_from_i(1);
            a.ptr.pp_complex[n-1][n-1] = ae_complex_from_i(1);
            errspec = errspec||ae_fp_neq(cmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
            errspec = errspec||ae_fp_neq(cmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_complex[i][i] = ae_complex_from_i(1);
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_complex[i][i] = ae_complex_from_d(0.1*ae_maxrealnumber);
            errspec = errspec||ae_fp_neq(cmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
            errspec = errspec||ae_fp_neq(cmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),(double)(0));
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testrmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&lua, 0, sizeof(lua));
    memset(&p, 0, sizeof(p));
    memset(&q50, 0, sizeof(q50));
    memset(&q90, 0, sizeof(q90));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 3+1, _state);
    ae_vector_set_length(&q90, 3+1, _state);
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * special test for zero matrix
         */
        testrcondunit_rmatrixgenzero(&a, n, _state);
        testrcondunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
        rmatrixlu(&lua, n, n, &p, _state);
        errspec = errspec||ae_fp_neq(rmatrixrcond1(&a, n, _state),(double)(0));
        errspec = errspec||ae_fp_neq(rmatrixrcondinf(&a, n, _state),(double)(0));
        errspec = errspec||ae_fp_neq(rmatrixlurcond1(&lua, n, _state),(double)(0));
        errspec = errspec||ae_fp_neq(rmatrixlurcondinf(&lua, n, _state),(double)(0));
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        for(i=0; i<=3; i++)
        {
            q50.ptr.p_double[i] = (double)(0);
            q90.ptr.p_double[i] = (double)(0);
        }
        for(pass=1; pass<=passcount; pass++)
        {
            rmatrixrndcond(n, ae_exp(ae_randomreal(_state)*ae_log((double)(1000), _state), _state), &a, _state);
            testrcondunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
            rmatrixlu(&lua, n, n, &p, _state);
            testrcondunit_rmatrixrefrcond(&a, n, &erc1, &ercinf, _state);
            
            /*
             * 1-norm, normal
             */
            v = 1/rmatrixrcond1(&a, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * 1-norm, LU
             */
            v = 1/rmatrixlurcond1(&lua, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Inf-norm, normal
             */
            v = 1/rmatrixrcondinf(&a, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[2] = q50.ptr.p_double[2]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[2] = q90.ptr.p_double[2]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
            
            /*
             * Inf-norm, LU
             */
            v = 1/rmatrixlurcondinf(&lua, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[3] = q50.ptr.p_double[3]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[3] = q90.ptr.p_double[3]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
        }
        for(i=0; i<=3; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            a.ptr.pp_double[0][0] = (double)(1);
            a.ptr.pp_double[n-1][n-1] = (double)(1);
            errspec = errspec||ae_fp_neq(rmatrixrcond1(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(rmatrixrcondinf(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(rmatrixlurcond1(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(rmatrixlurcondinf(&a, n, _state),(double)(0));
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = (double)(1);
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_double[i][i] = 0.1*ae_maxrealnumber;
            errspec = errspec||ae_fp_neq(rmatrixrcond1(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(rmatrixrcondinf(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(rmatrixlurcond1(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(rmatrixlurcondinf(&a, n, _state),(double)(0));
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testspdmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    ae_bool isupper;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&cha, 0, sizeof(cha));
    memset(&p, 0, sizeof(p));
    memset(&q50, 0, sizeof(q50));
    memset(&q90, 0, sizeof(q90));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 2, _state);
    ae_vector_set_length(&q90, 2, _state);
    for(n=1; n<=maxn; n++)
    {
        isupper = ae_fp_greater(ae_randomreal(_state),0.5);
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=1; i++)
        {
            q50.ptr.p_double[i] = (double)(0);
            q90.ptr.p_double[i] = (double)(0);
        }
        for(pass=1; pass<=passcount; pass++)
        {
            spdmatrixrndcond(n, ae_exp(ae_randomreal(_state)*ae_log((double)(1000), _state), _state), &a, _state);
            testrcondunit_rmatrixrefrcond(&a, n, &erc1, &ercinf, _state);
            testrcondunit_rmatrixdrophalf(&a, n, isupper, _state);
            testrcondunit_rmatrixmakeacopy(&a, n, n, &cha, _state);
            spdmatrixcholesky(&cha, n, isupper, _state);
            
            /*
             * normal
             */
            v = 1/spdmatrixrcond(&a, n, isupper, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Cholesky
             */
            v = 1/spdmatrixcholeskyrcond(&cha, n, isupper, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
        }
        for(i=0; i<=1; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            a.ptr.pp_double[0][0] = (double)(1);
            a.ptr.pp_double[n-1][n-1] = (double)(1);
            errspec = errspec||ae_fp_neq(spdmatrixrcond(&a, n, isupper, _state),(double)(-1));
            errspec = errspec||ae_fp_neq(spdmatrixcholeskyrcond(&a, n, isupper, _state),(double)(0));
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = (double)(1);
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_double[i][i] = 0.1*ae_maxrealnumber;
            errspec = errspec||ae_fp_neq(spdmatrixrcond(&a, n, isupper, _state),(double)(0));
            errspec = errspec||ae_fp_neq(spdmatrixcholeskyrcond(&a, n, isupper, _state),(double)(0));
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testcmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errless;
    ae_bool errspec;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&lua, 0, sizeof(lua));
    memset(&p, 0, sizeof(p));
    memset(&q50, 0, sizeof(q50));
    memset(&q90, 0, sizeof(q90));
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&q50, 3+1, _state);
    ae_vector_set_length(&q90, 3+1, _state);
    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    
    /*
     * process
     */
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * special test for zero matrix
         */
        testrcondunit_cmatrixgenzero(&a, n, _state);
        testrcondunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
        cmatrixlu(&lua, n, n, &p, _state);
        errspec = errspec||ae_fp_neq(cmatrixrcond1(&a, n, _state),(double)(0));
        errspec = errspec||ae_fp_neq(cmatrixrcondinf(&a, n, _state),(double)(0));
        errspec = errspec||ae_fp_neq(cmatrixlurcond1(&lua, n, _state),(double)(0));
        errspec = errspec||ae_fp_neq(cmatrixlurcondinf(&lua, n, _state),(double)(0));
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        for(i=0; i<=3; i++)
        {
            q50.ptr.p_double[i] = (double)(0);
            q90.ptr.p_double[i] = (double)(0);
        }
        for(pass=1; pass<=passcount; pass++)
        {
            cmatrixrndcond(n, ae_exp(ae_randomreal(_state)*ae_log((double)(1000), _state), _state), &a, _state);
            testrcondunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
            cmatrixlu(&lua, n, n, &p, _state);
            testrcondunit_cmatrixrefrcond(&a, n, &erc1, &ercinf, _state);
            
            /*
             * 1-norm, normal
             */
            v = 1/cmatrixrcond1(&a, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * 1-norm, LU
             */
            v = 1/cmatrixlurcond1(&lua, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Inf-norm, normal
             */
            v = 1/cmatrixrcondinf(&a, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[2] = q50.ptr.p_double[2]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[2] = q90.ptr.p_double[2]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
            
            /*
             * Inf-norm, LU
             */
            v = 1/cmatrixlurcondinf(&lua, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[3] = q50.ptr.p_double[3]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[3] = q90.ptr.p_double[3]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
        }
        for(i=0; i<=3; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            a.ptr.pp_complex[0][0] = ae_complex_from_i(1);
            a.ptr.pp_complex[n-1][n-1] = ae_complex_from_i(1);
            errspec = errspec||ae_fp_neq(cmatrixrcond1(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(cmatrixrcondinf(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(cmatrixlurcond1(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(cmatrixlurcondinf(&a, n, _state),(double)(0));
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_complex[i][i] = ae_complex_from_i(1);
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_complex[i][i] = ae_complex_from_d(0.1*ae_maxrealnumber);
            errspec = errspec||ae_fp_neq(cmatrixrcond1(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(cmatrixrcondinf(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(cmatrixlurcond1(&a, n, _state),(double)(0));
            errspec = errspec||ae_fp_neq(cmatrixlurcondinf(&a, n, _state),(double)(0));
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testhpdmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    ae_bool isupper;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&cha, 0, sizeof(cha));
    memset(&p, 0, sizeof(p));
    memset(&q50, 0, sizeof(q50));
    memset(&q90, 0, sizeof(q90));
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 2, _state);
    ae_vector_set_length(&q90, 2, _state);
    for(n=1; n<=maxn; n++)
    {
        isupper = ae_fp_greater(ae_randomreal(_state),0.5);
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=1; i++)
        {
            q50.ptr.p_double[i] = (double)(0);
            q90.ptr.p_double[i] = (double)(0);
        }
        for(pass=1; pass<=passcount; pass++)
        {
            hpdmatrixrndcond(n, ae_exp(ae_randomreal(_state)*ae_log((double)(1000), _state), _state), &a, _state);
            testrcondunit_cmatrixrefrcond(&a, n, &erc1, &ercinf, _state);
            testrcondunit_cmatrixdrophalf(&a, n, isupper, _state);
            testrcondunit_cmatrixmakeacopy(&a, n, n, &cha, _state);
            hpdmatrixcholesky(&cha, n, isupper, _state);
            
            /*
             * normal
             */
            v = 1/hpdmatrixrcond(&a, n, isupper, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Cholesky
             */
            v = 1/hpdmatrixcholeskyrcond(&cha, n, isupper, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
        }
        for(i=0; i<=1; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            a.ptr.pp_complex[0][0] = ae_complex_from_i(1);
            a.ptr.pp_complex[n-1][n-1] = ae_complex_from_i(1);
            errspec = errspec||ae_fp_neq(hpdmatrixrcond(&a, n, isupper, _state),(double)(-1));
            errspec = errspec||ae_fp_neq(hpdmatrixcholeskyrcond(&a, n, isupper, _state),(double)(0));
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_complex[i][i] = ae_complex_from_i(1);
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_complex[i][i] = ae_complex_from_d(0.1*ae_maxrealnumber);
            errspec = errspec||ae_fp_neq(hpdmatrixrcond(&a, n, isupper, _state),(double)(0));
            errspec = errspec||ae_fp_neq(hpdmatrixcholeskyrcond(&a, n, isupper, _state),(double)(0));
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}



static void testmatinvunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testmatinvunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);
static ae_bool testmatinvunit_rmatrixcheckinverse(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_spdmatrixcheckinverse(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* inva,
     ae_bool isupper,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_hpdmatrixcheckinverse(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* inva,
     ae_bool isupper,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_rmatrixcheckinversesingular(/* Real    */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_cmatrixcheckinverse(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_cmatrixcheckinversesingular(/* Complex */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static void testmatinvunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testmatinvunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testmatinvunit_testrtrinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* rtrerrors,
     ae_state *_state);
static void testmatinvunit_testctrinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* ctrerrors,
     ae_state *_state);
static void testmatinvunit_testrinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* rerrors,
     ae_state *_state);
static void testmatinvunit_testcinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* cerrors,
     ae_state *_state);
static void testmatinvunit_testspdinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* spderrors,
     ae_state *_state);
static void testmatinvunit_testhpdinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* hpderrors,
     ae_state *_state);
static void testmatinvunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state);
static void testmatinvunit_cunset2d(/* Complex */ ae_matrix* x,
     ae_state *_state);
static void testmatinvunit_unsetrep(matinvreport* r, ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testmatinv(ae_bool silent, ae_state *_state)
{
    ae_int_t maxrn;
    ae_int_t maxcn;
    ae_int_t largen;
    ae_int_t passcount;
    double threshold;
    ae_bool rtrerrors;
    ae_bool ctrerrors;
    ae_bool rerrors;
    ae_bool cerrors;
    ae_bool spderrors;
    ae_bool hpderrors;
    ae_bool waserrors;
    ae_bool result;


    maxrn = 3*matrixtilesizea(_state)+1;
    maxcn = 3*matrixtilesizea(_state)+1;
    largen = 4*matrixtilesizeb(_state)+1;
    passcount = 1;
    threshold = 10000*ae_machineepsilon;
    rtrerrors = ae_false;
    ctrerrors = ae_false;
    rerrors = ae_false;
    cerrors = ae_false;
    spderrors = ae_false;
    hpderrors = ae_false;
    testmatinvunit_testrtrinv(1, maxrn, passcount, threshold, &rtrerrors, _state);
    testmatinvunit_testctrinv(1, maxcn, passcount, threshold, &ctrerrors, _state);
    testmatinvunit_testrinv(1, maxrn, passcount, threshold, &rerrors, _state);
    testmatinvunit_testspdinv(1, maxrn, passcount, threshold, &spderrors, _state);
    testmatinvunit_testcinv(1, maxcn, passcount, threshold, &cerrors, _state);
    testmatinvunit_testhpdinv(1, maxcn, passcount, threshold, &hpderrors, _state);
    testmatinvunit_testrtrinv(largen, largen, passcount, threshold, &rtrerrors, _state);
    testmatinvunit_testctrinv(largen, largen, passcount, threshold, &ctrerrors, _state);
    testmatinvunit_testrinv(largen, largen, passcount, threshold, &rerrors, _state);
    testmatinvunit_testspdinv(largen, largen, passcount, threshold, &spderrors, _state);
    testmatinvunit_testcinv(largen, largen, passcount, threshold, &cerrors, _state);
    testmatinvunit_testhpdinv(largen, largen, passcount, threshold, &hpderrors, _state);
    waserrors = ((((rtrerrors||ctrerrors)||rerrors)||cerrors)||spderrors)||hpderrors;
    if( !silent )
    {
        printf("TESTING MATINV\n");
        printf("* REAL TRIANGULAR:                        ");
        if( rtrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX TRIANGULAR:                     ");
        if( ctrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* REAL:                                   ");
        if( rerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX:                                ");
        if( cerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SPD:                                    ");
        if( spderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* HPD:                                    ");
        if( hpderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testmatinvunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Copy
*************************************************************************/
static void testmatinvunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}


/*************************************************************************
Checks whether inverse is correct
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_rmatrixcheckinverse(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &inva->ptr.pp_double[0][j], inva->stride, ae_v_len(0,n-1));
                if( i==j )
                {
                    v = v-1;
                }
                result = result&&ae_fp_less_eq(ae_fabs(v, _state),threshold);
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether inverse is correct
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_spdmatrixcheckinverse(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* inva,
     ae_bool isupper,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix _a;
    ae_matrix _inva;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&_a, 0, sizeof(_a));
    memset(&_inva, 0, sizeof(_inva));
    ae_matrix_init_copy(&_a, a, _state, ae_true);
    a = &_a;
    ae_matrix_init_copy(&_inva, inva, _state, ae_true);
    inva = &_inva;

    for(i=0; i<=n-2; i++)
    {
        if( isupper )
        {
            ae_v_move(&a->ptr.pp_double[i+1][i], a->stride, &a->ptr.pp_double[i][i+1], 1, ae_v_len(i+1,n-1));
            ae_v_move(&inva->ptr.pp_double[i+1][i], inva->stride, &inva->ptr.pp_double[i][i+1], 1, ae_v_len(i+1,n-1));
        }
        else
        {
            ae_v_move(&a->ptr.pp_double[i][i+1], 1, &a->ptr.pp_double[i+1][i], a->stride, ae_v_len(i+1,n-1));
            ae_v_move(&inva->ptr.pp_double[i][i+1], 1, &inva->ptr.pp_double[i+1][i], inva->stride, ae_v_len(i+1,n-1));
        }
    }
    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &inva->ptr.pp_double[0][j], inva->stride, ae_v_len(0,n-1));
                if( i==j )
                {
                    v = v-1;
                }
                result = result&&ae_fp_less_eq(ae_fabs(v, _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether inverse is correct
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_hpdmatrixcheckinverse(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* inva,
     ae_bool isupper,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix _a;
    ae_matrix _inva;
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&_a, 0, sizeof(_a));
    memset(&_inva, 0, sizeof(_inva));
    ae_matrix_init_copy(&_a, a, _state, ae_true);
    a = &_a;
    ae_matrix_init_copy(&_inva, inva, _state, ae_true);
    inva = &_inva;

    for(i=0; i<=n-2; i++)
    {
        if( isupper )
        {
            ae_v_cmove(&a->ptr.pp_complex[i+1][i], a->stride, &a->ptr.pp_complex[i][i+1], 1, "Conj", ae_v_len(i+1,n-1));
            ae_v_cmove(&inva->ptr.pp_complex[i+1][i], inva->stride, &inva->ptr.pp_complex[i][i+1], 1, "Conj", ae_v_len(i+1,n-1));
        }
        else
        {
            ae_v_cmove(&a->ptr.pp_complex[i][i+1], 1, &a->ptr.pp_complex[i+1][i], a->stride, "Conj", ae_v_len(i+1,n-1));
            ae_v_cmove(&inva->ptr.pp_complex[i][i+1], 1, &inva->ptr.pp_complex[i+1][i], inva->stride, "Conj", ae_v_len(i+1,n-1));
        }
    }
    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = ae_v_cdotproduct(&a->ptr.pp_complex[i][0], 1, "N", &inva->ptr.pp_complex[0][j], inva->stride, "N", ae_v_len(0,n-1));
                if( i==j )
                {
                    v = ae_c_sub_d(v,1);
                }
                result = result&&ae_fp_less_eq(ae_c_abs(v, _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether inversion result indicate singular matrix
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_rmatrixcheckinversesingular(/* Real    */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3&&info!=1 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,(double)(0))||ae_fp_greater(rep->r1,1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,(double)(0))||ae_fp_greater(rep->rinf,1000*ae_machineepsilon));
        if( info==-3 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    result = result&&ae_fp_eq(inva->ptr.pp_double[i][j],(double)(0));
                }
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether inverse is correct
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_cmatrixcheckinverse(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = ae_v_cdotproduct(&a->ptr.pp_complex[i][0], 1, "N", &inva->ptr.pp_complex[0][j], inva->stride, "N", ae_v_len(0,n-1));
                if( i==j )
                {
                    v = ae_c_sub_d(v,1);
                }
                result = result&&ae_fp_less_eq(ae_c_abs(v, _state),threshold);
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether inversion result indicate singular matrix
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_cmatrixcheckinversesingular(/* Complex */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3&&info!=1 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,(double)(0))||ae_fp_greater(rep->r1,1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,(double)(0))||ae_fp_greater(rep->rinf,1000*ae_machineepsilon));
        if( info==-3 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    result = result&&ae_c_eq_d(inva->ptr.pp_complex[i][j],(double)(0));
                }
            }
        }
    }
    return result;
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testmatinvunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_double[i][j] = (double)(1+2*i+3*j);
            }
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testmatinvunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_i(1+2*i+3*j);
            }
        }
    }
}


/*************************************************************************
Real TR inverse
*************************************************************************/
static void testmatinvunit_testrtrinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* rtrerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix b;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t task;
    ae_bool isupper;
    ae_bool isunit;
    double v;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * Test
     */
    for(n=minn; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n, n, _state);
        ae_matrix_set_length(&b, n, n, _state);
        for(task=0; task<=3; task++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Determine task
                 */
                isupper = task%2==0;
                isunit = task/2%2==0;
                
                /*
                 * Generate matrix
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j )
                        {
                            a.ptr.pp_double[i][i] = 1+ae_randomreal(_state);
                        }
                        else
                        {
                            a.ptr.pp_double[i][j] = 0.2*ae_randomreal(_state)-0.1;
                        }
                        b.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                    }
                }
                
                /*
                 * Inverse
                 */
                rmatrixtrinverse(&b, n, isupper, isunit, &info, &rep, _state);
                if( info<=0 )
                {
                    *rtrerrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Structural test
                 */
                if( isunit )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        *rtrerrors = *rtrerrors||ae_fp_neq(a.ptr.pp_double[i][i],b.ptr.pp_double[i][i]);
                    }
                }
                if( isupper )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=i-1; j++)
                        {
                            *rtrerrors = *rtrerrors||ae_fp_neq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                        }
                    }
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=i+1; j<=n-1; j++)
                        {
                            *rtrerrors = *rtrerrors||ae_fp_neq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                        }
                    }
                }
                
                /*
                 * Inverse test
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            a.ptr.pp_double[i][j] = (double)(0);
                            b.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                }
                if( isunit )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a.ptr.pp_double[i][i] = (double)(1);
                        b.ptr.pp_double[i][i] = (double)(1);
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &b.ptr.pp_double[0][j], b.stride, ae_v_len(0,n-1));
                        if( j!=i )
                        {
                            *rtrerrors = *rtrerrors||ae_fp_greater(ae_fabs(v, _state),threshold);
                        }
                        else
                        {
                            *rtrerrors = *rtrerrors||ae_fp_greater(ae_fabs(v-1, _state),threshold);
                        }
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Complex TR inverse
*************************************************************************/
static void testmatinvunit_testctrinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* ctrerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix b;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t task;
    ae_bool isupper;
    ae_bool isunit;
    ae_complex v;
    ae_int_t info;
    matinvreport rep;
    double emax;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * Test
     */
    for(n=minn; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n, n, _state);
        ae_matrix_set_length(&b, n, n, _state);
        for(task=0; task<=3; task++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Determine task
                 */
                isupper = task%2==0;
                isunit = task/2%2==0;
                
                /*
                 * Generate matrix
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j )
                        {
                            a.ptr.pp_complex[i][i].x = 1+ae_randomreal(_state);
                            a.ptr.pp_complex[i][i].y = 1+ae_randomreal(_state);
                        }
                        else
                        {
                            a.ptr.pp_complex[i][j].x = 0.2*ae_randomreal(_state)-0.1;
                            a.ptr.pp_complex[i][j].y = 0.2*ae_randomreal(_state)-0.1;
                        }
                        b.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
                    }
                }
                
                /*
                 * Inverse
                 */
                cmatrixtrinverse(&b, n, isupper, isunit, &info, &rep, _state);
                if( info<=0 )
                {
                    *ctrerrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Structural test
                 */
                if( isunit )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        *ctrerrors = *ctrerrors||ae_c_neq(a.ptr.pp_complex[i][i],b.ptr.pp_complex[i][i]);
                    }
                }
                if( isupper )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=i-1; j++)
                        {
                            *ctrerrors = *ctrerrors||ae_c_neq(a.ptr.pp_complex[i][j],b.ptr.pp_complex[i][j]);
                        }
                    }
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=i+1; j<=n-1; j++)
                        {
                            *ctrerrors = *ctrerrors||ae_c_neq(a.ptr.pp_complex[i][j],b.ptr.pp_complex[i][j]);
                        }
                    }
                }
                
                /*
                 * Inverse test
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            a.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                            b.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                        }
                    }
                }
                if( isunit )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a.ptr.pp_complex[i][i] = ae_complex_from_i(1);
                        b.ptr.pp_complex[i][i] = ae_complex_from_i(1);
                    }
                }
                emax = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &b.ptr.pp_complex[0][j], b.stride, "N", ae_v_len(0,n-1));
                        if( j==i )
                        {
                            v = ae_c_sub_d(v,1);
                        }
                        emax = ae_maxreal(emax, ae_c_abs(v, _state), _state);
                    }
                }
                *ctrerrors = *ctrerrors||ae_fp_greater(emax,threshold);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Real test
*************************************************************************/
static void testmatinvunit_testrinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* rerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_matrix inva;
    ae_matrix invlua;
    ae_vector p;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&lua, 0, sizeof(lua));
    memset(&inva, 0, sizeof(inva));
    memset(&invlua, 0, sizeof(invlua));
    memset(&p, 0, sizeof(p));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&invlua, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=minn; n<=maxn; n++)
        {
            
            /*
             * ********************************************************
             * WELL CONDITIONED TASKS
             * ability to find correct solution is tested
             * ********************************************************
             *
             * 1. generate random well conditioned matrix A.
             * 2. generate random solution vector xe
             * 3. generate right part b=A*xe
             * 4. test different methods on original A
             */
            rmatrixrndcond(n, (double)(1000), &a, _state);
            testmatinvunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
            rmatrixlu(&lua, n, n, &p, _state);
            testmatinvunit_rmatrixmakeacopy(&a, n, n, &inva, _state);
            testmatinvunit_rmatrixmakeacopy(&lua, n, n, &invlua, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            rmatrixinverse(&inva, n, &info, &rep, _state);
            *rerrors = *rerrors||!testmatinvunit_rmatrixcheckinverse(&a, &inva, n, threshold, info, &rep, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            rmatrixluinverse(&invlua, &p, n, &info, &rep, _state);
            *rerrors = *rerrors||!testmatinvunit_rmatrixcheckinverse(&a, &invlua, n, threshold, info, &rep, _state);
            
            /*
             * ********************************************************
             * EXACTLY SINGULAR MATRICES
             * ability to detect singularity is tested
             * ********************************************************
             *
             * 1. generate different types of singular matrices:
             *    * zero
             *    * with zero columns
             *    * with zero rows
             *    * with equal rows/columns
             * 2. test different methods
             */
            for(taskkind=0; taskkind<=4; taskkind++)
            {
                testmatinvunit_unset2d(&a, _state);
                if( taskkind==0 )
                {
                    
                    /*
                     * all zeros
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                }
                if( taskkind==1 )
                {
                    
                    /*
                     * there is zero column
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                }
                if( taskkind==2 )
                {
                    
                    /*
                     * there is zero row
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                }
                if( taskkind==3 )
                {
                    
                    /*
                     * equal columns
                     */
                    if( n<2 )
                    {
                        continue;
                    }
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = 1+ae_randominteger(n-1, _state);
                    ae_v_move(&a.ptr.pp_double[0][0], a.stride, &a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1));
                }
                if( taskkind==4 )
                {
                    
                    /*
                     * equal rows
                     */
                    if( n<2 )
                    {
                        continue;
                    }
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = 1+ae_randominteger(n-1, _state);
                    ae_v_move(&a.ptr.pp_double[0][0], 1, &a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1));
                }
                testmatinvunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
                rmatrixlu(&lua, n, n, &p, _state);
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                rmatrixinverse(&a, n, &info, &rep, _state);
                *rerrors = *rerrors||!testmatinvunit_rmatrixcheckinversesingular(&a, n, threshold, info, &rep, _state);
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                rmatrixluinverse(&lua, &p, n, &info, &rep, _state);
                *rerrors = *rerrors||!testmatinvunit_rmatrixcheckinversesingular(&lua, n, threshold, info, &rep, _state);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Complex test
*************************************************************************/
static void testmatinvunit_testcinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* cerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_matrix inva;
    ae_matrix invlua;
    ae_vector p;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&lua, 0, sizeof(lua));
    memset(&inva, 0, sizeof(inva));
    memset(&invlua, 0, sizeof(invlua));
    memset(&p, 0, sizeof(p));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&invlua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=minn; n<=maxn; n++)
        {
            
            /*
             * ********************************************************
             * WELL CONDITIONED TASKS
             * ability to find correct solution is tested
             * ********************************************************
             *
             * 1. generate random well conditioned matrix A.
             * 2. generate random solution vector xe
             * 3. generate right part b=A*xe
             * 4. test different methods on original A
             */
            cmatrixrndcond(n, (double)(1000), &a, _state);
            testmatinvunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
            cmatrixlu(&lua, n, n, &p, _state);
            testmatinvunit_cmatrixmakeacopy(&a, n, n, &inva, _state);
            testmatinvunit_cmatrixmakeacopy(&lua, n, n, &invlua, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            cmatrixinverse(&inva, n, &info, &rep, _state);
            *cerrors = *cerrors||!testmatinvunit_cmatrixcheckinverse(&a, &inva, n, threshold, info, &rep, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            cmatrixluinverse(&invlua, &p, n, &info, &rep, _state);
            *cerrors = *cerrors||!testmatinvunit_cmatrixcheckinverse(&a, &invlua, n, threshold, info, &rep, _state);
            
            /*
             * ********************************************************
             * EXACTLY SINGULAR MATRICES
             * ability to detect singularity is tested
             * ********************************************************
             *
             * 1. generate different types of singular matrices:
             *    * zero
             *    * with zero columns
             *    * with zero rows
             *    * with equal rows/columns
             * 2. test different methods
             */
            for(taskkind=0; taskkind<=4; taskkind++)
            {
                testmatinvunit_cunset2d(&a, _state);
                if( taskkind==0 )
                {
                    
                    /*
                     * all zeros
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                        }
                    }
                }
                if( taskkind==1 )
                {
                    
                    /*
                     * there is zero column
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                }
                if( taskkind==2 )
                {
                    
                    /*
                     * there is zero row
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                }
                if( taskkind==3 )
                {
                    
                    /*
                     * equal columns
                     */
                    if( n<2 )
                    {
                        continue;
                    }
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = 1+ae_randominteger(n-1, _state);
                    ae_v_cmove(&a.ptr.pp_complex[0][0], a.stride, &a.ptr.pp_complex[0][k], a.stride, "N", ae_v_len(0,n-1));
                }
                if( taskkind==4 )
                {
                    
                    /*
                     * equal rows
                     */
                    if( n<2 )
                    {
                        continue;
                    }
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = 1+ae_randominteger(n-1, _state);
                    ae_v_cmove(&a.ptr.pp_complex[0][0], 1, &a.ptr.pp_complex[k][0], 1, "N", ae_v_len(0,n-1));
                }
                testmatinvunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
                cmatrixlu(&lua, n, n, &p, _state);
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                cmatrixinverse(&a, n, &info, &rep, _state);
                *cerrors = *cerrors||!testmatinvunit_cmatrixcheckinversesingular(&a, n, threshold, info, &rep, _state);
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                cmatrixluinverse(&lua, &p, n, &info, &rep, _state);
                *cerrors = *cerrors||!testmatinvunit_cmatrixcheckinversesingular(&lua, n, threshold, info, &rep, _state);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
SPD test
*************************************************************************/
static void testmatinvunit_testspdinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* spderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_matrix inva;
    ae_matrix invcha;
    ae_bool isupper;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&cha, 0, sizeof(cha));
    memset(&inva, 0, sizeof(inva));
    memset(&invcha, 0, sizeof(invcha));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&invcha, 0, 0, DT_REAL, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=minn; n<=maxn; n++)
        {
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            
            /*
             * ********************************************************
             * WELL CONDITIONED TASKS
             * ability to find correct solution is tested
             * ********************************************************
             *
             * 1. generate random well conditioned matrix A.
             * 2. generate random solution vector xe
             * 3. generate right part b=A*xe
             * 4. test different methods on original A
             */
            spdmatrixrndcond(n, (double)(1000), &a, _state);
            testmatinvunit_rmatrixdrophalf(&a, n, isupper, _state);
            testmatinvunit_rmatrixmakeacopy(&a, n, n, &cha, _state);
            if( !spdmatrixcholesky(&cha, n, isupper, _state) )
            {
                continue;
            }
            testmatinvunit_rmatrixmakeacopy(&a, n, n, &inva, _state);
            testmatinvunit_rmatrixmakeacopy(&cha, n, n, &invcha, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            spdmatrixinverse(&inva, n, isupper, &info, &rep, _state);
            *spderrors = *spderrors||!testmatinvunit_spdmatrixcheckinverse(&a, &inva, isupper, n, threshold, info, &rep, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            spdmatrixcholeskyinverse(&invcha, n, isupper, &info, &rep, _state);
            *spderrors = *spderrors||!testmatinvunit_spdmatrixcheckinverse(&a, &invcha, isupper, n, threshold, info, &rep, _state);
            
            /*
             * ********************************************************
             * EXACTLY SINGULAR MATRICES
             * ability to detect singularity is tested
             * ********************************************************
             *
             * 1. generate different types of singular matrices:
             *    * zero
             *    * with zero columns
             *    * with zero rows
             * 2. test different methods
             */
            for(taskkind=0; taskkind<=2; taskkind++)
            {
                testmatinvunit_unset2d(&a, _state);
                if( taskkind==0 )
                {
                    
                    /*
                     * all zeros
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                }
                if( taskkind==1 )
                {
                    
                    /*
                     * there is zero column
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                }
                if( taskkind==2 )
                {
                    
                    /*
                     * there is zero row
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                }
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                spdmatrixcholeskyinverse(&a, n, isupper, &info, &rep, _state);
                if( info!=-3&&info!=1 )
                {
                    *spderrors = ae_true;
                }
                else
                {
                    *spderrors = (*spderrors||ae_fp_less(rep.r1,(double)(0)))||ae_fp_greater(rep.r1,1000*ae_machineepsilon);
                    *spderrors = (*spderrors||ae_fp_less(rep.rinf,(double)(0)))||ae_fp_greater(rep.rinf,1000*ae_machineepsilon);
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
HPD test
*************************************************************************/
static void testmatinvunit_testhpdinv(ae_int_t minn,
     ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* hpderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_matrix inva;
    ae_matrix invcha;
    ae_bool isupper;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&cha, 0, sizeof(cha));
    memset(&inva, 0, sizeof(inva));
    memset(&invcha, 0, sizeof(invcha));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&invcha, 0, 0, DT_COMPLEX, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=minn; n<=maxn; n++)
        {
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            
            /*
             * ********************************************************
             * WELL CONDITIONED TASKS
             * ability to find correct solution is tested
             * ********************************************************
             *
             * 1. generate random well conditioned matrix A.
             * 2. generate random solution vector xe
             * 3. generate right part b=A*xe
             * 4. test different methods on original A
             */
            hpdmatrixrndcond(n, (double)(1000), &a, _state);
            testmatinvunit_cmatrixdrophalf(&a, n, isupper, _state);
            testmatinvunit_cmatrixmakeacopy(&a, n, n, &cha, _state);
            if( !hpdmatrixcholesky(&cha, n, isupper, _state) )
            {
                continue;
            }
            testmatinvunit_cmatrixmakeacopy(&a, n, n, &inva, _state);
            testmatinvunit_cmatrixmakeacopy(&cha, n, n, &invcha, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            hpdmatrixinverse(&inva, n, isupper, &info, &rep, _state);
            *hpderrors = *hpderrors||!testmatinvunit_hpdmatrixcheckinverse(&a, &inva, isupper, n, threshold, info, &rep, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            hpdmatrixcholeskyinverse(&invcha, n, isupper, &info, &rep, _state);
            *hpderrors = *hpderrors||!testmatinvunit_hpdmatrixcheckinverse(&a, &invcha, isupper, n, threshold, info, &rep, _state);
            
            /*
             * ********************************************************
             * EXACTLY SINGULAR MATRICES
             * ability to detect singularity is tested
             * ********************************************************
             *
             * 1. generate different types of singular matrices:
             *    * zero
             *    * with zero columns
             *    * with zero rows
             * 2. test different methods
             */
            for(taskkind=0; taskkind<=2; taskkind++)
            {
                testmatinvunit_cunset2d(&a, _state);
                if( taskkind==0 )
                {
                    
                    /*
                     * all zeros
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                        }
                    }
                }
                if( taskkind==1 )
                {
                    
                    /*
                     * there is zero column
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                    ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                }
                if( taskkind==2 )
                {
                    
                    /*
                     * there is zero row
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                    ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                }
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                hpdmatrixcholeskyinverse(&a, n, isupper, &info, &rep, _state);
                if( info!=-3&&info!=1 )
                {
                    *hpderrors = ae_true;
                }
                else
                {
                    *hpderrors = (*hpderrors||ae_fp_less(rep.r1,(double)(0)))||ae_fp_greater(rep.r1,1000*ae_machineepsilon);
                    *hpderrors = (*hpderrors||ae_fp_less(rep.rinf,(double)(0)))||ae_fp_greater(rep.rinf,1000*ae_machineepsilon);
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Unsets real matrix
*************************************************************************/
static void testmatinvunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state)
{


    ae_matrix_set_length(x, 1, 1, _state);
    x->ptr.pp_double[0][0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets real matrix
*************************************************************************/
static void testmatinvunit_cunset2d(/* Complex */ ae_matrix* x,
     ae_state *_state)
{


    ae_matrix_set_length(x, 1, 1, _state);
    x->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets report
*************************************************************************/
static void testmatinvunit_unsetrep(matinvreport* r, ae_state *_state)
{


    r->r1 = (double)(-1);
    r->rinf = (double)(-1);
}








ae_bool testhblas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix ua;
    ae_matrix la;
    ae_vector x;
    ae_vector y1;
    ae_vector y2;
    ae_vector y3;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t i2;
    ae_bool waserrors;
    double mverr;
    double threshold;
    ae_complex alpha;
    ae_complex v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&ua, 0, sizeof(ua));
    memset(&la, 0, sizeof(la));
    memset(&x, 0, sizeof(x));
    memset(&y1, 0, sizeof(y1));
    memset(&y2, 0, sizeof(y2));
    memset(&y3, 0, sizeof(y3));
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&la, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&x, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y1, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y2, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y3, 0, DT_COMPLEX, _state, ae_true);

    mverr = (double)(0);
    waserrors = ae_false;
    maxn = 10;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * Test MV
     */
    for(n=2; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n+1, n+1, _state);
        ae_matrix_set_length(&ua, n+1, n+1, _state);
        ae_matrix_set_length(&la, n+1, n+1, _state);
        ae_vector_set_length(&x, n+1, _state);
        ae_vector_set_length(&y1, n+1, _state);
        ae_vector_set_length(&y2, n+1, _state);
        ae_vector_set_length(&y3, n+1, _state);
        
        /*
         * fill A, UA, LA
         */
        for(i=1; i<=n; i++)
        {
            a.ptr.pp_complex[i][i].x = 2*ae_randomreal(_state)-1;
            a.ptr.pp_complex[i][i].y = (double)(0);
            for(j=i+1; j<=n; j++)
            {
                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                a.ptr.pp_complex[j][i] = ae_c_conj(a.ptr.pp_complex[i][j], _state);
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                ua.ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=i; j<=n; j++)
            {
                ua.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                la.ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=i; j++)
            {
                la.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
            }
        }
        
        /*
         * test on different I1, I2
         */
        for(i1=1; i1<=n; i1++)
        {
            for(i2=i1; i2<=n; i2++)
            {
                
                /*
                 * Fill X, choose Alpha
                 */
                for(i=1; i<=i2-i1+1; i++)
                {
                    x.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                    x.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                }
                alpha.x = 2*ae_randomreal(_state)-1;
                alpha.y = 2*ae_randomreal(_state)-1;
                
                /*
                 * calculate A*x, UA*x, LA*x
                 */
                for(i=i1; i<=i2; i++)
                {
                    v = ae_v_cdotproduct(&a.ptr.pp_complex[i][i1], 1, "N", &x.ptr.p_complex[1], 1, "N", ae_v_len(i1,i2));
                    y1.ptr.p_complex[i-i1+1] = ae_c_mul(alpha,v);
                }
                hermitianmatrixvectormultiply(&ua, ae_true, i1, i2, &x, alpha, &y2, _state);
                hermitianmatrixvectormultiply(&la, ae_false, i1, i2, &x, alpha, &y3, _state);
                
                /*
                 * Calculate error
                 */
                ae_v_csub(&y2.ptr.p_complex[1], 1, &y1.ptr.p_complex[1], 1, "N", ae_v_len(1,i2-i1+1));
                v = ae_v_cdotproduct(&y2.ptr.p_complex[1], 1, "N", &y2.ptr.p_complex[1], 1, "Conj", ae_v_len(1,i2-i1+1));
                mverr = ae_maxreal(mverr, ae_sqrt(ae_c_abs(v, _state), _state), _state);
                ae_v_csub(&y3.ptr.p_complex[1], 1, &y1.ptr.p_complex[1], 1, "N", ae_v_len(1,i2-i1+1));
                v = ae_v_cdotproduct(&y3.ptr.p_complex[1], 1, "N", &y3.ptr.p_complex[1], 1, "Conj", ae_v_len(1,i2-i1+1));
                mverr = ae_maxreal(mverr, ae_sqrt(ae_c_abs(v, _state), _state), _state);
            }
        }
    }
    
    /*
     * report
     */
    waserrors = ae_fp_greater(mverr,threshold);
    if( !silent )
    {
        printf("TESTING HERMITIAN BLAS\n");
        printf("MV error:                                %5.3e\n",
            (double)(mverr));
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testsblas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix ua;
    ae_matrix la;
    ae_vector x;
    ae_vector y1;
    ae_vector y2;
    ae_vector y3;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t i2;
    ae_bool waserrors;
    double mverr;
    double threshold;
    double alpha;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&ua, 0, sizeof(ua));
    memset(&la, 0, sizeof(la));
    memset(&x, 0, sizeof(x));
    memset(&y1, 0, sizeof(y1));
    memset(&y2, 0, sizeof(y2));
    memset(&y3, 0, sizeof(y3));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ua, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&la, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y3, 0, DT_REAL, _state, ae_true);

    mverr = (double)(0);
    waserrors = ae_false;
    maxn = 10;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * Test MV
     */
    for(n=2; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n+1, n+1, _state);
        ae_matrix_set_length(&ua, n+1, n+1, _state);
        ae_matrix_set_length(&la, n+1, n+1, _state);
        ae_vector_set_length(&x, n+1, _state);
        ae_vector_set_length(&y1, n+1, _state);
        ae_vector_set_length(&y2, n+1, _state);
        ae_vector_set_length(&y3, n+1, _state);
        
        /*
         * fill A, UA, LA
         */
        for(i=1; i<=n; i++)
        {
            a.ptr.pp_double[i][i] = 2*ae_randomreal(_state)-1;
            for(j=i+1; j<=n; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                ua.ptr.pp_double[i][j] = (double)(0);
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=i; j<=n; j++)
            {
                ua.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                la.ptr.pp_double[i][j] = (double)(0);
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=i; j++)
            {
                la.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
            }
        }
        
        /*
         * test on different I1, I2
         */
        for(i1=1; i1<=n; i1++)
        {
            for(i2=i1; i2<=n; i2++)
            {
                
                /*
                 * Fill X, choose Alpha
                 */
                for(i=1; i<=i2-i1+1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                alpha = 2*ae_randomreal(_state)-1;
                
                /*
                 * calculate A*x, UA*x, LA*x
                 */
                for(i=i1; i<=i2; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][i1], 1, &x.ptr.p_double[1], 1, ae_v_len(i1,i2));
                    y1.ptr.p_double[i-i1+1] = alpha*v;
                }
                symmetricmatrixvectormultiply(&ua, ae_true, i1, i2, &x, alpha, &y2, _state);
                symmetricmatrixvectormultiply(&la, ae_false, i1, i2, &x, alpha, &y3, _state);
                
                /*
                 * Calculate error
                 */
                ae_v_sub(&y2.ptr.p_double[1], 1, &y1.ptr.p_double[1], 1, ae_v_len(1,i2-i1+1));
                v = ae_v_dotproduct(&y2.ptr.p_double[1], 1, &y2.ptr.p_double[1], 1, ae_v_len(1,i2-i1+1));
                mverr = ae_maxreal(mverr, ae_sqrt(v, _state), _state);
                ae_v_sub(&y3.ptr.p_double[1], 1, &y1.ptr.p_double[1], 1, ae_v_len(1,i2-i1+1));
                v = ae_v_dotproduct(&y3.ptr.p_double[1], 1, &y3.ptr.p_double[1], 1, ae_v_len(1,i2-i1+1));
                mverr = ae_maxreal(mverr, ae_sqrt(v, _state), _state);
            }
        }
    }
    
    /*
     * report
     */
    waserrors = ae_fp_greater(mverr,threshold);
    if( !silent )
    {
        printf("TESTING SYMMETRIC BLAS\n");
        printf("MV error:                                %5.3e\n",
            (double)(mverr));
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static double testortfacunit_rmatrixdiff(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* b,
     ae_int_t m,
     ae_int_t n,
     ae_state *_state);
static void testortfacunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testortfacunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);
static void testortfacunit_rmatrixfillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testortfacunit_cmatrixfillsparsea(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testortfacunit_internalmatrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     ae_state *_state);
static void testortfacunit_testrqrproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* qrerrors,
     ae_state *_state);
static void testortfacunit_testcqrproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* qrerrors,
     ae_state *_state);
static void testortfacunit_testrlqproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* lqerrors,
     ae_state *_state);
static void testortfacunit_testclqproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* lqerrors,
     ae_state *_state);
static void testortfacunit_testrbdproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* bderrors,
     ae_state *_state);
static void testortfacunit_testrhessproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* hesserrors,
     ae_state *_state);
static void testortfacunit_testrtdproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state);
static void testortfacunit_testctdproblem(/* Complex */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state);





/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testortfac(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    ae_int_t mx;
    ae_matrix ra;
    ae_matrix ca;
    ae_int_t m;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_bool rqrerrors;
    ae_bool rlqerrors;
    ae_bool cqrerrors;
    ae_bool clqerrors;
    ae_bool rbderrors;
    ae_bool rhesserrors;
    ae_bool rtderrors;
    ae_bool ctderrors;
    ae_bool waserrors;
    hqrndstate rs;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ra, 0, sizeof(ra));
    memset(&ca, 0, sizeof(ca));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    waserrors = ae_false;
    rqrerrors = ae_false;
    rlqerrors = ae_false;
    cqrerrors = ae_false;
    clqerrors = ae_false;
    rbderrors = ae_false;
    rhesserrors = ae_false;
    rtderrors = ae_false;
    ctderrors = ae_false;
    threshold = 5*1000*ae_machineepsilon;
    
    /*
     * Medium-scale problems with various sparseness profiles
     */
    for(mx=1; mx<=3*matrixtilesizea(_state)+1; mx++)
    {
        
        /*
         * Rectangular factorizations: QR, LQ, bidiagonal
         * Matrix types: zero, dense, sparse
         */
        n = 1+ae_randominteger(mx, _state);
        m = 1+ae_randominteger(mx, _state);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            n = mx;
        }
        else
        {
            m = mx;
        }
        ae_matrix_set_length(&ra, m, n, _state);
        ae_matrix_set_length(&ca, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ra.ptr.pp_double[i][j] = (double)(0);
                ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
        }
        testortfacunit_testrqrproblem(&ra, m, n, threshold, &rqrerrors, _state);
        testortfacunit_testrlqproblem(&ra, m, n, threshold, &rlqerrors, _state);
        testortfacunit_testcqrproblem(&ca, m, n, threshold, &cqrerrors, _state);
        testortfacunit_testclqproblem(&ca, m, n, threshold, &clqerrors, _state);
        testortfacunit_testrbdproblem(&ra, m, n, threshold, &rbderrors, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        testortfacunit_testrqrproblem(&ra, m, n, threshold, &rqrerrors, _state);
        testortfacunit_testrlqproblem(&ra, m, n, threshold, &rlqerrors, _state);
        testortfacunit_testcqrproblem(&ca, m, n, threshold, &cqrerrors, _state);
        testortfacunit_testclqproblem(&ca, m, n, threshold, &clqerrors, _state);
        testortfacunit_testrbdproblem(&ra, m, n, threshold, &rbderrors, _state);
        testortfacunit_rmatrixfillsparsea(&ra, m, n, 0.95, _state);
        testortfacunit_cmatrixfillsparsea(&ca, m, n, 0.95, _state);
        testortfacunit_testrqrproblem(&ra, m, n, threshold, &rqrerrors, _state);
        testortfacunit_testrlqproblem(&ra, m, n, threshold, &rlqerrors, _state);
        testortfacunit_testcqrproblem(&ca, m, n, threshold, &cqrerrors, _state);
        testortfacunit_testclqproblem(&ca, m, n, threshold, &clqerrors, _state);
        testortfacunit_testrbdproblem(&ra, m, n, threshold, &rbderrors, _state);
        
        /*
         * Square factorizations: Hessenberg, tridiagonal
         * Matrix types: zero, dense, sparse
         */
        ae_matrix_set_length(&ra, mx, mx, _state);
        ae_matrix_set_length(&ca, mx, mx, _state);
        for(i=0; i<=mx-1; i++)
        {
            for(j=0; j<=mx-1; j++)
            {
                ra.ptr.pp_double[i][j] = (double)(0);
                ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
        }
        testortfacunit_testrhessproblem(&ra, mx, threshold, &rhesserrors, _state);
        for(i=0; i<=mx-1; i++)
        {
            for(j=0; j<=mx-1; j++)
            {
                ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        testortfacunit_testrhessproblem(&ra, mx, threshold, &rhesserrors, _state);
        testortfacunit_rmatrixfillsparsea(&ra, mx, mx, 0.95, _state);
        testortfacunit_cmatrixfillsparsea(&ca, mx, mx, 0.95, _state);
        testortfacunit_testrhessproblem(&ra, mx, threshold, &rhesserrors, _state);
        
        /*
         * Symetric factorizations: tridiagonal
         * Matrix types: zero, dense, sparse
         */
        ae_matrix_set_length(&ra, mx, mx, _state);
        ae_matrix_set_length(&ca, mx, mx, _state);
        for(i=0; i<=mx-1; i++)
        {
            for(j=0; j<=mx-1; j++)
            {
                ra.ptr.pp_double[i][j] = (double)(0);
                ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
        }
        testortfacunit_testrtdproblem(&ra, mx, threshold, &rtderrors, _state);
        testortfacunit_testctdproblem(&ca, mx, threshold, &ctderrors, _state);
        for(i=0; i<=mx-1; i++)
        {
            for(j=i; j<=mx-1; j++)
            {
                ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
                ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
            }
        }
        for(i=0; i<=mx-1; i++)
        {
            ca.ptr.pp_complex[i][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
        }
        testortfacunit_testrtdproblem(&ra, mx, threshold, &rtderrors, _state);
        testortfacunit_testctdproblem(&ca, mx, threshold, &ctderrors, _state);
        testortfacunit_rmatrixfillsparsea(&ra, mx, mx, 0.95, _state);
        testortfacunit_cmatrixfillsparsea(&ca, mx, mx, 0.95, _state);
        for(i=0; i<=mx-1; i++)
        {
            for(j=i; j<=mx-1; j++)
            {
                ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
                ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
            }
        }
        for(i=0; i<=mx-1; i++)
        {
            ca.ptr.pp_complex[i][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
        }
        testortfacunit_testrtdproblem(&ra, mx, threshold, &rtderrors, _state);
        testortfacunit_testctdproblem(&ca, mx, threshold, &ctderrors, _state);
    }
    
    /*
     * Large-scale tests
     */
    for(mx=4*matrixtilesizeb(_state); mx<=4*matrixtilesizeb(_state); mx++)
    {
        
        /*
         * Rectangular factorizations: QR, LQ, bidiagonal
         * Matrix types: dense
         */
        n = 1+ae_randominteger(mx, _state);
        m = 1+ae_randominteger(mx, _state);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            n = mx;
        }
        else
        {
            m = mx;
        }
        ae_matrix_set_length(&ra, m, n, _state);
        ae_matrix_set_length(&ca, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        testortfacunit_testrqrproblem(&ra, m, n, threshold, &rqrerrors, _state);
        testortfacunit_testrlqproblem(&ra, m, n, threshold, &rlqerrors, _state);
        testortfacunit_testcqrproblem(&ca, m, n, threshold, &cqrerrors, _state);
        testortfacunit_testclqproblem(&ca, m, n, threshold, &clqerrors, _state);
        testortfacunit_testrbdproblem(&ra, m, n, threshold, &rbderrors, _state);
        
        /*
         * Square factorizations: Hessenberg, tridiagonal
         * Matrix types: dense
         */
        ae_matrix_set_length(&ra, mx, mx, _state);
        ae_matrix_set_length(&ca, mx, mx, _state);
        for(i=0; i<=mx-1; i++)
        {
            for(j=0; j<=mx-1; j++)
            {
                ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        testortfacunit_testrhessproblem(&ra, mx, threshold, &rhesserrors, _state);
        
        /*
         * Symetric factorizations: tridiagonal
         * Matrix types: dense
         */
        ae_matrix_set_length(&ra, mx, mx, _state);
        ae_matrix_set_length(&ca, mx, mx, _state);
        for(i=0; i<=mx-1; i++)
        {
            for(j=i; j<=mx-1; j++)
            {
                ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
                ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
            }
        }
        for(i=0; i<=mx-1; i++)
        {
            ca.ptr.pp_complex[i][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
        }
        testortfacunit_testrtdproblem(&ra, mx, threshold, &rtderrors, _state);
        testortfacunit_testctdproblem(&ca, mx, threshold, &ctderrors, _state);
    }
    
    /*
     * report
     */
    waserrors = ((((((rqrerrors||rlqerrors)||cqrerrors)||clqerrors)||rbderrors)||rhesserrors)||rtderrors)||ctderrors;
    if( !silent )
    {
        printf("TESTING ORTFAC UNIT\n");
        printf("RQR ERRORS:                              ");
        if( !rqrerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("RLQ ERRORS:                              ");
        if( !rlqerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("CQR ERRORS:                              ");
        if( !cqrerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("CLQ ERRORS:                              ");
        if( !clqerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("RBD ERRORS:                              ");
        if( !rbderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("RHESS ERRORS:                            ");
        if( !rhesserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("RTD ERRORS:                              ");
        if( !rtderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("CTD ERRORS:                              ");
        if( !ctderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Diff
*************************************************************************/
static double testortfacunit_rmatrixdiff(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* b,
     ae_int_t m,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double result;


    result = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            result = ae_maxreal(result, ae_fabs(b->ptr.pp_double[i][j]-a->ptr.pp_double[i][j], _state), _state);
        }
    }
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testortfacunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Copy
*************************************************************************/
static void testortfacunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}


/*************************************************************************
Sparse fill
*************************************************************************/
static void testortfacunit_rmatrixfillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_double[i][j] = (double)(0);
            }
        }
    }
}


/*************************************************************************
Sparse fill
*************************************************************************/
static void testortfacunit_cmatrixfillsparsea(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a->ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
        }
    }
}


/*************************************************************************
Matrix multiplication
*************************************************************************/
static void testortfacunit_internalmatrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t arows;
    ae_int_t acols;
    ae_int_t brows;
    ae_int_t bcols;
    ae_int_t crows;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    ae_int_t r;
    double v;
    ae_vector work;
    double beta;
    double alpha;

    ae_frame_make(_state, &_frame_block);
    memset(&work, 0, sizeof(work));
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    
    /*
     * Pre-setup
     */
    k = ae_maxint(ai2-ai1+1, aj2-aj1+1, _state);
    k = ae_maxint(k, bi2-bi1+1, _state);
    k = ae_maxint(k, bj2-bj1+1, _state);
    ae_vector_set_length(&work, k+1, _state);
    beta = (double)(0);
    alpha = (double)(1);
    
    /*
     * Setup
     */
    if( !transa )
    {
        arows = ai2-ai1+1;
        acols = aj2-aj1+1;
    }
    else
    {
        arows = aj2-aj1+1;
        acols = ai2-ai1+1;
    }
    if( !transb )
    {
        brows = bi2-bi1+1;
        bcols = bj2-bj1+1;
    }
    else
    {
        brows = bj2-bj1+1;
        bcols = bi2-bi1+1;
    }
    ae_assert(acols==brows, "MatrixMatrixMultiply: incorrect matrix sizes!", _state);
    if( ((arows<=0||acols<=0)||brows<=0)||bcols<=0 )
    {
        ae_frame_leave(_state);
        return;
    }
    crows = arows;
    
    /*
     * Test WORK
     */
    i = ae_maxint(arows, acols, _state);
    i = ae_maxint(brows, i, _state);
    i = ae_maxint(i, bcols, _state);
    work.ptr.p_double[1] = (double)(0);
    work.ptr.p_double[i] = (double)(0);
    
    /*
     * Prepare C
     */
    if( ae_fp_eq(beta,(double)(0)) )
    {
        for(i=ci1; i<=ci2; i++)
        {
            for(j=cj1; j<=cj2; j++)
            {
                c->ptr.pp_double[i][j] = (double)(0);
            }
        }
    }
    else
    {
        for(i=ci1; i<=ci2; i++)
        {
            ae_v_muld(&c->ptr.pp_double[i][cj1], 1, ae_v_len(cj1,cj2), beta);
        }
    }
    
    /*
     * A*B
     */
    if( !transa&&!transb )
    {
        for(l=ai1; l<=ai2; l++)
        {
            for(r=bi1; r<=bi2; r++)
            {
                v = alpha*a->ptr.pp_double[l][aj1+r-bi1];
                k = ci1+l-ai1;
                ae_v_addd(&c->ptr.pp_double[k][cj1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(cj1,cj2), v);
            }
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * A*B'
     */
    if( !transa&&transb )
    {
        if( arows*acols<brows*bcols )
        {
            for(r=bi1; r<=bi2; r++)
            {
                for(l=ai1; l<=ai2; l++)
                {
                    v = ae_v_dotproduct(&a->ptr.pp_double[l][aj1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(aj1,aj2));
                    c->ptr.pp_double[ci1+l-ai1][cj1+r-bi1] = c->ptr.pp_double[ci1+l-ai1][cj1+r-bi1]+alpha*v;
                }
            }
            ae_frame_leave(_state);
            return;
        }
        else
        {
            for(l=ai1; l<=ai2; l++)
            {
                for(r=bi1; r<=bi2; r++)
                {
                    v = ae_v_dotproduct(&a->ptr.pp_double[l][aj1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(aj1,aj2));
                    c->ptr.pp_double[ci1+l-ai1][cj1+r-bi1] = c->ptr.pp_double[ci1+l-ai1][cj1+r-bi1]+alpha*v;
                }
            }
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * A'*B
     */
    if( transa&&!transb )
    {
        for(l=aj1; l<=aj2; l++)
        {
            for(r=bi1; r<=bi2; r++)
            {
                v = alpha*a->ptr.pp_double[ai1+r-bi1][l];
                k = ci1+l-aj1;
                ae_v_addd(&c->ptr.pp_double[k][cj1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(cj1,cj2), v);
            }
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * A'*B'
     */
    if( transa&&transb )
    {
        if( arows*acols<brows*bcols )
        {
            for(r=bi1; r<=bi2; r++)
            {
                for(i=1; i<=crows; i++)
                {
                    work.ptr.p_double[i] = 0.0;
                }
                for(l=ai1; l<=ai2; l++)
                {
                    v = alpha*b->ptr.pp_double[r][bj1+l-ai1];
                    k = cj1+r-bi1;
                    ae_v_addd(&work.ptr.p_double[1], 1, &a->ptr.pp_double[l][aj1], 1, ae_v_len(1,crows), v);
                }
                ae_v_add(&c->ptr.pp_double[ci1][k], c->stride, &work.ptr.p_double[1], 1, ae_v_len(ci1,ci2));
            }
            ae_frame_leave(_state);
            return;
        }
        else
        {
            for(l=aj1; l<=aj2; l++)
            {
                k = ai2-ai1+1;
                ae_v_move(&work.ptr.p_double[1], 1, &a->ptr.pp_double[ai1][l], a->stride, ae_v_len(1,k));
                for(r=bi1; r<=bi2; r++)
                {
                    v = ae_v_dotproduct(&work.ptr.p_double[1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(1,k));
                    c->ptr.pp_double[ci1+l-aj1][cj1+r-bi1] = c->ptr.pp_double[ci1+l-aj1][cj1+r-bi1]+alpha*v;
                }
            }
            ae_frame_leave(_state);
            return;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testrqrproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* qrerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix b;
    ae_vector taub;
    ae_matrix q;
    ae_matrix r;
    ae_matrix q2;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&b, 0, sizeof(b));
    memset(&taub, 0, sizeof(taub));
    memset(&q, 0, sizeof(q));
    memset(&r, 0, sizeof(r));
    memset(&q2, 0, sizeof(q2));
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&taub, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test decompose-and-unpack error
     */
    testortfacunit_rmatrixmakeacopy(a, m, n, &b, _state);
    rmatrixqr(&b, m, n, &taub, _state);
    rmatrixqrunpackq(&b, m, n, &taub, m, &q, _state);
    rmatrixqrunpackr(&b, m, n, &r, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &r.ptr.pp_double[0][j], r.stride, ae_v_len(0,m-1));
            *qrerrors = *qrerrors||ae_fp_greater(ae_fabs(v-a->ptr.pp_double[i][j], _state),threshold);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=ae_minint(i, n-1, _state)-1; j++)
        {
            *qrerrors = *qrerrors||ae_fp_neq(r.ptr.pp_double[i][j],(double)(0));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &q.ptr.pp_double[j][0], 1, ae_v_len(0,m-1));
            if( i==j )
            {
                v = v-1;
            }
            *qrerrors = *qrerrors||ae_fp_greater_eq(ae_fabs(v, _state),threshold);
        }
    }
    
    /*
     * Test for other errors
     */
    k = 1+ae_randominteger(m, _state);
    rmatrixqrunpackq(&b, m, n, &taub, k, &q2, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            *qrerrors = *qrerrors||ae_fp_greater(ae_fabs(q2.ptr.pp_double[i][j]-q.ptr.pp_double[i][j], _state),10*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testcqrproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* qrerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix b;
    ae_vector taub;
    ae_matrix q;
    ae_matrix r;
    ae_matrix q2;
    ae_complex v;

    ae_frame_make(_state, &_frame_block);
    memset(&b, 0, sizeof(b));
    memset(&taub, 0, sizeof(taub));
    memset(&q, 0, sizeof(q));
    memset(&r, 0, sizeof(r));
    memset(&q2, 0, sizeof(q2));
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&taub, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&r, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * Test decompose-and-unpack error
     */
    testortfacunit_cmatrixmakeacopy(a, m, n, &b, _state);
    cmatrixqr(&b, m, n, &taub, _state);
    cmatrixqrunpackq(&b, m, n, &taub, m, &q, _state);
    cmatrixqrunpackr(&b, m, n, &r, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &r.ptr.pp_complex[0][j], r.stride, "N", ae_v_len(0,m-1));
            *qrerrors = *qrerrors||ae_fp_greater(ae_c_abs(ae_c_sub(v,a->ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=ae_minint(i, n-1, _state)-1; j++)
        {
            *qrerrors = *qrerrors||ae_c_neq_d(r.ptr.pp_complex[i][j],(double)(0));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,m-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            *qrerrors = *qrerrors||ae_fp_greater_eq(ae_c_abs(v, _state),threshold);
        }
    }
    
    /*
     * Test for other errors
     */
    k = 1+ae_randominteger(m, _state);
    cmatrixqrunpackq(&b, m, n, &taub, k, &q2, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            *qrerrors = *qrerrors||ae_fp_greater(ae_c_abs(ae_c_sub(q2.ptr.pp_complex[i][j],q.ptr.pp_complex[i][j]), _state),10*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testrlqproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* lqerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix b;
    ae_vector taub;
    ae_matrix q;
    ae_matrix l;
    ae_matrix q2;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&b, 0, sizeof(b));
    memset(&taub, 0, sizeof(taub));
    memset(&q, 0, sizeof(q));
    memset(&l, 0, sizeof(l));
    memset(&q2, 0, sizeof(q2));
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&taub, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&l, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test decompose-and-unpack error
     */
    testortfacunit_rmatrixmakeacopy(a, m, n, &b, _state);
    rmatrixlq(&b, m, n, &taub, _state);
    rmatrixlqunpackq(&b, m, n, &taub, n, &q, _state);
    rmatrixlqunpackl(&b, m, n, &l, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&l.ptr.pp_double[i][0], 1, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,n-1));
            *lqerrors = *lqerrors||ae_fp_greater_eq(ae_fabs(v-a->ptr.pp_double[i][j], _state),threshold);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=ae_minint(i, n-1, _state)+1; j<=n-1; j++)
        {
            *lqerrors = *lqerrors||ae_fp_neq(l.ptr.pp_double[i][j],(double)(0));
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &q.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            *lqerrors = *lqerrors||ae_fp_greater_eq(ae_fabs(v, _state),threshold);
        }
    }
    
    /*
     * Test for other errors
     */
    k = 1+ae_randominteger(n, _state);
    rmatrixlqunpackq(&b, m, n, &taub, k, &q2, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *lqerrors = *lqerrors||ae_fp_greater(ae_fabs(q2.ptr.pp_double[i][j]-q.ptr.pp_double[i][j], _state),10*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testclqproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* lqerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix b;
    ae_vector taub;
    ae_matrix q;
    ae_matrix l;
    ae_matrix q2;
    ae_complex v;

    ae_frame_make(_state, &_frame_block);
    memset(&b, 0, sizeof(b));
    memset(&taub, 0, sizeof(taub));
    memset(&q, 0, sizeof(q));
    memset(&l, 0, sizeof(l));
    memset(&q2, 0, sizeof(q2));
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&taub, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&l, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * Test decompose-and-unpack error
     */
    testortfacunit_cmatrixmakeacopy(a, m, n, &b, _state);
    cmatrixlq(&b, m, n, &taub, _state);
    cmatrixlqunpackq(&b, m, n, &taub, n, &q, _state);
    cmatrixlqunpackl(&b, m, n, &l, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&l.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[0][j], q.stride, "N", ae_v_len(0,n-1));
            *lqerrors = *lqerrors||ae_fp_greater_eq(ae_c_abs(ae_c_sub(v,a->ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=ae_minint(i, n-1, _state)+1; j<=n-1; j++)
        {
            *lqerrors = *lqerrors||ae_c_neq_d(l.ptr.pp_complex[i][j],(double)(0));
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            *lqerrors = *lqerrors||ae_fp_greater_eq(ae_c_abs(v, _state),threshold);
        }
    }
    
    /*
     * Test for other errors
     */
    k = 1+ae_randominteger(n, _state);
    cmatrixlqunpackq(&b, m, n, &taub, k, &q2, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *lqerrors = *lqerrors||ae_fp_greater(ae_c_abs(ae_c_sub(q2.ptr.pp_complex[i][j],q.ptr.pp_complex[i][j]), _state),10*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testrbdproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* bderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix t;
    ae_matrix pt;
    ae_matrix q;
    ae_matrix r;
    ae_matrix bd;
    ae_matrix x;
    ae_matrix r1;
    ae_matrix r2;
    ae_vector taup;
    ae_vector tauq;
    ae_vector d;
    ae_vector e;
    ae_bool up;
    double v;
    ae_int_t mtsize;

    ae_frame_make(_state, &_frame_block);
    memset(&t, 0, sizeof(t));
    memset(&pt, 0, sizeof(pt));
    memset(&q, 0, sizeof(q));
    memset(&r, 0, sizeof(r));
    memset(&bd, 0, sizeof(bd));
    memset(&x, 0, sizeof(x));
    memset(&r1, 0, sizeof(r1));
    memset(&r2, 0, sizeof(r2));
    memset(&taup, 0, sizeof(taup));
    memset(&tauq, 0, sizeof(tauq));
    memset(&d, 0, sizeof(d));
    memset(&e, 0, sizeof(e));
    ae_matrix_init(&t, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&pt, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&bd, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&taup, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tauq, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);

    
    /*
     * Bidiagonal decomposition error
     */
    testortfacunit_rmatrixmakeacopy(a, m, n, &t, _state);
    rmatrixbd(&t, m, n, &tauq, &taup, _state);
    rmatrixbdunpackq(&t, m, n, &tauq, m, &q, _state);
    rmatrixbdunpackpt(&t, m, n, &taup, n, &pt, _state);
    rmatrixbdunpackdiagonals(&t, m, n, &up, &d, &e, _state);
    ae_matrix_set_length(&bd, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            bd.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=ae_minint(m, n, _state)-1; i++)
    {
        bd.ptr.pp_double[i][i] = d.ptr.p_double[i];
    }
    if( up )
    {
        for(i=0; i<=ae_minint(m, n, _state)-2; i++)
        {
            bd.ptr.pp_double[i][i+1] = e.ptr.p_double[i];
        }
    }
    else
    {
        for(i=0; i<=ae_minint(m, n, _state)-2; i++)
        {
            bd.ptr.pp_double[i+1][i] = e.ptr.p_double[i];
        }
    }
    ae_matrix_set_length(&r, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &bd.ptr.pp_double[0][j], bd.stride, ae_v_len(0,m-1));
            r.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&r.ptr.pp_double[i][0], 1, &pt.ptr.pp_double[0][j], pt.stride, ae_v_len(0,n-1));
            *bderrors = *bderrors||ae_fp_greater(ae_fabs(v-a->ptr.pp_double[i][j], _state),threshold);
        }
    }
    
    /*
     * Orthogonality test for Q/PT
     */
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[0][i], q.stride, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,m-1));
            if( i==j )
            {
                *bderrors = *bderrors||ae_fp_greater(ae_fabs(v-1, _state),threshold);
            }
            else
            {
                *bderrors = *bderrors||ae_fp_greater(ae_fabs(v, _state),threshold);
            }
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&pt.ptr.pp_double[i][0], 1, &pt.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i==j )
            {
                *bderrors = *bderrors||ae_fp_greater(ae_fabs(v-1, _state),threshold);
            }
            else
            {
                *bderrors = *bderrors||ae_fp_greater(ae_fabs(v, _state),threshold);
            }
        }
    }
    
    /*
     * Partial unpacking test
     */
    k = 1+ae_randominteger(m, _state);
    rmatrixbdunpackq(&t, m, n, &tauq, k, &r, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            *bderrors = *bderrors||ae_fp_greater(ae_fabs(r.ptr.pp_double[i][j]-q.ptr.pp_double[i][j], _state),10*ae_machineepsilon);
        }
    }
    k = 1+ae_randominteger(n, _state);
    rmatrixbdunpackpt(&t, m, n, &taup, k, &r, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *bderrors = *bderrors||ae_fp_greater(ae_fabs(r.ptr.pp_double[i][j]-pt.ptr.pp_double[i][j], _state),10*ae_machineepsilon);
        }
    }
    
    /*
     * Multiplication test
     */
    ae_matrix_set_length(&x, ae_maxint(m, n, _state)-1+1, ae_maxint(m, n, _state)-1+1, _state);
    ae_matrix_set_length(&r, ae_maxint(m, n, _state)-1+1, ae_maxint(m, n, _state)-1+1, _state);
    ae_matrix_set_length(&r1, ae_maxint(m, n, _state)-1+1, ae_maxint(m, n, _state)-1+1, _state);
    ae_matrix_set_length(&r2, ae_maxint(m, n, _state)-1+1, ae_maxint(m, n, _state)-1+1, _state);
    for(i=0; i<=ae_maxint(m, n, _state)-1; i++)
    {
        for(j=0; j<=ae_maxint(m, n, _state)-1; j++)
        {
            x.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
        }
    }
    mtsize = 1+ae_randominteger(ae_maxint(m, n, _state), _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, m, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&r, 0, mtsize-1, 0, m-1, ae_false, &q, 0, m-1, 0, m-1, ae_false, &r1, 0, mtsize-1, 0, m-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, m, &r2, _state);
    rmatrixbdmultiplybyq(&t, m, n, &tauq, &r2, mtsize, m, ae_true, ae_false, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, mtsize, m, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, m, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&r, 0, mtsize-1, 0, m-1, ae_false, &q, 0, m-1, 0, m-1, ae_true, &r1, 0, mtsize-1, 0, m-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, m, &r2, _state);
    rmatrixbdmultiplybyq(&t, m, n, &tauq, &r2, mtsize, m, ae_true, ae_true, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, mtsize, m, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, m, mtsize, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&q, 0, m-1, 0, m-1, ae_false, &r, 0, m-1, 0, mtsize-1, ae_false, &r1, 0, m-1, 0, mtsize-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, m, mtsize, &r2, _state);
    rmatrixbdmultiplybyq(&t, m, n, &tauq, &r2, m, mtsize, ae_false, ae_false, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, m, mtsize, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, m, mtsize, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&q, 0, m-1, 0, m-1, ae_true, &r, 0, m-1, 0, mtsize-1, ae_false, &r1, 0, m-1, 0, mtsize-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, m, mtsize, &r2, _state);
    rmatrixbdmultiplybyq(&t, m, n, &tauq, &r2, m, mtsize, ae_false, ae_true, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, m, mtsize, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, n, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&r, 0, mtsize-1, 0, n-1, ae_false, &pt, 0, n-1, 0, n-1, ae_true, &r1, 0, mtsize-1, 0, n-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, n, &r2, _state);
    rmatrixbdmultiplybyp(&t, m, n, &taup, &r2, mtsize, n, ae_true, ae_false, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, mtsize, n, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, n, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&r, 0, mtsize-1, 0, n-1, ae_false, &pt, 0, n-1, 0, n-1, ae_false, &r1, 0, mtsize-1, 0, n-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, n, &r2, _state);
    rmatrixbdmultiplybyp(&t, m, n, &taup, &r2, mtsize, n, ae_true, ae_true, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, mtsize, n, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, n, mtsize, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&pt, 0, n-1, 0, n-1, ae_true, &r, 0, n-1, 0, mtsize-1, ae_false, &r1, 0, n-1, 0, mtsize-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, n, mtsize, &r2, _state);
    rmatrixbdmultiplybyp(&t, m, n, &taup, &r2, n, mtsize, ae_false, ae_false, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, n, mtsize, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, n, mtsize, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&pt, 0, n-1, 0, n-1, ae_false, &r, 0, n-1, 0, mtsize-1, ae_false, &r1, 0, n-1, 0, mtsize-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, n, mtsize, &r2, _state);
    rmatrixbdmultiplybyp(&t, m, n, &taup, &r2, n, mtsize, ae_false, ae_true, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, n, mtsize, _state),threshold);
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testrhessproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* hesserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix b;
    ae_matrix h;
    ae_matrix q;
    ae_matrix t1;
    ae_matrix t2;
    ae_vector tau;
    ae_int_t i;
    ae_int_t j;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&b, 0, sizeof(b));
    memset(&h, 0, sizeof(h));
    memset(&q, 0, sizeof(q));
    memset(&t1, 0, sizeof(t1));
    memset(&t2, 0, sizeof(t2));
    memset(&tau, 0, sizeof(tau));
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&h, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tau, 0, DT_REAL, _state, ae_true);

    testortfacunit_rmatrixmakeacopy(a, n, n, &b, _state);
    
    /*
     * Decomposition
     */
    rmatrixhessenberg(&b, n, &tau, _state);
    rmatrixhessenbergunpackq(&b, n, &tau, &q, _state);
    rmatrixhessenbergunpackh(&b, n, &h, _state);
    
    /*
     * Matrix properties
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[0][i], q.stride, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            *hesserrors = *hesserrors||ae_fp_greater(ae_fabs(v, _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=i-2; j++)
        {
            *hesserrors = *hesserrors||ae_fp_neq(h.ptr.pp_double[i][j],(double)(0));
        }
    }
    
    /*
     * Decomposition error
     */
    ae_matrix_set_length(&t1, n, n, _state);
    ae_matrix_set_length(&t2, n, n, _state);
    testortfacunit_internalmatrixmatrixmultiply(&q, 0, n-1, 0, n-1, ae_false, &h, 0, n-1, 0, n-1, ae_false, &t1, 0, n-1, 0, n-1, _state);
    testortfacunit_internalmatrixmatrixmultiply(&t1, 0, n-1, 0, n-1, ae_false, &q, 0, n-1, 0, n-1, ae_true, &t2, 0, n-1, 0, n-1, _state);
    *hesserrors = *hesserrors||ae_fp_greater(testortfacunit_rmatrixdiff(&t2, a, n, n, _state),threshold);
    ae_frame_leave(_state);
}


/*************************************************************************
Tridiagonal tester
*************************************************************************/
static void testortfacunit_testrtdproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_matrix ua;
    ae_matrix la;
    ae_matrix t;
    ae_matrix q;
    ae_matrix t2;
    ae_matrix t3;
    ae_vector tau;
    ae_vector d;
    ae_vector e;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&ua, 0, sizeof(ua));
    memset(&la, 0, sizeof(la));
    memset(&t, 0, sizeof(t));
    memset(&q, 0, sizeof(q));
    memset(&t2, 0, sizeof(t2));
    memset(&t3, 0, sizeof(t3));
    memset(&tau, 0, sizeof(tau));
    memset(&d, 0, sizeof(d));
    memset(&e, 0, sizeof(e));
    ae_matrix_init(&ua, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&la, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t3, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tau, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&ua, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&la, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&q, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t2, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t3, n-1+1, n-1+1, _state);
    
    /*
     * fill
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ua.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=i; j<=n-1; j++)
        {
            ua.ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            la.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=i; j++)
        {
            la.ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
    
    /*
     * Test 2tridiagonal: upper
     */
    smatrixtd(&ua, n, ae_true, &tau, &d, &e, _state);
    smatrixtdunpackq(&ua, n, ae_true, &tau, &q, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        t.ptr.pp_double[i][i] = d.ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        t.ptr.pp_double[i][i+1] = e.ptr.p_double[i];
        t.ptr.pp_double[i+1][i] = e.ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[0][i], q.stride, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
            t2.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&t2.ptr.pp_double[i][0], 1, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,n-1));
            t3.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *tderrors = *tderrors||ae_fp_greater(ae_fabs(t3.ptr.pp_double[i][j]-t.ptr.pp_double[i][j], _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &q.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            *tderrors = *tderrors||ae_fp_greater(ae_fabs(v, _state),threshold);
        }
    }
    
    /*
     * Test 2tridiagonal: lower
     */
    smatrixtd(&la, n, ae_false, &tau, &d, &e, _state);
    smatrixtdunpackq(&la, n, ae_false, &tau, &q, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        t.ptr.pp_double[i][i] = d.ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        t.ptr.pp_double[i][i+1] = e.ptr.p_double[i];
        t.ptr.pp_double[i+1][i] = e.ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[0][i], q.stride, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
            t2.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&t2.ptr.pp_double[i][0], 1, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,n-1));
            t3.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *tderrors = *tderrors||ae_fp_greater(ae_fabs(t3.ptr.pp_double[i][j]-t.ptr.pp_double[i][j], _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &q.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            *tderrors = *tderrors||ae_fp_greater(ae_fabs(v, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Hermitian problem tester
*************************************************************************/
static void testortfacunit_testctdproblem(/* Complex */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_matrix ua;
    ae_matrix la;
    ae_matrix t;
    ae_matrix q;
    ae_matrix t2;
    ae_matrix t3;
    ae_vector tau;
    ae_vector d;
    ae_vector e;
    ae_complex v;

    ae_frame_make(_state, &_frame_block);
    memset(&ua, 0, sizeof(ua));
    memset(&la, 0, sizeof(la));
    memset(&t, 0, sizeof(t));
    memset(&q, 0, sizeof(q));
    memset(&t2, 0, sizeof(t2));
    memset(&t3, 0, sizeof(t3));
    memset(&tau, 0, sizeof(tau));
    memset(&d, 0, sizeof(d));
    memset(&e, 0, sizeof(e));
    ae_matrix_init(&ua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&la, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&t, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&t2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&t3, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tau, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&ua, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&la, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&q, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t2, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t3, n-1+1, n-1+1, _state);
    
    /*
     * fill
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ua.ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=i; j<=n-1; j++)
        {
            ua.ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            la.ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=i; j++)
        {
            la.ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
    
    /*
     * Test 2tridiagonal: upper
     */
    hmatrixtd(&ua, n, ae_true, &tau, &d, &e, _state);
    hmatrixtdunpackq(&ua, n, ae_true, &tau, &q, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        t.ptr.pp_complex[i][i] = ae_complex_from_d(d.ptr.p_double[i]);
    }
    for(i=0; i<=n-2; i++)
    {
        t.ptr.pp_complex[i][i+1] = ae_complex_from_d(e.ptr.p_double[i]);
        t.ptr.pp_complex[i+1][i] = ae_complex_from_d(e.ptr.p_double[i]);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[0][i], q.stride, "Conj", &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,n-1));
            t2.ptr.pp_complex[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&t2.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[0][j], q.stride, "N", ae_v_len(0,n-1));
            t3.ptr.pp_complex[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *tderrors = *tderrors||ae_fp_greater(ae_c_abs(ae_c_sub(t3.ptr.pp_complex[i][j],t.ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            *tderrors = *tderrors||ae_fp_greater(ae_c_abs(v, _state),threshold);
        }
    }
    
    /*
     * Test 2tridiagonal: lower
     */
    hmatrixtd(&la, n, ae_false, &tau, &d, &e, _state);
    hmatrixtdunpackq(&la, n, ae_false, &tau, &q, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        t.ptr.pp_complex[i][i] = ae_complex_from_d(d.ptr.p_double[i]);
    }
    for(i=0; i<=n-2; i++)
    {
        t.ptr.pp_complex[i][i+1] = ae_complex_from_d(e.ptr.p_double[i]);
        t.ptr.pp_complex[i+1][i] = ae_complex_from_d(e.ptr.p_double[i]);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[0][i], q.stride, "Conj", &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,n-1));
            t2.ptr.pp_complex[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&t2.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[0][j], q.stride, "N", ae_v_len(0,n-1));
            t3.ptr.pp_complex[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *tderrors = *tderrors||ae_fp_greater(ae_c_abs(ae_c_sub(t3.ptr.pp_complex[i][j],t.ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            *tderrors = *tderrors||ae_fp_greater(ae_c_abs(v, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}








/*************************************************************************
Testing
*************************************************************************/
ae_bool testfbls(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_bool waserrors;
    ae_bool cgerrors;
    ae_bool lserrors;
    ae_bool cholerrors;
    double eps;
    double v;
    double v1;
    double v2;
    ae_vector tmp0;
    ae_vector tmp1;
    ae_vector tmp2;
    double scalea;
    ae_bool uppera;
    ae_matrix a;
    ae_matrix ea;
    ae_vector b;
    ae_vector x;
    ae_vector xe;
    ae_vector buf;
    double alpha;
    double e1;
    double e2;
    fblslincgstate cgstate;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&tmp0, 0, sizeof(tmp0));
    memset(&tmp1, 0, sizeof(tmp1));
    memset(&tmp2, 0, sizeof(tmp2));
    memset(&a, 0, sizeof(a));
    memset(&ea, 0, sizeof(ea));
    memset(&b, 0, sizeof(b));
    memset(&x, 0, sizeof(x));
    memset(&xe, 0, sizeof(xe));
    memset(&buf, 0, sizeof(buf));
    memset(&cgstate, 0, sizeof(cgstate));
    ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ea, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);
    _fblslincgstate_init(&cgstate, _state, ae_true);

    mx = 10;
    waserrors = ae_false;
    cgerrors = ae_false;
    lserrors = ae_false;
    cholerrors = ae_false;
    
    /*
     * Test CG solver:
     * * generate problem (A, B, Alpha, XE - exact solution) and initial approximation X
     * * E1 = ||A'A*x-b||
     * * solve
     * * E2 = ||A'A*x-b||
     * * test that E2<0.001*E1
     */
    for(n=1; n<=mx; n++)
    {
        for(m=1; m<=mx; m++)
        {
            ae_matrix_set_length(&a, m, n, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xe, n, _state);
            ae_vector_set_length(&tmp1, m, _state);
            ae_vector_set_length(&tmp2, n, _state);
            
            /*
             * init A, alpha, B, X (initial approximation), XE (exact solution)
             * X is initialized in such way that is has no chances to be equal to XE.
             */
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
            }
            alpha = ae_randomreal(_state)+0.1;
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x.ptr.p_double[i] = (2*ae_randominteger(2, _state)-1)*(2+ae_randomreal(_state));
            }
            
            /*
             * Test dense CG (which solves A'A*x=b and accepts dense A)
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (2*ae_randominteger(2, _state)-1)*(2+ae_randomreal(_state));
            }
            rmatrixmv(m, n, &a, 0, 0, 0, &x, 0, &tmp1, 0, _state);
            rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &tmp2, 0, _state);
            ae_v_addd(&tmp2.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
            ae_v_sub(&tmp2.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&tmp2.ptr.p_double[0], 1, &tmp2.ptr.p_double[0], 1, ae_v_len(0,n-1));
            e1 = ae_sqrt(v, _state);
            fblssolvecgx(&a, m, n, alpha, &b, &x, &buf, _state);
            rmatrixmv(m, n, &a, 0, 0, 0, &x, 0, &tmp1, 0, _state);
            rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &tmp2, 0, _state);
            ae_v_addd(&tmp2.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
            ae_v_sub(&tmp2.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&tmp2.ptr.p_double[0], 1, &tmp2.ptr.p_double[0], 1, ae_v_len(0,n-1));
            e2 = ae_sqrt(v, _state);
            cgerrors = cgerrors||ae_fp_greater(e2,0.001*e1);
            
            /*
             * Test sparse CG (which relies on reverse communication)
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (2*ae_randominteger(2, _state)-1)*(2+ae_randomreal(_state));
            }
            rmatrixmv(m, n, &a, 0, 0, 0, &x, 0, &tmp1, 0, _state);
            rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &tmp2, 0, _state);
            ae_v_addd(&tmp2.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
            ae_v_sub(&tmp2.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&tmp2.ptr.p_double[0], 1, &tmp2.ptr.p_double[0], 1, ae_v_len(0,n-1));
            e1 = ae_sqrt(v, _state);
            fblscgcreate(&x, &b, n, &cgstate, _state);
            while(fblscgiteration(&cgstate, _state))
            {
                rmatrixmv(m, n, &a, 0, 0, 0, &cgstate.x, 0, &tmp1, 0, _state);
                rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &cgstate.ax, 0, _state);
                ae_v_addd(&cgstate.ax.ptr.p_double[0], 1, &cgstate.x.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
                v1 = ae_v_dotproduct(&tmp1.ptr.p_double[0], 1, &tmp1.ptr.p_double[0], 1, ae_v_len(0,m-1));
                v2 = ae_v_dotproduct(&cgstate.x.ptr.p_double[0], 1, &cgstate.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                cgstate.xax = v1+alpha*v2;
            }
            rmatrixmv(m, n, &a, 0, 0, 0, &cgstate.xk, 0, &tmp1, 0, _state);
            rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &tmp2, 0, _state);
            ae_v_addd(&tmp2.ptr.p_double[0], 1, &cgstate.xk.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
            ae_v_sub(&tmp2.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&tmp2.ptr.p_double[0], 1, &tmp2.ptr.p_double[0], 1, ae_v_len(0,n-1));
            e2 = ae_sqrt(v, _state);
            cgerrors = cgerrors||ae_fp_greater(ae_fabs(e1-cgstate.e1, _state),100*ae_machineepsilon*e1);
            cgerrors = cgerrors||ae_fp_greater(ae_fabs(e2-cgstate.e2, _state),100*ae_machineepsilon*e1);
            cgerrors = cgerrors||ae_fp_greater(e2,0.001*e1);
        }
    }
    
    /*
     * Test linear least squares:
     * * try N=1..5, M=N..2*N
     *                           [ B ]
     * * generate MxN matrix A = [   ], where (M-N)xN submatrix B contains
     *                           [ C ]
     *   random values from [-1,+1], and NxN submatrix C is diagonally dominant
     *   (diagonal of C is equal to 1.0, and magnitude of off-diagonal elements
     *   is smaller than 0.01). Such matrix is guaranteed to be non-degenerate.
     * * generate random known solution xe, set right part b=A*xe
     * * check that results of FBLSSolveLS agree with xe
     */
    eps = 1.0E-6;
    for(n=1; n<=5; n++)
    {
        for(m=n; m<=2*n; m++)
        {
            ae_matrix_set_length(&a, m, n, _state);
            for(i=0; i<=m-n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
            }
            for(i=m-n; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.01*(2*ae_randomreal(_state)-1);
                }
                a.ptr.pp_double[i][i-(m-n)] = 1.0;
            }
            ae_vector_set_length(&xe, n, _state);
            for(i=0; i<=n-1; i++)
            {
                xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&b, m, _state);
            for(i=0; i<=m-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = v;
            }
            fblssolvels(&a, &b, m, n, &tmp0, &tmp1, &tmp2, _state);
            for(i=0; i<=n-1; i++)
            {
                lserrors = lserrors||ae_fp_greater(ae_fabs(b.ptr.p_double[i]-xe.ptr.p_double[i], _state),eps);
            }
        }
    }
    
    /*
     * Test Cholesky solver:
     * * generate trial A, ScaleA, XS and corresponding right part B
     */
    for(n=1; n<=30; n++)
    {
        
        /*
         * Generate test problem
         */
        ae_matrix_set_length(&a, n, n, _state);
        ae_matrix_set_length(&ea, n, n, _state);
        ae_vector_set_length(&xe, n, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&buf, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = 0.01*(ae_randomreal(_state)-0.5);
            }
            a.ptr.pp_double[i][i] = 1+ae_randomreal(_state);
            xe.ptr.p_double[i] = ae_randomreal(_state)-0.5;
        }
        scalea = ae_pow((double)(10), ae_randomreal(_state)*4-2, _state);
        uppera = ae_fp_greater(ae_randomreal(_state),0.5);
        for(i=0; i<=n-1; i++)
        {
            if( uppera )
            {
                for(j=0; j<=i-1; j++)
                {
                    ea.ptr.pp_double[i][j] = (double)(0);
                }
                for(j=i; j<=n-1; j++)
                {
                    ea.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                }
            }
            else
            {
                for(j=0; j<=i; j++)
                {
                    ea.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                }
                for(j=i+1; j<=n-1; j++)
                {
                    ea.ptr.pp_double[i][j] = (double)(0);
                }
            }
        }
        if( uppera )
        {
            rmatrixgemv(n, n, 1.0, &ea, 0, 0, 0, &xe, 0, 0.0, &buf, 0, _state);
            rmatrixgemv(n, n, 1.0, &ea, 0, 0, 1, &buf, 0, 0.0, &b, 0, _state);
        }
        else
        {
            rmatrixgemv(n, n, 1.0, &ea, 0, 0, 1, &xe, 0, 0.0, &buf, 0, _state);
            rmatrixgemv(n, n, 1.0, &ea, 0, 0, 0, &buf, 0, 0.0, &b, 0, _state);
        }
        ae_v_muld(&b.ptr.p_double[0], 1, ae_v_len(0,n-1), scalea);
        
        /*
         * Test
         */
        fblscholeskysolve(&a, ae_sqrt(scalea, _state), n, uppera, &b, &buf, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(&cholerrors, ae_fp_greater(ae_fabs(b.ptr.p_double[i]-xe.ptr.p_double[i], _state),1.0E3*ae_machineepsilon), __FILE__, __LINE__, "testfblsunit.ap:284");
        }
    }
    
    /*
     * report
     */
    waserrors = (cgerrors||lserrors)||cholerrors;
    if( !silent )
    {
        printf("TESTING FBLS\n");
        printf("CG ERRORS:                               ");
        if( cgerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LS ERRORS:                               ");
        if( lserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CHOL ERRORS:                             ");
        if( cholerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testcqmodels(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool eval0errors;
    ae_bool eval1errors;
    ae_bool eval2errors;
    ae_bool newton0errors;
    ae_bool newton1errors;
    ae_bool newton2errors;
    ae_bool waserrors;
    convexquadraticmodel s;
    ae_int_t nkind;
    ae_int_t kmax;
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;
    ae_int_t pass;
    ae_int_t j;
    double alpha;
    double theta;
    double tau;
    double v;
    double v2;
    double h;
    double f0;
    double mkind;
    double xtadx2;
    double noise;
    ae_matrix a;
    ae_matrix q;
    ae_vector b;
    ae_vector r;
    ae_vector x;
    ae_vector x0;
    ae_vector xc;
    ae_vector d;
    ae_vector ge;
    ae_vector gt;
    ae_vector tmp0;
    ae_vector adx;
    ae_vector adxe;
    ae_vector activeset;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    memset(&q, 0, sizeof(q));
    memset(&b, 0, sizeof(b));
    memset(&r, 0, sizeof(r));
    memset(&x, 0, sizeof(x));
    memset(&x0, 0, sizeof(x0));
    memset(&xc, 0, sizeof(xc));
    memset(&d, 0, sizeof(d));
    memset(&ge, 0, sizeof(ge));
    memset(&gt, 0, sizeof(gt));
    memset(&tmp0, 0, sizeof(tmp0));
    memset(&adx, 0, sizeof(adx));
    memset(&adxe, 0, sizeof(adxe));
    memset(&activeset, 0, sizeof(activeset));
    _convexquadraticmodel_init(&s, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ge, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gt, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&adx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&adxe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&activeset, 0, DT_BOOL, _state, ae_true);

    waserrors = ae_false;
    
    /*
     * Eval0 test: unconstrained model evaluation
     */
    eval0errors = ae_false;
    for(n=1; n<=5; n++)
    {
        for(k=0; k<=2*n; k++)
        {
            
            /*
             * Allocate place
             */
            ae_matrix_set_length(&a, n, n, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&ge, n, _state);
            ae_vector_set_length(&gt, n, _state);
            ae_vector_set_length(&tmp0, n, _state);
            if( k>0 )
            {
                ae_matrix_set_length(&q, k, n, _state);
                ae_vector_set_length(&r, k, _state);
            }
            
            /*
             * Generate problem
             */
            alpha = ae_randomreal(_state)+1.0;
            theta = ae_randomreal(_state)+1.0;
            tau = ae_randomreal(_state)*ae_randominteger(2, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 10*(1+ae_randomreal(_state));
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                d.ptr.p_double[i] = ae_randomreal(_state)+1;
                for(j=i+1; j<=n-1; j++)
                {
                    v = 0.1*ae_randomreal(_state)-0.05;
                    a.ptr.pp_double[i][j] = v;
                    a.ptr.pp_double[j][i] = v;
                }
                for(j=0; j<=k-1; j++)
                {
                    q.ptr.pp_double[j][i] = 2*ae_randomreal(_state)-1;
                }
            }
            for(i=0; i<=k-1; i++)
            {
                r.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            
            /*
             * Build model
             */
            cqminit(n, &s, _state);
            cqmseta(&s, &a, ae_fp_greater(ae_randomreal(_state),0.5), alpha, _state);
            cqmsetb(&s, &b, _state);
            cqmsetq(&s, &q, &r, k, theta, _state);
            cqmsetd(&s, &d, tau, _state);
            
            /*
             * Evaluate and compare:
             * * X          -   random point
             * * GE         -   "exact" gradient
             * * XTADX2     -   x'*(alpha*A+tau*D)*x/2
             * * ADXE       -   (alpha*A+tau*D)*x
             * * V          -   model value at X
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                ge.ptr.p_double[i] = 0.0;
            }
            v = 0.0;
            xtadx2 = 0.0;
            ae_vector_set_length(&adxe, n, _state);
            for(i=0; i<=n-1; i++)
            {
                adxe.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=n-1; i++)
            {
                v = v+x.ptr.p_double[i]*b.ptr.p_double[i];
                ge.ptr.p_double[i] = ge.ptr.p_double[i]+b.ptr.p_double[i];
                v = v+0.5*ae_sqr(x.ptr.p_double[i], _state)*tau*d.ptr.p_double[i];
                ge.ptr.p_double[i] = ge.ptr.p_double[i]+x.ptr.p_double[i]*tau*d.ptr.p_double[i];
                adxe.ptr.p_double[i] = adxe.ptr.p_double[i]+x.ptr.p_double[i]*tau*d.ptr.p_double[i];
                xtadx2 = xtadx2+0.5*ae_sqr(x.ptr.p_double[i], _state)*tau*d.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    v = v+0.5*alpha*x.ptr.p_double[i]*a.ptr.pp_double[i][j]*x.ptr.p_double[j];
                    ge.ptr.p_double[i] = ge.ptr.p_double[i]+alpha*a.ptr.pp_double[i][j]*x.ptr.p_double[j];
                    adxe.ptr.p_double[i] = adxe.ptr.p_double[i]+alpha*a.ptr.pp_double[i][j]*x.ptr.p_double[j];
                    xtadx2 = xtadx2+0.5*alpha*x.ptr.p_double[i]*a.ptr.pp_double[i][j]*x.ptr.p_double[j];
                }
            }
            for(i=0; i<=k-1; i++)
            {
                v2 = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                v = v+0.5*theta*ae_sqr(v2-r.ptr.p_double[i], _state);
                for(j=0; j<=n-1; j++)
                {
                    ge.ptr.p_double[j] = ge.ptr.p_double[j]+theta*(v2-r.ptr.p_double[i])*q.ptr.pp_double[i][j];
                }
            }
            v2 = cqmeval(&s, &x, _state);
            eval0errors = eval0errors||ae_fp_greater(ae_fabs(v-v2, _state),10000*ae_machineepsilon);
            cqmevalx(&s, &x, &v2, &noise, _state);
            eval0errors = eval0errors||ae_fp_greater(ae_fabs(v-v2, _state),10000*ae_machineepsilon);
            eval0errors = (eval0errors||ae_fp_less(noise,(double)(0)))||ae_fp_greater(noise,10000*ae_machineepsilon);
            v2 = cqmxtadx2(&s, &x, &tmp0, _state);
            eval0errors = eval0errors||ae_fp_greater(ae_fabs(xtadx2-v2, _state),10000*ae_machineepsilon);
            cqmgradunconstrained(&s, &x, &gt, _state);
            for(i=0; i<=n-1; i++)
            {
                eval0errors = eval0errors||ae_fp_greater(ae_fabs(ge.ptr.p_double[i]-gt.ptr.p_double[i], _state),10000*ae_machineepsilon);
            }
            cqmadx(&s, &x, &adx, _state);
            for(i=0; i<=n-1; i++)
            {
                eval0errors = eval0errors||ae_fp_greater(ae_fabs(adx.ptr.p_double[i]-adxe.ptr.p_double[i], _state),10000*ae_machineepsilon);
            }
        }
    }
    waserrors = waserrors||eval0errors;
    
    /*
     * Eval1 test: constrained model evaluation
     */
    eval1errors = ae_false;
    for(n=1; n<=5; n++)
    {
        for(k=0; k<=2*n; k++)
        {
            
            /*
             * Allocate place
             */
            ae_matrix_set_length(&a, n, n, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&activeset, n, _state);
            if( k>0 )
            {
                ae_matrix_set_length(&q, k, n, _state);
                ae_vector_set_length(&r, k, _state);
            }
            
            /*
             * Generate problem
             */
            alpha = ae_randomreal(_state)+1.0;
            theta = ae_randomreal(_state)+1.0;
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 10*(1+ae_randomreal(_state));
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                activeset.ptr.p_bool[i] = ae_fp_greater(ae_randomreal(_state),0.5);
                for(j=i+1; j<=n-1; j++)
                {
                    v = 0.1*ae_randomreal(_state)-0.05;
                    a.ptr.pp_double[i][j] = v;
                    a.ptr.pp_double[j][i] = v;
                }
                for(j=0; j<=k-1; j++)
                {
                    q.ptr.pp_double[j][i] = 2*ae_randomreal(_state)-1;
                }
            }
            for(i=0; i<=k-1; i++)
            {
                r.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            
            /*
             * Build model, evaluate at random point X, compare
             */
            cqminit(n, &s, _state);
            cqmseta(&s, &a, ae_fp_greater(ae_randomreal(_state),0.5), alpha, _state);
            cqmsetb(&s, &b, _state);
            cqmsetq(&s, &q, &r, k, theta, _state);
            cqmsetactiveset(&s, &xc, &activeset, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                if( !activeset.ptr.p_bool[i] )
                {
                    xc.ptr.p_double[i] = x.ptr.p_double[i];
                }
            }
            v = 0.0;
            for(i=0; i<=n-1; i++)
            {
                v = v+xc.ptr.p_double[i]*b.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    v = v+0.5*alpha*xc.ptr.p_double[i]*a.ptr.pp_double[i][j]*xc.ptr.p_double[j];
                }
            }
            for(i=0; i<=k-1; i++)
            {
                v2 = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &xc.ptr.p_double[0], 1, ae_v_len(0,n-1));
                v = v+0.5*theta*ae_sqr(v2-r.ptr.p_double[i], _state);
            }
            eval1errors = eval1errors||ae_fp_greater(ae_fabs(v-cqmeval(&s, &xc, _state), _state),10000*ae_machineepsilon);
            eval1errors = eval1errors||ae_fp_greater(ae_fabs(v-cqmdebugconstrainedevalt(&s, &x, _state), _state),10000*ae_machineepsilon);
            eval1errors = eval1errors||ae_fp_greater(ae_fabs(v-cqmdebugconstrainedevale(&s, &x, _state), _state),10000*ae_machineepsilon);
        }
    }
    waserrors = waserrors||eval1errors;
    
    /*
     * Eval2 test: we generate empty problem and apply sequence of random transformations,
     * re-evaluating and re-checking model after each modification.
     *
     * The purpose of such test is to ensure that our caching strategy works correctly.
     */
    eval2errors = ae_false;
    for(n=1; n<=5; n++)
    {
        kmax = 2*n;
        ae_matrix_set_length(&a, n, n, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&d, n, _state);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xc, n, _state);
        ae_matrix_set_length(&q, kmax, n, _state);
        ae_vector_set_length(&r, kmax, _state);
        ae_vector_set_length(&activeset, n, _state);
        ae_vector_set_length(&tmp0, n, _state);
        alpha = 0.0;
        theta = 0.0;
        k = 0;
        tau = 1.0+ae_randomreal(_state);
        for(i=0; i<=n-1; i++)
        {
            d.ptr.p_double[i] = 1.0;
            b.ptr.p_double[i] = 0.0;
            xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = (double)(0);
            }
        }
        cqminit(n, &s, _state);
        cqmsetd(&s, &d, tau, _state);
        for(pass=1; pass<=100; pass++)
        {
            
            /*
             * Select random modification type, apply modification.
             *
             * MKind is a random integer in [0,7] - number of specific 
             * modification to apply.
             */
            mkind = (double)(ae_randominteger(8, _state));
            if( ae_fp_eq(mkind,(double)(0)) )
            {
                
                /*
                 * Set non-zero D
                 */
                tau = 1.0+ae_randomreal(_state);
                for(i=0; i<=n-1; i++)
                {
                    d.ptr.p_double[i] = 2*ae_randomreal(_state)+1;
                }
                cqmsetd(&s, &d, tau, _state);
            }
            else
            {
                if( ae_fp_eq(mkind,(double)(1)) )
                {
                    
                    /*
                     * Set zero D.
                     * In case Alpha=0, set non-zero A.
                     */
                    if( ae_fp_eq(alpha,(double)(0)) )
                    {
                        alpha = 1.0+ae_randomreal(_state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i+1; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 0.2*ae_randomreal(_state)-0.1;
                                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            }
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            a.ptr.pp_double[i][i] = 4+2*ae_randomreal(_state);
                        }
                        cqmseta(&s, &a, ae_fp_greater(ae_randomreal(_state),0.5), alpha, _state);
                    }
                    tau = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        d.ptr.p_double[i] = (double)(0);
                    }
                    cqmsetd(&s, &d, 0.0, _state);
                }
                else
                {
                    if( ae_fp_eq(mkind,(double)(2)) )
                    {
                        
                        /*
                         * Set non-zero A
                         */
                        alpha = 1.0+ae_randomreal(_state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i+1; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 0.2*ae_randomreal(_state)-0.1;
                                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            }
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            a.ptr.pp_double[i][i] = 4+2*ae_randomreal(_state);
                        }
                        cqmseta(&s, &a, ae_fp_greater(ae_randomreal(_state),0.5), alpha, _state);
                    }
                    else
                    {
                        if( ae_fp_eq(mkind,(double)(3)) )
                        {
                            
                            /*
                             * Set zero A.
                             * In case Tau=0, set non-zero D.
                             */
                            if( ae_fp_eq(tau,(double)(0)) )
                            {
                                tau = 1.0+ae_randomreal(_state);
                                for(i=0; i<=n-1; i++)
                                {
                                    d.ptr.p_double[i] = 2*ae_randomreal(_state)+1;
                                }
                                cqmsetd(&s, &d, tau, _state);
                            }
                            alpha = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a.ptr.pp_double[i][j] = (double)(0);
                                }
                            }
                            cqmseta(&s, &a, ae_fp_greater(ae_randomreal(_state),0.5), alpha, _state);
                        }
                        else
                        {
                            if( ae_fp_eq(mkind,(double)(4)) )
                            {
                                
                                /*
                                 * Set B.
                                 */
                                for(i=0; i<=n-1; i++)
                                {
                                    b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                                }
                                cqmsetb(&s, &b, _state);
                            }
                            else
                            {
                                if( ae_fp_eq(mkind,(double)(5)) )
                                {
                                    
                                    /*
                                     * Set Q.
                                     */
                                    k = ae_randominteger(kmax+1, _state);
                                    theta = 1.0+ae_randomreal(_state);
                                    for(i=0; i<=k-1; i++)
                                    {
                                        r.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                                        for(j=0; j<=n-1; j++)
                                        {
                                            q.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                        }
                                    }
                                    cqmsetq(&s, &q, &r, k, theta, _state);
                                }
                                else
                                {
                                    if( ae_fp_eq(mkind,(double)(6)) )
                                    {
                                        
                                        /*
                                         * Set active set
                                         */
                                        for(i=0; i<=n-1; i++)
                                        {
                                            activeset.ptr.p_bool[i] = ae_fp_greater(ae_randomreal(_state),0.5);
                                            xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                                        }
                                        cqmsetactiveset(&s, &xc, &activeset, _state);
                                    }
                                    else
                                    {
                                        if( ae_fp_eq(mkind,(double)(7)) )
                                        {
                                            
                                            /*
                                             * Rewrite main diagonal
                                             */
                                            if( ae_fp_eq(alpha,(double)(0)) )
                                            {
                                                alpha = 1.0;
                                            }
                                            for(i=0; i<=n-1; i++)
                                            {
                                                tmp0.ptr.p_double[i] = 1+ae_randomreal(_state);
                                                a.ptr.pp_double[i][i] = tmp0.ptr.p_double[i]/alpha;
                                            }
                                            cqmrewritedensediagonal(&s, &tmp0, _state);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            /*
             * generate random point with respect to constraints,
             * test model at this point
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                if( activeset.ptr.p_bool[i] )
                {
                    x.ptr.p_double[i] = xc.ptr.p_double[i];
                }
            }
            v = 0.0;
            for(i=0; i<=n-1; i++)
            {
                v = v+x.ptr.p_double[i]*b.ptr.p_double[i];
            }
            if( ae_fp_greater(tau,(double)(0)) )
            {
                for(i=0; i<=n-1; i++)
                {
                    v = v+0.5*tau*d.ptr.p_double[i]*ae_sqr(x.ptr.p_double[i], _state);
                }
            }
            if( ae_fp_greater(alpha,(double)(0)) )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = v+0.5*alpha*x.ptr.p_double[i]*a.ptr.pp_double[i][j]*x.ptr.p_double[j];
                    }
                }
            }
            if( ae_fp_greater(theta,(double)(0)) )
            {
                for(i=0; i<=k-1; i++)
                {
                    v2 = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    v = v+0.5*theta*ae_sqr(v2-r.ptr.p_double[i], _state);
                }
            }
            v2 = cqmeval(&s, &x, _state);
            eval2errors = eval2errors||ae_fp_greater(ae_fabs(v-v2, _state),10000*ae_machineepsilon);
            v2 = cqmdebugconstrainedevalt(&s, &x, _state);
            eval2errors = eval2errors||ae_fp_greater(ae_fabs(v-v2, _state),10000*ae_machineepsilon);
            v2 = cqmdebugconstrainedevale(&s, &x, _state);
            eval2errors = eval2errors||ae_fp_greater(ae_fabs(v-v2, _state),10000*ae_machineepsilon);
        }
    }
    waserrors = waserrors||eval2errors;
    
    /*
     * Newton0 test: unconstrained optimization
     */
    newton0errors = ae_false;
    for(n=1; n<=5; n++)
    {
        for(k=0; k<=2*n; k++)
        {
            
            /*
             * Allocate place
             */
            ae_matrix_set_length(&a, n, n, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&x0, n, _state);
            if( k>0 )
            {
                ae_matrix_set_length(&q, k, n, _state);
                ae_vector_set_length(&r, k, _state);
            }
            
            /*
             * Generate problem with known solution x0:
             *   min f(x),
             *   f(x) = 0.5*(x-x0)'*A*(x-x0)
             *        = 0.5*x'*A*x + (-x0'*A)*x + 0.5*x0'*A*x0'
             */
            alpha = ae_randomreal(_state)+1.0;
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                a.ptr.pp_double[i][i] = 10*(1+ae_randomreal(_state));
                for(j=i+1; j<=n-1; j++)
                {
                    v = 0.1*ae_randomreal(_state)-0.05;
                    a.ptr.pp_double[i][j] = v;
                    a.ptr.pp_double[j][i] = v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = -alpha*v;
            }
            theta = ae_randomreal(_state)+1.0;
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    q.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                r.ptr.p_double[i] = v;
            }
            
            /*
             * Build model, evaluate at random point X, compare
             */
            cqminit(n, &s, _state);
            cqmseta(&s, &a, ae_fp_greater(ae_randomreal(_state),0.5), alpha, _state);
            cqmsetb(&s, &b, _state);
            cqmsetq(&s, &q, &r, k, theta, _state);
            cqmconstrainedoptimum(&s, &x, _state);
            for(i=0; i<=n-1; i++)
            {
                newton0errors = newton0errors||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-x0.ptr.p_double[i], _state),1.0E6*ae_machineepsilon);
            }
        }
    }
    waserrors = waserrors||newton0errors;
    
    /*
     * Newton1 test: constrained optimization
     */
    newton1errors = ae_false;
    h = 1.0E-3;
    for(n=1; n<=5; n++)
    {
        for(k=0; k<=2*n; k++)
        {
            
            /*
             * Allocate place
             */
            ae_matrix_set_length(&a, n, n, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&activeset, n, _state);
            if( k>0 )
            {
                ae_matrix_set_length(&q, k, n, _state);
                ae_vector_set_length(&r, k, _state);
            }
            
            /*
             * Generate test problem with unknown solution.
             */
            alpha = ae_randomreal(_state)+1.0;
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 10*(1+ae_randomreal(_state));
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                activeset.ptr.p_bool[i] = ae_fp_greater(ae_randomreal(_state),0.5);
                for(j=i+1; j<=n-1; j++)
                {
                    v = 0.1*ae_randomreal(_state)-0.05;
                    a.ptr.pp_double[i][j] = v;
                    a.ptr.pp_double[j][i] = v;
                }
            }
            theta = ae_randomreal(_state)+1.0;
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    q.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                r.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            
            /*
             * Build model, find solution
             */
            cqminit(n, &s, _state);
            cqmseta(&s, &a, ae_fp_greater(ae_randomreal(_state),0.5), alpha, _state);
            cqmsetb(&s, &b, _state);
            cqmsetq(&s, &q, &r, k, theta, _state);
            cqmsetactiveset(&s, &xc, &activeset, _state);
            if( cqmconstrainedoptimum(&s, &x, _state) )
            {
                
                /*
                 * Check that constraints are satisfied,
                 * and that solution is true optimum
                 */
                f0 = cqmeval(&s, &x, _state);
                for(i=0; i<=n-1; i++)
                {
                    newton1errors = newton1errors||(activeset.ptr.p_bool[i]&&ae_fp_neq(x.ptr.p_double[i],xc.ptr.p_double[i]));
                    if( !activeset.ptr.p_bool[i] )
                    {
                        v = x.ptr.p_double[i];
                        x.ptr.p_double[i] = v+h;
                        v2 = cqmeval(&s, &x, _state);
                        newton1errors = newton1errors||ae_fp_less(v2,f0);
                        x.ptr.p_double[i] = v-h;
                        v2 = cqmeval(&s, &x, _state);
                        newton1errors = newton1errors||ae_fp_less(v2,f0);
                        x.ptr.p_double[i] = v;
                    }
                }
            }
            else
            {
                newton1errors = ae_true;
            }
        }
    }
    waserrors = waserrors||newton1errors;
    
    /*
     * Newton2 test: we test ability to work with diagonal matrices, including
     * very large ones (up to 100.000 elements). This test checks that:
     * a) we can work with Alpha=0, i.e. when we have strictly diagonal A
     * b) diagonal problems are handled efficiently, i.e. algorithm will
     *    successfully solve problem with N=100.000
     *
     * Test problem:
     * * diagonal term D and rank-K term Q
     * * known solution X0,
     * * about 50% of constraints are active and equal to components of X0
     */
    newton2errors = ae_false;
    for(nkind=0; nkind<=5; nkind++)
    {
        for(k=0; k<=5; k++)
        {
            n = ae_round(ae_pow((double)(n), (double)(nkind), _state), _state);
            
            /*
             * generate problem
             */
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&activeset, n, _state);
            if( k>0 )
            {
                ae_matrix_set_length(&q, k, n, _state);
                ae_vector_set_length(&r, k, _state);
            }
            tau = 1+ae_randomreal(_state);
            theta = 1+ae_randomreal(_state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                d.ptr.p_double[i] = 1+ae_randomreal(_state);
                b.ptr.p_double[i] = -x0.ptr.p_double[i]*tau*d.ptr.p_double[i];
                activeset.ptr.p_bool[i] = ae_fp_greater(ae_randomreal(_state),0.5);
            }
            for(i=0; i<=k-1; i++)
            {
                v = 0.0;
                for(j=0; j<=n-1; j++)
                {
                    q.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    v = v+q.ptr.pp_double[i][j]*x0.ptr.p_double[j];
                }
                r.ptr.p_double[i] = v;
            }
            
            /*
             * Solve, test
             */
            cqminit(n, &s, _state);
            cqmsetb(&s, &b, _state);
            cqmsetd(&s, &d, tau, _state);
            cqmsetq(&s, &q, &r, k, theta, _state);
            cqmsetactiveset(&s, &x0, &activeset, _state);
            if( cqmconstrainedoptimum(&s, &x, _state) )
            {
                
                /*
                 * Check that constraints are satisfied,
                 * and that solution is true optimum
                 */
                f0 = cqmeval(&s, &x, _state);
                for(i=0; i<=n-1; i++)
                {
                    newton2errors = newton2errors||(activeset.ptr.p_bool[i]&&ae_fp_neq(x.ptr.p_double[i],x0.ptr.p_double[i]));
                    newton2errors = newton2errors||(!activeset.ptr.p_bool[i]&&ae_fp_greater(ae_fabs(x.ptr.p_double[i]-x0.ptr.p_double[i], _state),1000*ae_machineepsilon));
                }
                
                /*
                 * Check that constrained evaluation at some point gives correct results
                 */
                for(i=0; i<=n-1; i++)
                {
                    if( activeset.ptr.p_bool[i] )
                    {
                        x.ptr.p_double[i] = x0.ptr.p_double[i];
                    }
                    else
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                }
                v = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+0.5*tau*d.ptr.p_double[i]*ae_sqr(x.ptr.p_double[i], _state)+x.ptr.p_double[i]*b.ptr.p_double[i];
                }
                for(i=0; i<=k-1; i++)
                {
                    v2 = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    v = v+0.5*theta*ae_sqr(v2-r.ptr.p_double[i], _state);
                }
                v2 = cqmeval(&s, &x, _state);
                newton2errors = (newton2errors||!ae_isfinite(v2, _state))||ae_fp_greater(ae_fabs(v-v2, _state),10000*ae_machineepsilon);
                v2 = cqmdebugconstrainedevalt(&s, &x, _state);
                newton2errors = (newton2errors||!ae_isfinite(v2, _state))||ae_fp_greater(ae_fabs(v-v2, _state),10000*ae_machineepsilon);
                v2 = cqmdebugconstrainedevale(&s, &x, _state);
                newton2errors = (newton2errors||!ae_isfinite(v2, _state))||ae_fp_greater(ae_fabs(v-v2, _state),10000*ae_machineepsilon);
            }
            else
            {
                newton2errors = ae_true;
            }
        }
    }
    waserrors = waserrors||newton2errors;
    
    /*
     * report
     */
    if( !silent )
    {
        printf("TESTING CONVEX QUADRATIC MODELS\n");
        printf("Eval0 test:                               ");
        if( eval0errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("Eval1 test:                               ");
        if( eval1errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("Eval2 test:                               ");
        if( eval2errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("Newton0 test:                             ");
        if( newton0errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("Newton1 test:                             ");
        if( newton1errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("Newton2 test:                             ");
        if( newton2errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testbdsvdunit_fillidentity(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static void testbdsvdunit_fillsparsede(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testbdsvdunit_getbdsvderror(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool isupper,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_matrix* c,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_state *_state);
static void testbdsvdunit_checksvdmultiplication(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool isupper,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_matrix* c,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* err,
     ae_state *_state);
static void testbdsvdunit_testbdsvdproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_bool* wfailed,
     ae_int_t* failcount,
     ae_int_t* succcount,
     ae_state *_state);





/*************************************************************************
Testing bidiagonal SVD decomposition subroutine
*************************************************************************/
ae_bool testbdsvd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector d;
    ae_vector e;
    ae_matrix mempty;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t pass;
    ae_bool waserrors;
    ae_bool wsorted;
    ae_bool wfailed;
    double materr;
    double orterr;
    double threshold;
    double failr;
    ae_int_t failcount;
    ae_int_t succcount;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&d, 0, sizeof(d));
    memset(&e, 0, sizeof(e));
    memset(&mempty, 0, sizeof(mempty));
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mempty, 0, 0, DT_REAL, _state, ae_true);

    failcount = 0;
    succcount = 0;
    materr = (double)(0);
    orterr = (double)(0);
    wsorted = ae_true;
    wfailed = ae_false;
    waserrors = ae_false;
    maxn = 15;
    threshold = 5*100*ae_machineepsilon;
    ae_vector_set_length(&d, maxn-1+1, _state);
    ae_vector_set_length(&e, maxn-2+1, _state);
    
    /*
     * special case: zero divide matrix
     * unfixed LAPACK routine should fail on this problem
     */
    n = 7;
    d.ptr.p_double[0] = -6.96462904751731892700e-01;
    d.ptr.p_double[1] = 0.00000000000000000000e+00;
    d.ptr.p_double[2] = -5.73827770385971991400e-01;
    d.ptr.p_double[3] = -6.62562624399371191700e-01;
    d.ptr.p_double[4] = 5.82737148001782223600e-01;
    d.ptr.p_double[5] = 3.84825263580925003300e-01;
    d.ptr.p_double[6] = 9.84087420830525472200e-01;
    e.ptr.p_double[0] = -7.30307931760612871800e-02;
    e.ptr.p_double[1] = -2.30079042939542843800e-01;
    e.ptr.p_double[2] = -6.87824621739351216300e-01;
    e.ptr.p_double[3] = -1.77306437707837570600e-02;
    e.ptr.p_double[4] = 1.78285126526551632000e-15;
    e.ptr.p_double[5] = -4.89434737751289969400e-02;
    rmatrixbdsvd(&d, &e, n, ae_true, ae_false, &mempty, 0, &mempty, 0, &mempty, 0, _state);
    
    /*
     * zero matrix, several cases
     */
    for(i=0; i<=maxn-1; i++)
    {
        d.ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=maxn-2; i++)
    {
        e.ptr.p_double[i] = (double)(0);
    }
    for(n=1; n<=maxn; n++)
    {
        testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
    }
    
    /*
     * Dense matrix
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            for(i=0; i<=maxn-1; i++)
            {
                d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=maxn-2; i++)
            {
                e.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
        }
    }
    
    /*
     * Sparse matrices, very sparse matrices, incredible sparse matrices
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            testbdsvdunit_fillsparsede(&d, &e, n, 0.5, _state);
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
            testbdsvdunit_fillsparsede(&d, &e, n, 0.8, _state);
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
            testbdsvdunit_fillsparsede(&d, &e, n, 0.9, _state);
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
            testbdsvdunit_fillsparsede(&d, &e, n, 0.95, _state);
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
        }
    }
    
    /*
     * report
     */
    failr = (double)failcount/(double)(succcount+failcount);
    waserrors = ((wfailed||ae_fp_greater(materr,threshold))||ae_fp_greater(orterr,threshold))||!wsorted;
    if( !silent )
    {
        printf("TESTING BIDIAGONAL SVD DECOMPOSITION\n");
        printf("SVD decomposition error:                 %5.3e\n",
            (double)(materr));
        printf("SVD orthogonality error:                 %5.3e\n",
            (double)(orterr));
        printf("Singular values order:                   ");
        if( wsorted )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Always converged:                        ");
        if( !wfailed )
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
            printf("Fail ratio:                              %5.3f\n",
                (double)(failr));
        }
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testbdsvdunit_fillidentity(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    ae_matrix_set_length(a, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( i==j )
            {
                a->ptr.pp_double[i][j] = (double)(1);
            }
            else
            {
                a->ptr.pp_double[i][j] = (double)(0);
            }
        }
    }
}


static void testbdsvdunit_fillsparsede(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;


    ae_vector_set_length(d, n-1+1, _state);
    ae_vector_set_length(e, ae_maxint(0, n-2, _state)+1, _state);
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
        {
            d->ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        else
        {
            d->ptr.p_double[i] = (double)(0);
        }
    }
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
        {
            e->ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        else
        {
            e->ptr.p_double[i] = (double)(0);
        }
    }
}


static void testbdsvdunit_getbdsvderror(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool isupper,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_matrix* c,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double locerr;
    double sm;


    
    /*
     * decomposition error
     */
    locerr = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            sm = (double)(0);
            for(k=0; k<=n-1; k++)
            {
                sm = sm+w->ptr.p_double[k]*u->ptr.pp_double[i][k]*vt->ptr.pp_double[k][j];
            }
            if( isupper )
            {
                if( i==j )
                {
                    locerr = ae_maxreal(locerr, ae_fabs(d->ptr.p_double[i]-sm, _state), _state);
                }
                else
                {
                    if( i==j-1 )
                    {
                        locerr = ae_maxreal(locerr, ae_fabs(e->ptr.p_double[i]-sm, _state), _state);
                    }
                    else
                    {
                        locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
                    }
                }
            }
            else
            {
                if( i==j )
                {
                    locerr = ae_maxreal(locerr, ae_fabs(d->ptr.p_double[i]-sm, _state), _state);
                }
                else
                {
                    if( i-1==j )
                    {
                        locerr = ae_maxreal(locerr, ae_fabs(e->ptr.p_double[j]-sm, _state), _state);
                    }
                    else
                    {
                        locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
                    }
                }
            }
        }
    }
    *materr = ae_maxreal(*materr, locerr, _state);
    
    /*
     * check for C = U'
     * we consider it as decomposition error
     */
    locerr = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            locerr = ae_maxreal(locerr, ae_fabs(u->ptr.pp_double[i][j]-c->ptr.pp_double[j][i], _state), _state);
        }
    }
    *materr = ae_maxreal(*materr, locerr, _state);
    
    /*
     * orthogonality error
     */
    locerr = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=i; j<=n-1; j++)
        {
            sm = ae_v_dotproduct(&u->ptr.pp_double[0][i], u->stride, &u->ptr.pp_double[0][j], u->stride, ae_v_len(0,n-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm-1, _state), _state);
            }
            sm = ae_v_dotproduct(&vt->ptr.pp_double[i][0], 1, &vt->ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm-1, _state), _state);
            }
        }
    }
    *orterr = ae_maxreal(*orterr, locerr, _state);
    
    /*
     * values order error
     */
    for(i=1; i<=n-1; i++)
    {
        if( ae_fp_greater(w->ptr.p_double[i],w->ptr.p_double[i-1]) )
        {
            *wsorted = ae_false;
        }
    }
}


static void testbdsvdunit_checksvdmultiplication(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool isupper,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_matrix* c,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_vector wt;
    ae_matrix u2;
    ae_matrix c2;
    ae_matrix vt2;
    ae_matrix u1;
    ae_matrix c1;
    ae_matrix vt1;
    ae_int_t nru;
    ae_int_t ncc;
    ae_int_t ncvt;
    ae_int_t pass;
    hqrndstate rs;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&wt, 0, sizeof(wt));
    memset(&u2, 0, sizeof(u2));
    memset(&c2, 0, sizeof(c2));
    memset(&vt2, 0, sizeof(vt2));
    memset(&u1, 0, sizeof(u1));
    memset(&c1, 0, sizeof(c1));
    memset(&vt1, 0, sizeof(vt1));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&wt, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt1, 0, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    ae_vector_set_length(&wt, n, _state);
    
    /*
     * Perform nonsquare SVD
     */
    for(pass=1; pass<=20; pass++)
    {
        
        /*
         * Problem size
         */
        nru = hqrnduniformi(&rs, 2*n, _state);
        ncc = hqrnduniformi(&rs, 2*n, _state);
        ncvt = hqrnduniformi(&rs, 2*n, _state);
        
        /*
         * Reference matrices (copy 1) and working matrices (copy 2)
         */
        for(i=0; i<=n-1; i++)
        {
            wt.ptr.p_double[i] = d->ptr.p_double[i];
        }
        if( nru>0 )
        {
            
            /*
             * init U1/U2
             */
            ae_matrix_set_length(&u1, nru, n, _state);
            ae_matrix_set_length(&u2, nru, n, _state);
            for(i=0; i<=u1.rows-1; i++)
            {
                for(j=0; j<=u1.cols-1; j++)
                {
                    u1.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    u2.ptr.pp_double[i][j] = u1.ptr.pp_double[i][j];
                }
            }
        }
        else
        {
            
            /*
             * Set U1/U2 to 1x1 matrices; working with 1x1 matrices allows
             * to test correctness of code which passes them to MKL.
             */
            ae_matrix_set_length(&u1, 1, 1, _state);
            ae_matrix_set_length(&u2, 1, 1, _state);
        }
        if( ncc>0 )
        {
            ae_matrix_set_length(&c1, n, ncc, _state);
            ae_matrix_set_length(&c2, n, ncc, _state);
            for(i=0; i<=c1.rows-1; i++)
            {
                for(j=0; j<=c1.cols-1; j++)
                {
                    c1.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    c2.ptr.pp_double[i][j] = c1.ptr.pp_double[i][j];
                }
            }
        }
        else
        {
            
            /*
             * Set C1/C1 to 1x1 matrices; working with 1x1 matrices allows
             * to test correctness of code which passes them to MKL.
             */
            ae_matrix_set_length(&c1, 1, 1, _state);
            ae_matrix_set_length(&c2, 1, 1, _state);
        }
        if( ncvt>0 )
        {
            ae_matrix_set_length(&vt1, n, ncvt, _state);
            ae_matrix_set_length(&vt2, n, ncvt, _state);
            for(i=0; i<=vt1.rows-1; i++)
            {
                for(j=0; j<=vt1.cols-1; j++)
                {
                    vt1.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    vt2.ptr.pp_double[i][j] = vt1.ptr.pp_double[i][j];
                }
            }
        }
        else
        {
            
            /*
             * Set VT1/VT1 to 1x1 matrices; working with 1x1 matrices allows
             * to test correctness of code which passes them to MKL.
             */
            ae_matrix_set_length(&vt1, 1, 1, _state);
            ae_matrix_set_length(&vt2, 1, 1, _state);
        }
        
        /*
         * SVD with non-square U/C/VT
         */
        if( !rmatrixbdsvd(&wt, e, n, isupper, ae_fp_greater(hqrnduniformr(&rs, _state),(double)(0)), &u2, nru, &c2, ncc, &vt2, ncvt, _state) )
        {
            *err = 1.0;
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=nru-1; i++)
        {
            for(j=0; j<=u2.cols-1; j++)
            {
                v = ae_v_dotproduct(&u1.ptr.pp_double[i][0], 1, &u->ptr.pp_double[0][j], u->stride, ae_v_len(0,n-1));
                *err = ae_maxreal(*err, ae_fabs(v-u2.ptr.pp_double[i][j], _state), _state);
            }
        }
        for(i=0; i<=c2.rows-1; i++)
        {
            for(j=0; j<=ncc-1; j++)
            {
                v = ae_v_dotproduct(&c->ptr.pp_double[i][0], 1, &c1.ptr.pp_double[0][j], c1.stride, ae_v_len(0,n-1));
                *err = ae_maxreal(*err, ae_fabs(v-c2.ptr.pp_double[i][j], _state), _state);
            }
        }
        for(i=0; i<=vt2.rows-1; i++)
        {
            for(j=0; j<=ncvt-1; j++)
            {
                v = ae_v_dotproduct(&vt->ptr.pp_double[i][0], 1, &vt1.ptr.pp_double[0][j], vt1.stride, ae_v_len(0,n-1));
                *err = ae_maxreal(*err, ae_fabs(v-vt2.ptr.pp_double[i][j], _state), _state);
            }
        }
    }
    ae_frame_leave(_state);
}


static void testbdsvdunit_testbdsvdproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_bool* wfailed,
     ae_int_t* failcount,
     ae_int_t* succcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix u;
    ae_matrix vt;
    ae_matrix c;
    ae_vector w;
    ae_int_t i;
    double mx;

    ae_frame_make(_state, &_frame_block);
    memset(&u, 0, sizeof(u));
    memset(&vt, 0, sizeof(vt));
    memset(&c, 0, sizeof(c));
    memset(&w, 0, sizeof(w));
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    mx = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(ae_fabs(d->ptr.p_double[i], _state),mx) )
        {
            mx = ae_fabs(d->ptr.p_double[i], _state);
        }
    }
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_greater(ae_fabs(e->ptr.p_double[i], _state),mx) )
        {
            mx = ae_fabs(e->ptr.p_double[i], _state);
        }
    }
    if( ae_fp_eq(mx,(double)(0)) )
    {
        mx = (double)(1);
    }
    
    /*
     * Upper BDSVD tests
     */
    ae_vector_set_length(&w, n-1+1, _state);
    testbdsvdunit_fillidentity(&u, n, _state);
    testbdsvdunit_fillidentity(&vt, n, _state);
    testbdsvdunit_fillidentity(&c, n, _state);
    for(i=0; i<=n-1; i++)
    {
        w.ptr.p_double[i] = d->ptr.p_double[i];
    }
    if( !rmatrixbdsvd(&w, e, n, ae_true, ae_false, &u, n, &c, n, &vt, n, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testbdsvdunit_getbdsvderror(d, e, n, ae_true, &u, &c, &w, &vt, materr, orterr, wsorted, _state);
    testbdsvdunit_checksvdmultiplication(d, e, n, ae_true, &u, &c, &w, &vt, materr, _state);
    testbdsvdunit_fillidentity(&u, n, _state);
    testbdsvdunit_fillidentity(&vt, n, _state);
    testbdsvdunit_fillidentity(&c, n, _state);
    for(i=0; i<=n-1; i++)
    {
        w.ptr.p_double[i] = d->ptr.p_double[i];
    }
    if( !rmatrixbdsvd(&w, e, n, ae_true, ae_true, &u, n, &c, n, &vt, n, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testbdsvdunit_getbdsvderror(d, e, n, ae_true, &u, &c, &w, &vt, materr, orterr, wsorted, _state);
    testbdsvdunit_checksvdmultiplication(d, e, n, ae_true, &u, &c, &w, &vt, materr, _state);
    
    /*
     * Lower BDSVD tests
     */
    ae_vector_set_length(&w, n-1+1, _state);
    testbdsvdunit_fillidentity(&u, n, _state);
    testbdsvdunit_fillidentity(&vt, n, _state);
    testbdsvdunit_fillidentity(&c, n, _state);
    for(i=0; i<=n-1; i++)
    {
        w.ptr.p_double[i] = d->ptr.p_double[i];
    }
    if( !rmatrixbdsvd(&w, e, n, ae_false, ae_false, &u, n, &c, n, &vt, n, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testbdsvdunit_getbdsvderror(d, e, n, ae_false, &u, &c, &w, &vt, materr, orterr, wsorted, _state);
    testbdsvdunit_checksvdmultiplication(d, e, n, ae_false, &u, &c, &w, &vt, materr, _state);
    testbdsvdunit_fillidentity(&u, n, _state);
    testbdsvdunit_fillidentity(&vt, n, _state);
    testbdsvdunit_fillidentity(&c, n, _state);
    for(i=0; i<=n-1; i++)
    {
        w.ptr.p_double[i] = d->ptr.p_double[i];
    }
    if( !rmatrixbdsvd(&w, e, n, ae_false, ae_true, &u, n, &c, n, &vt, n, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testbdsvdunit_getbdsvderror(d, e, n, ae_false, &u, &c, &w, &vt, materr, orterr, wsorted, _state);
    testbdsvdunit_checksvdmultiplication(d, e, n, ae_false, &u, &c, &w, &vt, materr, _state);
    
    /*
     * update counter
     */
    *succcount = *succcount+1;
    ae_frame_leave(_state);
}



static void testblasunit_naivematrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     double alpha,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     double beta,
     ae_state *_state);





ae_bool testblas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t i;
    ae_int_t i1;
    ae_int_t i2;
    ae_int_t j;
    ae_int_t j1;
    ae_int_t j2;
    ae_int_t l;
    ae_int_t k;
    ae_int_t r;
    ae_int_t i3;
    ae_int_t j3;
    ae_int_t col1;
    ae_int_t col2;
    ae_int_t row1;
    ae_int_t row2;
    ae_vector x1;
    ae_vector x2;
    ae_matrix a;
    ae_matrix b;
    ae_matrix c1;
    ae_matrix c2;
    double err;
    double e1;
    double e2;
    double e3;
    double v;
    double scl1;
    double scl2;
    double scl3;
    ae_bool was1;
    ae_bool was2;
    ae_bool trans1;
    ae_bool trans2;
    double threshold;
    ae_bool n2errors;
    ae_bool hsnerrors;
    ae_bool amaxerrors;
    ae_bool mverrors;
    ae_bool iterrors;
    ae_bool cterrors;
    ae_bool mmerrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&c1, 0, sizeof(c1));
    memset(&c2, 0, sizeof(c2));
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c2, 0, 0, DT_REAL, _state, ae_true);

    n2errors = ae_false;
    amaxerrors = ae_false;
    hsnerrors = ae_false;
    mverrors = ae_false;
    iterrors = ae_false;
    cterrors = ae_false;
    mmerrors = ae_false;
    waserrors = ae_false;
    threshold = 10000*ae_machineepsilon;
    
    /*
     * Test Norm2
     */
    passcount = 1000;
    e1 = (double)(0);
    e2 = (double)(0);
    e3 = (double)(0);
    scl2 = 0.5*ae_maxrealnumber;
    scl3 = 2*ae_minrealnumber;
    for(pass=1; pass<=passcount; pass++)
    {
        n = 1+ae_randominteger(1000, _state);
        i1 = ae_randominteger(10, _state);
        i2 = n+i1-1;
        ae_vector_set_length(&x1, i2+1, _state);
        ae_vector_set_length(&x2, i2+1, _state);
        for(i=i1; i<=i2; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        v = (double)(0);
        for(i=i1; i<=i2; i++)
        {
            v = v+ae_sqr(x1.ptr.p_double[i], _state);
        }
        v = ae_sqrt(v, _state);
        e1 = ae_maxreal(e1, ae_fabs(v-vectornorm2(&x1, i1, i2, _state), _state), _state);
        for(i=i1; i<=i2; i++)
        {
            x2.ptr.p_double[i] = scl2*x1.ptr.p_double[i];
        }
        e2 = ae_maxreal(e2, ae_fabs(v*scl2-vectornorm2(&x2, i1, i2, _state), _state), _state);
        for(i=i1; i<=i2; i++)
        {
            x2.ptr.p_double[i] = scl3*x1.ptr.p_double[i];
        }
        e3 = ae_maxreal(e3, ae_fabs(v*scl3-vectornorm2(&x2, i1, i2, _state), _state), _state);
    }
    e2 = e2/scl2;
    e3 = e3/scl3;
    n2errors = (ae_fp_greater_eq(e1,threshold)||ae_fp_greater_eq(e2,threshold))||ae_fp_greater_eq(e3,threshold);
    
    /*
     * Testing VectorAbsMax, Column/Row AbsMax
     */
    ae_vector_set_length(&x1, 5+1, _state);
    x1.ptr.p_double[1] = 2.0;
    x1.ptr.p_double[2] = 0.2;
    x1.ptr.p_double[3] = -1.3;
    x1.ptr.p_double[4] = 0.7;
    x1.ptr.p_double[5] = -3.0;
    amaxerrors = (vectoridxabsmax(&x1, 1, 5, _state)!=5||vectoridxabsmax(&x1, 1, 4, _state)!=1)||vectoridxabsmax(&x1, 2, 4, _state)!=3;
    n = 30;
    ae_vector_set_length(&x1, n+1, _state);
    ae_matrix_set_length(&a, n+1, n+1, _state);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
        }
    }
    was1 = ae_false;
    was2 = ae_false;
    for(pass=1; pass<=1000; pass++)
    {
        j = 1+ae_randominteger(n, _state);
        i1 = 1+ae_randominteger(n, _state);
        i2 = i1+ae_randominteger(n+1-i1, _state);
        ae_v_move(&x1.ptr.p_double[i1], 1, &a.ptr.pp_double[i1][j], a.stride, ae_v_len(i1,i2));
        if( vectoridxabsmax(&x1, i1, i2, _state)!=columnidxabsmax(&a, i1, i2, j, _state) )
        {
            was1 = ae_true;
        }
        i = 1+ae_randominteger(n, _state);
        j1 = 1+ae_randominteger(n, _state);
        j2 = j1+ae_randominteger(n+1-j1, _state);
        ae_v_move(&x1.ptr.p_double[j1], 1, &a.ptr.pp_double[i][j1], 1, ae_v_len(j1,j2));
        if( vectoridxabsmax(&x1, j1, j2, _state)!=rowidxabsmax(&a, j1, j2, i, _state) )
        {
            was2 = ae_true;
        }
    }
    amaxerrors = (amaxerrors||was1)||was2;
    
    /*
     * Testing upper Hessenberg 1-norm
     */
    ae_matrix_set_length(&a, 3+1, 3+1, _state);
    ae_vector_set_length(&x1, 3+1, _state);
    a.ptr.pp_double[1][1] = (double)(2);
    a.ptr.pp_double[1][2] = (double)(3);
    a.ptr.pp_double[1][3] = (double)(1);
    a.ptr.pp_double[2][1] = (double)(4);
    a.ptr.pp_double[2][2] = (double)(-5);
    a.ptr.pp_double[2][3] = (double)(8);
    a.ptr.pp_double[3][1] = (double)(99);
    a.ptr.pp_double[3][2] = (double)(3);
    a.ptr.pp_double[3][3] = (double)(1);
    hsnerrors = ae_fp_greater(ae_fabs(upperhessenberg1norm(&a, 1, 3, 1, 3, &x1, _state)-11, _state),threshold);
    
    /*
     * Testing MatrixVectorMultiply
     */
    ae_matrix_set_length(&a, 3+1, 5+1, _state);
    ae_vector_set_length(&x1, 3+1, _state);
    ae_vector_set_length(&x2, 2+1, _state);
    a.ptr.pp_double[2][3] = (double)(2);
    a.ptr.pp_double[2][4] = (double)(-1);
    a.ptr.pp_double[2][5] = (double)(-1);
    a.ptr.pp_double[3][3] = (double)(1);
    a.ptr.pp_double[3][4] = (double)(-2);
    a.ptr.pp_double[3][5] = (double)(2);
    x1.ptr.p_double[1] = (double)(1);
    x1.ptr.p_double[2] = (double)(2);
    x1.ptr.p_double[3] = (double)(1);
    x2.ptr.p_double[1] = (double)(-1);
    x2.ptr.p_double[2] = (double)(-1);
    matrixvectormultiply(&a, 2, 3, 3, 5, ae_false, &x1, 1, 3, 1.0, &x2, 1, 2, 1.0, _state);
    matrixvectormultiply(&a, 2, 3, 3, 5, ae_true, &x2, 1, 2, 1.0, &x1, 1, 3, 1.0, _state);
    e1 = ae_fabs(x1.ptr.p_double[1]+5, _state)+ae_fabs(x1.ptr.p_double[2]-8, _state)+ae_fabs(x1.ptr.p_double[3]+1, _state)+ae_fabs(x2.ptr.p_double[1]+2, _state)+ae_fabs(x2.ptr.p_double[2]+2, _state);
    x1.ptr.p_double[1] = (double)(1);
    x1.ptr.p_double[2] = (double)(2);
    x1.ptr.p_double[3] = (double)(1);
    x2.ptr.p_double[1] = (double)(-1);
    x2.ptr.p_double[2] = (double)(-1);
    matrixvectormultiply(&a, 2, 3, 3, 5, ae_false, &x1, 1, 3, 1.0, &x2, 1, 2, 0.0, _state);
    matrixvectormultiply(&a, 2, 3, 3, 5, ae_true, &x2, 1, 2, 1.0, &x1, 1, 3, 0.0, _state);
    e2 = ae_fabs(x1.ptr.p_double[1]+3, _state)+ae_fabs(x1.ptr.p_double[2]-3, _state)+ae_fabs(x1.ptr.p_double[3]+1, _state)+ae_fabs(x2.ptr.p_double[1]+1, _state)+ae_fabs(x2.ptr.p_double[2]+1, _state);
    mverrors = ae_fp_greater_eq(e1+e2,threshold);
    
    /*
     * testing inplace transpose
     */
    n = 10;
    ae_matrix_set_length(&a, n+1, n+1, _state);
    ae_matrix_set_length(&b, n+1, n+1, _state);
    ae_vector_set_length(&x1, n-1+1, _state);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a.ptr.pp_double[i][j] = ae_randomreal(_state);
        }
    }
    passcount = 10000;
    was1 = ae_false;
    for(pass=1; pass<=passcount; pass++)
    {
        i1 = 1+ae_randominteger(n, _state);
        i2 = i1+ae_randominteger(n-i1+1, _state);
        j1 = 1+ae_randominteger(n-(i2-i1), _state);
        j2 = j1+(i2-i1);
        copymatrix(&a, i1, i2, j1, j2, &b, i1, i2, j1, j2, _state);
        inplacetranspose(&b, i1, i2, j1, j2, &x1, _state);
        for(i=i1; i<=i2; i++)
        {
            for(j=j1; j<=j2; j++)
            {
                if( ae_fp_neq(a.ptr.pp_double[i][j],b.ptr.pp_double[i1+(j-j1)][j1+(i-i1)]) )
                {
                    was1 = ae_true;
                }
            }
        }
    }
    iterrors = was1;
    
    /*
     * testing copy and transpose
     */
    n = 10;
    ae_matrix_set_length(&a, n+1, n+1, _state);
    ae_matrix_set_length(&b, n+1, n+1, _state);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a.ptr.pp_double[i][j] = ae_randomreal(_state);
        }
    }
    passcount = 10000;
    was1 = ae_false;
    for(pass=1; pass<=passcount; pass++)
    {
        i1 = 1+ae_randominteger(n, _state);
        i2 = i1+ae_randominteger(n-i1+1, _state);
        j1 = 1+ae_randominteger(n, _state);
        j2 = j1+ae_randominteger(n-j1+1, _state);
        copyandtranspose(&a, i1, i2, j1, j2, &b, j1, j2, i1, i2, _state);
        for(i=i1; i<=i2; i++)
        {
            for(j=j1; j<=j2; j++)
            {
                if( ae_fp_neq(a.ptr.pp_double[i][j],b.ptr.pp_double[j][i]) )
                {
                    was1 = ae_true;
                }
            }
        }
    }
    cterrors = was1;
    
    /*
     * Testing MatrixMatrixMultiply
     */
    n = 10;
    ae_matrix_set_length(&a, 2*n+1, 2*n+1, _state);
    ae_matrix_set_length(&b, 2*n+1, 2*n+1, _state);
    ae_matrix_set_length(&c1, 2*n+1, 2*n+1, _state);
    ae_matrix_set_length(&c2, 2*n+1, 2*n+1, _state);
    ae_vector_set_length(&x1, n+1, _state);
    ae_vector_set_length(&x2, n+1, _state);
    for(i=1; i<=2*n; i++)
    {
        for(j=1; j<=2*n; j++)
        {
            a.ptr.pp_double[i][j] = ae_randomreal(_state);
            b.ptr.pp_double[i][j] = ae_randomreal(_state);
        }
    }
    passcount = 1000;
    was1 = ae_false;
    for(pass=1; pass<=passcount; pass++)
    {
        for(i=1; i<=2*n; i++)
        {
            for(j=1; j<=2*n; j++)
            {
                c1.ptr.pp_double[i][j] = 2.1*i+3.1*j;
                c2.ptr.pp_double[i][j] = c1.ptr.pp_double[i][j];
            }
        }
        l = 1+ae_randominteger(n, _state);
        k = 1+ae_randominteger(n, _state);
        r = 1+ae_randominteger(n, _state);
        i1 = 1+ae_randominteger(n, _state);
        j1 = 1+ae_randominteger(n, _state);
        i2 = 1+ae_randominteger(n, _state);
        j2 = 1+ae_randominteger(n, _state);
        i3 = 1+ae_randominteger(n, _state);
        j3 = 1+ae_randominteger(n, _state);
        trans1 = ae_fp_greater(ae_randomreal(_state),0.5);
        trans2 = ae_fp_greater(ae_randomreal(_state),0.5);
        if( trans1 )
        {
            col1 = l;
            row1 = k;
        }
        else
        {
            col1 = k;
            row1 = l;
        }
        if( trans2 )
        {
            col2 = k;
            row2 = r;
        }
        else
        {
            col2 = r;
            row2 = k;
        }
        scl1 = ae_randomreal(_state);
        scl2 = ae_randomreal(_state);
        matrixmatrixmultiply(&a, i1, i1+row1-1, j1, j1+col1-1, trans1, &b, i2, i2+row2-1, j2, j2+col2-1, trans2, scl1, &c1, i3, i3+l-1, j3, j3+r-1, scl2, &x1, _state);
        testblasunit_naivematrixmatrixmultiply(&a, i1, i1+row1-1, j1, j1+col1-1, trans1, &b, i2, i2+row2-1, j2, j2+col2-1, trans2, scl1, &c2, i3, i3+l-1, j3, j3+r-1, scl2, _state);
        err = (double)(0);
        for(i=1; i<=l; i++)
        {
            for(j=1; j<=r; j++)
            {
                err = ae_maxreal(err, ae_fabs(c1.ptr.pp_double[i3+i-1][j3+j-1]-c2.ptr.pp_double[i3+i-1][j3+j-1], _state), _state);
            }
        }
        if( ae_fp_greater(err,threshold) )
        {
            was1 = ae_true;
            break;
        }
    }
    mmerrors = was1;
    
    /*
     * report
     */
    waserrors = (((((n2errors||amaxerrors)||hsnerrors)||mverrors)||iterrors)||cterrors)||mmerrors;
    if( !silent )
    {
        printf("TESTING BLAS\n");
        printf("VectorNorm2:                             ");
        if( n2errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("AbsMax (vector/row/column):              ");
        if( amaxerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("UpperHessenberg1Norm:                    ");
        if( hsnerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("MatrixVectorMultiply:                    ");
        if( mverrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("InplaceTranspose:                        ");
        if( iterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CopyAndTranspose:                        ");
        if( cterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("MatrixMatrixMultiply:                    ");
        if( mmerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testblasunit_naivematrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     double alpha,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     double beta,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t arows;
    ae_int_t acols;
    ae_int_t brows;
    ae_int_t bcols;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    ae_int_t r;
    double v;
    ae_vector x1;
    ae_vector x2;

    ae_frame_make(_state, &_frame_block);
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);

    
    /*
     * Setup
     */
    if( !transa )
    {
        arows = ai2-ai1+1;
        acols = aj2-aj1+1;
    }
    else
    {
        arows = aj2-aj1+1;
        acols = ai2-ai1+1;
    }
    if( !transb )
    {
        brows = bi2-bi1+1;
        bcols = bj2-bj1+1;
    }
    else
    {
        brows = bj2-bj1+1;
        bcols = bi2-bi1+1;
    }
    ae_assert(acols==brows, "NaiveMatrixMatrixMultiply: incorrect matrix sizes!", _state);
    if( ((arows<=0||acols<=0)||brows<=0)||bcols<=0 )
    {
        ae_frame_leave(_state);
        return;
    }
    l = arows;
    r = bcols;
    k = acols;
    ae_vector_set_length(&x1, k+1, _state);
    ae_vector_set_length(&x2, k+1, _state);
    for(i=1; i<=l; i++)
    {
        for(j=1; j<=r; j++)
        {
            if( !transa )
            {
                if( !transb )
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1][bj1+j-1], b->stride, &a->ptr.pp_double[ai1+i-1][aj1], 1, ae_v_len(bi1,bi2));
                }
                else
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1+j-1][bj1], 1, &a->ptr.pp_double[ai1+i-1][aj1], 1, ae_v_len(bj1,bj2));
                }
            }
            else
            {
                if( !transb )
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1][bj1+j-1], b->stride, &a->ptr.pp_double[ai1][aj1+i-1], a->stride, ae_v_len(bi1,bi2));
                }
                else
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1+j-1][bj1], 1, &a->ptr.pp_double[ai1][aj1+i-1], a->stride, ae_v_len(bj1,bj2));
                }
            }
            if( ae_fp_eq(beta,(double)(0)) )
            {
                c->ptr.pp_double[ci1+i-1][cj1+j-1] = alpha*v;
            }
            else
            {
                c->ptr.pp_double[ci1+i-1][cj1+j-1] = beta*c->ptr.pp_double[ci1+i-1][cj1+j-1]+alpha*v;
            }
        }
    }
    ae_frame_leave(_state);
}



static void testsvdunit_fillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testsvdunit_getsvderror(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_state *_state);
static void testsvdunit_testsvdproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double* materr,
     double* orterr,
     double* othererr,
     ae_bool* wsorted,
     ae_bool* wfailed,
     ae_int_t* failcount,
     ae_int_t* succcount,
     ae_state *_state);





/*************************************************************************
Testing SVD decomposition subroutine
*************************************************************************/
ae_bool testsvd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_int_t m;
    ae_int_t n;
    ae_int_t maxmn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t gpass;
    ae_int_t pass;
    ae_bool waserrors;
    ae_bool wsorted;
    ae_bool wfailed;
    double materr;
    double orterr;
    double othererr;
    double threshold;
    double failr;
    ae_int_t failcount;
    ae_int_t succcount;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    failcount = 0;
    succcount = 0;
    materr = (double)(0);
    orterr = (double)(0);
    othererr = (double)(0);
    wsorted = ae_true;
    wfailed = ae_false;
    waserrors = ae_false;
    maxmn = 30;
    threshold = 5*100*ae_machineepsilon;
    ae_matrix_set_length(&a, maxmn-1+1, maxmn-1+1, _state);
    
    /*
     * TODO: div by zero fail, convergence fail
     */
    for(gpass=1; gpass<=1; gpass++)
    {
        
        /*
         * zero matrix, several cases
         */
        for(i=0; i<=maxmn-1; i++)
        {
            for(j=0; j<=maxmn-1; j++)
            {
                a.ptr.pp_double[i][j] = (double)(0);
            }
        }
        for(i=1; i<=ae_minint(5, maxmn, _state); i++)
        {
            for(j=1; j<=ae_minint(5, maxmn, _state); j++)
            {
                testsvdunit_testsvdproblem(&a, i, j, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
            }
        }
        
        /*
         * Long dense matrix
         */
        for(i=0; i<=maxmn-1; i++)
        {
            for(j=0; j<=ae_minint(5, maxmn, _state)-1; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        for(i=1; i<=maxmn; i++)
        {
            for(j=1; j<=ae_minint(5, maxmn, _state); j++)
            {
                testsvdunit_testsvdproblem(&a, i, j, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
            }
        }
        for(i=0; i<=ae_minint(5, maxmn, _state)-1; i++)
        {
            for(j=0; j<=maxmn-1; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        for(i=1; i<=ae_minint(5, maxmn, _state); i++)
        {
            for(j=1; j<=maxmn; j++)
            {
                testsvdunit_testsvdproblem(&a, i, j, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
            }
        }
        
        /*
         * Dense matrices
         */
        for(m=1; m<=ae_minint(10, maxmn, _state); m++)
        {
            for(n=1; n<=ae_minint(10, maxmn, _state); n++)
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    }
                }
                testsvdunit_testsvdproblem(&a, m, n, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
            }
        }
        
        /*
         * Sparse matrices, very sparse matrices, incredible sparse matrices
         */
        for(m=1; m<=10; m++)
        {
            for(n=1; n<=10; n++)
            {
                for(pass=1; pass<=2; pass++)
                {
                    testsvdunit_fillsparsea(&a, m, n, 0.8, _state);
                    testsvdunit_testsvdproblem(&a, m, n, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
                    testsvdunit_fillsparsea(&a, m, n, 0.9, _state);
                    testsvdunit_testsvdproblem(&a, m, n, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
                    testsvdunit_fillsparsea(&a, m, n, 0.95, _state);
                    testsvdunit_testsvdproblem(&a, m, n, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
                }
            }
        }
    }
    
    /*
     * report
     */
    failr = (double)failcount/(double)(succcount+failcount);
    waserrors = (((wfailed||ae_fp_greater(materr,threshold))||ae_fp_greater(orterr,threshold))||ae_fp_greater(othererr,threshold))||!wsorted;
    if( !silent )
    {
        printf("TESTING SVD DECOMPOSITION\n");
        printf("SVD decomposition error:                 %5.3e\n",
            (double)(materr));
        printf("SVD orthogonality error:                 %5.3e\n",
            (double)(orterr));
        printf("SVD with different parameters error:     %5.3e\n",
            (double)(othererr));
        printf("Singular values order:                   ");
        if( wsorted )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Always converged:                        ");
        if( !wfailed )
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
            printf("Fail ratio:                              %5.3f\n",
                (double)(failr));
        }
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testsvdunit_fillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_double[i][j] = (double)(0);
            }
        }
    }
}


static void testsvdunit_getsvderror(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t minmn;
    double locerr;
    double sm;


    minmn = ae_minint(m, n, _state);
    
    /*
     * decomposition error
     */
    locerr = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            sm = (double)(0);
            for(k=0; k<=minmn-1; k++)
            {
                sm = sm+w->ptr.p_double[k]*u->ptr.pp_double[i][k]*vt->ptr.pp_double[k][j];
            }
            locerr = ae_maxreal(locerr, ae_fabs(a->ptr.pp_double[i][j]-sm, _state), _state);
        }
    }
    *materr = ae_maxreal(*materr, locerr, _state);
    
    /*
     * orthogonality error
     */
    locerr = (double)(0);
    for(i=0; i<=minmn-1; i++)
    {
        for(j=i; j<=minmn-1; j++)
        {
            sm = ae_v_dotproduct(&u->ptr.pp_double[0][i], u->stride, &u->ptr.pp_double[0][j], u->stride, ae_v_len(0,m-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm-1, _state), _state);
            }
            sm = ae_v_dotproduct(&vt->ptr.pp_double[i][0], 1, &vt->ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm-1, _state), _state);
            }
        }
    }
    *orterr = ae_maxreal(*orterr, locerr, _state);
    
    /*
     * values order error
     */
    for(i=1; i<=minmn-1; i++)
    {
        if( ae_fp_greater(w->ptr.p_double[i],w->ptr.p_double[i-1]) )
        {
            *wsorted = ae_false;
        }
    }
}


static void testsvdunit_testsvdproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double* materr,
     double* orterr,
     double* othererr,
     ae_bool* wsorted,
     ae_bool* wfailed,
     ae_int_t* failcount,
     ae_int_t* succcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix u;
    ae_matrix vt;
    ae_matrix u2;
    ae_matrix vt2;
    ae_vector w;
    ae_vector w2;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ujob;
    ae_int_t vtjob;
    ae_int_t memjob;
    ae_int_t ucheck;
    ae_int_t vtcheck;

    ae_frame_make(_state, &_frame_block);
    memset(&u, 0, sizeof(u));
    memset(&vt, 0, sizeof(vt));
    memset(&u2, 0, sizeof(u2));
    memset(&vt2, 0, sizeof(vt2));
    memset(&w, 0, sizeof(w));
    memset(&w2, 0, sizeof(w2));
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);

    
    /*
     * Main SVD test
     */
    if( !rmatrixsvd(a, m, n, 2, 2, 2, &w, &u, &vt, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testsvdunit_getsvderror(a, m, n, &u, &w, &vt, materr, orterr, wsorted, _state);
    
    /*
     * Additional SVD tests
     */
    for(ujob=0; ujob<=2; ujob++)
    {
        for(vtjob=0; vtjob<=2; vtjob++)
        {
            for(memjob=0; memjob<=2; memjob++)
            {
                if( !rmatrixsvd(a, m, n, ujob, vtjob, memjob, &w2, &u2, &vt2, _state) )
                {
                    *failcount = *failcount+1;
                    *wfailed = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                ucheck = 0;
                if( ujob==1 )
                {
                    ucheck = ae_minint(m, n, _state);
                }
                if( ujob==2 )
                {
                    ucheck = m;
                }
                vtcheck = 0;
                if( vtjob==1 )
                {
                    vtcheck = ae_minint(m, n, _state);
                }
                if( vtjob==2 )
                {
                    vtcheck = n;
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=ucheck-1; j++)
                    {
                        *othererr = ae_maxreal(*othererr, ae_fabs(u.ptr.pp_double[i][j]-u2.ptr.pp_double[i][j], _state), _state);
                    }
                }
                for(i=0; i<=vtcheck-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        *othererr = ae_maxreal(*othererr, ae_fabs(vt.ptr.pp_double[i][j]-vt2.ptr.pp_double[i][j], _state), _state);
                    }
                }
                for(i=0; i<=ae_minint(m, n, _state)-1; i++)
                {
                    *othererr = ae_maxreal(*othererr, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
                }
            }
        }
    }
    
    /*
     * update counter
     */
    *succcount = *succcount+1;
    ae_frame_leave(_state);
}



static void testoptservunit_testprec(ae_bool* wereerrors,
     ae_state *_state);





ae_bool testoptserv(ae_bool silent, ae_state *_state)
{
    ae_bool precerrors;
    ae_bool wereerrors;
    ae_bool result;


    precerrors = ae_false;
    testoptservunit_testprec(&precerrors, _state);
    
    /*
     * report
     */
    wereerrors = precerrors;
    if( !silent )
    {
        printf("TESTING OPTSERV\n");
        printf("TESTS:                                    \n");
        printf("* PRECONDITIONERS                         ");
        if( precerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( wereerrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !wereerrors;
    return result;
}


/*************************************************************************
This function checks preconditioning functions

On failure sets error flag.
*************************************************************************/
static void testoptservunit_testprec(ae_bool* wereerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i0;
    ae_int_t j0;
    ae_int_t j1;
    double v;
    double rho;
    double theta;
    double tolg;
    ae_matrix va;
    ae_vector vc;
    ae_vector vd;
    ae_vector vb;
    ae_vector s0;
    ae_vector s1;
    ae_vector s2;
    ae_vector g;
    precbuflbfgs buf;
    precbuflowrank lowrankbuf;
    ae_vector norms;
    ae_matrix sk;
    ae_matrix yk;
    ae_matrix bk;
    ae_vector bksk;
    ae_vector tmp;
    matinvreport rep;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&va, 0, sizeof(va));
    memset(&vc, 0, sizeof(vc));
    memset(&vd, 0, sizeof(vd));
    memset(&vb, 0, sizeof(vb));
    memset(&s0, 0, sizeof(s0));
    memset(&s1, 0, sizeof(s1));
    memset(&s2, 0, sizeof(s2));
    memset(&g, 0, sizeof(g));
    memset(&buf, 0, sizeof(buf));
    memset(&lowrankbuf, 0, sizeof(lowrankbuf));
    memset(&norms, 0, sizeof(norms));
    memset(&sk, 0, sizeof(sk));
    memset(&yk, 0, sizeof(yk));
    memset(&bk, 0, sizeof(bk));
    memset(&bksk, 0, sizeof(bksk));
    memset(&tmp, 0, sizeof(tmp));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&va, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vd, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    _precbuflbfgs_init(&buf, _state, ae_true);
    _precbuflowrank_init(&lowrankbuf, _state, ae_true);
    ae_vector_init(&norms, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&sk, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&yk, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&bk, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bksk, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Test for inexact L-BFGS preconditioner.
     *
     * We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V.
     * Different K's, from 0 to N, are tried. We test preconditioner
     * code which uses compact L-BFGS update against reference implementation
     * which uses non-compact BFGS scheme.
     *
     * For each K we perform two tests: first for KxN non-zero matrix V,
     * second one for NxN matrix V with last N-K rows set to zero. Last test
     * checks algorithm's ability to handle zero updates.
     */
    tolg = 1.0E-9;
    for(n=1; n<=10; n++)
    {
        for(k=0; k<=n; k++)
        {
            
            /*
             * Prepare problem:
             * * VD, VC, VA, with VC/VA reordered by ascending of VC[i]*norm(VA[i,...])^2
             * * trial vector S (copies are stored to S0,S1,S2)
             */
            ae_vector_set_length(&vd, n, _state);
            ae_vector_set_length(&s0, n, _state);
            ae_vector_set_length(&s1, n, _state);
            ae_vector_set_length(&s2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                vd.ptr.p_double[i] = ae_exp(hqrndnormal(&rs, _state), _state);
                s0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                s1.ptr.p_double[i] = s0.ptr.p_double[i];
                s2.ptr.p_double[i] = s0.ptr.p_double[i];
            }
            rmatrixrndcond(n, 1.0E2, &va, _state);
            rvectorsetlengthatleast(&vc, n, _state);
            for(i=0; i<=k-1; i++)
            {
                vc.ptr.p_double[i] = ae_exp(hqrndnormal(&rs, _state), _state);
            }
            for(i=k; i<=n-1; i++)
            {
                vc.ptr.p_double[i] = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    va.ptr.pp_double[i][j] = 0.0;
                }
            }
            ae_vector_set_length(&norms, k, _state);
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&va.ptr.pp_double[i][0], 1, &va.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                norms.ptr.p_double[i] = v*vc.ptr.p_double[i];
            }
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=k-2; j++)
                {
                    if( ae_fp_greater(norms.ptr.p_double[j],norms.ptr.p_double[j+1]) )
                    {
                        
                        /*
                         * Swap elements J and J+1
                         */
                        v = norms.ptr.p_double[j];
                        norms.ptr.p_double[j] = norms.ptr.p_double[j+1];
                        norms.ptr.p_double[j+1] = v;
                        v = vc.ptr.p_double[j];
                        vc.ptr.p_double[j] = vc.ptr.p_double[j+1];
                        vc.ptr.p_double[j+1] = v;
                        for(j0=0; j0<=n-1; j0++)
                        {
                            v = va.ptr.pp_double[j][j0];
                            va.ptr.pp_double[j][j0] = va.ptr.pp_double[j+1][j0];
                            va.ptr.pp_double[j+1][j0] = v;
                        }
                    }
                }
            }
            
            /*
             * Generate reference model and apply it to S2:
             * * generate approximate Hessian Bk
             * * calculate inv(Bk)
             * * calculate inv(Bk)*S2, store to S2
             */
            rmatrixsetlengthatleast(&sk, k, n, _state);
            rmatrixsetlengthatleast(&yk, k, n, _state);
            ae_matrix_set_length(&bk, n, n, _state);
            ae_vector_set_length(&bksk, n, _state);
            ae_vector_set_length(&tmp, n, _state);
            for(i=0; i<=k-1; i++)
            {
                ae_v_move(&sk.ptr.pp_double[i][0], 1, &va.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                v = ae_v_dotproduct(&va.ptr.pp_double[i][0], 1, &sk.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                v = v*vc.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    yk.ptr.pp_double[i][j] = vd.ptr.p_double[j]*sk.ptr.pp_double[i][j]+va.ptr.pp_double[i][j]*v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( i==j )
                    {
                        bk.ptr.pp_double[i][i] = vd.ptr.p_double[i];
                    }
                    else
                    {
                        bk.ptr.pp_double[i][j] = 0.0;
                    }
                }
            }
            for(i=0; i<=k-1; i++)
            {
                theta = 0.0;
                for(j0=0; j0<=n-1; j0++)
                {
                    bksk.ptr.p_double[j0] = (double)(0);
                    for(j1=0; j1<=n-1; j1++)
                    {
                        theta = theta+sk.ptr.pp_double[i][j0]*bk.ptr.pp_double[j0][j1]*sk.ptr.pp_double[i][j1];
                        bksk.ptr.p_double[j0] = bksk.ptr.p_double[j0]+bk.ptr.pp_double[j0][j1]*sk.ptr.pp_double[i][j1];
                    }
                }
                theta = 1/theta;
                rho = ae_v_dotproduct(&sk.ptr.pp_double[i][0], 1, &yk.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                rho = 1/rho;
                for(j0=0; j0<=n-1; j0++)
                {
                    for(j1=0; j1<=n-1; j1++)
                    {
                        bk.ptr.pp_double[j0][j1] = bk.ptr.pp_double[j0][j1]+rho*yk.ptr.pp_double[i][j0]*yk.ptr.pp_double[i][j1];
                    }
                }
                for(j0=0; j0<=n-1; j0++)
                {
                    for(j1=0; j1<=n-1; j1++)
                    {
                        bk.ptr.pp_double[j0][j1] = bk.ptr.pp_double[j0][j1]-theta*bksk.ptr.p_double[j0]*bksk.ptr.p_double[j1];
                    }
                }
            }
            rmatrixinverse(&bk, n, &j0, &rep, _state);
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&bk.ptr.pp_double[i][0], 1, &s2.ptr.p_double[0], 1, ae_v_len(0,n-1));
                tmp.ptr.p_double[i] = v;
            }
            for(i=0; i<=n-1; i++)
            {
                s2.ptr.p_double[i] = tmp.ptr.p_double[i];
            }
            
            /*
             * First test for non-zero V:
             * * apply preconditioner to X0
             * * compare reference model against implementation being tested
             */
            inexactlbfgspreconditioner(&s0, n, &vd, &vc, &va, k, &buf, _state);
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(s2.ptr.p_double[i]-s0.ptr.p_double[i], _state),tolg), __FILE__, __LINE__, "testoptservunit.ap:236");
            }
            
            /*
             * Second test - N-K zero rows appended to V and rows are
             * randomly reordered. Doing so should not change result,
             * algorithm must be able to order rows according to second derivative
             * and skip zero updates.
             */
            for(i=0; i<=n-1; i++)
            {
                i0 = i+hqrnduniformi(&rs, n-i, _state);
                v = vc.ptr.p_double[i];
                vc.ptr.p_double[i] = vc.ptr.p_double[i0];
                vc.ptr.p_double[i0] = v;
                for(j=0; j<=n-1; j++)
                {
                    v = va.ptr.pp_double[i][j];
                    va.ptr.pp_double[i][j] = va.ptr.pp_double[i0][j];
                    va.ptr.pp_double[i0][j] = v;
                }
            }
            inexactlbfgspreconditioner(&s1, n, &vd, &vc, &va, n, &buf, _state);
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(s2.ptr.p_double[i]-s1.ptr.p_double[i], _state),tolg), __FILE__, __LINE__, "testoptservunit.ap:259");
            }
        }
    }
    
    /*
     * Test for exact low-rank preconditioner.
     *
     * We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V.
     * Different K's, from 0 to N, are tried. We test preconditioner
     * code which uses Woodbury update against reference implementation
     * which performs straightforward matrix inversion.
     *
     * For each K we perform two tests: first for KxN non-zero matrix V,
     * second one for NxN matrix V with randomly appended N-K zero rows.
     * Last test checks algorithm's ability to handle zero updates.
     */
    tolg = 1.0E-9;
    for(n=1; n<=10; n++)
    {
        for(k=0; k<=n; k++)
        {
            
            /*
             * Prepare problem:
             * * VD, VC, VA
             * * trial vector S (copies are stored to S0,S1,S2)
             */
            ae_vector_set_length(&vd, n, _state);
            ae_vector_set_length(&s0, n, _state);
            ae_vector_set_length(&s1, n, _state);
            ae_vector_set_length(&s2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                vd.ptr.p_double[i] = ae_exp(hqrndnormal(&rs, _state), _state);
                s0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                s1.ptr.p_double[i] = s0.ptr.p_double[i];
                s2.ptr.p_double[i] = s0.ptr.p_double[i];
            }
            rmatrixrndcond(n, 1.0E2, &va, _state);
            rvectorsetlengthatleast(&vc, n, _state);
            for(i=0; i<=k-1; i++)
            {
                vc.ptr.p_double[i] = ae_exp(hqrndnormal(&rs, _state), _state);
            }
            for(i=k; i<=n-1; i++)
            {
                vc.ptr.p_double[i] = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    va.ptr.pp_double[i][j] = 0.0;
                }
            }
            
            /*
             * Generate reference model and apply it to S2
             */
            ae_matrix_set_length(&bk, n, n, _state);
            ae_vector_set_length(&tmp, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( i==j )
                    {
                        v = vd.ptr.p_double[i];
                    }
                    else
                    {
                        v = 0.0;
                    }
                    for(j1=0; j1<=k-1; j1++)
                    {
                        v = v+va.ptr.pp_double[j1][i]*vc.ptr.p_double[j1]*va.ptr.pp_double[j1][j];
                    }
                    bk.ptr.pp_double[i][j] = v;
                }
            }
            rmatrixinverse(&bk, n, &j, &rep, _state);
            ae_assert(j>0, "Assertion failed", _state);
            for(i=0; i<=n-1; i++)
            {
                v = 0.0;
                for(j=0; j<=n-1; j++)
                {
                    v = v+bk.ptr.pp_double[i][j]*s2.ptr.p_double[j];
                }
                tmp.ptr.p_double[i] = v;
            }
            for(i=0; i<=n-1; i++)
            {
                s2.ptr.p_double[i] = tmp.ptr.p_double[i];
            }
            
            /*
             * First test for non-zero V:
             * * apply preconditioner to X0
             * * compare reference model against implementation being tested
             */
            preparelowrankpreconditioner(&vd, &vc, &va, n, k, &lowrankbuf, _state);
            applylowrankpreconditioner(&s0, &lowrankbuf, _state);
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(s2.ptr.p_double[i]-s0.ptr.p_double[i], _state),tolg), __FILE__, __LINE__, "testoptservunit.ap:341");
            }
            
            /*
             * Second test - N-K zero rows appended to V and rows are
             * randomly reordered. Doing so should not change result,
             * algorithm must be able to order rows according to second derivative
             * and skip zero updates.
             */
            for(i=0; i<=n-1; i++)
            {
                i0 = i+hqrnduniformi(&rs, n-i, _state);
                v = vc.ptr.p_double[i];
                vc.ptr.p_double[i] = vc.ptr.p_double[i0];
                vc.ptr.p_double[i0] = v;
                for(j=0; j<=n-1; j++)
                {
                    v = va.ptr.pp_double[i][j];
                    va.ptr.pp_double[i][j] = va.ptr.pp_double[i0][j];
                    va.ptr.pp_double[i0][j] = v;
                }
            }
            preparelowrankpreconditioner(&vd, &vc, &va, n, n, &lowrankbuf, _state);
            applylowrankpreconditioner(&s1, &lowrankbuf, _state);
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(s2.ptr.p_double[i]-s1.ptr.p_double[i], _state),tolg), __FILE__, __LINE__, "testoptservunit.ap:365");
            }
        }
    }
    ae_frame_leave(_state);
}








ae_bool testsnnls(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool test0errors;
    ae_bool test1errors;
    ae_bool test2errors;
    ae_bool testnewtonerrors;
    ae_bool waserrors;
    double eps;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    ae_int_t ns;
    ae_int_t nd;
    ae_int_t nr;
    ae_matrix densea;
    ae_matrix effectivea;
    ae_vector isconstrained;
    ae_vector g;
    ae_vector b;
    ae_vector x;
    ae_vector xs;
    snnlssolver s;
    hqrndstate rs;
    double rho;
    double xtol;
    ae_int_t nmax;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&densea, 0, sizeof(densea));
    memset(&effectivea, 0, sizeof(effectivea));
    memset(&isconstrained, 0, sizeof(isconstrained));
    memset(&g, 0, sizeof(g));
    memset(&b, 0, sizeof(b));
    memset(&x, 0, sizeof(x));
    memset(&xs, 0, sizeof(xs));
    memset(&s, 0, sizeof(s));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&densea, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&effectivea, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&isconstrained, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    _snnlssolver_init(&s, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    test0errors = ae_false;
    test1errors = ae_false;
    test2errors = ae_false;
    testnewtonerrors = ae_false;
    waserrors = ae_false;
    hqrndrandomize(&rs, _state);
    nmax = 10;
    
    /*
     * Test 2 (comes first because it is very basic):
     * * NS=0
     * * ND in [1,NMAX]
     * * NR=ND
     * * DenseA is diagonal with positive entries
     * * B is random
     * * random constraints
     * Exact solution is known and can be tested
     */
    eps = 1.0E-12;
    for(nd=1; nd<=nmax; nd++)
    {
        
        /*
         * Generate problem
         */
        ns = 0;
        nr = nd;
        ae_matrix_set_length(&densea, nd, nd, _state);
        ae_vector_set_length(&b, nd, _state);
        ae_vector_set_length(&isconstrained, nd, _state);
        for(i=0; i<=nd-1; i++)
        {
            for(j=0; j<=nd-1; j++)
            {
                densea.ptr.pp_double[i][j] = (double)(0);
            }
            densea.ptr.pp_double[i][i] = (double)(1+hqrnduniformi(&rs, 2, _state));
            b.ptr.p_double[i] = (double)((1+hqrnduniformi(&rs, 2, _state))*(2*hqrnduniformi(&rs, 2, _state)-1));
            isconstrained.ptr.p_bool[i] = ae_fp_greater(hqrnduniformr(&rs, _state),0.5);
        }
        
        /*
         * Solve with SNNLS solver
         */
        snnlsinit(0, 0, 0, &s, _state);
        snnlssetproblem(&s, &densea, &b, 0, nd, nd, _state);
        for(i=0; i<=nd-1; i++)
        {
            if( !isconstrained.ptr.p_bool[i] )
            {
                snnlsdropnnc(&s, i, _state);
            }
        }
        snnlssolve(&s, &x, _state);
        
        /*
         * Check
         */
        for(i=0; i<=nd-1; i++)
        {
            if( isconstrained.ptr.p_bool[i] )
            {
                ae_set_error_flag(&test2errors, ae_fp_greater(ae_fabs(x.ptr.p_double[i]-ae_maxreal(b.ptr.p_double[i]/densea.ptr.pp_double[i][i], 0.0, _state), _state),eps), __FILE__, __LINE__, "testsnnlsunit.ap:86");
                ae_set_error_flag(&test2errors, ae_fp_less(x.ptr.p_double[i],0.0), __FILE__, __LINE__, "testsnnlsunit.ap:87");
            }
            else
            {
                ae_set_error_flag(&test2errors, ae_fp_greater(ae_fabs(x.ptr.p_double[i]-b.ptr.p_double[i]/densea.ptr.pp_double[i][i], _state),eps), __FILE__, __LINE__, "testsnnlsunit.ap:90");
            }
        }
    }
    
    /*
     * Test 0:
     * * NS in [0,NMAX]
     * * ND in [0,NMAX]
     * * NR in [NS,NS+ND+NMAX]
     * * NS+ND>0, NR>0
     * * about 50% of variables are constrained
     * * we check that constrained gradient is small at the solution
     */
    eps = 1.0E-5;
    for(ns=0; ns<=nmax; ns++)
    {
        for(nd=0; nd<=nmax; nd++)
        {
            for(nr=ns; nr<=ns+nd+nmax; nr++)
            {
                
                /*
                 * Skip NS+ND=0, NR=0
                 */
                if( ns+nd==0 )
                {
                    continue;
                }
                if( nr==0 )
                {
                    continue;
                }
                
                /*
                 * Generate problem:
                 * * DenseA, array[NR,ND]
                 * * EffectiveA, array[NR,NS+ND]
                 * * B, array[NR]
                 * * IsConstrained, array[NS+ND]
                 */
                if( nd>0 )
                {
                    ae_matrix_set_length(&densea, nr, nd, _state);
                    for(i=0; i<=nr-1; i++)
                    {
                        for(j=0; j<=nd-1; j++)
                        {
                            densea.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                }
                ae_matrix_set_length(&effectivea, nr, ns+nd, _state);
                for(i=0; i<=nr-1; i++)
                {
                    for(j=0; j<=ns+nd-1; j++)
                    {
                        effectivea.ptr.pp_double[i][j] = 0.0;
                    }
                }
                for(i=0; i<=ns-1; i++)
                {
                    effectivea.ptr.pp_double[i][i] = 1.0;
                }
                for(i=0; i<=nr-1; i++)
                {
                    for(j=0; j<=nd-1; j++)
                    {
                        effectivea.ptr.pp_double[i][ns+j] = densea.ptr.pp_double[i][j];
                    }
                }
                ae_vector_set_length(&b, nr, _state);
                for(i=0; i<=nr-1; i++)
                {
                    b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                ae_vector_set_length(&isconstrained, ns+nd, _state);
                for(i=0; i<=ns+nd-1; i++)
                {
                    isconstrained.ptr.p_bool[i] = ae_fp_greater(ae_randomreal(_state),0.5);
                }
                
                /*
                 * Solve with SNNLS solver
                 */
                snnlsinit(0, 0, 0, &s, _state);
                snnlssetproblem(&s, &densea, &b, ns, nd, nr, _state);
                for(i=0; i<=ns+nd-1; i++)
                {
                    if( !isconstrained.ptr.p_bool[i] )
                    {
                        snnlsdropnnc(&s, i, _state);
                    }
                }
                snnlssolve(&s, &x, _state);
                
                /*
                 * Check non-negativity
                 */
                for(i=0; i<=ns+nd-1; i++)
                {
                    ae_set_error_flag(&test0errors, isconstrained.ptr.p_bool[i]&&ae_fp_less(x.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testsnnlsunit.ap:160");
                }
                
                /*
                 * Calculate gradient A'*A*x-b'*A.
                 * Check projected gradient (each component must be less than Eps).
                 */
                ae_vector_set_length(&g, ns+nd, _state);
                for(i=0; i<=ns+nd-1; i++)
                {
                    v = ae_v_dotproduct(&b.ptr.p_double[0], 1, &effectivea.ptr.pp_double[0][i], effectivea.stride, ae_v_len(0,nr-1));
                    g.ptr.p_double[i] = -v;
                }
                for(i=0; i<=nr-1; i++)
                {
                    v = ae_v_dotproduct(&effectivea.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,ns+nd-1));
                    ae_v_addd(&g.ptr.p_double[0], 1, &effectivea.ptr.pp_double[i][0], 1, ae_v_len(0,ns+nd-1), v);
                }
                for(i=0; i<=ns+nd-1; i++)
                {
                    if( !isconstrained.ptr.p_bool[i]||ae_fp_greater(x.ptr.p_double[i],(double)(0)) )
                    {
                        ae_set_error_flag(&test0errors, ae_fp_greater(ae_fabs(g.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testsnnlsunit.ap:179");
                    }
                    else
                    {
                        ae_set_error_flag(&test0errors, ae_fp_less(g.ptr.p_double[i],-eps), __FILE__, __LINE__, "testsnnlsunit.ap:181");
                    }
                }
            }
        }
    }
    
    /*
     * Test 1: ability of the solver to take very short steps.
     *
     * We solve problem similar to one solver in test 0, but with
     * progressively decreased magnitude of variables. We generate
     * problem with already-known solution and compare results against it.
     */
    xtol = 1.0E-7;
    for(ns=0; ns<=nmax; ns++)
    {
        for(nd=0; nd<=nmax; nd++)
        {
            for(nr=ns; nr<=ns+nd+nmax; nr++)
            {
                for(k=0; k<=20; k++)
                {
                    
                    /*
                     * Skip NS+ND=0, NR=0
                     *
                     * Skip degenerate problems (NR<NS+ND) - important for this particular test.
                     */
                    if( ns+nd==0 )
                    {
                        continue;
                    }
                    if( nr==0 )
                    {
                        continue;
                    }
                    if( nr<ns+nd )
                    {
                        continue;
                    }
                    
                    /*
                     * Generate problem:
                     * * DenseA, array[NR,ND]
                     * * EffectiveA, array[NR,NS+ND]
                     * * B, array[NR]
                     * * IsConstrained, array[NS+ND]
                     */
                    rho = ae_pow((double)(10), (double)(-k), _state);
                    if( nd>0 )
                    {
                        ae_matrix_set_length(&densea, nr, nd, _state);
                        for(i=0; i<=nr-1; i++)
                        {
                            for(j=0; j<=nd-1; j++)
                            {
                                densea.ptr.pp_double[i][j] = 2*hqrnduniformr(&rs, _state)-1;
                            }
                        }
                    }
                    ae_matrix_set_length(&effectivea, nr, ns+nd, _state);
                    for(i=0; i<=nr-1; i++)
                    {
                        for(j=0; j<=ns+nd-1; j++)
                        {
                            effectivea.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                    for(i=0; i<=ns-1; i++)
                    {
                        effectivea.ptr.pp_double[i][i] = 1.0;
                    }
                    for(i=0; i<=nr-1; i++)
                    {
                        for(j=0; j<=nd-1; j++)
                        {
                            effectivea.ptr.pp_double[i][ns+j] = densea.ptr.pp_double[i][j];
                        }
                    }
                    ae_vector_set_length(&xs, ns+nd, _state);
                    ae_vector_set_length(&isconstrained, ns+nd, _state);
                    for(i=0; i<=ns+nd-1; i++)
                    {
                        xs.ptr.p_double[i] = rho*(hqrnduniformr(&rs, _state)-0.5);
                        isconstrained.ptr.p_bool[i] = ae_fp_greater(xs.ptr.p_double[i],0.0);
                    }
                    ae_vector_set_length(&b, nr, _state);
                    for(i=0; i<=nr-1; i++)
                    {
                        v = 0.0;
                        for(j=0; j<=ns+nd-1; j++)
                        {
                            v = v+effectivea.ptr.pp_double[i][j]*xs.ptr.p_double[j];
                        }
                        b.ptr.p_double[i] = v;
                    }
                    
                    /*
                     * Solve with SNNLS solver
                     */
                    snnlsinit(0, 0, 0, &s, _state);
                    snnlssetproblem(&s, &densea, &b, ns, nd, nr, _state);
                    for(i=0; i<=ns+nd-1; i++)
                    {
                        if( !isconstrained.ptr.p_bool[i] )
                        {
                            snnlsdropnnc(&s, i, _state);
                        }
                    }
                    snnlssolve(&s, &x, _state);
                    
                    /*
                     * Check non-negativity
                     */
                    for(i=0; i<=ns+nd-1; i++)
                    {
                        ae_set_error_flag(&test1errors, isconstrained.ptr.p_bool[i]&&ae_fp_less(x.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testsnnlsunit.ap:263");
                    }
                    
                    /*
                     * Compare with true solution
                     */
                    for(i=0; i<=ns+nd-1; i++)
                    {
                        ae_set_error_flag(&test1errors, ae_fp_greater(ae_fabs(xs.ptr.p_double[i]-x.ptr.p_double[i], _state),rho*xtol), __FILE__, __LINE__, "testsnnlsunit.ap:269");
                    }
                }
            }
        }
    }
    
    /*
     * Test for Newton phase:
     * * NS in [0,NMAX]
     * * ND in [0,NMAX]
     * * NR in [NS,NS+ND+NMAX]
     * * NS+ND>0, NR>0
     * * all variables are unconstrained
     * * S.DebugMaxNewton is set to 1, S.RefinementIts is set to 1,
     *   i.e. algorithm is terminated after one Newton iteration, and no
     *   iterative refinement is used.
     * * we test that gradient is small at solution, i.e. one Newton iteration
     *   on unconstrained problem is enough to find solution. In case of buggy
     *   Newton solver one iteration won't move us to the solution - it may
     *   decrease function value, but won't find exact solution.
     *
     * This test is intended to catch subtle bugs in the Newton solver which
     * do NOT prevent algorithm from converging to the solution, but slow it
     * down (convergence becomes linear or even slower).
     */
    eps = 1.0E-4;
    for(ns=0; ns<=nmax; ns++)
    {
        for(nd=0; nd<=nmax; nd++)
        {
            for(nr=ns; nr<=ns+nd+nmax; nr++)
            {
                
                /*
                 * Skip NS+ND=0, NR=0
                 */
                if( ns+nd==0 )
                {
                    continue;
                }
                if( nr==0 )
                {
                    continue;
                }
                
                /*
                 * Generate problem:
                 * * DenseA, array[NR,ND]
                 * * EffectiveA, array[NR,NS+ND]
                 * * B, array[NR]
                 * * IsConstrained, array[NS+ND]
                 */
                if( nd>0 )
                {
                    ae_matrix_set_length(&densea, nr, nd, _state);
                    for(i=0; i<=nr-1; i++)
                    {
                        for(j=0; j<=nd-1; j++)
                        {
                            densea.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                        }
                    }
                }
                ae_matrix_set_length(&effectivea, nr, ns+nd, _state);
                for(i=0; i<=nr-1; i++)
                {
                    for(j=0; j<=ns+nd-1; j++)
                    {
                        effectivea.ptr.pp_double[i][j] = 0.0;
                    }
                }
                for(i=0; i<=ns-1; i++)
                {
                    effectivea.ptr.pp_double[i][i] = 1.0;
                }
                for(i=0; i<=nr-1; i++)
                {
                    for(j=0; j<=nd-1; j++)
                    {
                        effectivea.ptr.pp_double[i][ns+j] = densea.ptr.pp_double[i][j];
                    }
                }
                ae_vector_set_length(&b, nr, _state);
                for(i=0; i<=nr-1; i++)
                {
                    b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                
                /*
                 * Solve with SNNLS solver
                 */
                snnlsinit(0, 0, 0, &s, _state);
                snnlssetproblem(&s, &densea, &b, ns, nd, nr, _state);
                for(i=0; i<=ns+nd-1; i++)
                {
                    snnlsdropnnc(&s, i, _state);
                }
                s.debugmaxinnerits = 1;
                snnlssolve(&s, &x, _state);
                
                /*
                 * Calculate gradient A'*A*x-b'*A.
                 * Check projected gradient (each component must be less than Eps).
                 */
                ae_vector_set_length(&g, ns+nd, _state);
                for(i=0; i<=ns+nd-1; i++)
                {
                    v = ae_v_dotproduct(&b.ptr.p_double[0], 1, &effectivea.ptr.pp_double[0][i], effectivea.stride, ae_v_len(0,nr-1));
                    g.ptr.p_double[i] = -v;
                }
                for(i=0; i<=nr-1; i++)
                {
                    v = ae_v_dotproduct(&effectivea.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,ns+nd-1));
                    ae_v_addd(&g.ptr.p_double[0], 1, &effectivea.ptr.pp_double[i][0], 1, ae_v_len(0,ns+nd-1), v);
                }
                for(i=0; i<=ns+nd-1; i++)
                {
                    ae_set_error_flag(&testnewtonerrors, ae_fp_greater(ae_fabs(g.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testsnnlsunit.ap:358");
                }
            }
        }
    }
    
    /*
     * report
     */
    waserrors = ((test0errors||test1errors)||test2errors)||testnewtonerrors;
    if( !silent )
    {
        printf("TESTING SPECIAL NNLS SOLVER\n");
        printf("TEST 0:                                   ");
        if( test0errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TEST 1:                                   ");
        if( test1errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TEST 2:                                   ");
        if( test2errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("NEWTON PHASE:                             ");
        if( testnewtonerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testsactivesetsunit_testspecproperties(ae_bool* err,
     ae_state *_state);





ae_bool testsactivesets(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool specerr;
    ae_bool result;


    specerr = ae_false;
    testsactivesetsunit_testspecproperties(&specerr, _state);
    
    /*
     * report
     */
    waserrors = specerr;
    if( !silent )
    {
        printf("TESTING ACTIVE SETS\n");
        printf("* SPECIAL PROPERTIES                      ");
        if( specerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
This function tests special properties.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testsactivesetsunit_testspecproperties(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t nec;
    ae_int_t nic;
    double v;
    double vv;
    sactiveset state;
    hqrndstate rs;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_vector s;
    ae_matrix c;
    ae_vector ct;
    ae_int_t scaletype;
    ae_int_t pass;
    ae_int_t distortidx;
    double distortmag;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rs, 0, sizeof(rs));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    memset(&s, 0, sizeof(s));
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    _sactiveset_init(&state, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * N-dimensional problem with Ne equality and Ni inequality constraints.
     *
     * Check that SActiveSet object uses efficient algorithm
     * to determine initial point: it avoids expensive (N+Ni)-dimensional
     * QP subproblem when initial point is feasible w.r.t. constraints.
     *
     * In order to do so we try to find initial point for a problem with
     * 2 equality constraints and 1000000 inequality constraints (+box
     * constraints). Inefficient algorithm will simply fail to allocate
     * enough memory, so we do not have to perform any checks here.
     */
    n = 5;
    nec = 2;
    nic = 1000000;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&bl, n, _state);
    ae_vector_set_length(&bu, n, _state);
    for(scaletype=0; scaletype<=1; scaletype++)
    {
        
        /*
         * Generate problem
         */
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = hqrnduniformr(&rs, _state);
            bl.ptr.p_double[i] = x.ptr.p_double[i]-hqrnduniformr(&rs, _state)*hqrnduniformi(&rs, 2, _state);
            bu.ptr.p_double[i] = x.ptr.p_double[i]+hqrnduniformr(&rs, _state)*hqrnduniformi(&rs, 2, _state);
        }
        ae_matrix_set_length(&c, nec+nic, n+1, _state);
        ae_vector_set_length(&ct, nec+nic, _state);
        for(i=0; i<=nec+nic-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                v = v+c.ptr.pp_double[i][j]*x.ptr.p_double[j];
            }
            c.ptr.pp_double[i][n] = v;
            if( i<nec )
            {
                ct.ptr.p_int[i] = 0;
            }
            else
            {
                c.ptr.pp_double[i][n] = c.ptr.pp_double[i][n]+0.1;
                ct.ptr.p_int[i] = -1;
            }
        }
        
        /*
         * Apply scaling (if needed), then randomly multiply C by
         * some large/small value in order to distort magnitude of
         * linear constraints.
         *
         * Correct SActiveSet implementation must be able to renormalize
         * constraints prior to checking feasibility of initial point.
         */
        ae_vector_set_length(&s, n, _state);
        for(i=0; i<=n-1; i++)
        {
            s.ptr.p_double[i] = (double)(1);
            if( scaletype!=0 )
            {
                s.ptr.p_double[i] = ae_pow((double)(10), 40*hqrnduniformr(&rs, _state)-20, _state);
            }
            x.ptr.p_double[i] = x.ptr.p_double[i]*s.ptr.p_double[i];
            bl.ptr.p_double[i] = bl.ptr.p_double[i]*s.ptr.p_double[i];
            bu.ptr.p_double[i] = bu.ptr.p_double[i]*s.ptr.p_double[i];
            for(j=0; j<=nec+nic-1; j++)
            {
                c.ptr.pp_double[j][i] = c.ptr.pp_double[j][i]/s.ptr.p_double[i];
            }
        }
        for(i=0; i<=nec+nic-1; i++)
        {
            v = ae_pow((double)(10), 40*hqrnduniformr(&rs, _state)-20, _state);
            ae_v_muld(&c.ptr.pp_double[i][0], 1, ae_v_len(0,n), v);
        }
        
        /*
         * Solve.
         * Simply being able to survive SASStartOptimization() call is enough for this test
         */
        sasinit(n, &state, _state);
        sassetscale(&state, &s, _state);
        sassetbc(&state, &bl, &bu, _state);
        sassetlc(&state, &c, &ct, nec+nic, _state);
        sasstartoptimization(&state, &x, _state);
    }
    
    /*
     * Test that algorithm can correctly distinguish between feasible
     * and non-feasible initial points. In order to do so we try to
     * call SASStartOptimization() for moderately sized problem and
     * examine State.FeasInitPt flag after call is done.
     *
     * This test is performed for equality-only constraints
     */
    for(pass=1; pass<=10; pass++)
    {
        n = 50+hqrnduniformi(&rs, 10, _state);
        nec = 10+hqrnduniformi(&rs, 10, _state);
        nic = 0;
        ae_vector_set_length(&x, n, _state);
        for(scaletype=0; scaletype<=1; scaletype++)
        {
            
            /*
             * Generate problem, prepare distortion which introduces infeasibility
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = hqrnduniformr(&rs, _state);
            }
            ae_matrix_set_length(&c, nec+nic, n+1, _state);
            ae_vector_set_length(&ct, nec+nic, _state);
            for(i=0; i<=nec+nic-1; i++)
            {
                v = (double)(0);
                vv = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                    vv = vv+ae_sqr(c.ptr.pp_double[i][j], _state);
                    v = v+c.ptr.pp_double[i][j]*x.ptr.p_double[j];
                }
                c.ptr.pp_double[i][n] = v;
                vv = coalesce(vv, (double)(1), _state);
                vv = 1/ae_sqrt(vv, _state);
                ae_v_muld(&c.ptr.pp_double[i][0], 1, ae_v_len(0,n), vv);
                ct.ptr.p_int[i] = 0;
            }
            distortidx = hqrnduniformi(&rs, n, _state);
            distortmag = (double)(0);
            for(i=0; i<=nec+nic-1; i++)
            {
                distortmag = ae_maxreal(distortmag, ae_fabs(c.ptr.pp_double[i][distortidx], _state), _state);
            }
            ae_assert(ae_fp_greater(distortmag,(double)(0)), "Assertion failed", _state);
            distortmag = 1.0E7*ae_machineepsilon/distortmag;
            
            /*
             * Apply scaling (if needed), then randomly multiply C by
             * some large/small value in order to distort magnitude of
             * linear constraints.
             *
             * Correct SActiveSet implementation must be able to renormalize
             * constraints prior to checking feasibility of initial point.
             */
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = (double)(1);
                if( scaletype!=0 )
                {
                    s.ptr.p_double[i] = ae_pow((double)(10), 40*hqrnduniformr(&rs, _state)-20, _state);
                }
                x.ptr.p_double[i] = x.ptr.p_double[i]*s.ptr.p_double[i];
                for(j=0; j<=nec+nic-1; j++)
                {
                    c.ptr.pp_double[j][i] = c.ptr.pp_double[j][i]/s.ptr.p_double[i];
                }
            }
            for(i=0; i<=nec+nic-1; i++)
            {
                v = ae_pow((double)(10), 40*hqrnduniformr(&rs, _state)-20, _state);
                ae_v_muld(&c.ptr.pp_double[i][0], 1, ae_v_len(0,n), v);
            }
            
            /*
             * Solve for feasible initial X and check State.FeasInitPt flag
             */
            sasinit(n, &state, _state);
            sassetscale(&state, &s, _state);
            sassetlc(&state, &c, &ct, nec+nic, _state);
            sasstartoptimization(&state, &x, _state);
            ae_set_error_flag(err, !state.feasinitpt, __FILE__, __LINE__, "testsactivesetsunit.ap:225");
            
            /*
             * Solve for infeasible initial X and check State.FeasInitPt flag
             */
            x.ptr.p_double[distortidx] = x.ptr.p_double[distortidx]+s.ptr.p_double[distortidx]*distortmag;
            sasinit(n, &state, _state);
            sassetscale(&state, &s, _state);
            sassetlc(&state, &c, &ct, nec+nic, _state);
            sasstartoptimization(&state, &x, _state);
            ae_set_error_flag(err, state.feasinitpt, __FILE__, __LINE__, "testsactivesetsunit.ap:235");
        }
    }
    ae_frame_leave(_state);
}








ae_bool testlinmin(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool result;


    waserrors = ae_false;
    if( !silent )
    {
        printf("TESTING LINMIN\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}



static void testminlbfgsunit_testfunc2(minlbfgsstate* state,
     ae_state *_state);
static void testminlbfgsunit_testfunc3(minlbfgsstate* state,
     ae_state *_state);
static void testminlbfgsunit_calciip2(minlbfgsstate* state,
     ae_int_t n,
     ae_state *_state);
static void testminlbfgsunit_testpreconditioning(ae_bool* err,
     ae_state *_state);
static void testminlbfgsunit_testother(ae_bool* err, ae_state *_state);
static ae_bool testminlbfgsunit_gradientchecktest(ae_state *_state);
static void testminlbfgsunit_funcderiv(double a,
     double b,
     double c,
     double d,
     double x0,
     double x1,
     double x2,
     /* Real    */ ae_vector* x,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);





ae_bool testminlbfgs(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool referror;
    ae_bool nonconverror;
    ae_bool eqerror;
    ae_bool converror;
    ae_bool crashtest;
    ae_bool othererrors;
    ae_bool restartserror;
    ae_bool precerror;
    ae_bool graderrors;
    ae_int_t n;
    ae_int_t m;
    ae_vector x;
    ae_vector xe;
    ae_vector b;
    ae_vector xlast;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_matrix a;
    ae_vector diagh;
    ae_int_t maxits;
    minlbfgsstate state;
    minlbfgsreport rep;
    double diffstep;
    ae_int_t dkind;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&xe, 0, sizeof(xe));
    memset(&b, 0, sizeof(b));
    memset(&xlast, 0, sizeof(xlast));
    memset(&a, 0, sizeof(a));
    memset(&diagh, 0, sizeof(diagh));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);
    _minlbfgsstate_init(&state, _state, ae_true);
    _minlbfgsreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    referror = ae_false;
    precerror = ae_false;
    nonconverror = ae_false;
    restartserror = ae_false;
    eqerror = ae_false;
    converror = ae_false;
    crashtest = ae_false;
    othererrors = ae_false;
    testminlbfgsunit_testpreconditioning(&precerror, _state);
    testminlbfgsunit_testother(&othererrors, _state);
    
    /*
     * Reference problem
     */
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, 3, _state);
        n = 3;
        m = 2;
        x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
        x.ptr.p_double[1] = 100*ae_randomreal(_state)-50;
        x.ptr.p_double[2] = 100*ae_randomreal(_state)-50;
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, (double)(0), (double)(0), (double)(0), 0, _state);
        while(minlbfgsiteration(&state, _state))
        {
            if( state.needf||state.needfg )
            {
                state.f = ae_sqr(state.x.ptr.p_double[0]-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
            }
            if( state.needfg )
            {
                state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]-2)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
            }
        }
        minlbfgsresults(&state, &x, &rep, _state);
        referror = (((referror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-2, _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-2, _state),0.001);
    }
    
    /*
     * nonconvex problems with complex surface: we start from point with very small
     * gradient, but we need ever smaller gradient in the next step due to
     * Wolfe conditions.
     */
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, 1, _state);
        n = 1;
        m = 1;
        v = (double)(-100);
        while(ae_fp_less(v,0.1))
        {
            x.ptr.p_double[0] = v;
            if( dkind==0 )
            {
                minlbfgscreate(n, m, &x, &state, _state);
            }
            if( dkind==1 )
            {
                minlbfgscreatef(n, m, &x, diffstep, &state, _state);
            }
            minlbfgssetcond(&state, 1.0E-9, (double)(0), (double)(0), 0, _state);
            while(minlbfgsiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0], _state)/(1+ae_sqr(state.x.ptr.p_double[0], _state));
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[0] = (2*state.x.ptr.p_double[0]*(1+ae_sqr(state.x.ptr.p_double[0], _state))-ae_sqr(state.x.ptr.p_double[0], _state)*2*state.x.ptr.p_double[0])/ae_sqr(1+ae_sqr(state.x.ptr.p_double[0], _state), _state);
                }
            }
            minlbfgsresults(&state, &x, &rep, _state);
            nonconverror = (nonconverror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0], _state),0.001);
            v = v+0.1;
        }
    }
    
    /*
     * F2 problem with restarts:
     * * make several iterations and restart BEFORE termination
     * * iterate and restart AFTER termination
     *
     * NOTE: step is bounded from above to avoid premature convergence
     */
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, 3, _state);
        n = 3;
        m = 2;
        x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetstpmax(&state, 0.1, _state);
        minlbfgssetcond(&state, 0.0000001, 0.0, 0.0, 0, _state);
        for(i=0; i<=10; i++)
        {
            if( !minlbfgsiteration(&state, _state) )
            {
                break;
            }
            testminlbfgsunit_testfunc2(&state, _state);
        }
        x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
        minlbfgsrestartfrom(&state, &x, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc2(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        restartserror = (((restartserror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log((double)(2), _state), _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log((double)(2), _state), _state),0.01);
        x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
        minlbfgsrestartfrom(&state, &x, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc2(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        restartserror = (((restartserror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log((double)(2), _state), _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log((double)(2), _state), _state),0.01);
    }
    
    /*
     * Linear equations
     */
    diffstep = 1.0E-6;
    for(n=1; n<=10; n++)
    {
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&xe, n-1+1, _state);
        ae_vector_set_length(&b, n-1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            a.ptr.pp_double[i][i] = a.ptr.pp_double[i][i]+3*ae_sign(a.ptr.pp_double[i][i], _state);
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
            b.ptr.p_double[i] = v;
        }
        
        /*
         * Test different M/DKind
         */
        for(m=1; m<=n; m++)
        {
            for(dkind=0; dkind<=1; dkind++)
            {
                
                /*
                 * Solve task
                 */
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( dkind==0 )
                {
                    minlbfgscreate(n, m, &x, &state, _state);
                }
                if( dkind==1 )
                {
                    minlbfgscreatef(n, m, &x, diffstep, &state, _state);
                }
                minlbfgssetcond(&state, (double)(0), (double)(0), (double)(0), 0, _state);
                while(minlbfgsiteration(&state, _state))
                {
                    if( state.needf||state.needfg )
                    {
                        state.f = (double)(0);
                    }
                    if( state.needfg )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            state.g.ptr.p_double[i] = (double)(0);
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        if( state.needf||state.needfg )
                        {
                            state.f = state.f+ae_sqr(v-b.ptr.p_double[i], _state);
                        }
                        if( state.needfg )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                state.g.ptr.p_double[j] = state.g.ptr.p_double[j]+2*(v-b.ptr.p_double[i])*a.ptr.pp_double[i][j];
                            }
                        }
                    }
                }
                minlbfgsresults(&state, &x, &rep, _state);
                eqerror = eqerror||rep.terminationtype<=0;
                for(i=0; i<=n-1; i++)
                {
                    eqerror = eqerror||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-xe.ptr.p_double[i], _state),0.001);
                }
            }
        }
    }
    
    /*
     * Testing convergence properties
     */
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, 3, _state);
        n = 3;
        m = 2;
        for(i=0; i<=2; i++)
        {
            x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, 0.001, (double)(0), (double)(0), 0, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc3(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        converror = converror||rep.terminationtype!=4;
        for(i=0; i<=2; i++)
        {
            x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, (double)(0), 0.001, (double)(0), 0, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc3(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        converror = converror||rep.terminationtype!=1;
        for(i=0; i<=2; i++)
        {
            x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, (double)(0), (double)(0), 0.001, 0, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc3(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        converror = converror||rep.terminationtype!=2;
        for(i=0; i<=2; i++)
        {
            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, (double)(0), (double)(0), (double)(0), 10, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc3(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        converror = (converror||rep.terminationtype!=5)||rep.iterationscount!=10;
    }
    
    /*
     * Crash test: too many iterations on a simple tasks
     * May fail when encounter zero step, underflow or something like that
     */
    ae_vector_set_length(&x, 2+1, _state);
    n = 3;
    m = 2;
    maxits = 10000;
    for(i=0; i<=2; i++)
    {
        x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
    }
    minlbfgscreate(n, m, &x, &state, _state);
    minlbfgssetcond(&state, (double)(0), (double)(0), (double)(0), maxits, _state);
    while(minlbfgsiteration(&state, _state))
    {
        state.f = ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
        state.g.ptr.p_double[0] = 2*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
        state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
        state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
    }
    minlbfgsresults(&state, &x, &rep, _state);
    crashtest = crashtest||rep.terminationtype<=0;
    
    /*
     *  Test for MinLBFGSGradientCheck
     */
    graderrors = testminlbfgsunit_gradientchecktest(_state);
    
    /*
     * end
     */
    waserrors = (((((((referror||nonconverror)||eqerror)||converror)||crashtest)||othererrors)||restartserror)||precerror)||graderrors;
    if( !silent )
    {
        printf("TESTING L-BFGS OPTIMIZATION\n");
        printf("REFERENCE PROBLEM:                        ");
        if( referror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("NON-CONVEX PROBLEM:                       ");
        if( nonconverror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LINEAR EQUATIONS:                         ");
        if( eqerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RESTARTS:                                 ");
        if( restartserror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("PRECONDITIONER:                           ");
        if( precerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE PROPERTIES:                   ");
        if( converror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CRASH TEST:                               ");
        if( crashtest )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TEST FOR VERIFICATION OF THE GRADIENT:    ");
        if( graderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Calculate test function #2

Simple variation of #1, much more nonlinear, which makes unlikely premature
convergence of algorithm .
*************************************************************************/
static void testminlbfgsunit_testfunc2(minlbfgsstate* state,
     ae_state *_state)
{


    if( ae_fp_less(state->x.ptr.p_double[0],(double)(100)) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state->x.ptr.p_double[1], _state), _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 4*state->x.ptr.p_double[1]*ae_sqr(state->x.ptr.p_double[1], _state);
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = (double)(0);
            state->g.ptr.p_double[2] = (double)(0);
        }
    }
}


/*************************************************************************
Calculate test function #3

Simple variation of #1, much more nonlinear, with non-zero value at minimum.
It achieve two goals:
* makes unlikely premature convergence of algorithm .
* solves some issues with EpsF stopping condition which arise when
  F(minimum) is zero

*************************************************************************/
static void testminlbfgsunit_testfunc3(minlbfgsstate* state,
     ae_state *_state)
{
    double s;


    s = 0.001;
    if( ae_fp_less(state->x.ptr.p_double[0],(double)(100)) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state->x.ptr.p_double[1], _state)+s, _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 2*(ae_sqr(state->x.ptr.p_double[1], _state)+s)*2*state->x.ptr.p_double[1];
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = (double)(0);
            state->g.ptr.p_double[2] = (double)(0);
        }
    }
}


/*************************************************************************
Calculate test function IIP2

f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1)

It has high condition number which makes fast convergence unlikely without
good preconditioner.

*************************************************************************/
static void testminlbfgsunit_calciip2(minlbfgsstate* state,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;


    if( state->needf||state->needfg )
    {
        state->f = (double)(0);
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->needf||state->needfg )
        {
            state->f = state->f+ae_sqr((double)(i*i+1), _state)*ae_sqr(state->x.ptr.p_double[i], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[i] = ae_sqr((double)(i*i+1), _state)*2*state->x.ptr.p_double[i];
        }
    }
}


/*************************************************************************
This function tests preconditioning

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminlbfgsunit_testpreconditioning(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t cntb1;
    ae_int_t cntb2;
    ae_int_t cntg1;
    ae_int_t cntg2;
    ae_int_t pkind;
    minlbfgsstate state;
    minlbfgsreport rep;
    ae_vector x;
    ae_vector s;
    ae_matrix a;
    ae_vector diagh;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&x, 0, sizeof(x));
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    memset(&diagh, 0, sizeof(diagh));
    _minlbfgsstate_init(&state, _state, ae_true);
    _minlbfgsreport_init(&rep, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);

    m = 1;
    k = 50;
    
    /*
     * Preconditioner test1.
     *
     * If
     * * B1 is default preconditioner
     * * B2 is Cholesky preconditioner with unit diagonal
     * * G1 is Cholesky preconditioner based on exact Hessian with perturbations
     * * G2 is diagonal precomditioner based on approximate diagonal of Hessian matrix
     * then "bad" preconditioners (B1/B2/..) are worse than "good" ones (G1/G2/..).
     * "Worse" means more iterations to converge.
     *
     * We test it using f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1) and L-BFGS
     * optimizer with deliberately small M=1.
     *
     * N        - problem size
     * PKind    - zero for upper triangular preconditioner, one for lower triangular.
     * K        - number of repeated passes (should be large enough to average out random factors)
     */
    for(n=10; n<=15; n++)
    {
        pkind = ae_randominteger(2, _state);
        ae_vector_set_length(&x, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)(0);
        }
        minlbfgscreate(n, m, &x, &state, _state);
        
        /*
         * Test it with default preconditioner
         */
        minlbfgssetprecdefault(&state, _state);
        cntb1 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntb1 = cntb1+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        
        /*
         * Test it with unit preconditioner
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    a.ptr.pp_double[i][i] = (double)(1);
                }
                else
                {
                    a.ptr.pp_double[i][j] = (double)(0);
                }
            }
        }
        minlbfgssetpreccholesky(&state, &a, pkind==0, _state);
        cntb2 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntb2 = cntb2+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        
        /*
         * Test it with perturbed Hessian preconditioner
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    a.ptr.pp_double[i][i] = (i*i+1)*(0.8+0.4*ae_randomreal(_state));
                }
                else
                {
                    if( (pkind==0&&j>i)||(pkind==1&&j<i) )
                    {
                        a.ptr.pp_double[i][j] = 0.1*ae_randomreal(_state)-0.05;
                    }
                    else
                    {
                        a.ptr.pp_double[i][j] = _state->v_nan;
                    }
                }
            }
        }
        minlbfgssetpreccholesky(&state, &a, pkind==0, _state);
        cntg1 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntg1 = cntg1+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        
        /*
         * Test it with perturbed diagonal preconditioner
         */
        ae_vector_set_length(&diagh, n, _state);
        for(i=0; i<=n-1; i++)
        {
            diagh.ptr.p_double[i] = 2*ae_sqr((double)(i*i+1), _state)*(0.8+0.4*ae_randomreal(_state));
        }
        minlbfgssetprecdiag(&state, &diagh, _state);
        cntg2 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntg2 = cntg2+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        
        /*
         * Compare
         */
        *err = *err||cntb1<cntg1;
        *err = *err||cntb2<cntg1;
        *err = *err||cntb1<cntg2;
        *err = *err||cntb2<cntg2;
    }
    
    /*
     * Preconditioner test 2.
     *
     * If
     * * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * then B2 is worse than G2.
     * "Worse" means more iterations to converge.
     */
    for(n=10; n<=15; n++)
    {
        ae_vector_set_length(&x, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)(0);
        }
        minlbfgscreate(n, m, &x, &state, _state);
        ae_vector_set_length(&s, n, _state);
        for(i=0; i<=n-1; i++)
        {
            s.ptr.p_double[i] = 1/ae_sqrt(2*ae_pow((double)(i*i+1), (double)(2), _state)*(0.8+0.4*ae_randomreal(_state)), _state);
        }
        minlbfgssetprecdefault(&state, _state);
        minlbfgssetscale(&state, &s, _state);
        cntb2 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntb2 = cntb2+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        minlbfgssetprecscale(&state, _state);
        minlbfgssetscale(&state, &s, _state);
        cntg2 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntg2 = cntg2+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        *err = *err||cntb2<cntg2;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests other properties

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminlbfgsunit_testother(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_vector x;
    ae_vector a;
    ae_vector b;
    ae_vector s;
    ae_vector h;
    ae_vector x0;
    ae_vector x1;
    ae_vector xlast;
    ae_matrix fulla;
    ae_bool hasxlast;
    double lastscaledstep;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    minlbfgsstate state;
    minlbfgsreport rep;
    double fprev;
    double xprev;
    double v;
    double stpmax;
    double tmpeps;
    double epsg;
    ae_int_t pkind;
    ae_int_t ckind;
    ae_int_t mkind;
    double vc;
    double vm;
    double diffstep;
    ae_int_t dkind;
    ae_bool wasf;
    ae_bool wasfg;
    double r;
    hqrndstate rs;
    ae_int_t spoiliteration;
    ae_int_t stopiteration;
    ae_int_t spoilvar;
    double spoilval;
    double ss;
    ae_bool terminationrequested;
    ae_int_t pass;
    ae_int_t stopcallidx;
    ae_int_t callidx;
    ae_int_t maxits;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&s, 0, sizeof(s));
    memset(&h, 0, sizeof(h));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&xlast, 0, sizeof(xlast));
    memset(&fulla, 0, sizeof(fulla));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&h, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    _minlbfgsstate_init(&state, _state, ae_true);
    _minlbfgsreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Test reports (F should form monotone sequence)
     */
    n = 50;
    m = 2;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&xlast, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = (double)(1);
    }
    minlbfgscreate(n, m, &x, &state, _state);
    minlbfgssetcond(&state, (double)(0), (double)(0), (double)(0), 100, _state);
    minlbfgssetxrep(&state, ae_true, _state);
    fprev = ae_maxrealnumber;
    while(minlbfgsiteration(&state, _state))
    {
        if( state.needfg )
        {
            state.f = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
            }
        }
        if( state.xupdated )
        {
            *err = *err||ae_fp_greater(state.f,fprev);
            if( ae_fp_eq(fprev,ae_maxrealnumber) )
            {
                for(i=0; i<=n-1; i++)
                {
                    *err = *err||ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]);
                }
            }
            fprev = state.f;
            ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        }
    }
    minlbfgsresults(&state, &x, &rep, _state);
    for(i=0; i<=n-1; i++)
    {
        *err = *err||ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]);
    }
    
    /*
     * Test differentiation vs. analytic gradient
     * (first one issues NeedF requests, second one issues NeedFG requests)
     */
    n = 50;
    m = 5;
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)(1);
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, (double)(0), (double)(0), (double)(0), n/2, _state);
        wasf = ae_false;
        wasfg = ae_false;
        while(minlbfgsiteration(&state, _state))
        {
            if( state.needf||state.needfg )
            {
                state.f = (double)(0);
            }
            for(i=0; i<=n-1; i++)
            {
                if( state.needf||state.needfg )
                {
                    state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                }
            }
            wasf = wasf||state.needf;
            wasfg = wasfg||state.needfg;
        }
        minlbfgsresults(&state, &x, &rep, _state);
        if( dkind==0 )
        {
            *err = (*err||wasf)||!wasfg;
        }
        if( dkind==1 )
        {
            *err = (*err||!wasf)||wasfg;
        }
    }
    
    /*
     * Test that numerical differentiation uses scaling.
     *
     * In order to test that we solve simple optimization
     * problem: min(x^2) with initial x equal to 0.0.
     *
     * We choose random DiffStep and S, then we check that
     * optimizer evaluates function at +-DiffStep*S only.
     */
    ae_vector_set_length(&x, 1, _state);
    ae_vector_set_length(&s, 1, _state);
    diffstep = ae_randomreal(_state)*1.0E-6;
    s.ptr.p_double[0] = ae_exp(ae_randomreal(_state)*4-2, _state);
    x.ptr.p_double[0] = (double)(0);
    minlbfgscreatef(1, 1, &x, diffstep, &state, _state);
    minlbfgssetcond(&state, 1.0E-6, (double)(0), (double)(0), 0, _state);
    minlbfgssetscale(&state, &s, _state);
    v = (double)(0);
    while(minlbfgsiteration(&state, _state))
    {
        state.f = ae_sqr(state.x.ptr.p_double[0], _state);
        v = ae_maxreal(v, ae_fabs(state.x.ptr.p_double[0], _state), _state);
    }
    minlbfgsresults(&state, &x, &rep, _state);
    r = v/(s.ptr.p_double[0]*diffstep);
    *err = *err||ae_fp_greater(ae_fabs(ae_log(r, _state), _state),ae_log(1+1000*ae_machineepsilon, _state));
    
    /*
     * test maximum step
     */
    n = 1;
    m = 1;
    ae_vector_set_length(&x, n, _state);
    x.ptr.p_double[0] = (double)(100);
    stpmax = 0.05+0.05*ae_randomreal(_state);
    minlbfgscreate(n, m, &x, &state, _state);
    minlbfgssetcond(&state, 1.0E-9, (double)(0), (double)(0), 0, _state);
    minlbfgssetstpmax(&state, stpmax, _state);
    minlbfgssetxrep(&state, ae_true, _state);
    xprev = x.ptr.p_double[0];
    while(minlbfgsiteration(&state, _state))
    {
        if( state.needfg )
        {
            state.f = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
            state.g.ptr.p_double[0] = ae_exp(state.x.ptr.p_double[0], _state)-ae_exp(-state.x.ptr.p_double[0], _state);
            *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
        }
        if( state.xupdated )
        {
            *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
            xprev = state.x.ptr.p_double[0];
        }
    }
    
    /*
     * Test correctness of the scaling:
     * * initial point is random point from [+1,+2]^N
     * * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
     * * we use random scaling matrix
     * * we test different variants of the preconditioning:
     *   0) unit preconditioner
     *   1) random diagonal from [0.01,100]
     *   2) scale preconditioner
     * * we set stringent stopping conditions (we try EpsG and EpsX)
     * * and we test that in the extremum stopping conditions are
     *   satisfied subject to the current scaling coefficients.
     */
    tmpeps = 1.0E-10;
    m = 1;
    for(n=1; n<=10; n++)
    {
        for(pkind=0; pkind<=2; pkind++)
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xlast, n, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&s, n, _state);
            ae_vector_set_length(&h, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = ae_randomreal(_state)+1;
                a.ptr.p_double[i] = ae_exp(ae_log((double)(100), _state)*(2*ae_randomreal(_state)-1), _state);
                s.ptr.p_double[i] = ae_exp(ae_log((double)(100), _state)*(2*ae_randomreal(_state)-1), _state);
                h.ptr.p_double[i] = ae_exp(ae_log((double)(100), _state)*(2*ae_randomreal(_state)-1), _state);
            }
            minlbfgscreate(n, m, &x, &state, _state);
            minlbfgssetscale(&state, &s, _state);
            minlbfgssetxrep(&state, ae_true, _state);
            if( pkind==1 )
            {
                minlbfgssetprecdiag(&state, &h, _state);
            }
            if( pkind==2 )
            {
                minlbfgssetprecscale(&state, _state);
            }
            
            /*
             * Test gradient-based stopping condition
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = ae_randomreal(_state)+1;
            }
            minlbfgssetcond(&state, tmpeps, (double)(0), (double)(0), 0, _state);
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                if( state.needfg )
                {
                    state.f = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], (double)(4), _state);
                        state.g.ptr.p_double[i] = 4*a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], (double)(3), _state);
                    }
                }
            }
            minlbfgsresults(&state, &x, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            v = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                v = v+ae_sqr(s.ptr.p_double[i]*4*a.ptr.p_double[i]*ae_pow(x.ptr.p_double[i], (double)(3), _state), _state);
            }
            v = ae_sqrt(v, _state);
            *err = *err||ae_fp_greater(v,tmpeps);
            
            /*
             * Test step-based stopping condition
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = ae_randomreal(_state)+1;
            }
            hasxlast = ae_false;
            lastscaledstep = ae_maxrealnumber;
            minlbfgssetcond(&state, (double)(0), (double)(0), tmpeps, 0, _state);
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                if( state.needfg )
                {
                    state.f = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], (double)(4), _state);
                        state.g.ptr.p_double[i] = 4*a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], (double)(3), _state);
                    }
                }
                if( state.xupdated )
                {
                    if( hasxlast )
                    {
                        lastscaledstep = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            lastscaledstep = lastscaledstep+ae_sqr(state.x.ptr.p_double[i]-xlast.ptr.p_double[i], _state)/ae_sqr(s.ptr.p_double[i], _state);
                        }
                        lastscaledstep = ae_sqrt(lastscaledstep, _state);
                    }
                    else
                    {
                        lastscaledstep = (double)(0);
                    }
                    ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    hasxlast = ae_true;
                }
            }
            minlbfgsresults(&state, &x, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            *err = *err||ae_fp_greater(lastscaledstep,tmpeps);
        }
    }
    
    /*
     * Check correctness of the "trimming".
     *
     * Trimming is a technique which is used to help algorithm
     * cope with unbounded functions. In order to check this
     * technique we will try to solve following optimization
     * problem:
     *
     *     min f(x) subject to no constraints on X
     *            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
     *     f(x) = {
     *            { M, if x<=-0.999999 or x>=0.999999
     *
     * where c is either 1.0 or 1.0E+6, M is either 1.0E8, 1.0E20 or +INF
     * (we try different combinations)
     */
    for(ckind=0; ckind<=1; ckind++)
    {
        for(mkind=0; mkind<=2; mkind++)
        {
            
            /*
             * Choose c and M
             */
            vc = (double)(1);
            vm = (double)(1);
            if( ckind==0 )
            {
                vc = 1.0;
            }
            if( ckind==1 )
            {
                vc = 1.0E+6;
            }
            if( mkind==0 )
            {
                vm = 1.0E+8;
            }
            if( mkind==1 )
            {
                vm = 1.0E+20;
            }
            if( mkind==2 )
            {
                vm = _state->v_posinf;
            }
            
            /*
             * Create optimizer, solve optimization problem
             */
            epsg = 1.0E-6*vc;
            ae_vector_set_length(&x, 1, _state);
            x.ptr.p_double[0] = 0.0;
            minlbfgscreate(1, 1, &x, &state, _state);
            minlbfgssetcond(&state, epsg, (double)(0), (double)(0), 0, _state);
            while(minlbfgsiteration(&state, _state))
            {
                if( state.needfg )
                {
                    if( ae_fp_less(-0.999999,state.x.ptr.p_double[0])&&ae_fp_less(state.x.ptr.p_double[0],0.999999) )
                    {
                        state.f = 1/(1-state.x.ptr.p_double[0])+1/(1+state.x.ptr.p_double[0])+vc*state.x.ptr.p_double[0];
                        state.g.ptr.p_double[0] = 1/ae_sqr(1-state.x.ptr.p_double[0], _state)-1/ae_sqr(1+state.x.ptr.p_double[0], _state)+vc;
                    }
                    else
                    {
                        state.f = vm;
                    }
                }
            }
            minlbfgsresults(&state, &x, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            *err = *err||ae_fp_greater(ae_fabs(1/ae_sqr(1-x.ptr.p_double[0], _state)-1/ae_sqr(1+x.ptr.p_double[0], _state)+vc, _state),epsg);
        }
    }
    
    /*
     * Test integrity checks for NAN/INF:
     * * algorithm solves optimization problem, which is normal for some time (quadratic)
     * * after 5-th step we choose random component of gradient and consistently spoil
     *   it by NAN or INF.
     * * we check that correct termination code is returned (-8)
     */
    n = 100;
    for(pass=1; pass<=10; pass++)
    {
        spoiliteration = 5;
        stopiteration = 8;
        if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
        {
            
            /*
             * Gradient can be spoiled by +INF, -INF, NAN
             */
            spoilvar = hqrnduniformi(&rs, n, _state);
            i = hqrnduniformi(&rs, 3, _state);
            spoilval = _state->v_nan;
            if( i==0 )
            {
                spoilval = _state->v_neginf;
            }
            if( i==1 )
            {
                spoilval = _state->v_posinf;
            }
        }
        else
        {
            
            /*
             * Function value can be spoiled only by NAN
             * (+INF can be recognized as legitimate value during optimization)
             */
            spoilvar = -1;
            spoilval = _state->v_nan;
        }
        spdmatrixrndcond(n, 1.0E5, &fulla, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = hqrndnormal(&rs, _state);
            x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        minlbfgscreate(n, 1, &x0, &state, _state);
        minlbfgssetcond(&state, 0.0, 0.0, 0.0, stopiteration, _state);
        minlbfgssetxrep(&state, ae_true, _state);
        k = -1;
        while(minlbfgsiteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                    state.g.ptr.p_double[i] = b.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        state.f = state.f+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        state.g.ptr.p_double[i] = state.g.ptr.p_double[i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                    }
                }
                if( k>=spoiliteration )
                {
                    if( spoilvar<0 )
                    {
                        state.f = spoilval;
                    }
                    else
                    {
                        state.g.ptr.p_double[spoilvar] = spoilval;
                    }
                }
                continue;
            }
            if( state.xupdated )
            {
                inc(&k, _state);
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minlbfgsresults(&state, &x1, &rep, _state);
        ae_set_error_flag(err, rep.terminationtype!=-8, __FILE__, __LINE__, "testminlbfgsunit.ap:1119");
    }
    
    /*
     * Check algorithm ability to handle request for termination:
     * * to terminate with correct return code = 8
     * * to return point which was "current" at the moment of termination
     */
    for(pass=1; pass<=50; pass++)
    {
        n = 3;
        ss = (double)(100);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 6+ae_randomreal(_state);
        }
        stopcallidx = ae_randominteger(20, _state);
        maxits = 25;
        minlbfgscreate(n, 1, &x, &state, _state);
        minlbfgssetcond(&state, (double)(0), (double)(0), (double)(0), maxits, _state);
        minlbfgssetxrep(&state, ae_true, _state);
        callidx = 0;
        terminationrequested = ae_false;
        ae_v_move(&xlast.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        while(minlbfgsiteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ss*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = 2*ss*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0])*(-1);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
                if( callidx==stopcallidx )
                {
                    minlbfgsrequesttermination(&state, _state);
                    terminationrequested = ae_true;
                }
                inc(&callidx, _state);
                continue;
            }
            if( state.xupdated )
            {
                if( !terminationrequested )
                {
                    ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        ae_set_error_flag(err, rep.terminationtype!=8, __FILE__, __LINE__, "testminlbfgsunit.ap:1169");
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]), __FILE__, __LINE__, "testminlbfgsunit.ap:1171");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests, that gradient verified correctly.
*************************************************************************/
static ae_bool testminlbfgsunit_gradientchecktest(ae_state *_state)
{
    ae_frame _frame_block;
    minlbfgsstate state;
    minlbfgsreport rep;
    ae_int_t m;
    ae_int_t n;
    double a;
    double b;
    double c;
    double d;
    double x0;
    double x1;
    double x2;
    ae_vector x;
    double teststep;
    double noise;
    ae_int_t nbrcomp;
    ae_int_t func;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&x, 0, sizeof(x));
    _minlbfgsstate_init(&state, _state, ae_true);
    _minlbfgsreport_init(&rep, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    passcount = 35;
    teststep = 0.01;
    n = 3;
    m = 2;
    ae_vector_set_length(&x, n, _state);
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Prepare test's parameters
         */
        func = ae_randominteger(3, _state)+1;
        nbrcomp = ae_randominteger(n, _state);
        noise = (double)(10*(2*ae_randominteger(2, _state)-1));
        
        /*
         * Prepare function's parameters
         */
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
        }
        a = 5*ae_randomreal(_state)+1;
        b = 5*ae_randomreal(_state)+1;
        c = 5*ae_randomreal(_state)+1;
        d = 5*ae_randomreal(_state)+1;
        x0 = 5*(2*ae_randomreal(_state)-1);
        x1 = 5*(2*ae_randomreal(_state)-1);
        x2 = 5*(2*ae_randomreal(_state)-1);
        minlbfgscreate(n, m, &x, &state, _state);
        minlbfgssetcond(&state, (double)(0), (double)(0), (double)(0), 0, _state);
        minlbfgssetgradientcheck(&state, teststep, _state);
        
        /*
         * Check that the criterion passes a derivative if it is correct
         */
        while(minlbfgsiteration(&state, _state))
        {
            if( state.needfg )
            {
                testminlbfgsunit_funcderiv(a, b, c, d, x0, x1, x2, &state.x, func, &state.f, &state.g, _state);
            }
        }
        minlbfgsresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code does not equal to -7 and parameter .VarIdx
         * equal to -1.
         */
        if( rep.terminationtype==-7||rep.varidx!=-1 )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
        }
        minlbfgsrestartfrom(&state, &x, _state);
        
        /*
         * Check that the criterion does not miss a derivative if
         * it is incorrect
         */
        while(minlbfgsiteration(&state, _state))
        {
            if( state.needfg )
            {
                testminlbfgsunit_funcderiv(a, b, c, d, x0, x1, x2, &state.x, func, &state.f, &state.g, _state);
                state.g.ptr.p_double[nbrcomp] = state.g.ptr.p_double[nbrcomp]+noise;
            }
        }
        minlbfgsresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code equal to -7 and parameter .VarIdx
         * equal to number of incorrect component.
         */
        if( rep.terminationtype!=-7||rep.varidx!=nbrcomp )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function return function value and it derivatives. Function dimension
is 3.
    Function's list:
        * funcType=1:
            F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*(Z-Z0)^2+D;
        * funcType=2:
            F(X)=A*sin(X-X0)^2+B*sin(Y-Y0)^2+C*sin(Z-Z0)^2+D;
        * funcType=3:
            F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*((Z-Z0)-(X-X0))^2+D.
*************************************************************************/
static void testminlbfgsunit_funcderiv(double a,
     double b,
     double c,
     double d,
     double x0,
     double x1,
     double x2,
     /* Real    */ ae_vector* x,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{


    ae_assert(((ae_isfinite(a, _state)&&ae_isfinite(b, _state))&&ae_isfinite(c, _state))&&ae_isfinite(d, _state), "FuncDeriv: A, B, C or D contains NaN or Infinite.", _state);
    ae_assert((ae_isfinite(x0, _state)&&ae_isfinite(x1, _state))&&ae_isfinite(x2, _state), "FuncDeriv: X0, X1 or X2 contains NaN or Infinite.", _state);
    ae_assert(functype>=1&&functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).", _state);
    if( functype==1 )
    {
        *f = a*ae_sqr(x->ptr.p_double[0]-x0, _state)+b*ae_sqr(x->ptr.p_double[1]-x1, _state)+c*ae_sqr(x->ptr.p_double[2]-x2, _state)+d;
        g->ptr.p_double[0] = 2*a*(x->ptr.p_double[0]-x0);
        g->ptr.p_double[1] = 2*b*(x->ptr.p_double[1]-x1);
        g->ptr.p_double[2] = 2*c*(x->ptr.p_double[2]-x2);
        return;
    }
    if( functype==2 )
    {
        *f = a*ae_sqr(ae_sin(x->ptr.p_double[0]-x0, _state), _state)+b*ae_sqr(ae_sin(x->ptr.p_double[1]-x1, _state), _state)+c*ae_sqr(ae_sin(x->ptr.p_double[2]-x2, _state), _state)+d;
        g->ptr.p_double[0] = 2*a*ae_sin(x->ptr.p_double[0]-x0, _state)*ae_cos(x->ptr.p_double[0]-x0, _state);
        g->ptr.p_double[1] = 2*b*ae_sin(x->ptr.p_double[1]-x1, _state)*ae_cos(x->ptr.p_double[1]-x1, _state);
        g->ptr.p_double[2] = 2*c*ae_sin(x->ptr.p_double[2]-x2, _state)*ae_cos(x->ptr.p_double[2]-x2, _state);
        return;
    }
    if( functype==3 )
    {
        *f = a*ae_sqr(x->ptr.p_double[0]-x0, _state)+b*ae_sqr(x->ptr.p_double[1]-x1, _state)+c*ae_sqr(x->ptr.p_double[2]-x2-(x->ptr.p_double[0]-x0), _state)+d;
        g->ptr.p_double[0] = 2*a*(x->ptr.p_double[0]-x0)+2*c*(x->ptr.p_double[0]-x->ptr.p_double[2]-x0+x2);
        g->ptr.p_double[1] = 2*b*(x->ptr.p_double[1]-x1);
        g->ptr.p_double[2] = 2*c*(x->ptr.p_double[2]-x->ptr.p_double[0]-x2+x0);
        return;
    }
}








ae_bool testxblas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool approxerrors;
    ae_bool exactnesserrors;
    ae_bool waserrors;
    double approxthreshold;
    ae_int_t maxn;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t i;
    ae_int_t pass;
    double rv1;
    double rv2;
    double rv2err;
    ae_complex cv1;
    ae_complex cv2;
    double cv2err;
    ae_vector rx;
    ae_vector ry;
    ae_vector cx;
    ae_vector cy;
    ae_vector temp;
    double s;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&rx, 0, sizeof(rx));
    memset(&ry, 0, sizeof(ry));
    memset(&cx, 0, sizeof(cx));
    memset(&cy, 0, sizeof(cy));
    memset(&temp, 0, sizeof(temp));
    ae_vector_init(&rx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ry, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cx, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cy, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&temp, 0, DT_REAL, _state, ae_true);

    approxerrors = ae_false;
    exactnesserrors = ae_false;
    waserrors = ae_false;
    approxthreshold = 1000*ae_machineepsilon;
    maxn = 1000;
    passcount = 10;
    
    /*
     * tests:
     * 1. ability to calculate dot product
     * 2. higher precision
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             *  ability to approximately calculate real dot product
             */
            ae_vector_set_length(&rx, n, _state);
            ae_vector_set_length(&ry, n, _state);
            ae_vector_set_length(&temp, n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_greater(ae_randomreal(_state),0.2) )
                {
                    rx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    rx.ptr.p_double[i] = (double)(0);
                }
                if( ae_fp_greater(ae_randomreal(_state),0.2) )
                {
                    ry.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    ry.ptr.p_double[i] = (double)(0);
                }
            }
            rv1 = ae_v_dotproduct(&rx.ptr.p_double[0], 1, &ry.ptr.p_double[0], 1, ae_v_len(0,n-1));
            xdot(&rx, &ry, n, &temp, &rv2, &rv2err, _state);
            approxerrors = approxerrors||ae_fp_greater(ae_fabs(rv1-rv2, _state),approxthreshold);
            
            /*
             *  ability to approximately calculate complex dot product
             */
            ae_vector_set_length(&cx, n, _state);
            ae_vector_set_length(&cy, n, _state);
            ae_vector_set_length(&temp, 2*n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_greater(ae_randomreal(_state),0.2) )
                {
                    cx.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                    cx.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    cx.ptr.p_complex[i] = ae_complex_from_i(0);
                }
                if( ae_fp_greater(ae_randomreal(_state),0.2) )
                {
                    cy.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                    cy.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    cy.ptr.p_complex[i] = ae_complex_from_i(0);
                }
            }
            cv1 = ae_v_cdotproduct(&cx.ptr.p_complex[0], 1, "N", &cy.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
            xcdot(&cx, &cy, n, &temp, &cv2, &cv2err, _state);
            approxerrors = approxerrors||ae_fp_greater(ae_c_abs(ae_c_sub(cv1,cv2), _state),approxthreshold);
        }
    }
    
    /*
     * test of precision: real
     */
    n = 50000;
    ae_vector_set_length(&rx, n, _state);
    ae_vector_set_length(&ry, n, _state);
    ae_vector_set_length(&temp, n, _state);
    for(pass=0; pass<=passcount-1; pass++)
    {
        ae_assert(n%2==0, "Assertion failed", _state);
        
        /*
         * First test: X + X + ... + X - X - X - ... - X = 1*X
         */
        s = ae_exp((double)(ae_maxint(pass, 50, _state)), _state);
        if( pass==passcount-1&&pass>1 )
        {
            s = ae_maxrealnumber;
        }
        ry.ptr.p_double[0] = (2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state);
        for(i=1; i<=n-1; i++)
        {
            ry.ptr.p_double[i] = ry.ptr.p_double[0];
        }
        for(i=0; i<=n/2-1; i++)
        {
            rx.ptr.p_double[i] = (double)(1);
        }
        for(i=n/2; i<=n-2; i++)
        {
            rx.ptr.p_double[i] = (double)(-1);
        }
        rx.ptr.p_double[n-1] = (double)(0);
        xdot(&rx, &ry, n, &temp, &rv2, &rv2err, _state);
        exactnesserrors = exactnesserrors||ae_fp_less(rv2err,(double)(0));
        exactnesserrors = exactnesserrors||ae_fp_greater(rv2err,4*ae_machineepsilon*ae_fabs(ry.ptr.p_double[0], _state));
        exactnesserrors = exactnesserrors||ae_fp_greater(ae_fabs(rv2-ry.ptr.p_double[0], _state),rv2err);
        
        /*
         * First test: X + X + ... + X = N*X
         */
        s = ae_exp((double)(ae_maxint(pass, 50, _state)), _state);
        if( pass==passcount-1&&pass>1 )
        {
            s = ae_maxrealnumber;
        }
        ry.ptr.p_double[0] = (2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state);
        for(i=1; i<=n-1; i++)
        {
            ry.ptr.p_double[i] = ry.ptr.p_double[0];
        }
        for(i=0; i<=n-1; i++)
        {
            rx.ptr.p_double[i] = (double)(1);
        }
        xdot(&rx, &ry, n, &temp, &rv2, &rv2err, _state);
        exactnesserrors = exactnesserrors||ae_fp_less(rv2err,(double)(0));
        exactnesserrors = exactnesserrors||ae_fp_greater(rv2err,4*ae_machineepsilon*ae_fabs(ry.ptr.p_double[0], _state)*n);
        exactnesserrors = exactnesserrors||ae_fp_greater(ae_fabs(rv2-n*ry.ptr.p_double[0], _state),rv2err);
    }
    
    /*
     * test of precision: complex
     */
    n = 50000;
    ae_vector_set_length(&cx, n, _state);
    ae_vector_set_length(&cy, n, _state);
    ae_vector_set_length(&temp, 2*n, _state);
    for(pass=0; pass<=passcount-1; pass++)
    {
        ae_assert(n%2==0, "Assertion failed", _state);
        
        /*
         * First test: X + X + ... + X - X - X - ... - X = 1*X
         */
        s = ae_exp((double)(ae_maxint(pass, 50, _state)), _state);
        if( pass==passcount-1&&pass>1 )
        {
            s = ae_maxrealnumber;
        }
        cy.ptr.p_complex[0].x = (2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state);
        cy.ptr.p_complex[0].y = (2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state);
        for(i=1; i<=n-1; i++)
        {
            cy.ptr.p_complex[i] = cy.ptr.p_complex[0];
        }
        for(i=0; i<=n/2-1; i++)
        {
            cx.ptr.p_complex[i] = ae_complex_from_i(1);
        }
        for(i=n/2; i<=n-2; i++)
        {
            cx.ptr.p_complex[i] = ae_complex_from_i(-1);
        }
        cx.ptr.p_complex[n-1] = ae_complex_from_i(0);
        xcdot(&cx, &cy, n, &temp, &cv2, &cv2err, _state);
        exactnesserrors = exactnesserrors||ae_fp_less(cv2err,(double)(0));
        exactnesserrors = exactnesserrors||ae_fp_greater(cv2err,4*ae_machineepsilon*ae_c_abs(cy.ptr.p_complex[0], _state));
        exactnesserrors = exactnesserrors||ae_fp_greater(ae_c_abs(ae_c_sub(cv2,cy.ptr.p_complex[0]), _state),cv2err);
        
        /*
         * First test: X + X + ... + X = N*X
         */
        s = ae_exp((double)(ae_maxint(pass, 50, _state)), _state);
        if( pass==passcount-1&&pass>1 )
        {
            s = ae_maxrealnumber;
        }
        cy.ptr.p_complex[0] = ae_complex_from_d((2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state));
        for(i=1; i<=n-1; i++)
        {
            cy.ptr.p_complex[i] = cy.ptr.p_complex[0];
        }
        for(i=0; i<=n-1; i++)
        {
            cx.ptr.p_complex[i] = ae_complex_from_i(1);
        }
        xcdot(&cx, &cy, n, &temp, &cv2, &cv2err, _state);
        exactnesserrors = exactnesserrors||ae_fp_less(cv2err,(double)(0));
        exactnesserrors = exactnesserrors||ae_fp_greater(cv2err,4*ae_machineepsilon*ae_c_abs(cy.ptr.p_complex[0], _state)*n);
        exactnesserrors = exactnesserrors||ae_fp_greater(ae_c_abs(ae_c_sub(cv2,ae_c_mul_d(cy.ptr.p_complex[0],1.0*n)), _state),cv2err);
    }
    
    /*
     * report
     */
    waserrors = approxerrors||exactnesserrors;
    if( !silent )
    {
        printf("TESTING XBLAS\n");
        printf("APPROX.TESTS:                            ");
        if( approxerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("EXACT TESTS:                             ");
        if( exactnesserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static ae_bool testdirectdensesolversunit_rmatrixchecksolutionm(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_rmatrixchecksolutionmfast(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     /* Real    */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_rmatrixchecksolution(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_rmatrixchecksolutionfast(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     /* Real    */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_rmatrixchecksingularm(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_rmatrixchecksingularmfast(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     /* Real    */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_rmatrixchecksingular(ae_int_t n,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_rmatrixchecksingularfast(ae_int_t n,
     ae_int_t info,
     /* Real    */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_cmatrixchecksolutionm(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_cmatrixchecksolutionmfast(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     /* Complex */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_cmatrixchecksolution(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_cmatrixchecksolutionfast(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     /* Complex */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_cmatrixchecksingularm(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_cmatrixchecksingularmfast(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     /* Complex */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_cmatrixchecksingular(ae_int_t n,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdirectdensesolversunit_cmatrixchecksingularfast(ae_int_t n,
     ae_int_t info,
     /* Complex */ ae_vector* xs,
     ae_state *_state);
static void testdirectdensesolversunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testdirectdensesolversunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);
static void testdirectdensesolversunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testdirectdensesolversunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testdirectdensesolversunit_testrsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* rerrors,
     ae_bool* rfserrors,
     ae_state *_state);
static void testdirectdensesolversunit_testspdsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* spderrors,
     ae_bool* rfserrors,
     ae_state *_state);
static void testdirectdensesolversunit_testcsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* cerrors,
     ae_bool* rfserrors,
     ae_state *_state);
static void testdirectdensesolversunit_testhpdsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* hpderrors,
     ae_bool* rfserrors,
     ae_state *_state);
static void testdirectdensesolversunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state);
static void testdirectdensesolversunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state);
static void testdirectdensesolversunit_cunset2d(/* Complex */ ae_matrix* x,
     ae_state *_state);
static void testdirectdensesolversunit_cunset1d(/* Complex */ ae_vector* x,
     ae_state *_state);
static void testdirectdensesolversunit_unsetrep(densesolverreport* r,
     ae_state *_state);
static void testdirectdensesolversunit_unsetlsrep(densesolverlsreport* r,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testdirectdensesolvers(ae_bool silent, ae_state *_state)
{
    ae_int_t maxn;
    ae_int_t maxm;
    ae_int_t passcount;
    double threshold;
    ae_bool rerrors;
    ae_bool cerrors;
    ae_bool spderrors;
    ae_bool hpderrors;
    ae_bool rfserrors;
    ae_bool waserrors;
    ae_bool result;


    maxn = 10;
    maxm = 5;
    passcount = 5;
    threshold = 10000*ae_machineepsilon;
    rfserrors = ae_false;
    rerrors = ae_false;
    cerrors = ae_false;
    spderrors = ae_false;
    hpderrors = ae_false;
    testdirectdensesolversunit_testrsolver(maxn, maxm, passcount, threshold, &rerrors, &rfserrors, _state);
    testdirectdensesolversunit_testspdsolver(maxn, maxm, passcount, threshold, &spderrors, &rfserrors, _state);
    testdirectdensesolversunit_testcsolver(maxn, maxm, passcount, threshold, &cerrors, &rfserrors, _state);
    testdirectdensesolversunit_testhpdsolver(maxn, maxm, passcount, threshold, &hpderrors, &rfserrors, _state);
    waserrors = (((rerrors||cerrors)||spderrors)||hpderrors)||rfserrors;
    if( !silent )
    {
        printf("TESTING DENSE SOLVER\n");
        printf("* REAL:                                   ");
        if( rerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX:                                ");
        if( cerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SPD:                                    ");
        if( spderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* HPD:                                    ");
        if( hpderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* ITERATIVE IMPROVEMENT:                  ");
        if( rfserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_rmatrixchecksolutionm(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result&&ae_fp_less_eq(ae_fabs(xe->ptr.pp_double[i][j]-xs->ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_rmatrixchecksolutionmfast(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     /* Real    */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result&&ae_fp_less_eq(ae_fabs(xe->ptr.pp_double[i][j]-xs->ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_rmatrixchecksolution(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xsm, 0, sizeof(xsm));
    ae_matrix_init(&xsm, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_move(&xsm.ptr.pp_double[0][0], xsm.stride, &xs->ptr.p_double[0], 1, ae_v_len(0,n-1));
    result = testdirectdensesolversunit_rmatrixchecksolutionm(xe, n, 1, threshold, info, rep, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_rmatrixchecksolutionfast(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     /* Real    */ ae_vector* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            result = result&&ae_fp_less_eq(ae_fabs(xe->ptr.pp_double[i][0]-xs->ptr.p_double[i], _state),threshold);
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_rmatrixchecksingularm(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3&&info!=1 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,(double)(0))||ae_fp_greater(rep->r1,1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,(double)(0))||ae_fp_greater(rep->rinf,1000*ae_machineepsilon));
        if( info==-3 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    result = result&&ae_fp_eq(xs->ptr.pp_double[i][j],(double)(0));
                }
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_rmatrixchecksingularmfast(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     /* Real    */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3 )
    {
        result = ae_false;
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result&&ae_fp_eq(xs->ptr.pp_double[i][j],(double)(0));
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_rmatrixchecksingular(ae_int_t n,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xsm, 0, sizeof(xsm));
    ae_matrix_init(&xsm, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_move(&xsm.ptr.pp_double[0][0], xsm.stride, &xs->ptr.p_double[0], 1, ae_v_len(0,n-1));
    result = testdirectdensesolversunit_rmatrixchecksingularm(n, 1, info, rep, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_rmatrixchecksingularfast(ae_int_t n,
     ae_int_t info,
     /* Real    */ ae_vector* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_bool result;


    result = ae_true;
    if( info!=-3 )
    {
        result = ae_false;
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            result = result&&ae_fp_eq(xs->ptr.p_double[i],(double)(0));
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_cmatrixchecksolutionm(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result&&ae_fp_less_eq(ae_c_abs(ae_c_sub(xe->ptr.pp_complex[i][j],xs->ptr.pp_complex[i][j]), _state),threshold);
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_cmatrixchecksolutionmfast(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     /* Complex */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
        return result;
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            result = result&&ae_fp_less_eq(ae_c_abs(ae_c_sub(xe->ptr.pp_complex[i][j],xs->ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_cmatrixchecksolution(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xsm, 0, sizeof(xsm));
    ae_matrix_init(&xsm, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_cmove(&xsm.ptr.pp_complex[0][0], xsm.stride, &xs->ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
    result = testdirectdensesolversunit_cmatrixchecksolutionm(xe, n, 1, threshold, info, rep, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_cmatrixchecksolutionfast(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     /* Complex */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xsm, 0, sizeof(xsm));
    ae_matrix_init(&xsm, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_cmove(&xsm.ptr.pp_complex[0][0], xsm.stride, &xs->ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
    result = testdirectdensesolversunit_cmatrixchecksolutionmfast(xe, n, 1, threshold, info, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_cmatrixchecksingularm(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3&&info!=1 )
    {
        result = ae_false;
        return result;
    }
    result = result&&!(ae_fp_less(rep->r1,(double)(0))||ae_fp_greater(rep->r1,1000*ae_machineepsilon));
    result = result&&!(ae_fp_less(rep->rinf,(double)(0))||ae_fp_greater(rep->rinf,1000*ae_machineepsilon));
    if( info==-3 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result&&ae_c_eq_d(xs->ptr.pp_complex[i][j],(double)(0));
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_cmatrixchecksingularmfast(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     /* Complex */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3 )
    {
        result = ae_false;
        return result;
    }
    if( info==-3 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result&&ae_c_eq_d(xs->ptr.pp_complex[i][j],(double)(0));
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_cmatrixchecksingular(ae_int_t n,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xsm, 0, sizeof(xsm));
    ae_matrix_init(&xsm, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_cmove(&xsm.ptr.pp_complex[0][0], xsm.stride, &xs->ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
    result = testdirectdensesolversunit_cmatrixchecksingularm(n, 1, info, rep, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdirectdensesolversunit_cmatrixchecksingularfast(ae_int_t n,
     ae_int_t info,
     /* Complex */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xsm, 0, sizeof(xsm));
    ae_matrix_init(&xsm, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_cmove(&xsm.ptr.pp_complex[0][0], xsm.stride, &xs->ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
    result = testdirectdensesolversunit_cmatrixchecksingularmfast(n, 1, info, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testdirectdensesolversunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Copy
*************************************************************************/
static void testdirectdensesolversunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testdirectdensesolversunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_double[i][j] = (double)(1+2*i+3*j);
            }
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testdirectdensesolversunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_i(1+2*i+3*j);
            }
        }
    }
}


/*************************************************************************
Real test
*************************************************************************/
static void testdirectdensesolversunit_testrsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* rerrors,
     ae_bool* rfserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_matrix atmp;
    ae_vector p;
    ae_matrix xe;
    ae_matrix b;
    ae_vector bv;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t pass;
    ae_int_t taskkind;
    double v;
    double verr;
    ae_int_t info;
    densesolverreport rep;
    densesolverlsreport repls;
    ae_matrix x;
    ae_vector xv;
    ae_vector y;
    ae_vector tx;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&lua, 0, sizeof(lua));
    memset(&atmp, 0, sizeof(atmp));
    memset(&p, 0, sizeof(p));
    memset(&xe, 0, sizeof(xe));
    memset(&b, 0, sizeof(b));
    memset(&bv, 0, sizeof(bv));
    memset(&rep, 0, sizeof(rep));
    memset(&repls, 0, sizeof(repls));
    memset(&x, 0, sizeof(x));
    memset(&xv, 0, sizeof(xv));
    memset(&y, 0, sizeof(y));
    memset(&tx, 0, sizeof(tx));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&atmp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xe, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bv, 0, DT_REAL, _state, ae_true);
    _densesolverreport_init(&rep, _state, ae_true);
    _densesolverlsreport_init(&repls, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * ********************************************************
                 * WELL CONDITIONED TASKS
                 * ability to find correct solution is tested
                 * ********************************************************
                 *
                 * 1. generate random well conditioned matrix A.
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods on original A
                 */
                rmatrixrndcond(n, (double)(1000), &a, _state);
                testdirectdensesolversunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
                rmatrixlu(&lua, n, n, &p, _state);
                ae_matrix_set_length(&xe, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        xe.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    }
                }
                ae_matrix_set_length(&b, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.pp_double[0][j], xe.stride, ae_v_len(0,n-1));
                        b.ptr.pp_double[i][j] = v;
                    }
                }
                
                /*
                 * Test solvers
                 */
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset2d(&x, _state);
                rmatrixsolvem(&a, n, &b, m, ae_fp_greater(ae_randomreal(_state),0.5), &info, &rep, &x, _state);
                *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                ae_matrix_set_length(&x, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        x.ptr.pp_double[i][j] = b.ptr.pp_double[i][j];
                    }
                }
                rmatrixsolvemfast(&a, n, &x, m, &info, _state);
                *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksolutionmfast(&xe, n, m, threshold, info, &x, _state);
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
                *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixsolvefast(&a, n, &bv, &info, _state);
                *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksolutionfast(&xe, n, threshold, info, &bv, _state);
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset2d(&x, _state);
                rmatrixlusolvem(&lua, &p, n, &b, m, &info, &rep, &x, _state);
                *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                ae_matrix_set_length(&x, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        x.ptr.pp_double[i][j] = b.ptr.pp_double[i][j];
                    }
                }
                rmatrixlusolvemfast(&lua, &p, n, &x, m, &info, _state);
                ae_set_error_flag(rerrors, !testdirectdensesolversunit_rmatrixchecksolutionmfast(&xe, n, m, threshold, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:552");
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixlusolve(&lua, &p, n, &bv, &info, &rep, &xv, _state);
                *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&xv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixlusolvefast(&lua, &p, n, &xv, &info, _state);
                ae_set_error_flag(rerrors, !testdirectdensesolversunit_rmatrixchecksolutionfast(&xe, n, threshold, info, &xv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:566");
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset2d(&x, _state);
                rmatrixmixedsolvem(&a, &lua, &p, n, &b, m, &info, &rep, &x, _state);
                *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixmixedsolve(&a, &lua, &p, n, &bv, &info, &rep, &xv, _state);
                *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                
                /*
                 * Test DenseSolverRLS():
                 * * test on original system A*x = b
                 * * test on overdetermined system with the same solution: (A' A')'*x = (b' b')'
                 * * test on underdetermined system with the same solution: (A 0 0 0 ) * z = b
                 */
                info = 0;
                testdirectdensesolversunit_unsetlsrep(&repls, _state);
                testdirectdensesolversunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixsolvels(&a, n, n, &bv, 0.0, &info, &repls, &xv, _state);
                if( info<=0 )
                {
                    *rerrors = ae_true;
                }
                else
                {
                    *rerrors = (*rerrors||ae_fp_less(repls.r2,100*ae_machineepsilon))||ae_fp_greater(repls.r2,1+1000*ae_machineepsilon);
                    *rerrors = (*rerrors||repls.n!=n)||repls.k!=0;
                    for(i=0; i<=n-1; i++)
                    {
                        *rerrors = *rerrors||ae_fp_greater(ae_fabs(xe.ptr.pp_double[i][0]-xv.ptr.p_double[i], _state),threshold);
                    }
                }
                info = 0;
                testdirectdensesolversunit_unsetlsrep(&repls, _state);
                testdirectdensesolversunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, 2*n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                ae_v_move(&bv.ptr.p_double[n], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(n,2*n-1));
                ae_matrix_set_length(&atmp, 2*n, n, _state);
                copymatrix(&a, 0, n-1, 0, n-1, &atmp, 0, n-1, 0, n-1, _state);
                copymatrix(&a, 0, n-1, 0, n-1, &atmp, n, 2*n-1, 0, n-1, _state);
                rmatrixsolvels(&atmp, 2*n, n, &bv, 0.0, &info, &repls, &xv, _state);
                if( info<=0 )
                {
                    *rerrors = ae_true;
                }
                else
                {
                    *rerrors = (*rerrors||ae_fp_less(repls.r2,100*ae_machineepsilon))||ae_fp_greater(repls.r2,1+1000*ae_machineepsilon);
                    *rerrors = (*rerrors||repls.n!=n)||repls.k!=0;
                    for(i=0; i<=n-1; i++)
                    {
                        *rerrors = *rerrors||ae_fp_greater(ae_fabs(xe.ptr.pp_double[i][0]-xv.ptr.p_double[i], _state),threshold);
                    }
                }
                info = 0;
                testdirectdensesolversunit_unsetlsrep(&repls, _state);
                testdirectdensesolversunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                ae_matrix_set_length(&atmp, n, 2*n, _state);
                copymatrix(&a, 0, n-1, 0, n-1, &atmp, 0, n-1, 0, n-1, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=n; j<=2*n-1; j++)
                    {
                        atmp.ptr.pp_double[i][j] = (double)(0);
                    }
                }
                rmatrixsolvels(&atmp, n, 2*n, &bv, 0.0, &info, &repls, &xv, _state);
                if( info<=0 )
                {
                    *rerrors = ae_true;
                }
                else
                {
                    *rerrors = *rerrors||ae_fp_neq(repls.r2,(double)(0));
                    *rerrors = (*rerrors||repls.n!=2*n)||repls.k!=n;
                    for(i=0; i<=n-1; i++)
                    {
                        *rerrors = *rerrors||ae_fp_greater(ae_fabs(xe.ptr.pp_double[i][0]-xv.ptr.p_double[i], _state),threshold);
                    }
                    for(i=n; i<=2*n-1; i++)
                    {
                        *rerrors = *rerrors||ae_fp_greater(ae_fabs(xv.ptr.p_double[i], _state),threshold);
                    }
                }
                
                /*
                 * ********************************************************
                 * EXACTLY SINGULAR MATRICES
                 * ability to detect singularity is tested
                 * ********************************************************
                 *
                 * 1. generate different types of singular matrices:
                 *    * zero (TaskKind=0)
                 *    * with zero columns (TaskKind=1)
                 *    * with zero rows (TaskKind=2)
                 *    * with equal rows/columns (TaskKind=2 or 3)
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods
                 */
                for(taskkind=0; taskkind<=4; taskkind++)
                {
                    testdirectdensesolversunit_unset2d(&a, _state);
                    if( taskkind==0 )
                    {
                        
                        /*
                         * all zeros
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = (double)(0);
                            }
                        }
                    }
                    if( taskkind==1 )
                    {
                        
                        /*
                         * there is zero column
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==2 )
                    {
                        
                        /*
                         * there is zero row
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==3 )
                    {
                        
                        /*
                         * equal columns
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_move(&a.ptr.pp_double[0][0], a.stride, &a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1));
                    }
                    if( taskkind==4 )
                    {
                        
                        /*
                         * equal rows
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_move(&a.ptr.pp_double[0][0], 1, &a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1));
                    }
                    ae_matrix_set_length(&xe, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            xe.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    ae_matrix_set_length(&b, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.pp_double[0][j], xe.stride, ae_v_len(0,n-1));
                            b.ptr.pp_double[i][j] = v;
                        }
                    }
                    testdirectdensesolversunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
                    rmatrixlu(&lua, n, n, &p, _state);
                    
                    /*
                     * Test RMatrixSolveM()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_unset2d(&x, _state);
                    rmatrixsolvem(&a, n, &b, m, ae_fp_greater(ae_randomreal(_state),0.5), &info, &rep, &x, _state);
                    *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test RMatrixSolveMFast(); performed only for matrices
                     * with zero rows or columns
                     */
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_matrix_set_length(&x, n, m, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x.ptr.pp_double[i][j] = b.ptr.pp_double[i][j];
                            }
                        }
                        rmatrixsolvemfast(&a, n, &x, m, &info, _state);
                        *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksingularmfast(n, m, info, &x, _state);
                    }
                    
                    /*
                     * Test RMatrixSolve()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_unset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                    rmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
                    *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * Test RMatrixSolveFast(); performed only for matrices
                     * with zero rows or columns
                     */
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_vector_set_length(&bv, n, _state);
                        ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                        rmatrixsolvefast(&a, n, &bv, &info, _state);
                        ae_set_error_flag(rerrors, !testdirectdensesolversunit_rmatrixchecksingularfast(n, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:784");
                    }
                    
                    /*
                     * Test RMatrixLUSolveM()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_unset2d(&x, _state);
                    rmatrixlusolvem(&lua, &p, n, &b, m, &info, &rep, &x, _state);
                    *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test RMatrixLUSolveMFast(); performed only for matrices
                     * with zero rows or columns
                     */
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_matrix_set_length(&x, n, m, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x.ptr.pp_double[i][j] = b.ptr.pp_double[i][j];
                            }
                        }
                        rmatrixlusolvemfast(&lua, &p, n, &x, m, &info, _state);
                        ae_set_error_flag(rerrors, !testdirectdensesolversunit_rmatrixchecksingularmfast(n, m, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:808");
                    }
                    
                    /*
                     * Test RMatrixLUSolve()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_unset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                    rmatrixlusolve(&lua, &p, n, &bv, &info, &rep, &xv, _state);
                    *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * Test RMatrixLUSolveFast(); performed only for matrices
                     * with zero rows or columns
                     */
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_vector_set_length(&bv, n, _state);
                        ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                        rmatrixlusolvefast(&lua, &p, n, &bv, &info, _state);
                        ae_set_error_flag(rerrors, !testdirectdensesolversunit_rmatrixchecksingularfast(n, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:832");
                    }
                    
                    /*
                     * Test RMatrixMixedSolveM()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_unset2d(&x, _state);
                    rmatrixmixedsolvem(&a, &lua, &p, n, &b, m, &info, &rep, &x, _state);
                    *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test RMatrixMixedSolve()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_unset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                    rmatrixmixedsolve(&a, &lua, &p, n, &bv, &info, &rep, &xv, _state);
                    *rerrors = *rerrors||!testdirectdensesolversunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                }
            }
        }
    }
    
    /*
     * test iterative improvement
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Test iterative improvement matrices
         *
         * A matrix/right part are constructed such that both matrix
         * and solution components are within (-1,+1). Such matrix/right part
         * have nice properties - system can be solved using iterative
         * improvement with |A*x-b| about several ulps of max(1,|b|).
         */
        n = 100;
        ae_matrix_set_length(&a, n, n, _state);
        ae_matrix_set_length(&b, n, 1, _state);
        ae_vector_set_length(&bv, n, _state);
        ae_vector_set_length(&tx, n, _state);
        ae_vector_set_length(&xv, n, _state);
        ae_vector_set_length(&y, n, _state);
        for(i=0; i<=n-1; i++)
        {
            xv.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            ae_v_move(&y.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            xdot(&y, &xv, n, &tx, &v, &verr, _state);
            bv.ptr.p_double[i] = v;
        }
        ae_v_move(&b.ptr.pp_double[0][0], b.stride, &bv.ptr.p_double[0], 1, ae_v_len(0,n-1));
        
        /*
         * Test RMatrixSolveM()
         */
        testdirectdensesolversunit_unset2d(&x, _state);
        rmatrixsolvem(&a, n, &b, 1, ae_true, &info, &rep, &x, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            ae_vector_set_length(&xv, n, _state);
            ae_v_move(&xv.ptr.p_double[0], 1, &x.ptr.pp_double[0][0], x.stride, ae_v_len(0,n-1));
            for(i=0; i<=n-1; i++)
            {
                ae_v_move(&y.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                xdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_fabs(v-b.ptr.pp_double[i][0], _state),8*ae_machineepsilon*ae_maxreal((double)(1), ae_fabs(b.ptr.pp_double[i][0], _state), _state));
            }
        }
        
        /*
         * Test RMatrixSolve()
         */
        testdirectdensesolversunit_unset1d(&xv, _state);
        rmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            for(i=0; i<=n-1; i++)
            {
                ae_v_move(&y.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                xdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_fabs(v-bv.ptr.p_double[i], _state),8*ae_machineepsilon*ae_maxreal((double)(1), ae_fabs(bv.ptr.p_double[i], _state), _state));
            }
        }
        
        /*
         * Test LS-solver on the same matrix
         */
        rmatrixsolvels(&a, n, n, &bv, 0.0, &info, &repls, &xv, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            for(i=0; i<=n-1; i++)
            {
                ae_v_move(&y.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                xdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_fabs(v-bv.ptr.p_double[i], _state),8*ae_machineepsilon*ae_maxreal((double)(1), ae_fabs(bv.ptr.p_double[i], _state), _state));
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
SPD test
*************************************************************************/
static void testdirectdensesolversunit_testspdsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* spderrors,
     ae_bool* rfserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_matrix atmp;
    ae_vector p;
    ae_matrix xe;
    ae_matrix b;
    ae_vector bv;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t pass;
    ae_int_t taskkind;
    double v;
    ae_bool isupper;
    ae_int_t info;
    densesolverreport rep;
    densesolverlsreport repls;
    ae_matrix x;
    ae_vector xv;
    ae_vector y;
    ae_vector tx;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&cha, 0, sizeof(cha));
    memset(&atmp, 0, sizeof(atmp));
    memset(&p, 0, sizeof(p));
    memset(&xe, 0, sizeof(xe));
    memset(&b, 0, sizeof(b));
    memset(&bv, 0, sizeof(bv));
    memset(&rep, 0, sizeof(rep));
    memset(&repls, 0, sizeof(repls));
    memset(&x, 0, sizeof(x));
    memset(&xv, 0, sizeof(xv));
    memset(&y, 0, sizeof(y));
    memset(&tx, 0, sizeof(tx));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&atmp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xe, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bv, 0, DT_REAL, _state, ae_true);
    _densesolverreport_init(&rep, _state, ae_true);
    _densesolverlsreport_init(&repls, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * ********************************************************
                 * WELL CONDITIONED TASKS
                 * ability to find correct solution is tested
                 * ********************************************************
                 *
                 * 1. generate random well conditioned matrix A.
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods on original A
                 */
                isupper = ae_fp_greater(ae_randomreal(_state),0.5);
                spdmatrixrndcond(n, (double)(1000), &a, _state);
                testdirectdensesolversunit_rmatrixmakeacopy(&a, n, n, &cha, _state);
                if( !spdmatrixcholesky(&cha, n, isupper, _state) )
                {
                    *spderrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                ae_matrix_set_length(&xe, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        xe.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    }
                }
                ae_matrix_set_length(&b, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.pp_double[0][j], xe.stride, ae_v_len(0,n-1));
                        b.ptr.pp_double[i][j] = v;
                    }
                }
                testdirectdensesolversunit_rmatrixdrophalf(&a, n, isupper, _state);
                testdirectdensesolversunit_rmatrixdrophalf(&cha, n, isupper, _state);
                
                /*
                 * Test solvers
                 */
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset2d(&x, _state);
                spdmatrixsolvem(&a, n, isupper, &b, m, &info, &rep, &x, _state);
                *spderrors = *spderrors||!testdirectdensesolversunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                ae_matrix_set_length(&x, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        x.ptr.pp_double[i][j] = b.ptr.pp_double[i][j];
                    }
                }
                spdmatrixsolvemfast(&a, n, isupper, &x, m, &info, _state);
                ae_set_error_flag(spderrors, !testdirectdensesolversunit_rmatrixchecksolutionmfast(&xe, n, m, threshold, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1023");
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                spdmatrixsolve(&a, n, isupper, &bv, &info, &rep, &xv, _state);
                *spderrors = *spderrors||!testdirectdensesolversunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                spdmatrixsolvefast(&a, n, isupper, &bv, &info, _state);
                ae_set_error_flag(spderrors, !testdirectdensesolversunit_rmatrixchecksolutionfast(&xe, n, threshold, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1037");
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset2d(&x, _state);
                spdmatrixcholeskysolvem(&cha, n, isupper, &b, m, &info, &rep, &x, _state);
                *spderrors = *spderrors||!testdirectdensesolversunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                ae_matrix_set_length(&x, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        x.ptr.pp_double[i][j] = b.ptr.pp_double[i][j];
                    }
                }
                spdmatrixcholeskysolvemfast(&cha, n, isupper, &x, m, &info, _state);
                ae_set_error_flag(spderrors, !testdirectdensesolversunit_rmatrixchecksolutionmfast(&xe, n, m, threshold, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1051");
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                spdmatrixcholeskysolve(&cha, n, isupper, &bv, &info, &rep, &xv, _state);
                *spderrors = *spderrors||!testdirectdensesolversunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                spdmatrixcholeskysolvefast(&cha, n, isupper, &bv, &info, _state);
                ae_set_error_flag(spderrors, !testdirectdensesolversunit_rmatrixchecksolutionfast(&xe, n, threshold, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1065");
                
                /*
                 * ********************************************************
                 * EXACTLY SINGULAR MATRICES
                 * ability to detect singularity is tested
                 * ********************************************************
                 *
                 * 1. generate different types of singular matrices:
                 *    * zero
                 *    * with zero columns
                 *    * with zero rows
                 *    * with equal rows/columns
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods
                 */
                for(taskkind=0; taskkind<=3; taskkind++)
                {
                    testdirectdensesolversunit_unset2d(&a, _state);
                    if( taskkind==0 )
                    {
                        
                        /*
                         * all zeros
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = (double)(0);
                            }
                        }
                    }
                    if( taskkind==1 )
                    {
                        
                        /*
                         * there is zero column
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                        ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==2 )
                    {
                        
                        /*
                         * there is zero row
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                        ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==3 )
                    {
                        
                        /*
                         * equal columns/rows
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_move(&a.ptr.pp_double[0][0], a.stride, &a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1));
                        ae_v_move(&a.ptr.pp_double[0][0], 1, &a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1));
                    }
                    ae_matrix_set_length(&xe, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            xe.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    ae_matrix_set_length(&b, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.pp_double[0][j], xe.stride, ae_v_len(0,n-1));
                            b.ptr.pp_double[i][j] = v;
                        }
                    }
                    testdirectdensesolversunit_rmatrixmakeacopy(&a, n, n, &cha, _state);
                    testdirectdensesolversunit_rmatrixdrophalf(&a, n, isupper, _state);
                    testdirectdensesolversunit_rmatrixdrophalf(&cha, n, isupper, _state);
                    
                    /*
                     * Test SPDMatrixSolveM()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_unset2d(&x, _state);
                    spdmatrixsolvem(&a, n, isupper, &b, m, &info, &rep, &x, _state);
                    *spderrors = *spderrors||!testdirectdensesolversunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test SPDMatrixSolveMFast()
                     */
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_matrix_set_length(&x, n, m, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x.ptr.pp_double[i][j] = b.ptr.pp_double[i][j];
                            }
                        }
                        spdmatrixsolvemfast(&a, n, isupper, &x, m, &info, _state);
                        ae_set_error_flag(spderrors, !testdirectdensesolversunit_rmatrixchecksingularmfast(n, m, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1181");
                    }
                    
                    /*
                     * Test SPDMatrixSolve()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_unset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                    spdmatrixsolve(&a, n, isupper, &bv, &info, &rep, &xv, _state);
                    *spderrors = *spderrors||!testdirectdensesolversunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * Test SPDMatrixSolveFast()
                     */
                    info = 0;
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                    spdmatrixsolvefast(&a, n, isupper, &bv, &info, _state);
                    ae_set_error_flag(spderrors, !testdirectdensesolversunit_rmatrixchecksingular(n, info, &rep, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1202");
                    
                    /*
                     * 'equal columns/rows' are degenerate, but
                     * Cholesky matrix with equal columns/rows IS NOT degenerate,
                     * so it is not used for testing purposes.
                     */
                    if( taskkind!=3 )
                    {
                        
                        /*
                         * Test SPDMatrixLUSolveM() (and fast version)
                         */
                        info = 0;
                        testdirectdensesolversunit_unsetrep(&rep, _state);
                        testdirectdensesolversunit_unset2d(&x, _state);
                        spdmatrixcholeskysolvem(&cha, n, isupper, &b, m, &info, &rep, &x, _state);
                        *spderrors = *spderrors||!testdirectdensesolversunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                        if( (taskkind==0||taskkind==1)||taskkind==2 )
                        {
                            info = 0;
                            ae_matrix_set_length(&x, n, m, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    x.ptr.pp_double[i][j] = b.ptr.pp_double[i][j];
                                }
                            }
                            spdmatrixcholeskysolvemfast(&a, n, isupper, &x, m, &info, _state);
                            ae_set_error_flag(spderrors, !testdirectdensesolversunit_rmatrixchecksingularmfast(n, m, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1227");
                        }
                        
                        /*
                         * Test SPDMatrixLUSolve()
                         */
                        info = 0;
                        testdirectdensesolversunit_unsetrep(&rep, _state);
                        testdirectdensesolversunit_unset2d(&x, _state);
                        ae_vector_set_length(&bv, n, _state);
                        ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                        spdmatrixcholeskysolve(&cha, n, isupper, &bv, &info, &rep, &xv, _state);
                        *spderrors = *spderrors||!testdirectdensesolversunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                        if( (taskkind==0||taskkind==1)||taskkind==2 )
                        {
                            info = 0;
                            ae_vector_set_length(&bv, n, _state);
                            ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                            spdmatrixcholeskysolvefast(&a, n, isupper, &bv, &info, _state);
                            ae_set_error_flag(spderrors, !testdirectdensesolversunit_rmatrixchecksingularfast(n, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1246");
                        }
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Real test
*************************************************************************/
static void testdirectdensesolversunit_testcsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* cerrors,
     ae_bool* rfserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_matrix atmp;
    ae_vector p;
    ae_matrix xe;
    ae_matrix b;
    ae_vector bv;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t pass;
    ae_int_t taskkind;
    double verr;
    ae_complex v;
    ae_int_t info;
    densesolverreport rep;
    densesolverlsreport repls;
    ae_matrix x;
    ae_vector xv;
    ae_vector y;
    ae_vector tx;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&lua, 0, sizeof(lua));
    memset(&atmp, 0, sizeof(atmp));
    memset(&p, 0, sizeof(p));
    memset(&xe, 0, sizeof(xe));
    memset(&b, 0, sizeof(b));
    memset(&bv, 0, sizeof(bv));
    memset(&rep, 0, sizeof(rep));
    memset(&repls, 0, sizeof(repls));
    memset(&x, 0, sizeof(x));
    memset(&xv, 0, sizeof(xv));
    memset(&y, 0, sizeof(y));
    memset(&tx, 0, sizeof(tx));
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&atmp, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xe, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&bv, 0, DT_COMPLEX, _state, ae_true);
    _densesolverreport_init(&rep, _state, ae_true);
    _densesolverlsreport_init(&repls, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&xv, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * ********************************************************
                 * WELL CONDITIONED TASKS
                 * ability to find correct solution is tested
                 * ********************************************************
                 *
                 * 1. generate random well conditioned matrix A.
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods on original A
                 */
                cmatrixrndcond(n, (double)(1000), &a, _state);
                testdirectdensesolversunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
                cmatrixlu(&lua, n, n, &p, _state);
                ae_matrix_set_length(&xe, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        xe.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                        xe.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                    }
                }
                ae_matrix_set_length(&b, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &xe.ptr.pp_complex[0][j], xe.stride, "N", ae_v_len(0,n-1));
                        b.ptr.pp_complex[i][j] = v;
                    }
                }
                
                /*
                 * Test solvers
                 */
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset2d(&x, _state);
                cmatrixsolvem(&a, n, &b, m, ae_fp_greater(ae_randomreal(_state),0.5), &info, &rep, &x, _state);
                *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                ae_matrix_set_length(&x, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        x.ptr.pp_complex[i][j] = b.ptr.pp_complex[i][j];
                    }
                }
                cmatrixsolvemfast(&a, n, &x, m, &info, _state);
                *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksolutionmfast(&xe, n, m, threshold, info, &x, _state);
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                cmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
                *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                cmatrixsolvefast(&a, n, &bv, &info, _state);
                *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksolutionfast(&xe, n, threshold, info, &bv, _state);
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset2d(&x, _state);
                cmatrixlusolvem(&lua, &p, n, &b, m, &info, &rep, &x, _state);
                *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                ae_matrix_set_length(&x, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        x.ptr.pp_complex[i][j] = b.ptr.pp_complex[i][j];
                    }
                }
                cmatrixlusolvemfast(&lua, &p, n, &x, m, &info, _state);
                ae_set_error_flag(cerrors, !testdirectdensesolversunit_cmatrixchecksolutionmfast(&xe, n, m, threshold, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1358");
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                cmatrixlusolve(&lua, &p, n, &bv, &info, &rep, &xv, _state);
                *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                cmatrixlusolvefast(&lua, &p, n, &bv, &info, _state);
                ae_set_error_flag(cerrors, !testdirectdensesolversunit_cmatrixchecksolutionfast(&xe, n, threshold, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1372");
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset2d(&x, _state);
                cmatrixmixedsolvem(&a, &lua, &p, n, &b, m, &info, &rep, &x, _state);
                *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                cmatrixmixedsolve(&a, &lua, &p, n, &bv, &info, &rep, &xv, _state);
                *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                
                /*
                 * ********************************************************
                 * EXACTLY SINGULAR MATRICES
                 * ability to detect singularity is tested
                 * ********************************************************
                 *
                 * 1. generate different types of singular matrices:
                 *    * zero
                 *    * with zero columns
                 *    * with zero rows
                 *    * with equal rows/columns
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods
                 */
                for(taskkind=0; taskkind<=4; taskkind++)
                {
                    testdirectdensesolversunit_cunset2d(&a, _state);
                    if( taskkind==0 )
                    {
                        
                        /*
                         * all zeros
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                            }
                        }
                    }
                    if( taskkind==1 )
                    {
                        
                        /*
                         * there is zero column
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==2 )
                    {
                        
                        /*
                         * there is zero row
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==3 )
                    {
                        
                        /*
                         * equal columns
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_cmove(&a.ptr.pp_complex[0][0], a.stride, &a.ptr.pp_complex[0][k], a.stride, "N", ae_v_len(0,n-1));
                    }
                    if( taskkind==4 )
                    {
                        
                        /*
                         * equal rows
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_cmove(&a.ptr.pp_complex[0][0], 1, &a.ptr.pp_complex[k][0], 1, "N", ae_v_len(0,n-1));
                    }
                    ae_matrix_set_length(&xe, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            xe.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                        }
                    }
                    ae_matrix_set_length(&b, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &xe.ptr.pp_complex[0][j], xe.stride, "N", ae_v_len(0,n-1));
                            b.ptr.pp_complex[i][j] = v;
                        }
                    }
                    testdirectdensesolversunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
                    cmatrixlu(&lua, n, n, &p, _state);
                    
                    /*
                     * Test CMatrixSolveM()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_cunset2d(&x, _state);
                    cmatrixsolvem(&a, n, &b, m, ae_fp_greater(ae_randomreal(_state),0.5), &info, &rep, &x, _state);
                    *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test CMatrixSolveMFast(); performed only for matrices
                     * with zero rows or columns
                     */
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_matrix_set_length(&x, n, m, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x.ptr.pp_complex[i][j] = b.ptr.pp_complex[i][j];
                            }
                        }
                        cmatrixsolvemfast(&a, n, &x, m, &info, _state);
                        ae_set_error_flag(cerrors, !testdirectdensesolversunit_cmatrixchecksingularmfast(n, m, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1515");
                    }
                    
                    /*
                     * Test CMatrixSolve()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_cunset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                    cmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
                    *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_vector_set_length(&bv, n, _state);
                        ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                        cmatrixsolvefast(&a, n, &bv, &info, _state);
                        ae_set_error_flag(cerrors, !testdirectdensesolversunit_cmatrixchecksingularfast(n, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1535");
                    }
                    
                    /*
                     * Test CMatrixLUSolveM()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_cunset2d(&x, _state);
                    cmatrixlusolvem(&lua, &p, n, &b, m, &info, &rep, &x, _state);
                    *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test CMatrixLUSolveMFast()
                     */
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_matrix_set_length(&x, n, m, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x.ptr.pp_complex[i][j] = b.ptr.pp_complex[i][j];
                            }
                        }
                        cmatrixlusolvemfast(&lua, &p, n, &x, m, &info, _state);
                        ae_set_error_flag(cerrors, !testdirectdensesolversunit_cmatrixchecksingularmfast(n, m, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1559");
                    }
                    
                    /*
                     * Test CMatrixLUSolve()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_cunset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                    cmatrixlusolve(&lua, &p, n, &bv, &info, &rep, &xv, _state);
                    *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * Test CMatrixLUSolveFast()
                     */
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_vector_set_length(&bv, n, _state);
                        ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                        cmatrixlusolvefast(&lua, &p, n, &bv, &info, _state);
                        ae_set_error_flag(cerrors, !testdirectdensesolversunit_cmatrixchecksingularfast(n, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1582");
                    }
                    
                    /*
                     * Test CMatrixMixedSolveM()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_cunset2d(&x, _state);
                    cmatrixmixedsolvem(&a, &lua, &p, n, &b, m, &info, &rep, &x, _state);
                    *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test CMatrixMixedSolve()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_cunset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                    cmatrixmixedsolve(&a, &lua, &p, n, &bv, &info, &rep, &xv, _state);
                    *cerrors = *cerrors||!testdirectdensesolversunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                }
            }
        }
    }
    
    /*
     * test iterative improvement
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Test iterative improvement matrices
         *
         * A matrix/right part are constructed such that both matrix
         * and solution components magnitudes are within (-1,+1).
         * Such matrix/right part have nice properties - system can
         * be solved using iterative improvement with |A*x-b| about
         * several ulps of max(1,|b|).
         */
        n = 100;
        ae_matrix_set_length(&a, n, n, _state);
        ae_matrix_set_length(&b, n, 1, _state);
        ae_vector_set_length(&bv, n, _state);
        ae_vector_set_length(&tx, 2*n, _state);
        ae_vector_set_length(&xv, n, _state);
        ae_vector_set_length(&y, n, _state);
        for(i=0; i<=n-1; i++)
        {
            xv.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            xv.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
            ae_v_cmove(&y.ptr.p_complex[0], 1, &a.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
            xcdot(&y, &xv, n, &tx, &v, &verr, _state);
            bv.ptr.p_complex[i] = v;
        }
        ae_v_cmove(&b.ptr.pp_complex[0][0], b.stride, &bv.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
        
        /*
         * Test CMatrixSolveM()
         */
        testdirectdensesolversunit_cunset2d(&x, _state);
        cmatrixsolvem(&a, n, &b, 1, ae_true, &info, &rep, &x, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            ae_vector_set_length(&xv, n, _state);
            ae_v_cmove(&xv.ptr.p_complex[0], 1, &x.ptr.pp_complex[0][0], x.stride, "N", ae_v_len(0,n-1));
            for(i=0; i<=n-1; i++)
            {
                ae_v_cmove(&y.ptr.p_complex[0], 1, &a.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
                xcdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_c_abs(ae_c_sub(v,b.ptr.pp_complex[i][0]), _state),8*ae_machineepsilon*ae_maxreal((double)(1), ae_c_abs(b.ptr.pp_complex[i][0], _state), _state));
            }
        }
        
        /*
         * Test CMatrixSolve()
         */
        testdirectdensesolversunit_cunset1d(&xv, _state);
        cmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            for(i=0; i<=n-1; i++)
            {
                ae_v_cmove(&y.ptr.p_complex[0], 1, &a.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
                xcdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_c_abs(ae_c_sub(v,bv.ptr.p_complex[i]), _state),8*ae_machineepsilon*ae_maxreal((double)(1), ae_c_abs(bv.ptr.p_complex[i], _state), _state));
            }
        }
        
        /*
         * TODO: Test LS-solver on the same matrix
         */
    }
    ae_frame_leave(_state);
}


/*************************************************************************
HPD test
*************************************************************************/
static void testdirectdensesolversunit_testhpdsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* hpderrors,
     ae_bool* rfserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_matrix atmp;
    ae_vector p;
    ae_matrix xe;
    ae_matrix b;
    ae_vector bv;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_complex v;
    ae_bool isupper;
    ae_int_t info;
    densesolverreport rep;
    densesolverlsreport repls;
    ae_matrix x;
    ae_vector xv;
    ae_vector y;
    ae_vector tx;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&cha, 0, sizeof(cha));
    memset(&atmp, 0, sizeof(atmp));
    memset(&p, 0, sizeof(p));
    memset(&xe, 0, sizeof(xe));
    memset(&b, 0, sizeof(b));
    memset(&bv, 0, sizeof(bv));
    memset(&rep, 0, sizeof(rep));
    memset(&repls, 0, sizeof(repls));
    memset(&x, 0, sizeof(x));
    memset(&xv, 0, sizeof(xv));
    memset(&y, 0, sizeof(y));
    memset(&tx, 0, sizeof(tx));
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&atmp, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xe, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&bv, 0, DT_COMPLEX, _state, ae_true);
    _densesolverreport_init(&rep, _state, ae_true);
    _densesolverlsreport_init(&repls, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&xv, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tx, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * ********************************************************
                 * WELL CONDITIONED TASKS
                 * ability to find correct solution is tested
                 * ********************************************************
                 *
                 * 1. generate random well conditioned matrix A.
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods on original A
                 */
                isupper = ae_fp_greater(ae_randomreal(_state),0.5);
                hpdmatrixrndcond(n, (double)(1000), &a, _state);
                testdirectdensesolversunit_cmatrixmakeacopy(&a, n, n, &cha, _state);
                if( !hpdmatrixcholesky(&cha, n, isupper, _state) )
                {
                    *hpderrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                ae_matrix_set_length(&xe, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        xe.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                        xe.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                    }
                }
                ae_matrix_set_length(&b, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &xe.ptr.pp_complex[0][j], xe.stride, "N", ae_v_len(0,n-1));
                        b.ptr.pp_complex[i][j] = v;
                    }
                }
                testdirectdensesolversunit_cmatrixdrophalf(&a, n, isupper, _state);
                testdirectdensesolversunit_cmatrixdrophalf(&cha, n, isupper, _state);
                
                /*
                 * Test solvers
                 */
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset2d(&x, _state);
                hpdmatrixsolvem(&a, n, isupper, &b, m, &info, &rep, &x, _state);
                *hpderrors = *hpderrors||!testdirectdensesolversunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                ae_matrix_set_length(&x, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        x.ptr.pp_complex[i][j] = b.ptr.pp_complex[i][j];
                    }
                }
                hpdmatrixsolvemfast(&a, n, isupper, &x, m, &info, _state);
                ae_set_error_flag(hpderrors, !testdirectdensesolversunit_cmatrixchecksolutionmfast(&xe, n, m, threshold, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1771");
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                hpdmatrixsolve(&a, n, isupper, &bv, &info, &rep, &xv, _state);
                *hpderrors = *hpderrors||!testdirectdensesolversunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                hpdmatrixsolvefast(&a, n, isupper, &bv, &info, _state);
                *hpderrors = *hpderrors||!testdirectdensesolversunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &bv, _state);
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset2d(&x, _state);
                hpdmatrixcholeskysolvem(&cha, n, isupper, &b, m, &info, &rep, &x, _state);
                *hpderrors = *hpderrors||!testdirectdensesolversunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                ae_matrix_set_length(&x, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        x.ptr.pp_complex[i][j] = b.ptr.pp_complex[i][j];
                    }
                }
                hpdmatrixcholeskysolvemfast(&cha, n, isupper, &x, m, &info, _state);
                ae_set_error_flag(hpderrors, !testdirectdensesolversunit_cmatrixchecksolutionmfast(&xe, n, m, threshold, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1799");
                info = 0;
                testdirectdensesolversunit_unsetrep(&rep, _state);
                testdirectdensesolversunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                hpdmatrixcholeskysolve(&cha, n, isupper, &bv, &info, &rep, &xv, _state);
                *hpderrors = *hpderrors||!testdirectdensesolversunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                hpdmatrixcholeskysolvefast(&cha, n, isupper, &bv, &info, _state);
                ae_set_error_flag(hpderrors, !testdirectdensesolversunit_cmatrixchecksolutionfast(&xe, n, threshold, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1813");
                
                /*
                 * ********************************************************
                 * EXACTLY SINGULAR MATRICES
                 * ability to detect singularity is tested
                 * ********************************************************
                 *
                 * 1. generate different types of singular matrices:
                 *    * zero
                 *    * with zero columns
                 *    * with zero rows
                 *    * with equal rows/columns
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods
                 */
                for(taskkind=0; taskkind<=3; taskkind++)
                {
                    testdirectdensesolversunit_cunset2d(&a, _state);
                    if( taskkind==0 )
                    {
                        
                        /*
                         * all zeros
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j] = ae_complex_from_i(0);
                            }
                        }
                    }
                    if( taskkind==1 )
                    {
                        
                        /*
                         * there is zero column
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                                if( i==j )
                                {
                                    a.ptr.pp_complex[i][j].y = (double)(0);
                                }
                                a.ptr.pp_complex[j][i] = a.ptr.pp_complex[i][j];
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                        ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==2 )
                    {
                        
                        /*
                         * there is zero row
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                                if( i==j )
                                {
                                    a.ptr.pp_complex[i][j].y = (double)(0);
                                }
                                a.ptr.pp_complex[j][i] = a.ptr.pp_complex[i][j];
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                        ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==3 )
                    {
                        
                        /*
                         * equal columns/rows
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                                if( i==j )
                                {
                                    a.ptr.pp_complex[i][j].y = (double)(0);
                                }
                                a.ptr.pp_complex[j][i] = a.ptr.pp_complex[i][j];
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_cmove(&a.ptr.pp_complex[0][0], a.stride, &a.ptr.pp_complex[0][k], a.stride, "N", ae_v_len(0,n-1));
                        ae_v_cmove(&a.ptr.pp_complex[0][0], 1, &a.ptr.pp_complex[k][0], 1, "N", ae_v_len(0,n-1));
                    }
                    ae_matrix_set_length(&xe, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            xe.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                        }
                    }
                    ae_matrix_set_length(&b, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &xe.ptr.pp_complex[0][j], xe.stride, "N", ae_v_len(0,n-1));
                            b.ptr.pp_complex[i][j] = v;
                        }
                    }
                    testdirectdensesolversunit_cmatrixmakeacopy(&a, n, n, &cha, _state);
                    testdirectdensesolversunit_cmatrixdrophalf(&a, n, isupper, _state);
                    testdirectdensesolversunit_cmatrixdrophalf(&cha, n, isupper, _state);
                    
                    /*
                     * Test SPDMatrixSolveM() (and fast version)
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_cunset2d(&x, _state);
                    hpdmatrixsolvem(&a, n, isupper, &b, m, &info, &rep, &x, _state);
                    *hpderrors = *hpderrors||!testdirectdensesolversunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_matrix_set_length(&x, n, m, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x.ptr.pp_complex[i][j] = b.ptr.pp_complex[i][j];
                            }
                        }
                        hpdmatrixsolvemfast(&a, n, isupper, &x, m, &info, _state);
                        ae_set_error_flag(hpderrors, !testdirectdensesolversunit_cmatrixchecksingularmfast(n, m, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1934");
                    }
                    
                    /*
                     * Test SPDMatrixSolve()
                     */
                    info = 0;
                    testdirectdensesolversunit_unsetrep(&rep, _state);
                    testdirectdensesolversunit_cunset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                    hpdmatrixsolve(&a, n, isupper, &bv, &info, &rep, &xv, _state);
                    *hpderrors = *hpderrors||!testdirectdensesolversunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                    if( (taskkind==0||taskkind==1)||taskkind==2 )
                    {
                        info = 0;
                        ae_vector_set_length(&bv, n, _state);
                        ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                        hpdmatrixsolvefast(&a, n, isupper, &bv, &info, _state);
                        ae_set_error_flag(hpderrors, !testdirectdensesolversunit_cmatrixchecksingularfast(n, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1953");
                    }
                    
                    /*
                     * 'equal columns/rows' are degenerate, but
                     * Cholesky matrix with equal columns/rows IS NOT degenerate,
                     * so it is not used for testing purposes.
                     */
                    if( taskkind!=3 )
                    {
                        
                        /*
                         * Test SPDMatrixCholeskySolveM()/fast
                         */
                        info = 0;
                        testdirectdensesolversunit_unsetrep(&rep, _state);
                        testdirectdensesolversunit_cunset2d(&x, _state);
                        hpdmatrixcholeskysolvem(&cha, n, isupper, &b, m, &info, &rep, &x, _state);
                        *hpderrors = *hpderrors||!testdirectdensesolversunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                        if( (taskkind==0||taskkind==1)||taskkind==2 )
                        {
                            info = 0;
                            ae_matrix_set_length(&x, n, m, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    x.ptr.pp_complex[i][j] = b.ptr.pp_complex[i][j];
                                }
                            }
                            hpdmatrixcholeskysolvemfast(&cha, n, isupper, &x, m, &info, _state);
                            ae_set_error_flag(hpderrors, !testdirectdensesolversunit_cmatrixchecksingularmfast(n, m, info, &x, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1979");
                        }
                        
                        /*
                         * Test HPDMatrixCholeskySolve() (fast)
                         */
                        info = 0;
                        testdirectdensesolversunit_unsetrep(&rep, _state);
                        testdirectdensesolversunit_cunset2d(&x, _state);
                        ae_vector_set_length(&bv, n, _state);
                        ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                        hpdmatrixcholeskysolve(&cha, n, isupper, &bv, &info, &rep, &xv, _state);
                        *hpderrors = *hpderrors||!testdirectdensesolversunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                        if( (taskkind==0||taskkind==1)||taskkind==2 )
                        {
                            ae_vector_set_length(&bv, n, _state);
                            ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                            hpdmatrixcholeskysolvefast(&cha, n, isupper, &bv, &info, _state);
                            ae_set_error_flag(hpderrors, !testdirectdensesolversunit_cmatrixchecksingularfast(n, info, &bv, _state), __FILE__, __LINE__, "testdirectdensesolversunit.ap:1997");
                        }
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Unsets real matrix
*************************************************************************/
static void testdirectdensesolversunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state)
{


    ae_matrix_set_length(x, 1, 1, _state);
    x->ptr.pp_double[0][0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets real vector
*************************************************************************/
static void testdirectdensesolversunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state)
{


    ae_vector_set_length(x, 1, _state);
    x->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets real matrix
*************************************************************************/
static void testdirectdensesolversunit_cunset2d(/* Complex */ ae_matrix* x,
     ae_state *_state)
{


    ae_matrix_set_length(x, 1, 1, _state);
    x->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets real vector
*************************************************************************/
static void testdirectdensesolversunit_cunset1d(/* Complex */ ae_vector* x,
     ae_state *_state)
{


    ae_vector_set_length(x, 1, _state);
    x->ptr.p_complex[0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets report
*************************************************************************/
static void testdirectdensesolversunit_unsetrep(densesolverreport* r,
     ae_state *_state)
{


    r->r1 = (double)(-1);
    r->rinf = (double)(-1);
}


/*************************************************************************
Unsets report
*************************************************************************/
static void testdirectdensesolversunit_unsetlsrep(densesolverlsreport* r,
     ae_state *_state)
{


    r->r2 = (double)(-1);
    r->n = -1;
    r->k = -1;
    testdirectdensesolversunit_unset2d(&r->cx, _state);
}








ae_bool testnormestimator(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double tol;
    ae_int_t maxmn;
    ae_int_t m;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix a;
    ae_vector rowsizes;
    sparsematrix s;
    double snorm;
    double enorm;
    double enorm2;
    ae_int_t nbetter;
    double sigma;
    ae_int_t i;
    ae_int_t j;
    normestimatorstate e;
    normestimatorstate e2;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&rowsizes, 0, sizeof(rowsizes));
    memset(&s, 0, sizeof(s));
    memset(&e, 0, sizeof(e));
    memset(&e2, 0, sizeof(e2));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rowsizes, 0, DT_INT, _state, ae_true);
    _sparsematrix_init(&s, _state, ae_true);
    _normestimatorstate_init(&e, _state, ae_true);
    _normestimatorstate_init(&e2, _state, ae_true);

    tol = 0.01;
    maxmn = 5;
    waserrors = ae_false;
    
    /*
     * First test: algorithm must correctly determine matrix norm
     */
    for(m=1; m<=maxmn; m++)
    {
        for(n=1; n<=maxmn; n++)
        {
            
            /*
             * Create estimator with quite large NStart and NIts.
             * It should guarantee that we converge to the correct solution.
             */
            normestimatorcreate(m, n, 15, 15, &e, _state);
            
            /*
             * Try with zero A first
             */
            sparsecreate(m, n, 1, &s, _state);
            sparseconverttocrs(&s, _state);
            normestimatorestimatesparse(&e, &s, _state);
            normestimatorresults(&e, &enorm, _state);
            waserrors = waserrors||ae_fp_neq(enorm,(double)(0));
            
            /*
             * Choose random norm, try with non-zero matrix
             * with specified norm.
             */
            snorm = ae_exp(10*ae_randomreal(_state)-5, _state);
            sparsecreate(m, n, 1, &s, _state);
            if( m>=n )
            {
                
                /*
                 * Generate random orthogonal M*M matrix,
                 * use N leading columns as columns of A
                 */
                rmatrixrndorthogonal(m, &a, _state);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        sparseset(&s, i, j, snorm*a.ptr.pp_double[i][j], _state);
                    }
                }
            }
            else
            {
                
                /*
                 * Generate random orthogonal N*N matrix,
                 * use M leading rows as rows of A
                 */
                rmatrixrndorthogonal(n, &a, _state);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        sparseset(&s, i, j, snorm*a.ptr.pp_double[i][j], _state);
                    }
                }
            }
            sparseconverttocrs(&s, _state);
            normestimatorestimatesparse(&e, &s, _state);
            normestimatorresults(&e, &enorm, _state);
            waserrors = (waserrors||ae_fp_greater(enorm,snorm*(1+tol)))||ae_fp_less(enorm,snorm*(1-tol));
        }
    }
    
    /*
     * NStart=10 should give statistically better results than NStart=1.
     * In order to test it we perform PassCount attempts to solve random
     * problem by means of two estimators: one with NStart=10 and another
     * one with NStart=1. Every time we compare two estimates and choose
     * better one.
     *
     * Random variable NBetter is a number of cases when NStart=10 was better.
     * Under null hypothesis (no difference) it is binomially distributed
     * with mean PassCount/2 and variance PassCount/4. However, we expect
     * to have significant deviation to the right, in the area of larger
     * values.
     *
     * NOTE: we use fixed N because this test is independent of problem size.
     */
    n = 3;
    normestimatorcreate(n, n, 1, 1, &e, _state);
    normestimatorcreate(n, n, 10, 1, &e2, _state);
    normestimatorsetseed(&e, 0, _state);
    normestimatorsetseed(&e2, 0, _state);
    nbetter = 0;
    passcount = 2000;
    sigma = 5.0;
    for(pass=1; pass<=passcount; pass++)
    {
        snorm = ae_pow(10.0, 2*ae_randomreal(_state)-1, _state);
        sparsecreate(n, n, 1, &s, _state);
        rmatrixrndcond(n, 2.0, &a, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                sparseset(&s, i, j, snorm*a.ptr.pp_double[i][j], _state);
            }
        }
        sparseconverttocrs(&s, _state);
        normestimatorestimatesparse(&e, &s, _state);
        normestimatorresults(&e, &enorm, _state);
        normestimatorestimatesparse(&e2, &s, _state);
        normestimatorresults(&e2, &enorm2, _state);
        if( ae_fp_less(ae_fabs(enorm2-snorm, _state),ae_fabs(enorm-snorm, _state)) )
        {
            nbetter = nbetter+1;
        }
    }
    waserrors = waserrors||ae_fp_less((double)(nbetter),0.5*passcount+sigma*ae_sqrt(0.25*passcount, _state));
    
    /*
     * Same as previous one (for NStart), but tests dependence on NIts.
     */
    n = 3;
    normestimatorcreate(n, n, 1, 1, &e, _state);
    normestimatorcreate(n, n, 1, 10, &e2, _state);
    normestimatorsetseed(&e, 0, _state);
    normestimatorsetseed(&e2, 0, _state);
    nbetter = 0;
    passcount = 2000;
    sigma = 5.0;
    for(pass=1; pass<=passcount; pass++)
    {
        snorm = ae_pow(10.0, 2*ae_randomreal(_state)-1, _state);
        sparsecreate(n, n, 1, &s, _state);
        rmatrixrndcond(n, 2.0, &a, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                sparseset(&s, i, j, snorm*a.ptr.pp_double[i][j], _state);
            }
        }
        sparseconverttocrs(&s, _state);
        normestimatorestimatesparse(&e, &s, _state);
        normestimatorresults(&e, &enorm, _state);
        normestimatorestimatesparse(&e2, &s, _state);
        normestimatorresults(&e2, &enorm2, _state);
        if( ae_fp_less(ae_fabs(enorm2-snorm, _state),ae_fabs(enorm-snorm, _state)) )
        {
            nbetter = nbetter+1;
        }
    }
    waserrors = waserrors||ae_fp_less((double)(nbetter),0.5*passcount+sigma*ae_sqrt(0.25*passcount, _state));
    
    /*
     * report
     */
    if( !silent )
    {
        printf("TESTING NORM ESTIMATOR\n");
        printf("TEST:                                    ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static double testlinlsqrunit_e0 = 1.0E-6;
static double testlinlsqrunit_tolort = 1.0E-4;
static double testlinlsqrunit_emergencye0 = 1.0E-12;
static ae_bool testlinlsqrunit_svdtest(ae_bool silent, ae_state *_state);
static ae_bool testlinlsqrunit_mwcranksvdtest(ae_bool silent,
     ae_state *_state);
static ae_bool testlinlsqrunit_mwicranksvdtest(ae_bool silent,
     ae_state *_state);
static ae_bool testlinlsqrunit_bidiagonaltest(ae_bool silent,
     ae_state *_state);
static ae_bool testlinlsqrunit_zeromatrixtest(ae_bool silent,
     ae_state *_state);
static ae_bool testlinlsqrunit_reportcorrectnesstest(ae_bool silent,
     ae_state *_state);
static ae_bool testlinlsqrunit_stoppingcriteriatest(ae_bool silent,
     ae_state *_state);
static ae_bool testlinlsqrunit_analytictest(ae_bool silent,
     ae_state *_state);
static ae_bool testlinlsqrunit_isitgoodsolution(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     ae_int_t m,
     ae_int_t n,
     double lambdav,
     /* Real    */ ae_vector* x,
     double epserr,
     double epsort,
     ae_state *_state);
static ae_bool testlinlsqrunit_preconditionertest(ae_state *_state);





ae_bool testlinlsqr(ae_bool silent, ae_state *_state)
{
    ae_bool svdtesterrors;
    ae_bool mwcranksvderr;
    ae_bool mwicranksvderr;
    ae_bool bidiagonalerr;
    ae_bool zeromatrixerr;
    ae_bool reportcorrectnesserr;
    ae_bool stoppingcriteriaerr;
    ae_bool analytictesterrors;
    ae_bool prectesterrors;
    ae_bool waserrors;
    ae_bool result;


    svdtesterrors = testlinlsqrunit_svdtest(ae_true, _state);
    mwcranksvderr = testlinlsqrunit_mwcranksvdtest(ae_true, _state);
    mwicranksvderr = testlinlsqrunit_mwicranksvdtest(ae_true, _state);
    bidiagonalerr = testlinlsqrunit_bidiagonaltest(ae_true, _state);
    zeromatrixerr = testlinlsqrunit_zeromatrixtest(ae_true, _state);
    reportcorrectnesserr = testlinlsqrunit_reportcorrectnesstest(ae_true, _state);
    stoppingcriteriaerr = testlinlsqrunit_stoppingcriteriatest(ae_true, _state);
    analytictesterrors = testlinlsqrunit_analytictest(ae_true, _state);
    prectesterrors = testlinlsqrunit_preconditionertest(_state);
    
    /*
     * report
     */
    waserrors = (((((((svdtesterrors||mwcranksvderr)||mwicranksvderr)||bidiagonalerr)||zeromatrixerr)||reportcorrectnesserr)||stoppingcriteriaerr)||analytictesterrors)||prectesterrors;
    if( !silent )
    {
        printf("TESTING LinLSQR\n");
        printf("SVDTest:                                      ");
        if( svdtesterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("MWCRankSVDTest:                               ");
        if( mwcranksvderr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("MWICRankSVDTest:                              ");
        if( mwicranksvderr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("BidiagonalTest:                               ");
        if( bidiagonalerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("ZeroMatrixTest:                               ");
        if( zeromatrixerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("ReportCorrectnessTest:                        ");
        if( reportcorrectnesserr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("StoppingCriteriaTest:                         ");
        if( stoppingcriteriaerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("Analytic properties:                          ");
        if( analytictesterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("Preconditioner test:                          ");
        if( prectesterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        
        /*
         *was errors?
         */
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
This  function  generates  random  MxN  problem,  solves  it with LSQR and
compares with results obtained from SVD solver. Matrix A is  generated  as
MxN  matrix  with  uniformly  distributed  random  entries, i.e. it has no
special properties (like conditioning or separation of singular values).

We apply random amount regularization to our problem (from zero to  large)
in  order  to  test  regularizer.  Default  stopping  criteria  are  used.
Preconditioning is turned off because it skews results for  rank-deficient
problems.

INPUT: 
    Silent   -   if true then function output report

  -- ALGLIB --
     Copyright 30.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlinlsqrunit_svdtest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    linlsqrstate s;
    linlsqrreport rep;
    sparsematrix spa;
    ae_matrix a;
    ae_vector b;
    ae_vector x0;
    ae_int_t szn;
    ae_int_t szm;
    ae_int_t n;
    ae_int_t m;
    double lambdai;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&spa, 0, sizeof(spa));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    _linlsqrstate_init(&s, _state, ae_true);
    _linlsqrreport_init(&rep, _state, ae_true);
    _sparsematrix_init(&spa, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);

    szm = 5;
    szn = 5;
    for(m=1; m<=szm; m++)
    {
        for(n=1; n<=szn; n++)
        {
            
            /*
             * Prepare MxN matrix A, right part B, lambda
             */
            lambdai = ae_randomreal(_state);
            ae_matrix_set_length(&a, m, n, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
            }
            sparsecreate(m, n, 1, &spa, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    sparseset(&spa, i, j, a.ptr.pp_double[i][j], _state);
                }
            }
            sparseconverttocrs(&spa, _state);
            ae_vector_set_length(&b, m, _state);
            for(i=0; i<=m-1; i++)
            {
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            
            /*
             * Solve by calling LinLSQRIteration
             */
            linlsqrcreate(m, n, &s, _state);
            linlsqrsetb(&s, &b, _state);
            linlsqrsetlambdai(&s, lambdai, _state);
            linlsqrsetprecunit(&s, _state);
            while(linlsqriteration(&s, _state))
            {
                if( s.needmv )
                {
                    for(i=0; i<=m-1; i++)
                    {
                        s.mv.ptr.p_double[i] = (double)(0);
                        for(j=0; j<=n-1; j++)
                        {
                            s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                        }
                    }
                }
                if( s.needmtv )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        s.mtv.ptr.p_double[i] = (double)(0);
                        for(j=0; j<=m-1; j++)
                        {
                            s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                        }
                    }
                }
            }
            linlsqrresults(&s, &x0, &rep, _state);
            if( !testlinlsqrunit_isitgoodsolution(&a, &b, m, n, lambdai, &x0, testlinlsqrunit_e0, testlinlsqrunit_tolort, _state) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             *test LinLSQRRestart and LinLSQRSolveSparse
             */
            linlsqrrestart(&s, _state);
            linlsqrsolvesparse(&s, &spa, &b, _state);
            linlsqrresults(&s, &x0, &rep, _state);
            if( !testlinlsqrunit_isitgoodsolution(&a, &b, m, n, lambdai, &x0, testlinlsqrunit_e0, testlinlsqrunit_tolort, _state) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    if( !silent )
    {
        printf("SVDTest::Ok\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The test checks that algorithm can solve MxN (with N<=M)  well-conditioned
problems - and can do so within exactly  N  iterations.  We  use  moderate
condition numbers, from 1.0 to 10.0, because larger condition  number  may
require several additional iterations to converge.

We try different scalings of the A and B.

INPUT: 
    Silent   -   if true then function does not outputs results to console

  -- ALGLIB --
     Copyright 30.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlinlsqrunit_mwcranksvdtest(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    linlsqrstate s;
    linlsqrreport rep;
    ae_matrix a;
    ae_vector b;
    double bnorm;
    ae_vector x0;
    ae_int_t szm;
    ae_int_t n;
    ae_int_t m;
    ae_int_t ns0;
    ae_int_t ns1;
    ae_int_t nlambdai;
    double s0;
    double s1;
    double lambdai;
    double c;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    _linlsqrstate_init(&s, _state, ae_true);
    _linlsqrreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);

    szm = 5;
    for(m=1; m<=szm; m++)
    {
        for(n=1; n<=m; n++)
        {
            for(nlambdai=0; nlambdai<=3; nlambdai++)
            {
                for(ns0=-1; ns0<=1; ns0++)
                {
                    for(ns1=-1; ns1<=1; ns1++)
                    {
                        
                        /*
                         * Generate problem:
                         * * scale factors s0, s1
                         * * MxN well conditioned A (with condition number C in [1,10] and norm s0)
                         * * regularization coefficient LambdaI
                         * * right part b, with |b|=s1
                         */
                        s0 = ae_pow((double)(10), (double)(10*ns0), _state);
                        s1 = ae_pow((double)(10), (double)(10*ns1), _state);
                        lambdai = (double)(0);
                        if( nlambdai==0 )
                        {
                            lambdai = (double)(0);
                        }
                        if( nlambdai==1 )
                        {
                            lambdai = s0/1000;
                        }
                        if( nlambdai==2 )
                        {
                            lambdai = s0;
                        }
                        if( nlambdai==3 )
                        {
                            lambdai = s0*1000;
                        }
                        c = (10-1)*ae_randomreal(_state)+1;
                        rmatrixrndcond(m, c, &a, _state);
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = s0*a.ptr.pp_double[i][j];
                            }
                        }
                        ae_vector_set_length(&b, m, _state);
                        do
                        {
                            bnorm = (double)(0);
                            for(i=0; i<=m-1; i++)
                            {
                                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                                bnorm = bnorm+b.ptr.p_double[i]*b.ptr.p_double[i];
                            }
                            bnorm = ae_sqrt(bnorm, _state);
                        }
                        while(ae_fp_less_eq(bnorm,testlinlsqrunit_e0));
                        for(i=0; i<=m-1; i++)
                        {
                            b.ptr.p_double[i] = b.ptr.p_double[i]*s1/bnorm;
                        }
                        
                        /*
                         * Solve by LSQR method
                         */
                        linlsqrcreate(m, n, &s, _state);
                        linlsqrsetb(&s, &b, _state);
                        linlsqrsetcond(&s, (double)(0), (double)(0), n, _state);
                        linlsqrsetlambdai(&s, lambdai, _state);
                        while(linlsqriteration(&s, _state))
                        {
                            if( s.needmv )
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    s.mv.ptr.p_double[i] = (double)(0);
                                    for(j=0; j<=n-1; j++)
                                    {
                                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                                    }
                                }
                            }
                            if( s.needmtv )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    s.mtv.ptr.p_double[i] = (double)(0);
                                    for(j=0; j<=m-1; j++)
                                    {
                                        s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                                    }
                                }
                            }
                        }
                        linlsqrresults(&s, &x0, &rep, _state);
                        if( !testlinlsqrunit_isitgoodsolution(&a, &b, m, n, lambdai, &x0, testlinlsqrunit_e0, testlinlsqrunit_tolort, _state) )
                        {
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The test checks that algorithm can find a solution with minimum norm for a
singular rectangular problem. System matrix has special property - singular 
values are either zero or well separated from zero.

INPUT: 
    Silent   -   if true then function output report

  -- ALGLIB --
     Copyright 30.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlinlsqrunit_mwicranksvdtest(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    linlsqrstate s;
    linlsqrreport rep;
    sparsematrix spa;
    ae_vector b;
    double bnorm;
    ae_vector x0;
    ae_int_t szm;
    ae_int_t n;
    ae_int_t m;
    ae_int_t nz;
    ae_int_t ns0;
    ae_int_t ns1;
    ae_int_t nlambdai;
    double s0;
    double s1;
    double lambdai;
    ae_int_t i;
    ae_int_t j;
    ae_matrix a;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&spa, 0, sizeof(spa));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    memset(&a, 0, sizeof(a));
    _linlsqrstate_init(&s, _state, ae_true);
    _linlsqrreport_init(&rep, _state, ae_true);
    _sparsematrix_init(&spa, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    szm = 5;
    for(m=1; m<=szm; m++)
    {
        for(n=1; n<=m; n++)
        {
            for(nlambdai=0; nlambdai<=2; nlambdai++)
            {
                for(ns0=-1; ns0<=1; ns0++)
                {
                    for(ns1=-1; ns1<=1; ns1++)
                    {
                        for(nz=0; nz<=n-1; nz++)
                        {
                            
                            /*
                             * Generate problem:
                             * * scale coefficients s0, s1
                             * * regularization coefficient LambdaI
                             * * MxN matrix A, norm(A)=s0, with NZ zero singular values and N-NZ nonzero ones
                             * * right part b with norm(b)=s1
                             */
                            s0 = ae_pow((double)(10), (double)(10*ns0), _state);
                            s1 = ae_pow((double)(10), (double)(10*ns1), _state);
                            lambdai = (double)(0);
                            if( nlambdai==0 )
                            {
                                lambdai = (double)(0);
                            }
                            if( nlambdai==1 )
                            {
                                lambdai = s0;
                            }
                            if( nlambdai==2 )
                            {
                                lambdai = s0*1000;
                            }
                            ae_matrix_set_length(&a, m, n, _state);
                            for(i=0; i<=m-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a.ptr.pp_double[i][j] = (double)(0);
                                }
                            }
                            for(i=0; i<=n-nz-1; i++)
                            {
                                a.ptr.pp_double[i][i] = s0*(0.1+0.9*ae_randomreal(_state));
                            }
                            rmatrixrndorthogonalfromtheleft(&a, m, n, _state);
                            rmatrixrndorthogonalfromtheright(&a, m, n, _state);
                            ae_vector_set_length(&b, m, _state);
                            do
                            {
                                bnorm = (double)(0);
                                for(i=0; i<=m-1; i++)
                                {
                                    b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                                    bnorm = bnorm+b.ptr.p_double[i]*b.ptr.p_double[i];
                                }
                                bnorm = ae_sqrt(bnorm, _state);
                            }
                            while(ae_fp_less_eq(bnorm,testlinlsqrunit_e0));
                            for(i=0; i<=m-1; i++)
                            {
                                b.ptr.p_double[i] = b.ptr.p_double[i]*s1/bnorm;
                            }
                            
                            /*
                             * Solve by LSQR method
                             */
                            linlsqrcreate(m, n, &s, _state);
                            linlsqrsetb(&s, &b, _state);
                            linlsqrsetcond(&s, testlinlsqrunit_emergencye0, testlinlsqrunit_emergencye0, n, _state);
                            linlsqrsetlambdai(&s, lambdai, _state);
                            while(linlsqriteration(&s, _state))
                            {
                                if( s.needmv )
                                {
                                    for(i=0; i<=m-1; i++)
                                    {
                                        s.mv.ptr.p_double[i] = (double)(0);
                                        for(j=0; j<=n-1; j++)
                                        {
                                            s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                                        }
                                    }
                                }
                                if( s.needmtv )
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        s.mtv.ptr.p_double[i] = (double)(0);
                                        for(j=0; j<=m-1; j++)
                                        {
                                            s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                                        }
                                    }
                                }
                            }
                            linlsqrresults(&s, &x0, &rep, _state);
                            
                            /*
                             * Check
                             */
                            if( !testlinlsqrunit_isitgoodsolution(&a, &b, m, n, lambdai, &x0, testlinlsqrunit_e0, testlinlsqrunit_tolort, _state) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The test does check, that algorithm can find a solution with minimum norm,
if a problem has bidiagonal matrix on diagonals of a lot of zeros. This
problem has to lead to case when State.Alpha and State.Beta are zero, and we
we can be sure that the algorithm correctly handles it.

We do not use iteration count as stopping condition, because problem can
be degenerate and we may need more than N iterations to converge.

INPUT: 
    Silent   -   if true then function output report

  -- ALGLIB --
     Copyright 30.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlinlsqrunit_bidiagonaltest(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    linlsqrstate s;
    linlsqrreport rep;
    ae_matrix a;
    ae_vector b;
    double bnorm;
    ae_vector x0;
    ae_int_t sz;
    ae_int_t n;
    ae_int_t m;
    ae_int_t minmn;
    ae_int_t ns0;
    ae_int_t ns1;
    double s0;
    double s1;
    ae_int_t i;
    ae_int_t j;
    ae_int_t p;
    ae_int_t diag;
    double pz;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    _linlsqrstate_init(&s, _state, ae_true);
    _linlsqrreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);

    sz = 5;
    for(m=1; m<=sz; m++)
    {
        for(n=1; n<=sz; n++)
        {
            minmn = ae_minint(m, n, _state);
            for(p=0; p<=2; p++)
            {
                for(ns0=-1; ns0<=1; ns0++)
                {
                    for(ns1=-1; ns1<=1; ns1++)
                    {
                        for(diag=0; diag<=1; diag++)
                        {
                            
                            /*
                             * Generate problem:
                             * * scaling coefficients s0, s1
                             * * bidiagonal A, with probability of having zero element at diagonal equal to PZ
                             */
                            s0 = ae_pow((double)(10), (double)(10*ns0), _state);
                            s1 = ae_pow((double)(10), (double)(10*ns1), _state);
                            pz = 0.0;
                            if( p==0 )
                            {
                                pz = 0.25;
                            }
                            if( p==1 )
                            {
                                pz = 0.5;
                            }
                            if( p==2 )
                            {
                                pz = 0.75;
                            }
                            ae_matrix_set_length(&a, m, n, _state);
                            for(i=0; i<=m-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a.ptr.pp_double[i][j] = (double)(0);
                                }
                            }
                            for(i=0; i<=minmn-1; i++)
                            {
                                if( ae_fp_greater_eq(ae_randomreal(_state),pz) )
                                {
                                    a.ptr.pp_double[i][i] = 2*ae_randomreal(_state)-1;
                                }
                            }
                            for(i=1; i<=minmn-1; i++)
                            {
                                if( ae_fp_greater_eq(ae_randomreal(_state),pz) )
                                {
                                    if( diag==0 )
                                    {
                                        a.ptr.pp_double[i-1][i] = 2*ae_randomreal(_state)-1;
                                    }
                                    if( diag==1 )
                                    {
                                        a.ptr.pp_double[i][i-1] = 2*ae_randomreal(_state)-1;
                                    }
                                }
                            }
                            for(i=0; i<=m-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a.ptr.pp_double[i][j] = s0*a.ptr.pp_double[i][j];
                                }
                            }
                            ae_vector_set_length(&b, m, _state);
                            do
                            {
                                bnorm = (double)(0);
                                for(i=0; i<=m-1; i++)
                                {
                                    b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                                    bnorm = bnorm+b.ptr.p_double[i]*b.ptr.p_double[i];
                                }
                                bnorm = ae_sqrt(bnorm, _state);
                            }
                            while(ae_fp_less_eq(bnorm,testlinlsqrunit_e0));
                            for(i=0; i<=m-1; i++)
                            {
                                b.ptr.p_double[i] = b.ptr.p_double[i]*s1/bnorm;
                            }
                            
                            /*
                             * LSQR solution
                             */
                            linlsqrcreate(m, n, &s, _state);
                            linlsqrsetb(&s, &b, _state);
                            linlsqrsetcond(&s, testlinlsqrunit_e0, testlinlsqrunit_e0, 0, _state);
                            while(linlsqriteration(&s, _state))
                            {
                                if( s.needmv )
                                {
                                    for(i=0; i<=m-1; i++)
                                    {
                                        s.mv.ptr.p_double[i] = (double)(0);
                                        for(j=0; j<=n-1; j++)
                                        {
                                            s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                                        }
                                    }
                                }
                                if( s.needmtv )
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        s.mtv.ptr.p_double[i] = (double)(0);
                                        for(j=0; j<=m-1; j++)
                                        {
                                            s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                                        }
                                    }
                                }
                            }
                            linlsqrresults(&s, &x0, &rep, _state);
                            
                            /*
                             * Check
                             */
                            if( !testlinlsqrunit_isitgoodsolution(&a, &b, m, n, 0.0, &x0, testlinlsqrunit_e0, testlinlsqrunit_tolort, _state) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The test does check, that algorithm correctly solves a problem in cases:
    1. A=0, B<>0;
    2. A<>0, B=0;
    3. A=0, B=0.
If some part is not zero then it filled with ones.

INPUT: 
    Silent   -   if true then function output report

  -- ALGLIB --
     Copyright 30.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlinlsqrunit_zeromatrixtest(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    linlsqrstate s;
    linlsqrreport rep;
    ae_matrix a;
    ae_vector b;
    ae_vector x0;
    ae_int_t sz;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t nzeropart;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    _linlsqrstate_init(&s, _state, ae_true);
    _linlsqrreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);

    sz = 5;
    result = ae_false;
    for(m=1; m<=sz; m++)
    {
        for(n=1; n<=sz; n++)
        {
            for(nzeropart=0; nzeropart<=2; nzeropart++)
            {
                
                /*
                 * Initialize A, b
                 */
                ae_matrix_set_length(&a, m, n, _state);
                if( nzeropart==0||nzeropart==2 )
                {
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                }
                else
                {
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = (double)(1);
                        }
                    }
                }
                ae_vector_set_length(&b, m, _state);
                if( nzeropart==1||nzeropart==2 )
                {
                    for(i=0; i<=m-1; i++)
                    {
                        b.ptr.p_double[i] = (double)(0);
                    }
                }
                else
                {
                    for(i=0; i<=m-1; i++)
                    {
                        b.ptr.p_double[i] = (double)(1);
                    }
                }
                
                /*
                 * LSQR
                 */
                linlsqrcreate(m, n, &s, _state);
                linlsqrsetb(&s, &b, _state);
                linlsqrsetcond(&s, (double)(0), (double)(0), n, _state);
                while(linlsqriteration(&s, _state))
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            s.mv.ptr.p_double[i] = (double)(0);
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                            }
                        }
                    }
                    if( s.needmtv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mtv.ptr.p_double[i] = (double)(0);
                            for(j=0; j<=m-1; j++)
                            {
                                s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                            }
                        }
                    }
                }
                linlsqrresults(&s, &x0, &rep, _state);
                
                /*
                 * Check
                 */
                if( !testlinlsqrunit_isitgoodsolution(&a, &b, m, n, 0.0, &x0, testlinlsqrunit_e0, testlinlsqrunit_tolort, _state) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The test does check, that algorithm correctly displays a progress report.

INPUT: 
    Silent   -   if true then function output report

  -- ALGLIB --
     Copyright 30.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlinlsqrunit_reportcorrectnesstest(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    linlsqrstate s;
    linlsqrreport rep;
    ae_matrix a;
    ae_matrix u;
    ae_matrix v;
    ae_vector w;
    ae_vector b;
    ae_vector x0;
    ae_vector firstx;
    ae_vector lastx;
    double rnorm;
    double tnorm;
    ae_int_t sz;
    ae_int_t n;
    ae_int_t m;
    ae_int_t lambdai;
    double mn;
    double mx;
    double c;
    ae_int_t i;
    ae_int_t j;
    ae_int_t its;
    double tmp;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&u, 0, sizeof(u));
    memset(&v, 0, sizeof(v));
    memset(&w, 0, sizeof(w));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    memset(&firstx, 0, sizeof(firstx));
    memset(&lastx, 0, sizeof(lastx));
    _linlsqrstate_init(&s, _state, ae_true);
    _linlsqrreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&firstx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lastx, 0, DT_REAL, _state, ae_true);

    eps = 0.001;
    sz = 5;
    mn = (double)(-100);
    mx = (double)(100);
    c = (double)(100);
    for(m=1; m<=sz; m++)
    {
        for(n=1; n<=m; n++)
        {
            for(lambdai=0; lambdai<=1; lambdai++)
            {
                its = -1;
                
                /*
                 *initialize matrix A
                 */
                spdmatrixrndcond(m+n, c, &a, _state);
                for(i=m; i<=m+n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i-m==j )
                        {
                            a.ptr.pp_double[i][j] = (double)(lambdai);
                        }
                        else
                        {
                            a.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                }
                
                /*
                 *initialize b
                 */
                ae_vector_set_length(&b, m+n, _state);
                rnorm = (double)(0);
                for(i=0; i<=m+n-1; i++)
                {
                    if( i<m )
                    {
                        b.ptr.p_double[i] = (mx-mn)*ae_randomreal(_state)+mn;
                        rnorm = rnorm+b.ptr.p_double[i]*b.ptr.p_double[i];
                    }
                    else
                    {
                        b.ptr.p_double[i] = (double)(0);
                    }
                }
                
                /*
                 *initialize FirstX and LastX
                 */
                ae_vector_set_length(&firstx, n, _state);
                ae_vector_set_length(&lastx, n, _state);
                
                /*
                 *calculating by LSQR method
                 */
                linlsqrcreate(m, n, &s, _state);
                linlsqrsetb(&s, &b, _state);
                linlsqrsetcond(&s, (double)(0), (double)(0), n, _state);
                linlsqrsetlambdai(&s, (double)(lambdai), _state);
                linlsqrsetxrep(&s, ae_true, _state);
                while(linlsqriteration(&s, _state))
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            s.mv.ptr.p_double[i] = (double)(0);
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                            }
                        }
                    }
                    if( s.needmtv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mtv.ptr.p_double[i] = (double)(0);
                            for(j=0; j<=m-1; j++)
                            {
                                s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                            }
                        }
                    }
                    if( s.xupdated )
                    {
                        tnorm = (double)(0);
                        for(i=0; i<=m+n-1; i++)
                        {
                            tmp = (double)(0);
                            for(j=0; j<=n-1; j++)
                            {
                                tmp = tmp+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                            }
                            tnorm = tnorm+(b.ptr.p_double[i]-tmp)*(b.ptr.p_double[i]-tmp);
                        }
                        
                        /*
                         *check, that RNorm is't more than S.R2
                         *and difference between S.R2 and TNorm
                         *is't more than 'eps'(here S.R2=||rk||,
                         *calculated by the algorithm for LSQR, and
                         *TNorm=||A*S.x-b||, calculated by test function).
                         */
                        if( ae_fp_greater(s.r2,rnorm)||ae_fp_greater(ae_fabs(s.r2-tnorm, _state),eps) )
                        {
                            if( !silent )
                            {
                                printf("ReportCorrectnessTest::Fail\n");
                                printf("TNorm=%0.2e;RNorm=%0.2e;S.R2=%0.2e;\n",
                                    (double)(tnorm),
                                    (double)(rnorm),
                                    (double)(s.r2));
                            }
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                        rnorm = s.r2;
                        its = its+1;
                        
                        /*
                         *get X value from first iteration 
                         *and from last iteration.
                         */
                        if( its==0 )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                firstx.ptr.p_double[i] = s.x.ptr.p_double[i];
                            }
                        }
                        if( its==n )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                lastx.ptr.p_double[i] = s.x.ptr.p_double[i];
                            }
                        }
                    }
                }
                linlsqrresults(&s, &x0, &rep, _state);
                
                /*
                 *check, that FirstX is equal to zero and LastX
                 *is equal to x0.
                 */
                for(i=0; i<=n-1; i++)
                {
                    if( ae_fp_neq(firstx.ptr.p_double[i],(double)(0))||ae_fp_neq(lastx.ptr.p_double[i],x0.ptr.p_double[i]) )
                    {
                        if( !silent )
                        {
                            printf("ReportCorrectnessTest::Fail\n");
                            printf("IterationsCount=%0d\n",
                                (int)(rep.iterationscount));
                            printf("NMV=%0d\n",
                                (int)(rep.nmv));
                            printf("TerminationType=%0d\n",
                                (int)(rep.terminationtype));
                            printf("X and LastX...\n");
                            for(j=0; j<=n-1; j++)
                            {
                                printf("x[%0d]=%0.10e; LastX[%0d]=%0.10e\n",
                                    (int)(j),
                                    (double)(x0.ptr.p_double[j]),
                                    (int)(j),
                                    (double)(lastx.ptr.p_double[j]));
                            }
                        }
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
    }
    if( !silent )
    {
        printf("ReportCorrectnessTest::Ok\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The test does check, that correctly executed stop criteria by algorithm.

INPUT: 
    Silent   -   if true then function output report

  -- ALGLIB --
     Copyright 30.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlinlsqrunit_stoppingcriteriatest(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    linlsqrstate s;
    linlsqrreport rep;
    ae_matrix a;
    ae_matrix u;
    ae_matrix v;
    ae_vector w;
    ae_vector b;
    double bnorm;
    ae_vector x0;
    ae_int_t sz;
    ae_int_t n;
    ae_int_t k0;
    ae_int_t k1;
    ae_vector ark;
    double anorm;
    double arknorm;
    double rknorm;
    ae_vector rk;
    double mn;
    double mx;
    double c;
    ae_int_t maxits;
    ae_int_t i;
    ae_int_t j;
    double tmp;
    double eps;
    double epsmod;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&u, 0, sizeof(u));
    memset(&v, 0, sizeof(v));
    memset(&w, 0, sizeof(w));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    memset(&ark, 0, sizeof(ark));
    memset(&rk, 0, sizeof(rk));
    _linlsqrstate_init(&s, _state, ae_true);
    _linlsqrreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ark, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rk, 0, DT_REAL, _state, ae_true);

    sz = 5;
    mn = (double)(-100);
    mx = (double)(100);
    c = (double)(100);
    for(n=1; n<=sz; n++)
    {
        
        /*
         * Initialize A, unit b
         */
        spdmatrixrndcond(n, c, &a, _state);
        ae_vector_set_length(&b, n, _state);
        bnorm = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = (mx-mn)*ae_randomreal(_state)+mn;
            bnorm = bnorm+b.ptr.p_double[i]*b.ptr.p_double[i];
        }
        bnorm = ae_sqrt(bnorm, _state);
        
        /*
         * Test MaxIts
         *
         * NOTE: we do not check TerminationType because algorithm may terminate for
         * several reasons. The only thing which is guaranteed is that stopping condition
         * MaxIts holds.
         */
        maxits = 1+ae_randominteger(n, _state);
        linlsqrcreate(n, n, &s, _state);
        linlsqrsetb(&s, &b, _state);
        linlsqrsetcond(&s, (double)(0), (double)(0), maxits, _state);
        while(linlsqriteration(&s, _state))
        {
            if( s.needmv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                }
            }
            if( s.needmtv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mtv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                    }
                }
            }
        }
        linlsqrresults(&s, &x0, &rep, _state);
        if( rep.iterationscount>maxits||rep.terminationtype<=0 )
        {
            if( !silent )
            {
                printf("StoppingCriteriaTest::Fail\n");
                printf("N=%0d\n",
                    (int)(n));
                printf("IterationsCount=%0d\n",
                    (int)(rep.iterationscount));
                printf("NMV=%0d\n",
                    (int)(rep.nmv));
                printf("TerminationType=%0d\n",
                    (int)(rep.terminationtype));
            }
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Test EpsB.
         * Set EpsB=eps, check that |r|<epsMod*|b|, where epsMod=1.1*eps.
         * This modified epsilon is used to avoid influence of the numerical errors.
         *
         * NOTE: we do not check TerminationType because algorithm may terminate for
         * several reasons. The only thing which is guaranteed is that stopping condition
         * EpsB holds.
         */
        eps = ae_pow((double)(10), (double)(-(2+ae_randominteger(3, _state))), _state);
        epsmod = 1.1*eps;
        ae_vector_set_length(&rk, n, _state);
        linlsqrcreate(n, n, &s, _state);
        linlsqrsetb(&s, &b, _state);
        linlsqrsetcond(&s, (double)(0), eps, 0, _state);
        while(linlsqriteration(&s, _state))
        {
            if( s.needmv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                }
            }
            if( s.needmtv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mtv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                    }
                }
            }
        }
        linlsqrresults(&s, &x0, &rep, _state);
        rknorm = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            tmp = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                tmp = tmp+a.ptr.pp_double[i][j]*x0.ptr.p_double[j];
            }
            rknorm = rknorm+(tmp-b.ptr.p_double[i])*(tmp-b.ptr.p_double[i]);
        }
        rknorm = ae_sqrt(rknorm, _state);
        if( ae_fp_greater(rknorm,epsmod*bnorm)||rep.terminationtype<=0 )
        {
            if( !silent )
            {
                printf("StoppingCriteriaTest::Fail\n");
                printf("rkNorm=%0.2e\n",
                    (double)(rknorm));
                printf("IterationsCount=%0d\n",
                    (int)(rep.iterationscount));
                printf("NMV=%0d\n",
                    (int)(rep.nmv));
                printf("TerminationType=%0d\n",
                    (int)(rep.terminationtype));
            }
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * Test EpsA.
     *
     * Generate well conditioned underdetermined system with nonzero residual
     * at the solution. Such system can be generated by generating random
     * orthogonal matrix (N>=2) and using first N-1 columns as rectangular
     * system matrix, and sum of all columns with random non-zero coefficients 
     * as right part.
     */
    for(n=2; n<=sz; n++)
    {
        for(k0=-1; k0<=1; k0++)
        {
            for(k1=-1; k1<=1; k1++)
            {
                
                /*
                 * Initialize A with non-unit norm 10^(10*K0), b with non-unit norm 10^(10*K1)
                 */
                anorm = ae_pow((double)(10), (double)(10*k0), _state);
                rmatrixrndorthogonal(n, &a, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = anorm*a.ptr.pp_double[i][j];
                    }
                }
                ae_vector_set_length(&b, n, _state);
                for(j=0; j<=n-1; j++)
                {
                    b.ptr.p_double[j] = (double)(0);
                }
                for(i=0; i<=n-1; i++)
                {
                    tmp = 1+ae_randomreal(_state);
                    ae_v_addd(&b.ptr.p_double[0], 1, &a.ptr.pp_double[0][i], a.stride, ae_v_len(0,n-1), tmp);
                }
                tmp = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    tmp = tmp+ae_sqr(b.ptr.p_double[i], _state);
                }
                tmp = ae_pow((double)(10), (double)(10*k1), _state)/ae_sqrt(tmp, _state);
                ae_v_muld(&b.ptr.p_double[0], 1, ae_v_len(0,n-1), tmp);
                
                /*
                 * Test EpsA
                 *
                 * NOTE: it is guaranteed that algorithm will terminate with correct
                 * TerminationType because other stopping criteria (EpsB) won't be satisfied
                 * on such system.
                 */
                eps = ae_pow((double)(10), (double)(-(2+ae_randominteger(3, _state))), _state);
                epsmod = 1.1*eps;
                linlsqrcreate(n, n-1, &s, _state);
                linlsqrsetb(&s, &b, _state);
                linlsqrsetcond(&s, eps, (double)(0), 0, _state);
                while(linlsqriteration(&s, _state))
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv.ptr.p_double[i] = (double)(0);
                            for(j=0; j<=n-2; j++)
                            {
                                s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                            }
                        }
                    }
                    if( s.needmtv )
                    {
                        for(i=0; i<=n-2; i++)
                        {
                            s.mtv.ptr.p_double[i] = (double)(0);
                            for(j=0; j<=n-1; j++)
                            {
                                s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                            }
                        }
                    }
                }
                linlsqrresults(&s, &x0, &rep, _state);
                
                /*
                 * Check condition
                 */
                ae_vector_set_length(&rk, n, _state);
                ae_vector_set_length(&ark, n-1, _state);
                rknorm = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    rk.ptr.p_double[i] = b.ptr.p_double[i];
                    for(j=0; j<=n-2; j++)
                    {
                        rk.ptr.p_double[i] = rk.ptr.p_double[i]-a.ptr.pp_double[i][j]*x0.ptr.p_double[j];
                    }
                    rknorm = rknorm+ae_sqr(rk.ptr.p_double[i], _state);
                }
                rknorm = ae_sqrt(rknorm, _state);
                arknorm = (double)(0);
                for(i=0; i<=n-2; i++)
                {
                    ark.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        ark.ptr.p_double[i] = ark.ptr.p_double[i]+a.ptr.pp_double[j][i]*rk.ptr.p_double[j];
                    }
                    arknorm = arknorm+ae_sqr(ark.ptr.p_double[i], _state);
                }
                arknorm = ae_sqrt(arknorm, _state);
                if( ae_fp_greater(arknorm/(anorm*rknorm),epsmod)||rep.terminationtype!=4 )
                {
                    if( !silent )
                    {
                        printf("StoppingCriteriaTest::Fail\n");
                        printf("N=%0d\n",
                            (int)(n));
                        printf("IterationsCount=%0d\n",
                            (int)(rep.iterationscount));
                        printf("NMV=%0d\n",
                            (int)(rep.nmv));
                        printf("TerminationType=%0d\n",
                            (int)(rep.terminationtype));
                    }
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    if( !silent )
    {
        printf("StoppingCriteriaTest::Ok\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This test compares LSQR  for  original  system  A*x=b  against  CG  for  a
modified system (A'*A)x = A*b. Both algorithms should give same  sequences
of trial points (under exact arithmetics, or  for  very  good  conditioned
systems).

INPUT: 
    Silent   -   if true then function does not output report

  -- ALGLIB --
     Copyright 30.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlinlsqrunit_analytictest(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    linlsqrstate s;
    linlsqrreport rep;
    ae_matrix a;
    ae_matrix xk;
    ae_matrix ap;
    ae_matrix r;
    ae_vector b;
    ae_vector tmp;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t smallk;
    ae_int_t pointsstored;
    double v;
    double tol;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&xk, 0, sizeof(xk));
    memset(&ap, 0, sizeof(ap));
    memset(&r, 0, sizeof(r));
    memset(&b, 0, sizeof(b));
    memset(&tmp, 0, sizeof(tmp));
    _linlsqrstate_init(&s, _state, ae_true);
    _linlsqrreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xk, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ap, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);

    
    /*
     * Set:
     * * SmallK - number of steps to check, must be small number in order
     *   to reduce influence of the rounding errors
     * * Tol - error tolerance for orthogonality/conjugacy criteria
     */
    result = ae_false;
    smallk = 4;
    tol = 1.0E-7;
    for(m=smallk; m<=smallk+5; m++)
    {
        for(n=smallk; n<=m; n++)
        {
            
            /*
             * Prepare problem:
             * * MxN matrix A, Mx1 vector B
             * * A is filled with random values from [-1,+1]
             * * diagonal elements are filled with large positive values
             *   (should make system better conditioned)
             */
            ae_matrix_set_length(&a, m, n, _state);
            ae_vector_set_length(&b, m, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 10*(1+ae_randomreal(_state));
            }
            
            /*
             * Solve with LSQR, save trial points into XK[] array
             */
            ae_matrix_set_length(&xk, smallk+1, n, _state);
            linlsqrcreate(m, n, &s, _state);
            linlsqrsetb(&s, &b, _state);
            linlsqrsetcond(&s, (double)(0), (double)(0), smallk, _state);
            linlsqrsetxrep(&s, ae_true, _state);
            pointsstored = 0;
            while(linlsqriteration(&s, _state))
            {
                if( s.needmv )
                {
                    for(i=0; i<=m-1; i++)
                    {
                        s.mv.ptr.p_double[i] = (double)(0);
                        for(j=0; j<=n-1; j++)
                        {
                            s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                        }
                    }
                }
                if( s.needmtv )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        s.mtv.ptr.p_double[i] = (double)(0);
                        for(j=0; j<=m-1; j++)
                        {
                            s.mtv.ptr.p_double[i] = s.mtv.ptr.p_double[i]+a.ptr.pp_double[j][i]*s.x.ptr.p_double[j];
                        }
                    }
                }
                if( s.xupdated )
                {
                    ae_assert(pointsstored<xk.rows, "LinLSQR test: internal error", _state);
                    ae_v_move(&xk.ptr.pp_double[pointsstored][0], 1, &s.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    pointsstored = pointsstored+1;
                }
            }
            if( pointsstored<3 )
            {
                
                /*
                 * At least two iterations should be performed
                 * (our task is not that easy to solve)
                 */
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * We have recorded sequence of points generated by LSQR,
             * and now we want to make a comparion against linear CG.
             *
             * Of course, we could just perform CG solution of (A'*A)*x = A'*b,
             * but it will need a CG solver, and we do not want to reference one
             * just to perform testing.
             *
             * However, we can do better - we can check that sequence of steps
             * satisfies orthogonality/conjugacy conditions, which are stated
             * as follows:
             * * (r[i]^T)*r[j]=0 for i<>j
             * * (p[i]^T)*A'*A*p[j]=0 for i<>j
             * where r[i]=(A'*A)*x[i]-A'*b is I-th residual , p[i] is I-th step.
             *
             * In order to test these criteria we generate two matrices:
             * * (PointsStored-1)*M matrix AP (matrix of A*p products)
             * * (PointsStored-1)*N matrix R  (matrix of residuals)
             * Then, we check that each matrix has orthogonal rows.
             */
            ae_matrix_set_length(&ap, pointsstored-1, m, _state);
            ae_matrix_set_length(&r, pointsstored-1, n, _state);
            ae_vector_set_length(&tmp, m, _state);
            for(k=0; k<=pointsstored-2; k++)
            {
                
                /*
                 * Calculate K-th row of AP
                 */
                for(i=0; i<=m-1; i++)
                {
                    ap.ptr.pp_double[k][i] = 0.0;
                    for(j=0; j<=n-1; j++)
                    {
                        ap.ptr.pp_double[k][i] = ap.ptr.pp_double[k][i]+a.ptr.pp_double[i][j]*(xk.ptr.pp_double[k+1][j]-xk.ptr.pp_double[k][j]);
                    }
                }
                
                /*
                 * Calculate K-th row of R
                 */
                for(i=0; i<=m-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xk.ptr.pp_double[k][0], 1, ae_v_len(0,n-1));
                    tmp.ptr.p_double[i] = v-b.ptr.p_double[i];
                }
                for(j=0; j<=n-1; j++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[0][j], a.stride, &tmp.ptr.p_double[0], 1, ae_v_len(0,m-1));
                    r.ptr.pp_double[k][j] = v;
                }
            }
            for(i=0; i<=pointsstored-2; i++)
            {
                for(j=0; j<=pointsstored-2; j++)
                {
                    if( i!=j )
                    {
                        v = ae_v_dotproduct(&ap.ptr.pp_double[i][0], 1, &ap.ptr.pp_double[j][0], 1, ae_v_len(0,m-1));
                        result = result||ae_fp_greater(ae_fabs(v, _state),tol);
                        v = ae_v_dotproduct(&r.ptr.pp_double[i][0], 1, &r.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                        result = result||ae_fp_greater(ae_fabs(v, _state),tol);
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function compares solution calculated by LSQR with one calculated  by
SVD solver. Following comparisons are performed:
1. either:
   1.a) residual norm |Rk| for LSQR solution is at most epsErr*|B|
   1.b) |A^T*Rk|/(|A|*|Rk|)<=EpsOrt
2. norm(LSQR_solution) is at most 1.2*norm(SVD_solution)

Test (1) verifies that LSQR found good solution, test  (2)  verifies  that
LSQR finds solution with close-to-minimum norm. We use factor as large  as
1.2 to test deviation from SVD solution because LSQR is not very  good  at
solving degenerate problems.

INPUT PARAMETERS:
    A       -   array[M,N], system matrix
    B       -   right part
    M, N    -   problem size
    LambdaV -   regularization value for the problem, >=0
    X       -   array[N], solution found by LSQR
    EpsErr  -   tolerance for |A*x-b|
    EpsOrt  -   tolerance for |A^T*Rk|/(|A|*|Rk|)

RESULT
    True, for solution which passess all the tests
*************************************************************************/
static ae_bool testlinlsqrunit_isitgoodsolution(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     ae_int_t m,
     ae_int_t n,
     double lambdav,
     /* Real    */ ae_vector* x,
     double epserr,
     double epsort,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix svda;
    ae_matrix u;
    ae_matrix vt;
    ae_vector w;
    ae_vector svdx;
    ae_vector tmparr;
    ae_vector r;
    ae_int_t i;
    ae_int_t j;
    ae_int_t minmn;
    ae_bool svdresult;
    double v;
    double rnorm;
    double bnorm;
    double anorm;
    double atrnorm;
    double xnorm;
    double svdxnorm;
    ae_bool clause1holds;
    ae_bool clause2holds;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&svda, 0, sizeof(svda));
    memset(&u, 0, sizeof(u));
    memset(&vt, 0, sizeof(vt));
    memset(&w, 0, sizeof(w));
    memset(&svdx, 0, sizeof(svdx));
    memset(&tmparr, 0, sizeof(tmparr));
    memset(&r, 0, sizeof(r));
    ae_matrix_init(&svda, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&svdx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmparr, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r, 0, DT_REAL, _state, ae_true);

    
    /*
     * Solve regularized problem with SVD solver
     */
    ae_matrix_set_length(&svda, m+n, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            svda.ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
    for(i=m; i<=m+n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( i-m==j )
            {
                svda.ptr.pp_double[i][j] = lambdav;
            }
            else
            {
                svda.ptr.pp_double[i][j] = (double)(0);
            }
        }
    }
    svdresult = rmatrixsvd(&svda, m+n, n, 1, 1, 0, &w, &u, &vt, _state);
    ae_assert(svdresult, "LINLSQR: internal error in unit test (SVD failed)", _state);
    minmn = ae_minint(m, n, _state);
    ae_vector_set_length(&svdx, n, _state);
    ae_vector_set_length(&tmparr, minmn, _state);
    for(i=0; i<=minmn-1; i++)
    {
        tmparr.ptr.p_double[i] = (double)(0);
        for(j=0; j<=m-1; j++)
        {
            tmparr.ptr.p_double[i] = tmparr.ptr.p_double[i]+u.ptr.pp_double[j][i]*b->ptr.p_double[j];
        }
        if( ae_fp_less_eq(w.ptr.p_double[i],ae_sqrt(ae_machineepsilon, _state)*w.ptr.p_double[0]) )
        {
            tmparr.ptr.p_double[i] = (double)(0);
        }
        else
        {
            tmparr.ptr.p_double[i] = tmparr.ptr.p_double[i]/w.ptr.p_double[i];
        }
    }
    for(i=0; i<=n-1; i++)
    {
        svdx.ptr.p_double[i] = (double)(0);
        for(j=0; j<=minmn-1; j++)
        {
            svdx.ptr.p_double[i] = svdx.ptr.p_double[i]+vt.ptr.pp_double[j][i]*tmparr.ptr.p_double[j];
        }
    }
    
    /*
     * Calculate residual, perform checks 1.a and 1.b:
     * * first, we check 1.a
     * * in case 1.a fails we check 1.b
     */
    ae_vector_set_length(&r, m+n, _state);
    for(i=0; i<=m+n-1; i++)
    {
        v = ae_v_dotproduct(&svda.ptr.pp_double[i][0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
        r.ptr.p_double[i] = v;
        if( i<m )
        {
            r.ptr.p_double[i] = r.ptr.p_double[i]-b->ptr.p_double[i];
        }
    }
    v = ae_v_dotproduct(&r.ptr.p_double[0], 1, &r.ptr.p_double[0], 1, ae_v_len(0,m+n-1));
    rnorm = ae_sqrt(v, _state);
    v = ae_v_dotproduct(&b->ptr.p_double[0], 1, &b->ptr.p_double[0], 1, ae_v_len(0,m-1));
    bnorm = ae_sqrt(v, _state);
    if( ae_fp_less_eq(rnorm,epserr*bnorm) )
    {
        
        /*
         * 1.a is true, no further checks is needed
         */
        clause1holds = ae_true;
    }
    else
    {
        
        /*
         * 1.a is false, we have to check 1.b
         *
         * In order to do so, we calculate ||A|| and ||A^T*Rk||. We do
         * not store product of A and Rk to some array, all we need is
         * just one component of product at time, stored in V.
         * 
         */
        anorm = (double)(0);
        atrnorm = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=m+n-1; j++)
            {
                v = v+svda.ptr.pp_double[j][i]*r.ptr.p_double[j];
                anorm = anorm+ae_sqr(svda.ptr.pp_double[j][i], _state);
            }
            atrnorm = atrnorm+ae_sqr(v, _state);
        }
        anorm = ae_sqrt(anorm, _state);
        atrnorm = ae_sqrt(atrnorm, _state);
        clause1holds = ae_fp_eq(anorm*rnorm,(double)(0))||ae_fp_less_eq(atrnorm/(anorm*rnorm),epsort);
    }
    
    /*
     * Check (2).
     * Here we assume that Result=True when we enter this block.
     */
    v = ae_v_dotproduct(&x->ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
    xnorm = ae_sqrt(v, _state);
    v = ae_v_dotproduct(&svdx.ptr.p_double[0], 1, &svdx.ptr.p_double[0], 1, ae_v_len(0,n-1));
    svdxnorm = ae_sqrt(v, _state);
    clause2holds = ae_fp_less_eq(xnorm,1.2*svdxnorm);
    
    /*
     * End
     */
    result = clause1holds&&clause2holds;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing preconditioned LSQR method.

  -- ALGLIB --
     Copyright 14.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlinlsqrunit_preconditionertest(ae_state *_state)
{
    ae_frame _frame_block;
    linlsqrstate s;
    linlsqrreport rep;
    ae_matrix a;
    ae_matrix ta;
    sparsematrix sa;
    ae_vector b;
    ae_vector d;
    ae_vector xe;
    ae_vector x0;
    ae_bool bflag;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&ta, 0, sizeof(ta));
    memset(&sa, 0, sizeof(sa));
    memset(&b, 0, sizeof(b));
    memset(&d, 0, sizeof(d));
    memset(&xe, 0, sizeof(xe));
    memset(&x0, 0, sizeof(x0));
    _linlsqrstate_init(&s, _state, ae_true);
    _linlsqrreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ta, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test 1.
     *
     * We test automatic diagonal preconditioning used by SolveSparse.
     * In order to do so we:
     * 1. generate 20*20 matrix A0 with condition number equal to 1.0E1
     * 2. generate random "exact" solution xe and right part b=A0*xe
     * 3. generate random ill-conditioned diagonal scaling matrix D with
     *    condition number equal to 1.0E50:
     * 4. transform A*x=b into badly scaled problem:
     *    A0*x0=b0
     *    (A0*D)*(inv(D)*x0)=b0
     *    finally we got new problem A*x=b with A=A0*D, b=b0, x=inv(D)*x0
     *
     * Then we solve A*x=b:
     * 1. with default preconditioner
     * 2. with explicitly activayed diagonal preconditioning
     * 3. with unit preconditioner.
     * 1st and 2nd solutions must be close to xe, 3rd solution must be very
     * far from the true one.
     */
    n = 20;
    rmatrixrndcond(n, 1.0E1, &ta, _state);
    ae_vector_set_length(&xe, n, _state);
    for(i=0; i<=n-1; i++)
    {
        xe.ptr.p_double[i] = randomnormal(_state);
    }
    ae_vector_set_length(&b, n, _state);
    for(i=0; i<=n-1; i++)
    {
        b.ptr.p_double[i] = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            b.ptr.p_double[i] = b.ptr.p_double[i]+ta.ptr.pp_double[i][j]*xe.ptr.p_double[j];
        }
    }
    ae_vector_set_length(&d, n, _state);
    for(i=0; i<=n-1; i++)
    {
        d.ptr.p_double[i] = ae_pow((double)(10), 100*ae_randomreal(_state)-50, _state);
    }
    ae_matrix_set_length(&a, n, n, _state);
    sparsecreate(n, n, n*n, &sa, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a.ptr.pp_double[i][j] = ta.ptr.pp_double[i][j]*d.ptr.p_double[j];
            sparseset(&sa, i, j, a.ptr.pp_double[i][j], _state);
        }
        xe.ptr.p_double[i] = xe.ptr.p_double[i]/d.ptr.p_double[i];
    }
    sparseconverttocrs(&sa, _state);
    linlsqrcreate(n, n, &s, _state);
    linlsqrsetcond(&s, (double)(0), (double)(0), 2*n, _state);
    linlsqrsolvesparse(&s, &sa, &b, _state);
    linlsqrresults(&s, &x0, &rep, _state);
    if( rep.terminationtype<=0 )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(ae_fabs(xe.ptr.p_double[i]-x0.ptr.p_double[i], _state),5.0E-2/d.ptr.p_double[i]) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    linlsqrsetprecunit(&s, _state);
    linlsqrsolvesparse(&s, &sa, &b, _state);
    linlsqrresults(&s, &x0, &rep, _state);
    if( rep.terminationtype>0 )
    {
        bflag = ae_false;
        for(i=0; i<=n-1; i++)
        {
            bflag = bflag||ae_fp_greater(ae_fabs(xe.ptr.p_double[i]-x0.ptr.p_double[i], _state),5.0E-2/d.ptr.p_double[i]);
        }
        if( !bflag )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    linlsqrsetprecdiag(&s, _state);
    linlsqrsolvesparse(&s, &sa, &b, _state);
    linlsqrresults(&s, &x0, &rep, _state);
    if( rep.terminationtype<=0 )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(ae_fabs(xe.ptr.p_double[i]-x0.ptr.p_double[i], _state),5.0E-2/d.ptr.p_double[i]) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     *test has been passed
     */
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}



static void testmincgunit_testfunc2(mincgstate* state, ae_state *_state);
static void testmincgunit_testfunc3(mincgstate* state, ae_state *_state);
static void testmincgunit_calciip2(mincgstate* state,
     ae_int_t n,
     ae_state *_state);
static void testmincgunit_calclowrank(mincgstate* state,
     ae_int_t n,
     ae_int_t vcnt,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_matrix* v,
     /* Real    */ ae_vector* vd,
     /* Real    */ ae_vector* x0,
     ae_state *_state);
static void testmincgunit_testpreconditioning(ae_bool* err,
     ae_state *_state);
static ae_bool testmincgunit_gradientchecktest(ae_state *_state);
static void testmincgunit_funcderiv(double a,
     double b,
     double c,
     double d,
     double x0,
     double x1,
     double x2,
     /* Real    */ ae_vector* x,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);





ae_bool testmincg(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool referror;
    ae_bool eqerror;
    ae_bool linerror1;
    ae_bool linerror2;
    ae_bool restartserror;
    ae_bool precerror;
    ae_bool converror;
    ae_bool othererrors;
    ae_bool graderrors;
    ae_int_t n;
    ae_vector x;
    ae_vector xe;
    ae_vector b;
    ae_vector xlast;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_matrix a;
    ae_vector diagh;
    mincgstate state;
    mincgreport rep;
    ae_int_t cgtype;
    ae_int_t difftype;
    double diffstep;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&xe, 0, sizeof(xe));
    memset(&b, 0, sizeof(b));
    memset(&xlast, 0, sizeof(xlast));
    memset(&a, 0, sizeof(a));
    memset(&diagh, 0, sizeof(diagh));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);
    _mincgstate_init(&state, _state, ae_true);
    _mincgreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    referror = ae_false;
    linerror1 = ae_false;
    linerror2 = ae_false;
    eqerror = ae_false;
    converror = ae_false;
    restartserror = ae_false;
    othererrors = ae_false;
    precerror = ae_false;
    testmincgunit_testpreconditioning(&precerror, _state);
    testother(&othererrors, _state);
    for(difftype=0; difftype<=1; difftype++)
    {
        for(cgtype=-1; cgtype<=1; cgtype++)
        {
            
            /*
             * Reference problem
             */
            ae_vector_set_length(&x, 2+1, _state);
            n = 3;
            diffstep = 1.0E-6;
            x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
            x.ptr.p_double[1] = 100*ae_randomreal(_state)-50;
            x.ptr.p_double[2] = 100*ae_randomreal(_state)-50;
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0]-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]-2)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                    state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                    state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
                }
            }
            mincgresults(&state, &x, &rep, _state);
            referror = (((referror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-2, _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-2, _state),0.001);
            
            /*
             * F2 problem with restarts:
             * * make several iterations and restart BEFORE termination
             * * iterate and restart AFTER termination
             *
             * NOTE: step is bounded from above to avoid premature convergence
             */
            ae_vector_set_length(&x, 3, _state);
            n = 3;
            diffstep = 1.0E-6;
            x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcgtype(&state, cgtype, _state);
            mincgsetstpmax(&state, 0.1, _state);
            mincgsetcond(&state, 0.0000001, 0.0, 0.0, 0, _state);
            for(i=0; i<=10; i++)
            {
                if( !mincgiteration(&state, _state) )
                {
                    break;
                }
                testmincgunit_testfunc2(&state, _state);
            }
            x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
            mincgrestartfrom(&state, &x, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc2(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            restartserror = (((restartserror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log((double)(2), _state), _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log((double)(2), _state), _state),0.01);
            x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
            mincgrestartfrom(&state, &x, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc2(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            restartserror = (((restartserror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log((double)(2), _state), _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log((double)(2), _state), _state),0.01);
            
            /*
             * 1D problem #1
             */
            ae_vector_set_length(&x, 0+1, _state);
            n = 1;
            diffstep = 1.0E-6;
            x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = -ae_cos(state.x.ptr.p_double[0], _state);
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[0] = ae_sin(state.x.ptr.p_double[0], _state);
                }
            }
            mincgresults(&state, &x, &rep, _state);
            linerror1 = (linerror1||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]/ae_pi-ae_round(x.ptr.p_double[0]/ae_pi, _state), _state),0.001);
            
            /*
             * 1D problem #2
             */
            ae_vector_set_length(&x, 0+1, _state);
            n = 1;
            diffstep = 1.0E-6;
            x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0], _state)/(1+ae_sqr(state.x.ptr.p_double[0], _state));
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[0] = (2*state.x.ptr.p_double[0]*(1+ae_sqr(state.x.ptr.p_double[0], _state))-ae_sqr(state.x.ptr.p_double[0], _state)*2*state.x.ptr.p_double[0])/ae_sqr(1+ae_sqr(state.x.ptr.p_double[0], _state), _state);
                }
            }
            mincgresults(&state, &x, &rep, _state);
            linerror2 = (linerror2||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0], _state),0.001);
            
            /*
             * Linear equations
             */
            diffstep = 1.0E-6;
            for(n=1; n<=10; n++)
            {
                
                /*
                 * Prepare task
                 */
                ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
                ae_vector_set_length(&x, n-1+1, _state);
                ae_vector_set_length(&xe, n-1+1, _state);
                ae_vector_set_length(&b, n-1+1, _state);
                for(i=0; i<=n-1; i++)
                {
                    xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    }
                    a.ptr.pp_double[i][i] = a.ptr.pp_double[i][i]+3*ae_sign(a.ptr.pp_double[i][i], _state);
                }
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    b.ptr.p_double[i] = v;
                }
                
                /*
                 * Solve task
                 */
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( difftype==0 )
                {
                    mincgcreate(n, &x, &state, _state);
                }
                if( difftype==1 )
                {
                    mincgcreatef(n, &x, diffstep, &state, _state);
                }
                mincgsetcgtype(&state, cgtype, _state);
                while(mincgiteration(&state, _state))
                {
                    if( state.needf||state.needfg )
                    {
                        state.f = (double)(0);
                    }
                    if( state.needfg )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            state.g.ptr.p_double[i] = (double)(0);
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        if( state.needf||state.needfg )
                        {
                            state.f = state.f+ae_sqr(v-b.ptr.p_double[i], _state);
                        }
                        if( state.needfg )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                state.g.ptr.p_double[j] = state.g.ptr.p_double[j]+2*(v-b.ptr.p_double[i])*a.ptr.pp_double[i][j];
                            }
                        }
                    }
                }
                mincgresults(&state, &x, &rep, _state);
                eqerror = eqerror||rep.terminationtype<=0;
                for(i=0; i<=n-1; i++)
                {
                    eqerror = eqerror||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-xe.ptr.p_double[i], _state),0.001);
                }
            }
            
            /*
             * Testing convergence properties
             */
            diffstep = 1.0E-6;
            n = 3;
            ae_vector_set_length(&x, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
            }
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, 0.001, 0.0, 0.0, 0, _state);
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc3(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            converror = converror||rep.terminationtype!=4;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
            }
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, 0.0, 0.001, 0.0, 0, _state);
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc3(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            converror = converror||rep.terminationtype!=1;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
            }
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, 0.0, 0.0, 0.001, 0, _state);
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc3(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            converror = converror||rep.terminationtype!=2;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, 0.0, 0.0, 0.0, 10, _state);
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc3(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            converror = converror||!((rep.terminationtype==5&&rep.iterationscount==10)||rep.terminationtype==7);
        }
    }
    
    /*
     *  Test for MinCGGradientCheck
     */
    graderrors = testmincgunit_gradientchecktest(_state);
    
    /*
     * end
     */
    waserrors = (((((((referror||eqerror)||linerror1)||linerror2)||converror)||othererrors)||restartserror)||precerror)||graderrors;
    if( !silent )
    {
        printf("TESTING CG OPTIMIZATION\n");
        printf("REFERENCE PROBLEM:                        ");
        if( referror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LIN-1 PROBLEM:                            ");
        if( linerror1 )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LIN-2 PROBLEM:                            ");
        if( linerror2 )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LINEAR EQUATIONS:                         ");
        if( eqerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RESTARTS:                                 ");
        if( restartserror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("PRECONDITIONING:                          ");
        if( precerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE PROPERTIES:                   ");
        if( converror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TEST FOR VERIFICATION OF THE GRADIENT:    ");
        if( graderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Other properties
*************************************************************************/
void testother(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_vector x;
    ae_vector s;
    ae_vector a;
    ae_vector b;
    ae_vector h;
    ae_vector x0;
    ae_vector x1;
    ae_vector xlast;
    ae_matrix fulla;
    double fprev;
    double xprev;
    double stpmax;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    mincgstate state;
    mincgreport rep;
    ae_int_t cgtype;
    double tmpeps;
    double epsg;
    double v;
    double r;
    ae_bool hasxlast;
    double lastscaledstep;
    ae_int_t pkind;
    ae_int_t ckind;
    ae_int_t mkind;
    ae_int_t dkind;
    double diffstep;
    double vc;
    double vm;
    ae_bool wasf;
    ae_bool wasfg;
    hqrndstate rs;
    ae_int_t spoiliteration;
    ae_int_t stopiteration;
    ae_int_t spoilvar;
    double spoilval;
    ae_int_t pass;
    double ss;
    ae_int_t callidx;
    ae_int_t stopcallidx;
    ae_int_t maxits;
    ae_bool terminationrequested;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&s, 0, sizeof(s));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&h, 0, sizeof(h));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&xlast, 0, sizeof(xlast));
    memset(&fulla, 0, sizeof(fulla));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&h, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    _mincgstate_init(&state, _state, ae_true);
    _mincgreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    for(cgtype=-1; cgtype<=1; cgtype++)
    {
        
        /*
         * Test reports (F should form monotone sequence)
         */
        n = 50;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)(1);
        }
        mincgcreate(n, &x, &state, _state);
        mincgsetcond(&state, (double)(0), (double)(0), (double)(0), 100, _state);
        mincgsetxrep(&state, ae_true, _state);
        fprev = ae_maxrealnumber;
        while(mincgiteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                }
            }
            if( state.xupdated )
            {
                *err = *err||ae_fp_greater(state.f,fprev);
                if( ae_fp_eq(fprev,ae_maxrealnumber) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        *err = *err||ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]);
                    }
                }
                fprev = state.f;
                ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            }
        }
        mincgresults(&state, &x, &rep, _state);
        for(i=0; i<=n-1; i++)
        {
            *err = *err||ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]);
        }
        
        /*
         * Test differentiation vs. analytic gradient
         * (first one issues NeedF requests, second one issues NeedFG requests)
         */
        n = 50;
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (double)(1);
            }
            if( dkind==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( dkind==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, (double)(0), (double)(0), (double)(0), n/2, _state);
            wasf = ae_false;
            wasfg = ae_false;
            while(mincgiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = (double)(0);
                }
                for(i=0; i<=n-1; i++)
                {
                    if( state.needf||state.needfg )
                    {
                        state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    }
                    if( state.needfg )
                    {
                        state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                    }
                }
                wasf = wasf||state.needf;
                wasfg = wasfg||state.needfg;
            }
            mincgresults(&state, &x, &rep, _state);
            if( dkind==0 )
            {
                *err = (*err||wasf)||!wasfg;
            }
            if( dkind==1 )
            {
                *err = (*err||!wasf)||wasfg;
            }
        }
        
        /*
         * Test that numerical differentiation uses scaling.
         *
         * In order to test that we solve simple optimization
         * problem: min(x^2) with initial x equal to 0.0.
         *
         * We choose random DiffStep and S, then we check that
         * optimizer evaluates function at +-DiffStep*S only.
         */
        ae_vector_set_length(&x, 1, _state);
        ae_vector_set_length(&s, 1, _state);
        diffstep = ae_randomreal(_state)*1.0E-6;
        s.ptr.p_double[0] = ae_exp(ae_randomreal(_state)*4-2, _state);
        x.ptr.p_double[0] = (double)(0);
        mincgcreatef(1, &x, diffstep, &state, _state);
        mincgsetcond(&state, 1.0E-6, (double)(0), (double)(0), 0, _state);
        mincgsetscale(&state, &s, _state);
        v = (double)(0);
        while(mincgiteration(&state, _state))
        {
            state.f = ae_sqr(state.x.ptr.p_double[0], _state);
            v = ae_maxreal(v, ae_fabs(state.x.ptr.p_double[0], _state), _state);
        }
        mincgresults(&state, &x, &rep, _state);
        r = v/(s.ptr.p_double[0]*diffstep);
        *err = *err||ae_fp_greater(ae_fabs(ae_log(r, _state), _state),ae_log(1+1000*ae_machineepsilon, _state));
        
        /*
         * Test maximum step
         */
        n = 1;
        ae_vector_set_length(&x, n, _state);
        x.ptr.p_double[0] = (double)(100);
        stpmax = 0.05+0.05*ae_randomreal(_state);
        mincgcreate(n, &x, &state, _state);
        mincgsetcond(&state, 1.0E-9, (double)(0), (double)(0), 0, _state);
        mincgsetstpmax(&state, stpmax, _state);
        mincgsetxrep(&state, ae_true, _state);
        xprev = x.ptr.p_double[0];
        while(mincgiteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = ae_exp(state.x.ptr.p_double[0], _state)-ae_exp(-state.x.ptr.p_double[0], _state);
                *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
            }
            if( state.xupdated )
            {
                *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
                xprev = state.x.ptr.p_double[0];
            }
        }
        
        /*
         * Test correctness of the scaling:
         * * initial point is random point from [+1,+2]^N
         * * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
         * * we use random scaling matrix
         * * we test different variants of the preconditioning:
         *   0) unit preconditioner
         *   1) random diagonal from [0.01,100]
         *   2) scale preconditioner
         * * we set stringent stopping conditions (we try EpsG and EpsX)
         * * and we test that in the extremum stopping conditions are
         *   satisfied subject to the current scaling coefficients.
         */
        tmpeps = 1.0E-10;
        for(n=1; n<=10; n++)
        {
            for(pkind=0; pkind<=2; pkind++)
            {
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&xlast, n, _state);
                ae_vector_set_length(&a, n, _state);
                ae_vector_set_length(&s, n, _state);
                ae_vector_set_length(&h, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = ae_randomreal(_state)+1;
                    a.ptr.p_double[i] = ae_exp(ae_log((double)(100), _state)*(2*ae_randomreal(_state)-1), _state);
                    s.ptr.p_double[i] = ae_exp(ae_log((double)(100), _state)*(2*ae_randomreal(_state)-1), _state);
                    h.ptr.p_double[i] = ae_exp(ae_log((double)(100), _state)*(2*ae_randomreal(_state)-1), _state);
                }
                mincgcreate(n, &x, &state, _state);
                mincgsetscale(&state, &s, _state);
                mincgsetxrep(&state, ae_true, _state);
                if( pkind==1 )
                {
                    mincgsetprecdiag(&state, &h, _state);
                }
                if( pkind==2 )
                {
                    mincgsetprecscale(&state, _state);
                }
                
                /*
                 * Test gradient-based stopping condition
                 */
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = ae_randomreal(_state)+1;
                }
                mincgsetcond(&state, tmpeps, (double)(0), (double)(0), 0, _state);
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    if( state.needfg )
                    {
                        state.f = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], (double)(4), _state);
                            state.g.ptr.p_double[i] = 4*a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], (double)(3), _state);
                        }
                    }
                }
                mincgresults(&state, &x, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                v = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    v = v+ae_sqr(s.ptr.p_double[i]*4*a.ptr.p_double[i]*ae_pow(x.ptr.p_double[i], (double)(3), _state), _state);
                }
                v = ae_sqrt(v, _state);
                *err = *err||ae_fp_greater(v,tmpeps);
                
                /*
                 * Test step-based stopping condition
                 */
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = ae_randomreal(_state)+1;
                }
                hasxlast = ae_false;
                mincgsetcond(&state, (double)(0), (double)(0), tmpeps, 0, _state);
                mincgrestartfrom(&state, &x, _state);
                lastscaledstep = (double)(0);
                while(mincgiteration(&state, _state))
                {
                    if( state.needfg )
                    {
                        state.f = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], (double)(4), _state);
                            state.g.ptr.p_double[i] = 4*a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], (double)(3), _state);
                        }
                    }
                    if( state.xupdated )
                    {
                        if( hasxlast )
                        {
                            lastscaledstep = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                lastscaledstep = lastscaledstep+ae_sqr(state.x.ptr.p_double[i]-xlast.ptr.p_double[i], _state)/ae_sqr(s.ptr.p_double[i], _state);
                            }
                            lastscaledstep = ae_sqrt(lastscaledstep, _state);
                        }
                        else
                        {
                            lastscaledstep = (double)(0);
                        }
                        ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        hasxlast = ae_true;
                    }
                }
                mincgresults(&state, &x, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                *err = *err||ae_fp_greater(lastscaledstep,tmpeps);
            }
        }
        
        /*
         * Check correctness of the "trimming".
         *
         * Trimming is a technique which is used to help algorithm
         * cope with unbounded functions. In order to check this
         * technique we will try to solve following optimization
         * problem:
         *
         *     min f(x) subject to no constraints on X
         *            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
         *     f(x) = {
         *            { M, if x<=-0.999999 or x>=0.999999
         *
         * where c is either 1.0 or 1.0E+6, M is either 1.0E8, 1.0E20 or +INF
         * (we try different combinations)
         */
        for(ckind=0; ckind<=1; ckind++)
        {
            for(mkind=0; mkind<=2; mkind++)
            {
                
                /*
                 * Choose c and M
                 */
                vc = 1.0;
                vm = 1.0E+8;
                if( ckind==1 )
                {
                    vc = 1.0E+6;
                }
                if( mkind==1 )
                {
                    vm = 1.0E+20;
                }
                if( mkind==2 )
                {
                    vm = _state->v_posinf;
                }
                
                /*
                 * Create optimizer, solve optimization problem
                 */
                epsg = 1.0E-6*vc;
                ae_vector_set_length(&x, 1, _state);
                x.ptr.p_double[0] = 0.0;
                mincgcreate(1, &x, &state, _state);
                mincgsetcond(&state, epsg, (double)(0), (double)(0), 0, _state);
                mincgsetcgtype(&state, cgtype, _state);
                while(mincgiteration(&state, _state))
                {
                    if( state.needfg )
                    {
                        if( ae_fp_less(-0.999999,state.x.ptr.p_double[0])&&ae_fp_less(state.x.ptr.p_double[0],0.999999) )
                        {
                            state.f = 1/(1-state.x.ptr.p_double[0])+1/(1+state.x.ptr.p_double[0])+vc*state.x.ptr.p_double[0];
                            state.g.ptr.p_double[0] = 1/ae_sqr(1-state.x.ptr.p_double[0], _state)-1/ae_sqr(1+state.x.ptr.p_double[0], _state)+vc;
                        }
                        else
                        {
                            state.f = vm;
                        }
                    }
                }
                mincgresults(&state, &x, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                *err = *err||ae_fp_greater(ae_fabs(1/ae_sqr(1-x.ptr.p_double[0], _state)-1/ae_sqr(1+x.ptr.p_double[0], _state)+vc, _state),epsg);
            }
        }
    }
    
    /*
     * Test integrity checks for NAN/INF:
     * * algorithm solves optimization problem, which is normal for some time (quadratic)
     * * after 5-th step we choose random component of gradient and consistently spoil
     *   it by NAN or INF.
     * * we check that correct termination code is returned (-8)
     */
    n = 100;
    for(pass=1; pass<=10; pass++)
    {
        spoiliteration = 5;
        stopiteration = 8;
        if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
        {
            
            /*
             * Gradient can be spoiled by +INF, -INF, NAN
             */
            spoilvar = hqrnduniformi(&rs, n, _state);
            i = hqrnduniformi(&rs, 3, _state);
            spoilval = _state->v_nan;
            if( i==0 )
            {
                spoilval = _state->v_neginf;
            }
            if( i==1 )
            {
                spoilval = _state->v_posinf;
            }
        }
        else
        {
            
            /*
             * Function value can be spoiled only by NAN
             * (+INF can be recognized as legitimate value during optimization)
             */
            spoilvar = -1;
            spoilval = _state->v_nan;
        }
        spdmatrixrndcond(n, 1.0E5, &fulla, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = hqrndnormal(&rs, _state);
            x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        mincgcreate(n, &x0, &state, _state);
        mincgsetcond(&state, 0.0, 0.0, 0.0, stopiteration, _state);
        mincgsetxrep(&state, ae_true, _state);
        k = -1;
        while(mincgiteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                    state.g.ptr.p_double[i] = b.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        state.f = state.f+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        state.g.ptr.p_double[i] = state.g.ptr.p_double[i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                    }
                }
                if( k>=spoiliteration )
                {
                    if( spoilvar<0 )
                    {
                        state.f = spoilval;
                    }
                    else
                    {
                        state.g.ptr.p_double[spoilvar] = spoilval;
                    }
                }
                continue;
            }
            if( state.xupdated )
            {
                inc(&k, _state);
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        mincgresults(&state, &x1, &rep, _state);
        ae_set_error_flag(err, rep.terminationtype!=-8, __FILE__, __LINE__, "testmincgunit.ap:1147");
    }
    
    /*
     * Check algorithm ability to handle request for termination:
     * * to terminate with correct return code = 8
     * * to return point which was "current" at the moment of termination
     */
    for(pass=1; pass<=50; pass++)
    {
        n = 3;
        ss = (double)(100);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 6+ae_randomreal(_state);
        }
        stopcallidx = ae_randominteger(20, _state);
        maxits = 25;
        mincgcreate(n, &x, &state, _state);
        mincgsetcond(&state, (double)(0), (double)(0), (double)(0), maxits, _state);
        mincgsetxrep(&state, ae_true, _state);
        callidx = 0;
        terminationrequested = ae_false;
        ae_v_move(&xlast.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        while(mincgiteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ss*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = 2*ss*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0])*(-1);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
                if( callidx==stopcallidx )
                {
                    mincgrequesttermination(&state, _state);
                    terminationrequested = ae_true;
                }
                inc(&callidx, _state);
                continue;
            }
            if( state.xupdated )
            {
                if( !terminationrequested )
                {
                    ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        mincgresults(&state, &x, &rep, _state);
        ae_set_error_flag(err, rep.terminationtype!=8, __FILE__, __LINE__, "testmincgunit.ap:1197");
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]), __FILE__, __LINE__, "testmincgunit.ap:1199");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Calculate test function #2

Simple variation of #1, much more nonlinear, which makes unlikely premature
convergence of algorithm .
*************************************************************************/
static void testmincgunit_testfunc2(mincgstate* state, ae_state *_state)
{


    if( ae_fp_less(state->x.ptr.p_double[0],(double)(100)) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state->x.ptr.p_double[1], _state), _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 4*state->x.ptr.p_double[1]*ae_sqr(state->x.ptr.p_double[1], _state);
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = (double)(0);
            state->g.ptr.p_double[2] = (double)(0);
        }
    }
}


/*************************************************************************
Calculate test function #3

Simple variation of #1, much more nonlinear, with non-zero value at minimum.
It achieve two goals:
* makes unlikely premature convergence of algorithm .
* solves some issues with EpsF stopping condition which arise when
  F(minimum) is zero

*************************************************************************/
static void testmincgunit_testfunc3(mincgstate* state, ae_state *_state)
{
    double s;


    s = 0.001;
    if( ae_fp_less(state->x.ptr.p_double[0],(double)(100)) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state->x.ptr.p_double[1], _state)+s, _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 2*(ae_sqr(state->x.ptr.p_double[1], _state)+s)*2*state->x.ptr.p_double[1];
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = (double)(0);
            state->g.ptr.p_double[2] = (double)(0);
        }
    }
}


/*************************************************************************
Calculate test function IIP2

f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1)

It has high condition number which makes fast convergence unlikely without
good preconditioner.

*************************************************************************/
static void testmincgunit_calciip2(mincgstate* state,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;


    if( state->needf||state->needfg )
    {
        state->f = (double)(0);
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->needf||state->needfg )
        {
            state->f = state->f+ae_sqr((double)(i*i+1), _state)*ae_sqr(state->x.ptr.p_double[i], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[i] = ae_sqr((double)(i*i+1), _state)*2*state->x.ptr.p_double[i];
        }
    }
}


/*************************************************************************
Calculate test function f(x) = 0.5*(x-x0)'*A*(x-x0), A = D+V'*Vd*V
*************************************************************************/
static void testmincgunit_calclowrank(mincgstate* state,
     ae_int_t n,
     ae_int_t vcnt,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_matrix* v,
     /* Real    */ ae_vector* vd,
     /* Real    */ ae_vector* x0,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double dx;
    double t;
    double t2;


    state->f = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        state->g.ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=n-1; i++)
    {
        dx = state->x.ptr.p_double[i]-x0->ptr.p_double[i];
        state->f = state->f+0.5*dx*d->ptr.p_double[i]*dx;
        state->g.ptr.p_double[i] = state->g.ptr.p_double[i]+d->ptr.p_double[i]*dx;
    }
    for(i=0; i<=vcnt-1; i++)
    {
        t = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            t = t+v->ptr.pp_double[i][j]*(state->x.ptr.p_double[j]-x0->ptr.p_double[j]);
        }
        state->f = state->f+0.5*t*vd->ptr.p_double[i]*t;
        t2 = t*vd->ptr.p_double[i];
        ae_v_addd(&state->g.ptr.p_double[0], 1, &v->ptr.pp_double[i][0], 1, ae_v_len(0,n-1), t2);
    }
}


/*************************************************************************
This function tests preconditioning

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmincgunit_testpreconditioning(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t n;
    ae_vector x;
    ae_vector x0;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t vs;
    ae_matrix v;
    ae_vector vd;
    ae_vector d;
    ae_vector s;
    ae_int_t cntb1;
    ae_int_t cntg1;
    ae_int_t cntb2;
    ae_int_t cntg2;
    ae_vector diagh;
    mincgstate state;
    mincgreport rep;
    ae_int_t cgtype;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&x0, 0, sizeof(x0));
    memset(&v, 0, sizeof(v));
    memset(&vd, 0, sizeof(vd));
    memset(&d, 0, sizeof(d));
    memset(&s, 0, sizeof(s));
    memset(&diagh, 0, sizeof(diagh));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vd, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);
    _mincgstate_init(&state, _state, ae_true);
    _mincgreport_init(&rep, _state, ae_true);

    k = 50;
    for(cgtype=-1; cgtype<=1; cgtype++)
    {
        
        /*
         * Preconditioner test 1.
         *
         * If
         * * B1 is default preconditioner
         * * G1 is diagonal precomditioner based on approximate diagonal of Hessian matrix
         * then "bad" preconditioner is worse than "good" one.
         * "Worse" means more iterations to converge.
         *
         *
         * We test it using f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1).
         *
         * N        - problem size
         * K        - number of repeated passes (should be large enough to average out random factors)
         */
        for(n=10; n<=15; n++)
        {
            ae_vector_set_length(&x, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (double)(0);
            }
            mincgcreate(n, &x, &state, _state);
            mincgsetcgtype(&state, cgtype, _state);
            
            /*
             * Test it with default preconditioner
             */
            mincgsetprecdefault(&state, _state);
            cntb1 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    testmincgunit_calciip2(&state, n, _state);
                }
                mincgresults(&state, &x, &rep, _state);
                cntb1 = cntb1+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            
            /*
             * Test it with perturbed diagonal preconditioner
             */
            ae_vector_set_length(&diagh, n, _state);
            for(i=0; i<=n-1; i++)
            {
                diagh.ptr.p_double[i] = 2*ae_sqr((double)(i*i+1), _state)*(0.8+0.4*ae_randomreal(_state));
            }
            mincgsetprecdiag(&state, &diagh, _state);
            cntg1 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    testmincgunit_calciip2(&state, n, _state);
                }
                mincgresults(&state, &x, &rep, _state);
                cntg1 = cntg1+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            
            /*
             * Compare
             */
            *err = *err||cntb1<cntg1;
        }
        
        /*
         * Preconditioner test 2.
         *
         * If
         * * B1 is default preconditioner
         * * G1 is low rank exact preconditioner
         * then "bad" preconditioner is worse than "good" one.
         * "Worse" means more iterations to converge.
         *
         * Target function is f(x) = 0.5*(x-x0)'*A*(x-x0), A = D+V'*Vd*V
         *
         * N        - problem size
         * K        - number of repeated passes (should be large enough to average out random factors)
         */
        for(n=10; n<=15; n++)
        {
            for(vs=0; vs<=5; vs++)
            {
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&d, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = (double)(0);
                    x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    d.ptr.p_double[i] = ae_exp(2*ae_randomreal(_state), _state);
                }
                if( vs>0 )
                {
                    ae_matrix_set_length(&v, vs, n, _state);
                    ae_vector_set_length(&vd, vs, _state);
                    for(i=0; i<=vs-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            v.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                        vd.ptr.p_double[i] = ae_exp(2*ae_randomreal(_state), _state);
                    }
                }
                mincgcreate(n, &x, &state, _state);
                mincgsetcgtype(&state, cgtype, _state);
                
                /*
                 * Test it with default preconditioner
                 */
                mincgsetprecdefault(&state, _state);
                cntb1 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    mincgrestartfrom(&state, &x, _state);
                    while(mincgiteration(&state, _state))
                    {
                        testmincgunit_calclowrank(&state, n, vs, &d, &v, &vd, &x0, _state);
                    }
                    mincgresults(&state, &x, &rep, _state);
                    cntb1 = cntb1+rep.iterationscount;
                    *err = *err||rep.terminationtype<=0;
                }
                
                /*
                 * Test it with low rank preconditioner
                 */
                mincgsetpreclowrankfast(&state, &d, &vd, &v, vs, _state);
                cntg1 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    mincgrestartfrom(&state, &x, _state);
                    while(mincgiteration(&state, _state))
                    {
                        testmincgunit_calclowrank(&state, n, vs, &d, &v, &vd, &x0, _state);
                    }
                    mincgresults(&state, &x, &rep, _state);
                    cntg1 = cntg1+rep.iterationscount;
                    *err = *err||rep.terminationtype<=0;
                }
                
                /*
                 * Compare
                 */
                *err = *err||cntb1<cntg1;
            }
        }
        
        /*
         * Preconditioner test 3.
         *
         * If
         * * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
         * * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
         * then B2 is worse than G2.
         * "Worse" means more iterations to converge.
         */
        for(n=10; n<=15; n++)
        {
            ae_vector_set_length(&x, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (double)(0);
            }
            mincgcreate(n, &x, &state, _state);
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = 1/ae_sqrt(2*ae_pow((double)(i*i+1), (double)(2), _state)*(0.8+0.4*ae_randomreal(_state)), _state);
            }
            mincgsetprecdefault(&state, _state);
            mincgsetscale(&state, &s, _state);
            cntb2 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    testmincgunit_calciip2(&state, n, _state);
                }
                mincgresults(&state, &x, &rep, _state);
                cntb2 = cntb2+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            mincgsetprecscale(&state, _state);
            mincgsetscale(&state, &s, _state);
            cntg2 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    testmincgunit_calciip2(&state, n, _state);
                }
                mincgresults(&state, &x, &rep, _state);
                cntg2 = cntg2+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            *err = *err||cntb2<cntg2;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests, that gradient verified correctly.
*************************************************************************/
static ae_bool testmincgunit_gradientchecktest(ae_state *_state)
{
    ae_frame _frame_block;
    mincgstate state;
    mincgreport rep;
    ae_int_t n;
    double a;
    double b;
    double c;
    double d;
    double x0;
    double x1;
    double x2;
    ae_vector x;
    double teststep;
    double noise;
    ae_int_t nbrcomp;
    ae_int_t func;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&x, 0, sizeof(x));
    _mincgstate_init(&state, _state, ae_true);
    _mincgreport_init(&rep, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    passcount = 35;
    teststep = 0.01;
    n = 3;
    ae_vector_set_length(&x, n, _state);
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Prepare test's parameters
         */
        func = ae_randominteger(3, _state)+1;
        nbrcomp = ae_randominteger(n, _state);
        noise = (double)(2*ae_randominteger(2, _state)-1);
        
        /*
         * Prepare function's parameters
         */
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
        }
        a = 5*ae_randomreal(_state)+1;
        b = 5*ae_randomreal(_state)+1;
        c = 5*ae_randomreal(_state)+1;
        d = 5*ae_randomreal(_state)+1;
        x0 = 5*(2*ae_randomreal(_state)-1);
        x1 = 5*(2*ae_randomreal(_state)-1);
        x2 = 5*(2*ae_randomreal(_state)-1);
        mincgcreate(n, &x, &state, _state);
        mincgsetcond(&state, (double)(0), (double)(0), (double)(0), 0, _state);
        mincgsetgradientcheck(&state, teststep, _state);
        
        /*
         * Check that the criterion passes a derivative if it is correct
         */
        while(mincgiteration(&state, _state))
        {
            if( state.needfg )
            {
                testmincgunit_funcderiv(a, b, c, d, x0, x1, x2, &state.x, func, &state.f, &state.g, _state);
            }
        }
        mincgresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code does not equal to -7 and parameter .VarIdx
         * equal to -1.
         */
        if( rep.terminationtype==-7||rep.varidx!=-1 )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
        }
        mincgrestartfrom(&state, &x, _state);
        
        /*
         * Check that the criterion does not miss a derivative if
         * it is incorrect
         */
        while(mincgiteration(&state, _state))
        {
            if( state.needfg )
            {
                testmincgunit_funcderiv(a, b, c, d, x0, x1, x2, &state.x, func, &state.f, &state.g, _state);
                state.g.ptr.p_double[nbrcomp] = state.g.ptr.p_double[nbrcomp]+noise;
            }
        }
        mincgresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code equal to -7 and parameter .VarIdx
         * equal to number of incorrect component.
         */
        if( rep.terminationtype!=-7||rep.varidx!=nbrcomp )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function return function value and it derivatives. Function dimension
is 3.
    Function's list:
        * funcType=1:
            F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*(Z-Z0)^2+D;
        * funcType=2:
            F(X)=A*sin(X-X0)^2+B*sin(Y-Y0)^2+C*sin(Z-Z0)^2+D;
        * funcType=3:
            F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*((Z-Z0)-(X-X0))^2+D.
*************************************************************************/
static void testmincgunit_funcderiv(double a,
     double b,
     double c,
     double d,
     double x0,
     double x1,
     double x2,
     /* Real    */ ae_vector* x,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{


    ae_assert(((ae_isfinite(a, _state)&&ae_isfinite(b, _state))&&ae_isfinite(c, _state))&&ae_isfinite(d, _state), "FuncDeriv: A, B, C or D contains NaN or Infinite.", _state);
    ae_assert((ae_isfinite(x0, _state)&&ae_isfinite(x1, _state))&&ae_isfinite(x2, _state), "FuncDeriv: X0, X1 or X2 contains NaN or Infinite.", _state);
    ae_assert(functype>=1&&functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).", _state);
    if( functype==1 )
    {
        *f = a*ae_sqr(x->ptr.p_double[0]-x0, _state)+b*ae_sqr(x->ptr.p_double[1]-x1, _state)+c*ae_sqr(x->ptr.p_double[2]-x2, _state)+d;
        g->ptr.p_double[0] = 2*a*(x->ptr.p_double[0]-x0);
        g->ptr.p_double[1] = 2*b*(x->ptr.p_double[1]-x1);
        g->ptr.p_double[2] = 2*c*(x->ptr.p_double[2]-x2);
        return;
    }
    if( functype==2 )
    {
        *f = a*ae_sqr(ae_sin(x->ptr.p_double[0]-x0, _state), _state)+b*ae_sqr(ae_sin(x->ptr.p_double[1]-x1, _state), _state)+c*ae_sqr(ae_sin(x->ptr.p_double[2]-x2, _state), _state)+d;
        g->ptr.p_double[0] = 2*a*ae_sin(x->ptr.p_double[0]-x0, _state)*ae_cos(x->ptr.p_double[0]-x0, _state);
        g->ptr.p_double[1] = 2*b*ae_sin(x->ptr.p_double[1]-x1, _state)*ae_cos(x->ptr.p_double[1]-x1, _state);
        g->ptr.p_double[2] = 2*c*ae_sin(x->ptr.p_double[2]-x2, _state)*ae_cos(x->ptr.p_double[2]-x2, _state);
        return;
    }
    if( functype==3 )
    {
        *f = a*ae_sqr(x->ptr.p_double[0]-x0, _state)+b*ae_sqr(x->ptr.p_double[1]-x1, _state)+c*ae_sqr(x->ptr.p_double[2]-x2-(x->ptr.p_double[0]-x0), _state)+d;
        g->ptr.p_double[0] = 2*a*(x->ptr.p_double[0]-x0)+2*c*(x->ptr.p_double[0]-x->ptr.p_double[2]-x0+x2);
        g->ptr.p_double[1] = 2*b*(x->ptr.p_double[1]-x1);
        g->ptr.p_double[2] = 2*c*(x->ptr.p_double[2]-x->ptr.p_double[0]-x2+x0);
        return;
    }
}



static void testminbleicunit_calciip2(minbleicstate* state,
     ae_int_t n,
     ae_int_t fk,
     ae_state *_state);
static void testminbleicunit_testfeasibility(ae_bool* feaserr,
     ae_bool* converr,
     ae_bool* interr,
     ae_state *_state);
static void testminbleicunit_testother(ae_bool* err, ae_state *_state);
static void testminbleicunit_testconv(ae_bool* err, ae_state *_state);
static void testminbleicunit_testpreconditioning(ae_bool* err,
     ae_state *_state);
static void testminbleicunit_setrandompreconditioner(minbleicstate* state,
     ae_int_t n,
     ae_int_t preckind,
     ae_state *_state);
static void testminbleicunit_testgradientcheck(ae_bool* testg,
     ae_state *_state);
static void testminbleicunit_testbugs(ae_bool* err, ae_state *_state);
static void testminbleicunit_funcderiv(double a,
     double b,
     double c,
     double d,
     double x0,
     double x1,
     double x2,
     /* Real    */ ae_vector* x,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);





ae_bool testminbleic(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool feasibilityerrors;
    ae_bool othererrors;
    ae_bool precerrors;
    ae_bool interrors;
    ae_bool converrors;
    ae_bool graderrors;
    ae_bool bugs;
    ae_bool result;


    waserrors = ae_false;
    feasibilityerrors = ae_false;
    othererrors = ae_false;
    precerrors = ae_false;
    interrors = ae_false;
    converrors = ae_false;
    graderrors = ae_false;
    bugs = ae_false;
    testminbleicunit_testfeasibility(&feasibilityerrors, &converrors, &interrors, _state);
    testminbleicunit_testother(&othererrors, _state);
    testminbleicunit_testconv(&converrors, _state);
    testminbleicunit_testbugs(&bugs, _state);
    testminbleicunit_testpreconditioning(&precerrors, _state);
    testminbleicunit_testgradientcheck(&graderrors, _state);
    
    /*
     * end
     */
    waserrors = (((((feasibilityerrors||othererrors)||converrors)||interrors)||precerrors)||graderrors)||bugs;
    if( !silent )
    {
        printf("TESTING BLEIC OPTIMIZATION\n");
        printf("FEASIBILITY PROPERTIES:                   ");
        if( feasibilityerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("PRECONDITIONING:                          ");
        if( precerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE PROPERTIES:                   ");
        if( converrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("INTERNAL ERRORS:                          ");
        if( interrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TEST FOR VERIFICATION OF THE GRADIENT:    ");
        if( graderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("FIXED BUGS:                               ");
        if( bugs )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Calculate test function IIP2

f(x) = sum( ((i*i+1)^FK*x[i])^2, i=0..N-1)

It has high condition number which makes fast convergence unlikely without
good preconditioner.

*************************************************************************/
static void testminbleicunit_calciip2(minbleicstate* state,
     ae_int_t n,
     ae_int_t fk,
     ae_state *_state)
{
    ae_int_t i;


    if( state->needfg )
    {
        state->f = (double)(0);
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->needfg )
        {
            state->f = state->f+ae_pow((double)(i*i+1), (double)(2*fk), _state)*ae_sqr(state->x.ptr.p_double[i], _state);
            state->g.ptr.p_double[i] = ae_pow((double)(i*i+1), (double)(2*fk), _state)*2*state->x.ptr.p_double[i];
        }
    }
}


/*************************************************************************
This function test feasibility properties.
It launches a sequence of problems and examines their solutions.
Most of the attention is directed towards feasibility properties,
although we make some quick checks to ensure that actual solution is found.

On failure sets FeasErr (or ConvErr, depending on failure type) to True,
or leaves it unchanged otherwise.

IntErr is set to True on internal errors (errors in the control flow).
*************************************************************************/
static void testminbleicunit_testfeasibility(ae_bool* feaserr,
     ae_bool* converr,
     ae_bool* interr,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pkind;
    ae_int_t preckind;
    ae_int_t passcount;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t nmax;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t p;
    double v;
    double v2;
    double v3;
    double vv;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_vector g;
    ae_vector x0;
    ae_vector xc;
    ae_vector xs;
    ae_vector svdw;
    ae_matrix c;
    ae_matrix svdu;
    ae_matrix svdvt;
    ae_vector ct;
    minbleicstate state;
    double epsx;
    double epsfeas;
    double weakepsg;
    minbleicreport rep;
    ae_int_t dkind;
    double diffstep;

    ae_frame_make(_state, &_frame_block);
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    memset(&g, 0, sizeof(g));
    memset(&x0, 0, sizeof(x0));
    memset(&xc, 0, sizeof(xc));
    memset(&xs, 0, sizeof(xs));
    memset(&svdw, 0, sizeof(svdw));
    memset(&c, 0, sizeof(c));
    memset(&svdu, 0, sizeof(svdu));
    memset(&svdvt, 0, sizeof(svdvt));
    memset(&ct, 0, sizeof(ct));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&svdw, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&svdu, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&svdvt, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);

    nmax = 5;
    weakepsg = 1.0E-4;
    epsx = 1.0E-4;
    epsfeas = 1.0E-6;
    passcount = 10;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Test problem 1:
         * * no boundary and inequality constraints
         * * randomly generated plane as equality constraint
         * * random point (not necessarily on the plane)
         * * f = |x|^P, P = {2, 4} is used as target function
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * either analytic gradient or numerical differentiation are used
         * * we check that after work is over we are on the plane and
         *   that we are in the stationary point of constrained F
         */
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            for(preckind=0; preckind<=2; preckind++)
            {
                for(pkind=1; pkind<=2; pkind++)
                {
                    for(n=1; n<=nmax; n++)
                    {
                        
                        /*
                         * Generate X, BL, BU, CT and left part of C.
                         *
                         * Right part of C is generated using somewhat complex algo:
                         * * we generate random vector and multiply it by C.
                         * * result is used as the right part.
                         * * calculations are done on the fly, vector itself is not stored
                         * We use such algo to be sure that our system is consistent.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&x, n, _state);
                        ae_vector_set_length(&g, n, _state);
                        ae_matrix_set_length(&c, 1, n+1, _state);
                        ae_vector_set_length(&ct, 1, _state);
                        c.ptr.pp_double[0][n] = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                            c.ptr.pp_double[0][i] = 2*ae_randomreal(_state)-1;
                            v = 2*ae_randomreal(_state)-1;
                            c.ptr.pp_double[0][n] = c.ptr.pp_double[0][n]+c.ptr.pp_double[0][i]*v;
                        }
                        ct.ptr.p_int[0] = 0;
                        
                        /*
                         * Create and optimize
                         */
                        if( dkind==0 )
                        {
                            minbleiccreate(n, &x, &state, _state);
                        }
                        if( dkind==1 )
                        {
                            minbleiccreatef(n, &x, diffstep, &state, _state);
                        }
                        minbleicsetlc(&state, &c, &ct, 1, _state);
                        minbleicsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needf||state.needfg )
                            {
                                state.f = (double)(0);
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                if( state.needf||state.needfg )
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i], (double)(p), _state);
                                }
                                if( state.needfg )
                                {
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i], (double)(p-1), _state);
                                }
                            }
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * Test feasibility of solution
                         */
                        v = ae_v_dotproduct(&c.ptr.pp_double[0][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        *feaserr = *feaserr||ae_fp_greater(ae_fabs(v-c.ptr.pp_double[0][n], _state),epsfeas);
                        
                        /*
                         * if C is nonzero, test that result is
                         * a stationary point of constrained F.
                         *
                         * NOTE: this check is done only if C is nonzero
                         */
                        vv = ae_v_dotproduct(&c.ptr.pp_double[0][0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                        if( ae_fp_neq(vv,(double)(0)) )
                        {
                            
                            /*
                             * Calculate gradient at the result
                             * Project gradient into C
                             * Check projected norm
                             */
                            for(i=0; i<=n-1; i++)
                            {
                                g.ptr.p_double[i] = p*ae_pow(x.ptr.p_double[i], (double)(p-1), _state);
                            }
                            v2 = ae_v_dotproduct(&c.ptr.pp_double[0][0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                            v = ae_v_dotproduct(&c.ptr.pp_double[0][0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            vv = v/v2;
                            ae_v_subd(&g.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1), vv);
                            v3 = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            *converr = *converr||ae_fp_greater(ae_sqrt(v3, _state),weakepsg);
                        }
                    }
                }
            }
        }
        
        /*
         * Test problem 2 (multiple equality constraints):
         * * 1<=N<=NMax, 1<=K<=N
         * * no boundary constraints
         * * N-dimensional space
         * * randomly generated point xs
         * * K randomly generated hyperplanes which all pass through xs
         *   define K equality constraints: (a[k],x)=b[k]
         * * equality constraints are checked for being well conditioned
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * f(x) = |x-x0|^2, x0 = xs+a[0]
         * * either analytic gradient or numerical differentiation are used
         * * extremum of f(x) is exactly xs because:
         *   * xs is the closest point in the plane defined by (a[0],x)=b[0]
         *   * xs is feasible by definition
         */
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            for(preckind=0; preckind<=2; preckind++)
            {
                for(n=2; n<=nmax; n++)
                {
                    for(k=1; k<=n; k++)
                    {
                        
                        /*
                         * Generate X, X0, XS, BL, BU, CT and left part of C.
                         *
                         * Right part of C is generated using somewhat complex algo:
                         * * we generate random vector and multiply it by C.
                         * * result is used as the right part.
                         * * calculations are done on the fly, vector itself is not stored
                         * We use such algo to be sure that our system is consistent.
                         */
                        ae_vector_set_length(&x, n, _state);
                        ae_vector_set_length(&x0, n, _state);
                        ae_vector_set_length(&xs, n, _state);
                        ae_vector_set_length(&g, n, _state);
                        ae_matrix_set_length(&c, k, n+1, _state);
                        ae_vector_set_length(&ct, k, _state);
                        c.ptr.pp_double[0][n] = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                            xs.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        }
                        do
                        {
                            for(i=0; i<=k-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                }
                                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &xs.ptr.p_double[0], 1, ae_v_len(0,n-1));
                                c.ptr.pp_double[i][n] = v;
                                ct.ptr.p_int[i] = 0;
                            }
                            ae_set_error_flag(feaserr, !rmatrixsvd(&c, k, n, 0, 0, 0, &svdw, &svdu, &svdvt, _state), __FILE__, __LINE__, "testminbleicunit.ap:339");
                        }
                        while(!(ae_fp_greater(svdw.ptr.p_double[0],(double)(0))&&ae_fp_greater(svdw.ptr.p_double[k-1],0.001*svdw.ptr.p_double[0])));
                        ae_v_move(&x0.ptr.p_double[0], 1, &xs.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        ae_v_add(&x0.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                        
                        /*
                         * Create and optimize
                         */
                        if( dkind==0 )
                        {
                            minbleiccreate(n, &x, &state, _state);
                        }
                        if( dkind==1 )
                        {
                            minbleiccreatef(n, &x, diffstep, &state, _state);
                        }
                        minbleicsetlc(&state, &c, &ct, k, _state);
                        minbleicsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needf||state.needfg )
                            {
                                state.f = (double)(0);
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                if( state.needf||state.needfg )
                                {
                                    state.f = state.f+ae_sqr(state.x.ptr.p_double[i]-x0.ptr.p_double[i], _state);
                                }
                                if( state.needfg )
                                {
                                    state.g.ptr.p_double[i] = 2*(state.x.ptr.p_double[i]-x0.ptr.p_double[i]);
                                }
                            }
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * check feasiblity properties
                         */
                        for(i=0; i<=k-1; i++)
                        {
                            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            *feaserr = *feaserr||ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),epsx);
                        }
                        
                        /*
                         * Compare with XS
                         */
                        v = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            v = v+ae_sqr(x.ptr.p_double[i]-xs.ptr.p_double[i], _state);
                        }
                        v = ae_sqrt(v, _state);
                        *converr = *converr||ae_fp_greater(ae_fabs(v, _state),0.001);
                    }
                }
            }
        }
        
        /*
         * Another simple problem:
         * * bound constraints 0 <= x[i] <= 1
         * * no linear constraints
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * with such simple boundaries and function it is easy to find
         *   analytic form of solution: S[i] = bound(x0[i], 0, 1)
         * * we also check that both final solution and subsequent iterates
         *   are strictly feasible
         */
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            for(preckind=0; preckind<=2; preckind++)
            {
                for(pkind=1; pkind<=2; pkind++)
                {
                    for(n=1; n<=nmax; n++)
                    {
                        
                        /*
                         * Generate X, BL, BU.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&bl, n, _state);
                        ae_vector_set_length(&bu, n, _state);
                        ae_vector_set_length(&x, n, _state);
                        ae_vector_set_length(&x0, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            bl.ptr.p_double[i] = (double)(0);
                            bu.ptr.p_double[i] = (double)(1);
                            x.ptr.p_double[i] = ae_randomreal(_state);
                            x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                        }
                        
                        /*
                         * Create and optimize
                         */
                        if( dkind==0 )
                        {
                            minbleiccreate(n, &x, &state, _state);
                        }
                        if( dkind==1 )
                        {
                            minbleiccreatef(n, &x, diffstep, &state, _state);
                        }
                        minbleicsetbc(&state, &bl, &bu, _state);
                        minbleicsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needf||state.needfg )
                            {
                                state.f = (double)(0);
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                if( state.needf||state.needfg )
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p), _state);
                                }
                                if( state.needfg )
                                {
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state);
                                }
                                *feaserr = *feaserr||ae_fp_less(state.x.ptr.p_double[i],0.0);
                                *feaserr = *feaserr||ae_fp_greater(state.x.ptr.p_double[i],1.0);
                            }
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * * compare solution with analytic one
                         * * check feasibility
                         */
                        v = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            if( ae_fp_greater(x.ptr.p_double[i],(double)(0))&&ae_fp_less(x.ptr.p_double[i],(double)(1)) )
                            {
                                v = v+ae_sqr(p*ae_pow(x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state), _state);
                            }
                            *feaserr = *feaserr||ae_fp_less(x.ptr.p_double[i],0.0);
                            *feaserr = *feaserr||ae_fp_greater(x.ptr.p_double[i],1.0);
                        }
                        *converr = *converr||ae_fp_greater(ae_sqrt(v, _state),weakepsg);
                    }
                }
            }
        }
        
        /*
         * Same as previous problem, but with minor modifications:
         * * some bound constraints are 0<=x[i]<=1, some are Ci=x[i]=Ci
         * * no linear constraints
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * with such simple boundaries and function it is easy to find
         *   analytic form of solution: S[i] = bound(x0[i], 0, 1)
         * * we also check that both final solution and subsequent iterates
         *   are strictly feasible
         */
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            for(preckind=0; preckind<=2; preckind++)
            {
                for(pkind=1; pkind<=2; pkind++)
                {
                    for(n=1; n<=nmax; n++)
                    {
                        
                        /*
                         * Generate X, BL, BU.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&bl, n, _state);
                        ae_vector_set_length(&bu, n, _state);
                        ae_vector_set_length(&x, n, _state);
                        ae_vector_set_length(&x0, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            if( ae_fp_greater(ae_randomreal(_state),0.5) )
                            {
                                bl.ptr.p_double[i] = (double)(0);
                                bu.ptr.p_double[i] = (double)(1);
                            }
                            else
                            {
                                bl.ptr.p_double[i] = ae_randomreal(_state);
                                bu.ptr.p_double[i] = bl.ptr.p_double[i];
                            }
                            x.ptr.p_double[i] = ae_randomreal(_state);
                            x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                        }
                        
                        /*
                         * Create and optimize
                         */
                        if( dkind==0 )
                        {
                            minbleiccreate(n, &x, &state, _state);
                        }
                        if( dkind==1 )
                        {
                            minbleiccreatef(n, &x, diffstep, &state, _state);
                        }
                        minbleicsetbc(&state, &bl, &bu, _state);
                        minbleicsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needf||state.needfg )
                            {
                                state.f = (double)(0);
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                if( state.needf||state.needfg )
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p), _state);
                                }
                                if( state.needfg )
                                {
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state);
                                }
                                *feaserr = *feaserr||ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]);
                                *feaserr = *feaserr||ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i]);
                            }
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * * compare solution with analytic one
                         * * check feasibility
                         */
                        v = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            if( ae_fp_greater(x.ptr.p_double[i],bl.ptr.p_double[i])&&ae_fp_less(x.ptr.p_double[i],bu.ptr.p_double[i]) )
                            {
                                v = v+ae_sqr(p*ae_pow(x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state), _state);
                            }
                            *feaserr = *feaserr||ae_fp_less(x.ptr.p_double[i],bl.ptr.p_double[i]);
                            *feaserr = *feaserr||ae_fp_greater(x.ptr.p_double[i],bu.ptr.p_double[i]);
                        }
                        *converr = *converr||ae_fp_greater(ae_sqrt(v, _state),weakepsg);
                    }
                }
            }
        }
        
        /*
         * Same as previous one, but with bound constraints posed
         * as general linear ones:
         * * no bound constraints
         * * 2*N linear constraints 0 <= x[i] <= 1
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * with such simple constraints and function it is easy to find
         *   analytic form of solution: S[i] = bound(x0[i], 0, 1).
         * * however, we can't guarantee that solution is strictly feasible
         *   with respect to nonlinearity constraint, so we check
         *   for approximate feasibility.
         */
        for(preckind=0; preckind<=2; preckind++)
        {
            for(pkind=1; pkind<=2; pkind++)
            {
                for(n=1; n<=nmax; n++)
                {
                    
                    /*
                     * Generate X, BL, BU.
                     */
                    p = 2*pkind;
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&x0, n, _state);
                    ae_matrix_set_length(&c, 2*n, n+1, _state);
                    ae_vector_set_length(&ct, 2*n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = ae_randomreal(_state);
                        x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                        for(j=0; j<=n; j++)
                        {
                            c.ptr.pp_double[2*i+0][j] = (double)(0);
                            c.ptr.pp_double[2*i+1][j] = (double)(0);
                        }
                        c.ptr.pp_double[2*i+0][i] = (double)(1);
                        c.ptr.pp_double[2*i+0][n] = (double)(0);
                        ct.ptr.p_int[2*i+0] = 1;
                        c.ptr.pp_double[2*i+1][i] = (double)(1);
                        c.ptr.pp_double[2*i+1][n] = (double)(1);
                        ct.ptr.p_int[2*i+1] = -1;
                    }
                    
                    /*
                     * Create and optimize
                     */
                    minbleiccreate(n, &x, &state, _state);
                    minbleicsetlc(&state, &c, &ct, 2*n, _state);
                    minbleicsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                    testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        if( state.needfg )
                        {
                            state.f = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p), _state);
                                state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state);
                            }
                            continue;
                        }
                        
                        /*
                         * Unknown protocol specified
                         */
                        *interr = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    if( rep.terminationtype<=0 )
                    {
                        *converr = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    
                    /*
                     * * compare solution with analytic one
                     * * check feasibility
                     */
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        if( ae_fp_greater(x.ptr.p_double[i],0.02)&&ae_fp_less(x.ptr.p_double[i],0.98) )
                        {
                            v = v+ae_sqr(p*ae_pow(x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state), _state);
                        }
                        *feaserr = *feaserr||ae_fp_less(x.ptr.p_double[i],0.0-epsfeas);
                        *feaserr = *feaserr||ae_fp_greater(x.ptr.p_double[i],1.0+epsfeas);
                    }
                    *converr = *converr||ae_fp_greater(ae_sqrt(v, _state),weakepsg);
                }
            }
        }
        
        /*
         * Feasibility problem:
         * * bound constraints 0<=x[i]<=1
         * * starting point xs with xs[i] in [-1,+2]
         * * random point xc from [0,1] is used to generate K<=N
         *   random linear equality/inequality constraints of the form
         *   (c,x-xc)=0.0 (or, alternatively, >= or <=), where
         *   c is a random vector.
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * we do not know analytic form of the solution, and, if fact, we do not
         *   check for solution correctness. We just check that algorithm converges
         *   to the feasible points.
         */
        for(preckind=0; preckind<=2; preckind++)
        {
            for(pkind=1; pkind<=2; pkind++)
            {
                for(n=1; n<=nmax; n++)
                {
                    for(k=1; k<=n; k++)
                    {
                        
                        /*
                         * Generate X, BL, BU.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&x0, n, _state);
                        ae_vector_set_length(&xc, n, _state);
                        ae_vector_set_length(&xs, n, _state);
                        ae_matrix_set_length(&c, k, n+1, _state);
                        ae_vector_set_length(&ct, k, _state);
                        ae_vector_set_length(&bl, n, _state);
                        ae_vector_set_length(&bu, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                            xs.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                            xc.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
                            bl.ptr.p_double[i] = (double)(0);
                            bu.ptr.p_double[i] = (double)(1);
                        }
                        for(i=0; i<=k-1; i++)
                        {
                            c.ptr.pp_double[i][n] = (double)(0);
                            for(j=0; j<=n-1; j++)
                            {
                                c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                c.ptr.pp_double[i][n] = c.ptr.pp_double[i][n]+c.ptr.pp_double[i][j]*xc.ptr.p_double[j];
                            }
                            ct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
                        }
                        
                        /*
                         * Create and optimize
                         */
                        minbleiccreate(n, &xs, &state, _state);
                        minbleicsetbc(&state, &bl, &bu, _state);
                        minbleicsetlc(&state, &c, &ct, k, _state);
                        minbleicsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needfg )
                            {
                                state.f = (double)(0);
                                for(i=0; i<=n-1; i++)
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p), _state);
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state);
                                }
                                continue;
                            }
                            
                            /*
                             * Unknown protocol specified
                             */
                            *interr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * Check feasibility
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            *feaserr = *feaserr||ae_fp_less(x.ptr.p_double[i],0.0);
                            *feaserr = *feaserr||ae_fp_greater(x.ptr.p_double[i],1.0);
                        }
                        for(i=0; i<=k-1; i++)
                        {
                            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            v = v-c.ptr.pp_double[i][n];
                            if( ct.ptr.p_int[i]==0 )
                            {
                                *feaserr = *feaserr||ae_fp_greater(ae_fabs(v, _state),epsfeas);
                            }
                            if( ct.ptr.p_int[i]<0 )
                            {
                                *feaserr = *feaserr||ae_fp_greater(v,epsfeas);
                            }
                            if( ct.ptr.p_int[i]>0 )
                            {
                                *feaserr = *feaserr||ae_fp_less(v,-epsfeas);
                            }
                        }
                    }
                }
            }
        }
        
        /*
         * Infeasible problem:
         * * all bound constraints are 0 <= x[i] <= 1 except for one
         * * that one is 0 >= x[i] >= 1
         * * no linear constraints
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from detecting
         *   infeasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * algorithm must return correct error code on such problem
         */
        for(preckind=0; preckind<=2; preckind++)
        {
            for(pkind=1; pkind<=2; pkind++)
            {
                for(n=1; n<=nmax; n++)
                {
                    
                    /*
                     * Generate X, BL, BU.
                     */
                    p = 2*pkind;
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&x0, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        bl.ptr.p_double[i] = (double)(0);
                        bu.ptr.p_double[i] = (double)(1);
                        x.ptr.p_double[i] = ae_randomreal(_state);
                        x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                    }
                    i = ae_randominteger(n, _state);
                    bl.ptr.p_double[i] = (double)(1);
                    bu.ptr.p_double[i] = (double)(0);
                    
                    /*
                     * Create and optimize
                     */
                    minbleiccreate(n, &x, &state, _state);
                    minbleicsetbc(&state, &bl, &bu, _state);
                    minbleicsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                    testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        if( state.needfg )
                        {
                            state.f = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p), _state);
                                state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state);
                            }
                            continue;
                        }
                        
                        /*
                         * Unknown protocol specified
                         */
                        *interr = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    *feaserr = *feaserr||rep.terminationtype!=-3;
                }
            }
        }
        
        /*
         * Infeasible problem (2):
         * * no bound and inequality constraints
         * * 1<=K<=N arbitrary equality constraints
         * * (K+1)th constraint which is equal to the first constraint a*x=c,
         *   but with c:=c+1. I.e. we have both a*x=c and a*x=c+1, which can't
         *   be true (other constraints may be inconsistent too, but we don't
         *   have to check it).
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from detecting
         *   infeasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x|^P, where P={2,4}
         * * algorithm must return correct error code on such problem
         */
        for(preckind=0; preckind<=2; preckind++)
        {
            for(pkind=1; pkind<=2; pkind++)
            {
                for(n=1; n<=nmax; n++)
                {
                    for(k=1; k<=n; k++)
                    {
                        
                        /*
                         * Generate X, BL, BU.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&x, n, _state);
                        ae_matrix_set_length(&c, k+1, n+1, _state);
                        ae_vector_set_length(&ct, k+1, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            x.ptr.p_double[i] = ae_randomreal(_state);
                        }
                        for(i=0; i<=k-1; i++)
                        {
                            for(j=0; j<=n; j++)
                            {
                                c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                            ct.ptr.p_int[i] = 0;
                        }
                        ct.ptr.p_int[k] = 0;
                        ae_v_move(&c.ptr.pp_double[k][0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                        c.ptr.pp_double[k][n] = c.ptr.pp_double[0][n]+1;
                        
                        /*
                         * Create and optimize
                         */
                        minbleiccreate(n, &x, &state, _state);
                        minbleicsetlc(&state, &c, &ct, k+1, _state);
                        minbleicsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needfg )
                            {
                                state.f = (double)(0);
                                for(i=0; i<=n-1; i++)
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i], (double)(p), _state);
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i], (double)(p-1), _state);
                                }
                                continue;
                            }
                            
                            /*
                             * Unknown protocol specified
                             */
                            *interr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        *feaserr = *feaserr||rep.terminationtype!=-3;
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function additional properties.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminbleicunit_testother(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t passcount;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_vector xf;
    ae_vector x0;
    ae_vector x1;
    ae_vector b;
    ae_vector xlast;
    ae_vector a;
    ae_vector s;
    ae_vector h;
    ae_matrix c;
    ae_matrix fulla;
    ae_vector ct;
    double fprev;
    double xprev;
    double stpmax;
    double v;
    ae_int_t nec;
    ae_int_t nic;
    ae_int_t pkind;
    ae_int_t ckind;
    ae_int_t mkind;
    double vc;
    double vm;
    minbleicstate state;
    double epsx;
    double epsg;
    double eps;
    double tmpeps;
    minbleicreport rep;
    double diffstep;
    ae_int_t dkind;
    ae_bool wasf;
    ae_bool wasfg;
    double r;
    hqrndstate rs;
    ae_int_t spoiliteration;
    ae_int_t stopiteration;
    ae_int_t spoilvar;
    double spoilval;
    double ss;
    ae_int_t stopcallidx;
    ae_int_t callidx;
    ae_int_t maxits;
    ae_bool terminationrequested;
    ae_int_t scaletype;

    ae_frame_make(_state, &_frame_block);
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    memset(&xf, 0, sizeof(xf));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&b, 0, sizeof(b));
    memset(&xlast, 0, sizeof(xlast));
    memset(&a, 0, sizeof(a));
    memset(&s, 0, sizeof(s));
    memset(&h, 0, sizeof(h));
    memset(&c, 0, sizeof(c));
    memset(&fulla, 0, sizeof(fulla));
    memset(&ct, 0, sizeof(ct));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&h, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    epsx = 1.0E-4;
    epsg = 1.0E-8;
    passcount = 10;
    
    /*
     * Try to reproduce bug 570 (optimizer hangs on problems where it is required
     * to perform very small step - less than 1E-50 - in order to activate constraints).
     *
     * The problem being solved is:
     *
     *     min x[0]+x[1]+...+x[n-1]
     *
     * subject to
     *
     *     x[i]>=0, for i=0..n-1
     *
     * with initial point
     *
     *     x[0] = 1.0E-100, x[1]=x[2]=...=0.5
     *
     * We try to reproduce this problem in different settings:
     * * boundary-only constraints - we test that completion code is positive,
     *   and all x[] are EXACTLY zero
     * * boundary constraints posed as general linear ones - we test that
     *   completion code is positive, and all x[] are APPROXIMATELY zero.
     */
    n = 10;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&bl, n, _state);
    ae_vector_set_length(&bu, n, _state);
    ae_matrix_set_length(&c, n, n+1, _state);
    ae_vector_set_length(&ct, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = 0.5;
        bl.ptr.p_double[i] = 0.0;
        bu.ptr.p_double[i] = _state->v_posinf;
        ct.ptr.p_int[i] = 1;
        for(j=0; j<=n; j++)
        {
            c.ptr.pp_double[i][j] = 0.0;
        }
        c.ptr.pp_double[i][i] = 1.0;
    }
    x.ptr.p_double[0] = 1.0E-100;
    minbleiccreate(n, &x, &state, _state);
    minbleicsetbc(&state, &bl, &bu, _state);
    minbleicsetcond(&state, (double)(0), (double)(0), (double)(0), 2*n, _state);
    while(minbleiciteration(&state, _state))
    {
        if( state.needfg )
        {
            state.f = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                state.f = state.f+state.x.ptr.p_double[i];
                state.g.ptr.p_double[i] = 1.0;
            }
        }
    }
    minbleicresults(&state, &xf, &rep, _state);
    ae_set_error_flag(err, rep.terminationtype<=0, __FILE__, __LINE__, "testminbleicunit.ap:1027");
    if( rep.terminationtype>0 )
    {
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_neq(xf.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testminbleicunit.ap:1030");
        }
    }
    minbleiccreate(n, &x, &state, _state);
    minbleicsetlc(&state, &c, &ct, n, _state);
    minbleicsetcond(&state, 1.0E-64, (double)(0), (double)(0), 10, _state);
    while(minbleiciteration(&state, _state))
    {
        if( state.needfg )
        {
            state.f = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                state.f = state.f+state.x.ptr.p_double[i];
                state.g.ptr.p_double[i] = 1.0;
            }
        }
    }
    minbleicresults(&state, &xf, &rep, _state);
    ae_set_error_flag(err, rep.terminationtype<=0, __FILE__, __LINE__, "testminbleicunit.ap:1048");
    if( rep.terminationtype>0 )
    {
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(xf.ptr.p_double[i], _state),1.0E-10), __FILE__, __LINE__, "testminbleicunit.ap:1051");
        }
    }
    
    /*
     * Test reports:
     * * first value must be starting point
     * * last value must be last point
     */
    for(pass=1; pass<=passcount; pass++)
    {
        n = 50;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)(10);
            bl.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            bu.ptr.p_double[i] = _state->v_posinf;
        }
        minbleiccreate(n, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetcond(&state, 1.0E-64, (double)(0), (double)(0), 10, _state);
        minbleicsetxrep(&state, ae_true, _state);
        fprev = ae_maxrealnumber;
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                }
            }
            if( state.xupdated )
            {
                if( ae_fp_eq(fprev,ae_maxrealnumber) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        *err = *err||ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]);
                    }
                }
                fprev = state.f;
                ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        for(i=0; i<=n-1; i++)
        {
            *err = *err||ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]);
        }
    }
    
    /*
     * Test differentiation vs. analytic gradient
     * (first one issues NeedF requests, second one issues NeedFG requests)
     */
    for(pass=1; pass<=passcount; pass++)
    {
        n = 10;
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (double)(1);
            }
            if( dkind==0 )
            {
                minbleiccreate(n, &x, &state, _state);
            }
            if( dkind==1 )
            {
                minbleiccreatef(n, &x, diffstep, &state, _state);
            }
            minbleicsetcond(&state, 1.0E-6, (double)(0), epsx, 0, _state);
            wasf = ae_false;
            wasfg = ae_false;
            while(minbleiciteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = (double)(0);
                }
                for(i=0; i<=n-1; i++)
                {
                    if( state.needf||state.needfg )
                    {
                        state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    }
                    if( state.needfg )
                    {
                        state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                    }
                }
                wasf = wasf||state.needf;
                wasfg = wasfg||state.needfg;
            }
            minbleicresults(&state, &x, &rep, _state);
            if( dkind==0 )
            {
                *err = (*err||wasf)||!wasfg;
            }
            if( dkind==1 )
            {
                *err = (*err||!wasf)||wasfg;
            }
        }
    }
    
    /*
     * Test that numerical differentiation uses scaling.
     *
     * In order to test that we solve simple optimization
     * problem: min(x^2) with initial x equal to 0.0.
     *
     * We choose random DiffStep and S, then we check that
     * optimizer evaluates function at +-DiffStep*S only.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        ae_vector_set_length(&x, 1, _state);
        ae_vector_set_length(&s, 1, _state);
        diffstep = ae_randomreal(_state)*1.0E-6;
        s.ptr.p_double[0] = ae_exp(ae_randomreal(_state)*4-2, _state);
        x.ptr.p_double[0] = (double)(0);
        minbleiccreatef(1, &x, diffstep, &state, _state);
        minbleicsetcond(&state, 1.0E-6, (double)(0), epsx, 0, _state);
        minbleicsetscale(&state, &s, _state);
        v = (double)(0);
        while(minbleiciteration(&state, _state))
        {
            state.f = ae_sqr(state.x.ptr.p_double[0], _state);
            v = ae_maxreal(v, ae_fabs(state.x.ptr.p_double[0], _state), _state);
        }
        minbleicresults(&state, &x, &rep, _state);
        r = v/(s.ptr.p_double[0]*diffstep);
        *err = *err||ae_fp_greater(ae_fabs(ae_log(r, _state), _state),ae_log(1+1000*ae_machineepsilon, _state));
    }
    
    /*
     * Test stpmax
     */
    for(pass=1; pass<=passcount; pass++)
    {
        n = 1;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        x.ptr.p_double[0] = (double)(100);
        bl.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
        bu.ptr.p_double[0] = _state->v_posinf;
        stpmax = 0.05+0.05*ae_randomreal(_state);
        minbleiccreate(n, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetcond(&state, epsg, (double)(0), epsx, 0, _state);
        minbleicsetxrep(&state, ae_true, _state);
        minbleicsetstpmax(&state, stpmax, _state);
        xprev = x.ptr.p_double[0];
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = ae_exp(state.x.ptr.p_double[0], _state)-ae_exp(-state.x.ptr.p_double[0], _state);
                *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
            }
            if( state.xupdated )
            {
                *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
                xprev = state.x.ptr.p_double[0];
            }
        }
    }
    
    /*
     * Ability to solve problems with function which is unbounded from below
     */
    for(pass=1; pass<=passcount; pass++)
    {
        n = 1;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        bl.ptr.p_double[0] = 4*ae_randomreal(_state)+1;
        bu.ptr.p_double[0] = bl.ptr.p_double[0]+1;
        x.ptr.p_double[0] = 0.5*(bl.ptr.p_double[0]+bu.ptr.p_double[0]);
        minbleiccreate(n, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetcond(&state, epsg, (double)(0), epsx, 0, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = -1.0E8*ae_sqr(state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = -2.0E8*state.x.ptr.p_double[0];
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-bu.ptr.p_double[0], _state),epsx);
    }
    
    /*
     * Test correctness of the scaling:
     * * initial point is random point from [+1,+2]^N
     * * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
     * * function is EFFECTIVELY unconstrained; it has formal constraints,
     *   but they are inactive at the solution; we try different variants
     *   in order to explore different control paths of the optimizer:
     *   0) absense of constraints
     *   1) bound constraints -100000<=x[i]<=100000
     *   2) one linear constraint 0*x=0
     *   3) combination of (1) and (2)
     * * we use random scaling matrix
     * * we test different variants of the preconditioning:
     *   0) unit preconditioner
     *   1) random diagonal from [0.01,100]
     *   2) scale preconditioner
     * * we set very stringent stopping conditions
     * * and we test that in the extremum stopping conditions are
     *   satisfied subject to the current scaling coefficients.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        tmpeps = 1.0E-5;
        for(n=1; n<=10; n++)
        {
            for(ckind=0; ckind<=3; ckind++)
            {
                for(pkind=0; pkind<=2; pkind++)
                {
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&a, n, _state);
                    ae_vector_set_length(&s, n, _state);
                    ae_vector_set_length(&h, n, _state);
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    ae_matrix_set_length(&c, 1, n+1, _state);
                    ae_vector_set_length(&ct, 1, _state);
                    ct.ptr.p_int[0] = 0;
                    c.ptr.pp_double[0][n] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = ae_randomreal(_state)+1;
                        bl.ptr.p_double[i] = (double)(-100000);
                        bu.ptr.p_double[i] = (double)(100000);
                        c.ptr.pp_double[0][i] = (double)(0);
                        a.ptr.p_double[i] = ae_exp(ae_log((double)(10), _state)*(2*ae_randomreal(_state)-1), _state);
                        s.ptr.p_double[i] = ae_exp(ae_log((double)(10), _state)*(2*ae_randomreal(_state)-1), _state);
                        h.ptr.p_double[i] = ae_exp(ae_log((double)(10), _state)*(2*ae_randomreal(_state)-1), _state);
                    }
                    minbleiccreate(n, &x, &state, _state);
                    if( ckind==1||ckind==3 )
                    {
                        minbleicsetbc(&state, &bl, &bu, _state);
                    }
                    if( ckind==2||ckind==3 )
                    {
                        minbleicsetlc(&state, &c, &ct, 1, _state);
                    }
                    if( pkind==1 )
                    {
                        minbleicsetprecdiag(&state, &h, _state);
                    }
                    if( pkind==2 )
                    {
                        minbleicsetprecscale(&state, _state);
                    }
                    minbleicsetcond(&state, tmpeps, (double)(0), (double)(0), 0, _state);
                    minbleicsetscale(&state, &s, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        if( state.needfg )
                        {
                            state.f = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+a.ptr.p_double[i]*ae_sqr(state.x.ptr.p_double[i], _state);
                                state.g.ptr.p_double[i] = 2*a.ptr.p_double[i]*state.x.ptr.p_double[i];
                            }
                        }
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    if( rep.terminationtype<=0 )
                    {
                        *err = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    v = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+ae_sqr(s.ptr.p_double[i]*2*a.ptr.p_double[i]*x.ptr.p_double[i], _state);
                    }
                    v = ae_sqrt(v, _state);
                    ae_set_error_flag(err, ae_fp_greater(v,tmpeps), __FILE__, __LINE__, "testminbleicunit.ap:1317");
                }
            }
        }
    }
    
    /*
     * Check correctness of the "trimming".
     *
     * Trimming is a technique which is used to help algorithm
     * cope with unbounded functions. In order to check this
     * technique we will try to solve following optimization
     * problem:
     *
     *     min f(x) subject to no constraints on X
     *            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
     *     f(x) = {
     *            { M, if x<=-0.999999 or x>=0.999999
     *
     * where c is either 1.0 or 1.0E+4, M is either 1.0E8, 1.0E20 or +INF
     * (we try different combinations)
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(ckind=0; ckind<=1; ckind++)
        {
            for(mkind=0; mkind<=2; mkind++)
            {
                
                /*
                 * Choose c and M
                 */
                vc = (double)(1);
                vm = (double)(1);
                if( ckind==0 )
                {
                    vc = 1.0;
                }
                if( ckind==1 )
                {
                    vc = 1.0E+4;
                }
                if( mkind==0 )
                {
                    vm = 1.0E+8;
                }
                if( mkind==1 )
                {
                    vm = 1.0E+20;
                }
                if( mkind==2 )
                {
                    vm = _state->v_posinf;
                }
                
                /*
                 * Create optimizer, solve optimization problem
                 */
                epsg = 1.0E-6*vc;
                ae_vector_set_length(&x, 1, _state);
                x.ptr.p_double[0] = 0.0;
                minbleiccreate(1, &x, &state, _state);
                minbleicsetcond(&state, epsg, (double)(0), (double)(0), 0, _state);
                while(minbleiciteration(&state, _state))
                {
                    if( state.needfg )
                    {
                        if( ae_fp_less(-0.999999,state.x.ptr.p_double[0])&&ae_fp_less(state.x.ptr.p_double[0],0.999999) )
                        {
                            state.f = 1/(1-state.x.ptr.p_double[0])+1/(1+state.x.ptr.p_double[0])+vc*state.x.ptr.p_double[0];
                            state.g.ptr.p_double[0] = 1/ae_sqr(1-state.x.ptr.p_double[0], _state)-1/ae_sqr(1+state.x.ptr.p_double[0], _state)+vc;
                        }
                        else
                        {
                            state.f = vm;
                            state.g.ptr.p_double[0] = (double)(0);
                        }
                    }
                }
                minbleicresults(&state, &x, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                *err = *err||ae_fp_greater(ae_fabs(1/ae_sqr(1-x.ptr.p_double[0], _state)-1/ae_sqr(1+x.ptr.p_double[0], _state)+vc, _state),epsg);
            }
        }
    }
    
    /*
     * Test behaviour on noisy functions.
     *
     * Consider following problem:
     * * f(x,y) = (x+1)^2 + (y+1)^2 + 10000*MachineEpsilon*RandomReal()
     * * boundary constraints x>=0, y>=0
     * * starting point (x0,y0)=(10*MachineEpsilon,1.0)
     *
     * Such problem contains small numerical noise. Without noise its
     * solution is (xs,ys)=(0,0), which is easy to find. However, presence
     * of the noise makes it hard to solve:
     * * noisy f(x,y) is monotonically decreasing only when we perform
     *   steps orders of magnitude larger than 10000*MachineEpsilon
     * * at small scales f(x,y) is non-monotonic and non-convex
     * * however, our first step must be done towards
     *   (x1,y1) = (0,1-some_small_value), and length of such step is
     *   many times SMALLER than 10000*MachineEpsilon
     * * second step, from (x1,y1) to (xs,ys), will be large enough to
     *   ignore numerical noise, so the only problem is to perform
     *   first step
     *
     * Naive implementation of BLEIC should fail sometimes (sometimes -
     * due to non-deterministic nature of noise) on such problem. However,
     * our improved implementation should solve it correctly. We test
     * several variations of inner stopping criteria.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        eps = 1.0E-9;
        ae_vector_set_length(&x, 2, _state);
        ae_vector_set_length(&bl, 2, _state);
        ae_vector_set_length(&bu, 2, _state);
        x.ptr.p_double[0] = 10*ae_machineepsilon;
        x.ptr.p_double[1] = 1.0;
        bl.ptr.p_double[0] = 0.0;
        bu.ptr.p_double[0] = _state->v_posinf;
        bl.ptr.p_double[1] = 0.0;
        bu.ptr.p_double[1] = _state->v_posinf;
        for(ckind=0; ckind<=2; ckind++)
        {
            minbleiccreate(2, &x, &state, _state);
            minbleicsetbc(&state, &bl, &bu, _state);
            if( ckind==0 )
            {
                minbleicsetcond(&state, eps, (double)(0), (double)(0), 0, _state);
            }
            if( ckind==1 )
            {
                minbleicsetcond(&state, (double)(0), eps, (double)(0), 0, _state);
            }
            if( ckind==2 )
            {
                minbleicsetcond(&state, (double)(0), (double)(0), eps, 0, _state);
            }
            while(minbleiciteration(&state, _state))
            {
                if( state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0]+1, _state)+ae_sqr(state.x.ptr.p_double[1]+1, _state)+10000*ae_machineepsilon*ae_randomreal(_state);
                    state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]+1);
                    state.g.ptr.p_double[1] = 2*(state.x.ptr.p_double[1]+1);
                }
            }
            minbleicresults(&state, &xf, &rep, _state);
            if( (rep.terminationtype<=0||ae_fp_neq(xf.ptr.p_double[0],(double)(0)))||ae_fp_neq(xf.ptr.p_double[1],(double)(0)) )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
        }
    }
    
    /*
     * Deterministic variation of the previous problem.
     *
     * Consider following problem:
     * * boundary constraints x>=0, y>=0
     * * starting point (x0,y0)=(10*MachineEpsilon,1.0)
     *            / (x+1)^2 + (y+1)^2,        for (x,y)<>(x0,y0)
     * * f(x,y) = |
     *            \ (x+1)^2 + (y+1)^2 - 0.1,  for (x,y)=(x0,y0)
     *
     * Such problem contains deterministic numerical noise (-0.1 at
     * starting point). Without noise its solution is easy to find.
     * However, presence of the noise makes it hard to solve:
     * * our first step must be done towards (x1,y1) = (0,1-some_small_value),
     *   but such step will increase function valye by approximately 0.1  -
     *   instead of decreasing it.
     *
     * Naive implementation of BLEIC should fail on such problem. However,
     * our improved implementation should solve it correctly. We test
     * several variations of inner stopping criteria.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        eps = 1.0E-9;
        ae_vector_set_length(&x, 2, _state);
        ae_vector_set_length(&bl, 2, _state);
        ae_vector_set_length(&bu, 2, _state);
        x.ptr.p_double[0] = 10*ae_machineepsilon;
        x.ptr.p_double[1] = 1.0;
        bl.ptr.p_double[0] = 0.0;
        bu.ptr.p_double[0] = _state->v_posinf;
        bl.ptr.p_double[1] = 0.0;
        bu.ptr.p_double[1] = _state->v_posinf;
        for(ckind=0; ckind<=2; ckind++)
        {
            minbleiccreate(2, &x, &state, _state);
            minbleicsetbc(&state, &bl, &bu, _state);
            if( ckind==0 )
            {
                minbleicsetcond(&state, eps, (double)(0), (double)(0), 0, _state);
            }
            if( ckind==1 )
            {
                minbleicsetcond(&state, (double)(0), eps, (double)(0), 0, _state);
            }
            if( ckind==2 )
            {
                minbleicsetcond(&state, (double)(0), (double)(0), eps, 0, _state);
            }
            while(minbleiciteration(&state, _state))
            {
                if( state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0]+1, _state)+ae_sqr(state.x.ptr.p_double[1]+1, _state);
                    if( ae_fp_eq(state.x.ptr.p_double[0],x.ptr.p_double[0])&&ae_fp_eq(state.x.ptr.p_double[1],x.ptr.p_double[1]) )
                    {
                        state.f = state.f-0.1;
                    }
                    state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]+1);
                    state.g.ptr.p_double[1] = 2*(state.x.ptr.p_double[1]+1);
                }
            }
            minbleicresults(&state, &xf, &rep, _state);
            if( (rep.terminationtype<=0||ae_fp_neq(xf.ptr.p_double[0],(double)(0)))||ae_fp_neq(xf.ptr.p_double[1],(double)(0)) )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
        }
    }
    
    /*
     * Test integrity checks for NAN/INF:
     * * algorithm solves optimization problem, which is normal for some time (quadratic)
     * * after 5-th step we choose random component of gradient and consistently spoil
     *   it by NAN or INF.
     * * we check that correct termination code is returned (-8)
     */
    n = 100;
    for(pass=1; pass<=10; pass++)
    {
        spoiliteration = 5;
        stopiteration = 8;
        if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
        {
            
            /*
             * Gradient can be spoiled by +INF, -INF, NAN
             */
            spoilvar = hqrnduniformi(&rs, n, _state);
            i = hqrnduniformi(&rs, 3, _state);
            spoilval = _state->v_nan;
            if( i==0 )
            {
                spoilval = _state->v_neginf;
            }
            if( i==1 )
            {
                spoilval = _state->v_posinf;
            }
        }
        else
        {
            
            /*
             * Function value can be spoiled only by NAN
             * (+INF can be recognized as legitimate value during optimization)
             */
            spoilvar = -1;
            spoilval = _state->v_nan;
        }
        spdmatrixrndcond(n, 1.0E5, &fulla, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = hqrndnormal(&rs, _state);
            x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        minbleiccreate(n, &x0, &state, _state);
        minbleicsetcond(&state, 0.0, 0.0, 0.0, stopiteration, _state);
        minbleicsetxrep(&state, ae_true, _state);
        k = -1;
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                    state.g.ptr.p_double[i] = b.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        state.f = state.f+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        state.g.ptr.p_double[i] = state.g.ptr.p_double[i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                    }
                }
                if( k>=spoiliteration )
                {
                    if( spoilvar<0 )
                    {
                        state.f = spoilval;
                    }
                    else
                    {
                        state.g.ptr.p_double[spoilvar] = spoilval;
                    }
                }
                continue;
            }
            if( state.xupdated )
            {
                inc(&k, _state);
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minbleicresults(&state, &x1, &rep, _state);
        ae_set_error_flag(err, rep.terminationtype!=-8, __FILE__, __LINE__, "testminbleicunit.ap:1600");
    }
    
    /*
     * Check algorithm ability to handle request for termination:
     * * to terminate with correct return code = 8
     * * to return point which was "current" at the moment of termination
     *
     * NOTE: we solve problem with "corrupted" preconditioner which makes it hard
     *       to converge in less than StopCallIdx iterations
     */
    for(pass=1; pass<=50; pass++)
    {
        n = 3;
        ss = (double)(100);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 6+ae_randomreal(_state);
        }
        ae_vector_set_length(&s, 3, _state);
        s.ptr.p_double[0] = 0.00001;
        s.ptr.p_double[1] = 0.00001;
        s.ptr.p_double[2] = 10000.0;
        stopcallidx = ae_randominteger(20, _state);
        maxits = 25;
        minbleiccreate(n, &x, &state, _state);
        minbleicsetcond(&state, (double)(0), (double)(0), (double)(0), maxits, _state);
        minbleicsetxrep(&state, ae_true, _state);
        minbleicsetprecdiag(&state, &s, _state);
        callidx = 0;
        terminationrequested = ae_false;
        ae_v_move(&xlast.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ss*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = 2*ss*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0])*(-1);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
                if( callidx==stopcallidx )
                {
                    minbleicrequesttermination(&state, _state);
                    terminationrequested = ae_true;
                }
                inc(&callidx, _state);
                continue;
            }
            if( state.xupdated )
            {
                if( !terminationrequested )
                {
                    ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minbleicresults(&state, &x, &rep, _state);
        ae_set_error_flag(err, rep.terminationtype!=8, __FILE__, __LINE__, "testminbleicunit.ap:1658");
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]), __FILE__, __LINE__, "testminbleicunit.ap:1660");
        }
    }
    
    /*
     * N-dimensional problem with Ne equality and Ni inequality constraints.
     *
     * Check that internal SActiveSet object uses efficient algorithm
     * to determine initial point: it avoids expensive (N+Ni)-dimensional
     * QP subproblem when initial point is feasible w.r.t. constraints.
     *
     * In order to do so we try to solve 5-dimensional QP problem with
     * 2 equality constraints and 1000000 inequality constraints (+box
     * constraints). Inefficient algorithm will simply fail to allocate
     * enough memory, so we do not have to perform any checks here.
     */
    n = 5;
    nec = 2;
    nic = 1000000;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&bl, n, _state);
    ae_vector_set_length(&bu, n, _state);
    for(scaletype=0; scaletype<=1; scaletype++)
    {
        
        /*
         * Generate problem
         */
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = hqrnduniformr(&rs, _state);
            bl.ptr.p_double[i] = x.ptr.p_double[i]-hqrnduniformr(&rs, _state)*hqrnduniformi(&rs, 2, _state);
            bu.ptr.p_double[i] = x.ptr.p_double[i]+hqrnduniformr(&rs, _state)*hqrnduniformi(&rs, 2, _state);
        }
        ae_matrix_set_length(&c, nec+nic, n+1, _state);
        ae_vector_set_length(&ct, nec+nic, _state);
        for(i=0; i<=nec+nic-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                v = v+c.ptr.pp_double[i][j]*x.ptr.p_double[j];
            }
            c.ptr.pp_double[i][n] = v;
            if( i<nec )
            {
                ct.ptr.p_int[i] = 0;
            }
            else
            {
                c.ptr.pp_double[i][n] = c.ptr.pp_double[i][n]+0.1;
                ct.ptr.p_int[i] = -1;
            }
        }
        
        /*
         * Apply scaling (if needed), then randomly multiply C by
         * some large/small value in order to distort magnitude of
         * linear constraints.
         *
         * Correct SActiveSet implementation must be able to renormalize
         * constraints prior to checking feasibility of initial point.
         */
        ae_vector_set_length(&s, n, _state);
        for(i=0; i<=n-1; i++)
        {
            s.ptr.p_double[i] = (double)(1);
            if( scaletype!=0 )
            {
                s.ptr.p_double[i] = ae_pow((double)(10), 40*hqrnduniformr(&rs, _state)-20, _state);
            }
            x.ptr.p_double[i] = x.ptr.p_double[i]*s.ptr.p_double[i];
            bl.ptr.p_double[i] = bl.ptr.p_double[i]*s.ptr.p_double[i];
            bu.ptr.p_double[i] = bu.ptr.p_double[i]*s.ptr.p_double[i];
            for(j=0; j<=nec+nic-1; j++)
            {
                c.ptr.pp_double[j][i] = c.ptr.pp_double[j][i]/s.ptr.p_double[i];
            }
        }
        for(i=0; i<=nec+nic-1; i++)
        {
            v = ae_pow((double)(10), 40*hqrnduniformr(&rs, _state)-20, _state);
            ae_v_muld(&c.ptr.pp_double[i][0], 1, ae_v_len(0,n), v);
        }
        
        /*
         * Solve
         */
        minbleiccreate(n, &x, &state, _state);
        minbleicsetscale(&state, &s, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetlc(&state, &c, &ct, nec+nic, _state);
        minbleicsetcond(&state, 0.0, (double)(0), (double)(0), 1, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+state.x.ptr.p_double[i];
                    state.g.ptr.p_double[i] = 1.0;
                }
            }
        }
        minbleicresults(&state, &xf, &rep, _state);
    }
    
    /*
     * Test that SActiveSet object prevents steps which increase infeasibility.
     * In order to do so we:
     * * generate random SPD matrix A with moderate condition number ~1E3
     *   and huge norm (about 1E9)
     * * generate random initial point x0 with unit norm
     * * generate single linear constraint c'*x>=c'*x0, with c=A*x0
     * * run BLEIC algorithm from initial point x0 for target function f=0.5*x'*A*x
     *   and check that it stops at x0 (less than 1E-12 away from it)
     */
    n = 20;
    for(pass=0; pass<=20000; pass++)
    {
        spdmatrixrndcond(n, 1.0E3, &fulla, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                fulla.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j]*1.0E9;
            }
        }
        ae_vector_set_length(&x0, n, _state);
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = hqrnduniformr(&rs, _state);
            v = v+ae_sqr(x0.ptr.p_double[i], _state);
        }
        ae_assert(ae_fp_greater(v,(double)(0)), "MinBLEIC: integrity check failed in the unit test", _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = x0.ptr.p_double[i]/ae_sqrt(v, _state);
        }
        ae_matrix_set_length(&c, 1, n+1, _state);
        ae_vector_set_length(&ct, 1, _state);
        ct.ptr.p_int[0] = 1;
        c.ptr.pp_double[0][n] = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&fulla.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            c.ptr.pp_double[0][i] = v;
            c.ptr.pp_double[0][n] = c.ptr.pp_double[0][n]+v*x0.ptr.p_double[i];
        }
        ae_assert(ae_fp_greater(c.ptr.pp_double[0][n],(double)(0)), "MinBLEIC: integrity check failed in the unit test", _state);
        minbleiccreate(n, &x0, &state, _state);
        minbleicsetlc(&state, &c, &ct, 1, _state);
        minbleicsetcond(&state, 0.0, (double)(0), 1.0E-15, 0, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&fulla.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    state.f = state.f+0.5*v*state.x.ptr.p_double[i];
                    state.g.ptr.p_double[i] = v;
                }
            }
        }
        minbleicresults(&state, &xf, &rep, _state);
        ae_set_error_flag(err, rep.terminationtype<=0, __FILE__, __LINE__, "testminbleicunit.ap:1816");
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(x0.ptr.p_double[i]-xf.ptr.p_double[i], _state),1.0E-12), __FILE__, __LINE__, "testminbleicunit.ap:1818");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests convergence properties.
We solve several simple problems with different combinations of constraints

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminbleicunit_testconv(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t passcount;
    ae_int_t pass;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_vector b;
    ae_vector tmp;
    ae_vector g;
    ae_vector xf;
    ae_vector xs0;
    ae_vector xs1;
    ae_matrix a;
    ae_matrix c;
    ae_matrix ce;
    ae_vector ct;
    ae_vector nonnegative;
    minbleicstate state;
    double epsg;
    double epsfeas;
    double tol;
    minbleicreport rep;
    snnlssolver nnls;
    ae_int_t m;
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;
    ae_int_t j;
    double v;
    double vv;
    ae_int_t preckind;
    ae_int_t akind;
    ae_int_t shiftkind;
    ae_int_t bscale;
    double tolconstr;
    double f0;
    double f1;
    ae_int_t ccnt;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    memset(&b, 0, sizeof(b));
    memset(&tmp, 0, sizeof(tmp));
    memset(&g, 0, sizeof(g));
    memset(&xf, 0, sizeof(xf));
    memset(&xs0, 0, sizeof(xs0));
    memset(&xs1, 0, sizeof(xs1));
    memset(&a, 0, sizeof(a));
    memset(&c, 0, sizeof(c));
    memset(&ce, 0, sizeof(ce));
    memset(&ct, 0, sizeof(ct));
    memset(&nonnegative, 0, sizeof(nonnegative));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&nnls, 0, sizeof(nnls));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ce, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&nonnegative, 0, DT_BOOL, _state, ae_true);
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);
    _snnlssolver_init(&nnls, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    epsg = 1.0E-8;
    epsfeas = 1.0E-8;
    tol = 0.001;
    passcount = 10;
    
    /*
     * Three closely connected problems:
     * * 2-dimensional space
     * * octagonal area bounded by:
     *   * -1<=x<=+1
     *   * -1<=y<=+1
     *   * x+y<=1.5
     *   * x-y<=1.5
     *   * -x+y<=1.5
     *   * -x-y<=1.5
     * * several target functions:
     *   * f0=x+0.001*y, minimum at x=-1, y=-0.5
     *   * f1=(x+10)^2+y^2, minimum at x=-1, y=0
     *   * f2=(x+10)^2+(y-0.6)^2, minimum at x=-1, y=0.5
     */
    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&bl, 2, _state);
    ae_vector_set_length(&bu, 2, _state);
    ae_matrix_set_length(&c, 4, 3, _state);
    ae_vector_set_length(&ct, 4, _state);
    bl.ptr.p_double[0] = (double)(-1);
    bl.ptr.p_double[1] = (double)(-1);
    bu.ptr.p_double[0] = (double)(1);
    bu.ptr.p_double[1] = (double)(1);
    c.ptr.pp_double[0][0] = (double)(1);
    c.ptr.pp_double[0][1] = (double)(1);
    c.ptr.pp_double[0][2] = 1.5;
    ct.ptr.p_int[0] = -1;
    c.ptr.pp_double[1][0] = (double)(1);
    c.ptr.pp_double[1][1] = (double)(-1);
    c.ptr.pp_double[1][2] = 1.5;
    ct.ptr.p_int[1] = -1;
    c.ptr.pp_double[2][0] = (double)(-1);
    c.ptr.pp_double[2][1] = (double)(1);
    c.ptr.pp_double[2][2] = 1.5;
    ct.ptr.p_int[2] = -1;
    c.ptr.pp_double[3][0] = (double)(-1);
    c.ptr.pp_double[3][1] = (double)(-1);
    c.ptr.pp_double[3][2] = 1.5;
    ct.ptr.p_int[3] = -1;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * f0
         */
        x.ptr.p_double[0] = 0.2*ae_randomreal(_state)-0.1;
        x.ptr.p_double[1] = 0.2*ae_randomreal(_state)-0.1;
        minbleiccreate(2, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetlc(&state, &c, &ct, 4, _state);
        minbleicsetcond(&state, epsg, 0.0, 0.0, 0, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = state.x.ptr.p_double[0]+0.001*state.x.ptr.p_double[1];
                state.g.ptr.p_double[0] = (double)(1);
                state.g.ptr.p_double[1] = 0.001;
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[0]+1, _state),tol);
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[1]+0.5, _state),tol);
        }
        else
        {
            *err = ae_true;
        }
        
        /*
         * f1
         */
        x.ptr.p_double[0] = 0.2*ae_randomreal(_state)-0.1;
        x.ptr.p_double[1] = 0.2*ae_randomreal(_state)-0.1;
        minbleiccreate(2, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetlc(&state, &c, &ct, 4, _state);
        minbleicsetcond(&state, epsg, 0.0, 0.0, 0, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ae_sqr(state.x.ptr.p_double[0]+10, _state)+ae_sqr(state.x.ptr.p_double[1], _state);
                state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]+10);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[0]+1, _state),tol);
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),tol);
        }
        else
        {
            *err = ae_true;
        }
        
        /*
         * f2
         */
        x.ptr.p_double[0] = 0.2*ae_randomreal(_state)-0.1;
        x.ptr.p_double[1] = 0.2*ae_randomreal(_state)-0.1;
        minbleiccreate(2, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetlc(&state, &c, &ct, 4, _state);
        minbleicsetcond(&state, epsg, 0.0, 0.0, 0, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ae_sqr(state.x.ptr.p_double[0]+10, _state)+ae_sqr(state.x.ptr.p_double[1]-0.6, _state);
                state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]+10);
                state.g.ptr.p_double[1] = 2*(state.x.ptr.p_double[1]-0.6);
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[0]+1, _state),tol);
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[1]-0.5, _state),tol);
        }
        else
        {
            *err = ae_true;
        }
    }
    
    /*
     * Degenerate optimization problem with excessive constraints.
     *
     * * N=3..10, M=N div 3, K = 2*N
     * * f(x) = 0.5*|A*x-b|^2, where A is MxN random matrix, b is Mx1 random vector
     * * bound constraint:
     *   a) Ci=x[i]=Ci  for i=0..M-1
     *   b) 0<=x[i]<=1  for i=M..N-1
     * * linear constraints (for fixed feasible xf and random ai):
     *   a) ai*x  = ai*xf                   for i=0..M-1
     *   b) ai*x <= ai*xf+random(0.1,1.0)   for i=M..K-1
     * * preconditioner is chosen at random (we just want to be
     *   sure that preconditioning won't prevent us from detecting
     *   infeasible point):
     *   a) unit preconditioner
     *   b) random diagonal-based preconditioner
     *   c) random scale-based preconditioner
     * * we choose two random initial points from interior of the area
     *   given by bound constraints.
     *
     * We do not know analytic solution of this problem, and we do not need
     * to solve it :) we just perform two restarts from two different initial
     * points and check that both solutions give approximately same function
     * value.
     */
    for(preckind=0; preckind<=2; preckind++)
    {
        for(n=3; n<=10; n++)
        {
            
            /*
             * Generate problem
             */
            m = n/3;
            k = 2*n;
            ae_vector_set_length(&bl, n, _state);
            ae_vector_set_length(&bu, n, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xs0, n, _state);
            ae_vector_set_length(&xs1, n, _state);
            ae_vector_set_length(&xf, n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( i<m )
                {
                    v = ae_randomreal(_state);
                    bl.ptr.p_double[i] = v;
                    bu.ptr.p_double[i] = v;
                    xf.ptr.p_double[i] = v;
                    xs0.ptr.p_double[i] = v;
                    xs1.ptr.p_double[i] = v;
                }
                else
                {
                    bl.ptr.p_double[i] = (double)(0);
                    bu.ptr.p_double[i] = (double)(1);
                    xf.ptr.p_double[i] = ae_randomreal(_state);
                    xs0.ptr.p_double[i] = ae_randomreal(_state);
                    xs1.ptr.p_double[i] = ae_randomreal(_state);
                }
                x.ptr.p_double[i] = ae_randomreal(_state);
            }
            ae_matrix_set_length(&c, k, n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=k-1; i++)
            {
                v = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[i][j] = randomnormal(_state);
                    v = v+ae_sqr(c.ptr.pp_double[i][j], _state);
                }
                if( ae_fp_greater(v,(double)(0)) )
                {
                    for(j=0; j<=n-1; j++)
                    {
                        c.ptr.pp_double[i][j] = c.ptr.pp_double[i][j]/ae_sqrt(v, _state);
                    }
                }
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &xf.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                if( i<m )
                {
                    ct.ptr.p_int[i] = 0;
                }
                else
                {
                    ct.ptr.p_int[i] = -1;
                    c.ptr.pp_double[i][n] = c.ptr.pp_double[i][n]+0.1+0.9*ae_randomreal(_state);
                }
            }
            ae_matrix_set_length(&a, m, n+1, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n; j++)
                {
                    a.ptr.pp_double[i][j] = ae_randomreal(_state);
                }
            }
            
            /*
             * Create and optimize
             */
            minbleiccreate(n, &x, &state, _state);
            minbleicsetbc(&state, &bl, &bu, _state);
            minbleicsetlc(&state, &c, &ct, k, _state);
            minbleicsetcond(&state, epsg, 0.0, 0.0, 0, _state);
            testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
            
            /*
             * Solve problem 0:
             * * restart from XS0
             * * solve
             * * check convergence/feasibility
             * * calculate F0 - function value at solution
             */
            minbleicrestartfrom(&state, &xs0, _state);
            while(minbleiciteration(&state, _state))
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.g.ptr.p_double[i] = (double)(0);
                }
                for(i=0; i<=m-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    v = v-a.ptr.pp_double[i][n];
                    state.f = state.f+0.5*ae_sqr(v, _state);
                    ae_v_addd(&state.g.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
                }
            }
            minbleicresults(&state, &x, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_less(x.ptr.p_double[i],bl.ptr.p_double[i])||ae_fp_greater(x.ptr.p_double[i],bu.ptr.p_double[i]) )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
            }
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                v = v-c.ptr.pp_double[i][n];
                if( ct.ptr.p_int[i]==0&&ae_fp_greater(ae_fabs(v, _state),epsfeas) )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                if( ct.ptr.p_int[i]<0&&ae_fp_greater(v,epsfeas) )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
            }
            f0 = (double)(0);
            for(i=0; i<=m-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                v = v-a.ptr.pp_double[i][n];
                f0 = f0+0.5*ae_sqr(v, _state);
            }
            
            /*
             * Solve problem 1:
             * * restart from XS1
             * * solve
             * * check convergence/feasibility
             * * calculate F1 - function value at solution
             */
            minbleicrestartfrom(&state, &xs1, _state);
            while(minbleiciteration(&state, _state))
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.g.ptr.p_double[i] = (double)(0);
                }
                for(i=0; i<=m-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    v = v-a.ptr.pp_double[i][n];
                    state.f = state.f+0.5*ae_sqr(v, _state);
                    ae_v_addd(&state.g.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
                }
            }
            minbleicresults(&state, &x, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_less(x.ptr.p_double[i],bl.ptr.p_double[i])||ae_fp_greater(x.ptr.p_double[i],bu.ptr.p_double[i]) )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
            }
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                v = v-c.ptr.pp_double[i][n];
                if( ct.ptr.p_int[i]==0&&ae_fp_greater(ae_fabs(v, _state),epsfeas) )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                if( ct.ptr.p_int[i]<0&&ae_fp_greater(v,epsfeas) )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
            }
            f1 = (double)(0);
            for(i=0; i<=m-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                v = v-a.ptr.pp_double[i][n];
                f1 = f1+0.5*ae_sqr(v, _state);
            }
            
            /*
             * compare F0 and F1
             */
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(f0-f1, _state),1.0E-4), __FILE__, __LINE__, "testminbleicunit.ap:2205");
        }
    }
    
    /*
     * Convex/nonconvex optimization problem with excessive
     * (degenerate constraints):
     *
     * * N=2..8
     * * f = 0.5*x'*A*x+b'*x
     * * b has normally distributed entries with scale 10^BScale
     * * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank
     * * box constraints: x[i] in [-1,+1]
     * * 2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
     *   where v_k is one of 2^N vertices of feasible hypercube, v_shift is
     *   a shift parameter:
     *   * with zero v_shift such constraints are degenerate (each vertex has
     *     N box constraints and one "redundant" linear constraint)
     *   * with positive v_shift linear constraint is always inactive
     *   * with small (about machine epsilon) but negative v_shift,
     *     constraint is close to degenerate - but not exactly
     *
     * We check that constrained gradient is close to zero at solution.
     * Box constraint is considered active if distance to boundary is less
     * than TolConstr.
     *
     * NOTE: TolConstr must be large enough so it won't conflict with
     *       perturbation introduced by v_shift
     */
    tolconstr = 1.0E-8;
    for(n=2; n<=8; n++)
    {
        for(akind=0; akind<=3; akind++)
        {
            for(shiftkind=-5; shiftkind<=1; shiftkind++)
            {
                for(bscale=0; bscale>=-2; bscale--)
                {
                    
                    /*
                     * Generate A, B and initial point
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    ae_vector_set_length(&b, n, _state);
                    ae_vector_set_length(&x, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        b.ptr.p_double[i] = ae_pow((double)(10), (double)(bscale), _state)*hqrndnormal(&rs, _state);
                        x.ptr.p_double[i] = hqrnduniformr(&rs, _state)-0.5;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                    if( akind==1 )
                    {
                        
                        /*
                         * Dense well conditioned SPD
                         */
                        spdmatrixrndcond(n, 50.0, &a, _state);
                    }
                    if( akind==2 )
                    {
                        
                        /*
                         * Dense well conditioned indefinite
                         */
                        smatrixrndcond(n, 50.0, &a, _state);
                    }
                    if( akind==3 )
                    {
                        
                        /*
                         * Low rank
                         */
                        ae_vector_set_length(&tmp, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 0.0;
                            }
                        }
                        for(k=1; k<=ae_minint(3, n-1, _state); k++)
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                tmp.ptr.p_double[i] = hqrndnormal(&rs, _state);
                            }
                            v = hqrndnormal(&rs, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]+v*tmp.ptr.p_double[i]*tmp.ptr.p_double[j];
                                }
                            }
                        }
                    }
                    
                    /*
                     * Generate constraints
                     */
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        bl.ptr.p_double[i] = -1.0;
                        bu.ptr.p_double[i] = 1.0;
                    }
                    ccnt = ae_round(ae_pow((double)(2), (double)(n), _state), _state);
                    ae_matrix_set_length(&c, ccnt, n+1, _state);
                    ae_vector_set_length(&ct, ccnt, _state);
                    for(i=0; i<=ccnt-1; i++)
                    {
                        ct.ptr.p_int[i] = -1;
                        k = i;
                        c.ptr.pp_double[i][n] = ae_sign((double)(shiftkind), _state)*ae_pow((double)(10), ae_fabs((double)(shiftkind), _state), _state)*ae_machineepsilon;
                        for(j=0; j<=n-1; j++)
                        {
                            c.ptr.pp_double[i][j] = (double)(2*(k%2)-1);
                            c.ptr.pp_double[i][n] = c.ptr.pp_double[i][n]+c.ptr.pp_double[i][j]*c.ptr.pp_double[i][j];
                            k = k/2;
                        }
                    }
                    
                    /*
                     * Create and optimize
                     */
                    minbleiccreate(n, &x, &state, _state);
                    minbleicsetbc(&state, &bl, &bu, _state);
                    minbleicsetlc(&state, &c, &ct, ccnt, _state);
                    minbleicsetcond(&state, 1.0E-9, 0.0, 0.0, 0, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        ae_assert(state.needfg, "Assertion failed", _state);
                        state.f = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+state.x.ptr.p_double[i]*b.ptr.p_double[i];
                            state.g.ptr.p_double[i] = b.ptr.p_double[i];
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            state.f = state.f+0.5*state.x.ptr.p_double[i]*v;
                            state.g.ptr.p_double[i] = state.g.ptr.p_double[i]+v;
                        }
                    }
                    minbleicresults(&state, &xs0, &rep, _state);
                    ae_set_error_flag(err, rep.terminationtype<=0, __FILE__, __LINE__, "testminbleicunit.ap:2338");
                    if( *err )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    
                    /*
                     * Evaluate gradient at solution and test
                     */
                    vv = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xs0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        v = v+b.ptr.p_double[i];
                        if( ae_fp_less_eq(xs0.ptr.p_double[i],bl.ptr.p_double[i]+tolconstr)&&ae_fp_greater(v,(double)(0)) )
                        {
                            v = 0.0;
                        }
                        if( ae_fp_greater_eq(xs0.ptr.p_double[i],bu.ptr.p_double[i]-tolconstr)&&ae_fp_less(v,(double)(0)) )
                        {
                            v = 0.0;
                        }
                        vv = vv+ae_sqr(v, _state);
                    }
                    vv = ae_sqrt(vv, _state);
                    ae_set_error_flag(err, ae_fp_greater(vv,1.0E-5), __FILE__, __LINE__, "testminbleicunit.ap:2362");
                }
            }
        }
    }
    
    /*
     * Convex/nonconvex optimization problem with combination of
     * box and linear constraints:
     *
     * * N=2..8
     * * f = 0.5*x'*A*x+b'*x
     * * b has normally distributed entries with scale 10^BScale
     * * several kinds of A are tried: zero, well conditioned SPD,
     *   well conditioned indefinite, low rank
     * * box constraints: x[i] in [-1,+1]
     * * initial point x0 = [0 0 ... 0 0]
     * * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
     *   random mix of equality/inequality constraints is tried.
     *   x0 is guaranteed to be feasible.
     *
     * We check that constrained gradient is close to zero at solution.
     * Inequality constraint is considered active if distance to boundary
     * is less than TolConstr. We use nonnegative least squares solver
     * in order to compute constrained gradient.
     */
    tolconstr = 1.0E-8;
    for(n=2; n<=8; n++)
    {
        for(akind=0; akind<=3; akind++)
        {
            for(bscale=0; bscale>=-2; bscale--)
            {
                
                /*
                 * Generate A, B and initial point
                 */
                ae_matrix_set_length(&a, n, n, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&x, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    b.ptr.p_double[i] = ae_pow((double)(10), (double)(bscale), _state)*hqrndnormal(&rs, _state);
                    x.ptr.p_double[i] = 0.0;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = 0.0;
                    }
                }
                if( akind==1 )
                {
                    
                    /*
                     * Dense well conditioned SPD
                     */
                    spdmatrixrndcond(n, 50.0, &a, _state);
                }
                if( akind==2 )
                {
                    
                    /*
                     * Dense well conditioned indefinite
                     */
                    smatrixrndcond(n, 50.0, &a, _state);
                }
                if( akind==3 )
                {
                    
                    /*
                     * Low rank
                     */
                    ae_vector_set_length(&tmp, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                    for(k=1; k<=ae_minint(3, n-1, _state); k++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            tmp.ptr.p_double[i] = hqrndnormal(&rs, _state);
                        }
                        v = hqrndnormal(&rs, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]+v*tmp.ptr.p_double[i]*tmp.ptr.p_double[j];
                            }
                        }
                    }
                }
                
                /*
                 * Generate constraints
                 */
                ae_vector_set_length(&bl, n, _state);
                ae_vector_set_length(&bu, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    bl.ptr.p_double[i] = -1.0;
                    bu.ptr.p_double[i] = 1.0;
                }
                ccnt = ae_minint(3, n-1, _state);
                ae_matrix_set_length(&c, ccnt, n+1, _state);
                ae_vector_set_length(&ct, ccnt, _state);
                for(i=0; i<=ccnt-1; i++)
                {
                    ct.ptr.p_int[i] = hqrnduniformi(&rs, 3, _state)-1;
                    c.ptr.pp_double[i][n] = 0.0;
                    for(j=0; j<=n-1; j++)
                    {
                        c.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    }
                }
                
                /*
                 * Create and optimize
                 */
                minbleiccreate(n, &x, &state, _state);
                minbleicsetbc(&state, &bl, &bu, _state);
                minbleicsetlc(&state, &c, &ct, ccnt, _state);
                minbleicsetcond(&state, 1.0E-9, 0.0, 0.0, 0, _state);
                while(minbleiciteration(&state, _state))
                {
                    ae_assert(state.needfg, "Assertion failed", _state);
                    state.f = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+state.x.ptr.p_double[i]*b.ptr.p_double[i];
                        state.g.ptr.p_double[i] = b.ptr.p_double[i];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        state.f = state.f+0.5*state.x.ptr.p_double[i]*v;
                        state.g.ptr.p_double[i] = state.g.ptr.p_double[i]+v;
                    }
                }
                minbleicresults(&state, &xs0, &rep, _state);
                ae_set_error_flag(err, rep.terminationtype<=0, __FILE__, __LINE__, "testminbleicunit.ap:2484");
                if( *err )
                {
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * 1. evaluate unconstrained gradient at solution
                 *
                 * 2. calculate constrained gradient (NNLS solver is used
                 *    to evaluate gradient subject to active constraints).
                 *    In order to do this we form CE matrix, matrix of active
                 *    constraints (columns store constraint vectors). Then
                 *    we try to approximate gradient vector by columns of CE,
                 *    subject to non-negativity restriction placed on variables
                 *    corresponding to inequality constraints.
                 *
                 *    Residual from such regression is a constrained gradient vector.
                 */
                ae_vector_set_length(&g, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xs0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    g.ptr.p_double[i] = v+b.ptr.p_double[i];
                }
                ae_matrix_set_length(&ce, n, n+ccnt, _state);
                ae_vector_set_length(&nonnegative, n+ccnt, _state);
                k = 0;
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(err, ae_fp_less(xs0.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testminbleicunit.ap:2512");
                    ae_set_error_flag(err, ae_fp_greater(xs0.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testminbleicunit.ap:2513");
                    if( ae_fp_less_eq(xs0.ptr.p_double[i],bl.ptr.p_double[i]+tolconstr) )
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ce.ptr.pp_double[j][k] = 0.0;
                        }
                        ce.ptr.pp_double[i][k] = 1.0;
                        nonnegative.ptr.p_bool[k] = ae_true;
                        inc(&k, _state);
                        continue;
                    }
                    if( ae_fp_greater_eq(xs0.ptr.p_double[i],bu.ptr.p_double[i]-tolconstr) )
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ce.ptr.pp_double[j][k] = 0.0;
                        }
                        ce.ptr.pp_double[i][k] = -1.0;
                        nonnegative.ptr.p_bool[k] = ae_true;
                        inc(&k, _state);
                        continue;
                    }
                }
                for(i=0; i<=ccnt-1; i++)
                {
                    v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &xs0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    v = v-c.ptr.pp_double[i][n];
                    ae_set_error_flag(err, ct.ptr.p_int[i]==0&&ae_fp_greater(ae_fabs(v, _state),tolconstr), __FILE__, __LINE__, "testminbleicunit.ap:2537");
                    ae_set_error_flag(err, ct.ptr.p_int[i]>0&&ae_fp_less(v,-tolconstr), __FILE__, __LINE__, "testminbleicunit.ap:2538");
                    ae_set_error_flag(err, ct.ptr.p_int[i]<0&&ae_fp_greater(v,tolconstr), __FILE__, __LINE__, "testminbleicunit.ap:2539");
                    if( ct.ptr.p_int[i]==0 )
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ce.ptr.pp_double[j][k] = c.ptr.pp_double[i][j];
                        }
                        nonnegative.ptr.p_bool[k] = ae_false;
                        inc(&k, _state);
                        continue;
                    }
                    if( (ct.ptr.p_int[i]>0&&ae_fp_less_eq(v,tolconstr))||(ct.ptr.p_int[i]<0&&ae_fp_greater_eq(v,-tolconstr)) )
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ce.ptr.pp_double[j][k] = ae_sign((double)(ct.ptr.p_int[i]), _state)*c.ptr.pp_double[i][j];
                        }
                        nonnegative.ptr.p_bool[k] = ae_true;
                        inc(&k, _state);
                        continue;
                    }
                }
                snnlsinit(0, 0, 0, &nnls, _state);
                snnlssetproblem(&nnls, &ce, &g, 0, k, n, _state);
                for(i=0; i<=k-1; i++)
                {
                    if( !nonnegative.ptr.p_bool[i] )
                    {
                        snnlsdropnnc(&nnls, i, _state);
                    }
                }
                snnlssolve(&nnls, &tmp, _state);
                for(i=0; i<=k-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        g.ptr.p_double[j] = g.ptr.p_double[j]-tmp.ptr.p_double[i]*ce.ptr.pp_double[j][i];
                    }
                }
                vv = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                vv = ae_sqrt(vv, _state);
                ae_set_error_flag(err, ae_fp_greater(vv,1.0E-5), __FILE__, __LINE__, "testminbleicunit.ap:2568");
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests preconditioning

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminbleicunit_testpreconditioning(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t n;
    ae_vector x;
    ae_vector x0;
    ae_int_t i;
    ae_int_t k;
    ae_matrix v;
    ae_matrix c;
    ae_vector ct;
    ae_vector bl;
    ae_vector bu;
    ae_vector vd;
    ae_vector d;
    ae_vector units;
    ae_vector s;
    ae_int_t cntb1;
    ae_int_t cntb2;
    ae_int_t cntg1;
    ae_int_t cntg2;
    double epsg;
    ae_vector diagh;
    minbleicstate state;
    minbleicreport rep;
    ae_int_t ckind;
    ae_int_t fk;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&x0, 0, sizeof(x0));
    memset(&v, 0, sizeof(v));
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&vd, 0, sizeof(vd));
    memset(&d, 0, sizeof(d));
    memset(&units, 0, sizeof(units));
    memset(&s, 0, sizeof(s));
    memset(&diagh, 0, sizeof(diagh));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vd, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&units, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);

    
    /*
     * Preconditioner test 1.
     *
     * If
     * * B1 is default preconditioner with unit scale
     * * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix
     * * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * then B1 is worse than G1, B2 is worse than G2.
     * "Worse" means more iterations to converge.
     *
     * Test problem setup:
     * * f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1)
     * * constraints:
     *   0) absent
     *   1) boundary only
     *   2) linear equality only
     *   3) combination of boundary and linear equality constraints
     *
     * N        - problem size
     * K        - number of repeated passes (should be large enough to average out random factors)
     */
    k = 100;
    epsg = 1.0E-8;
    for(n=10; n<=10; n++)
    {
        for(ckind=0; ckind<=3; ckind++)
        {
            fk = 1;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&units, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (double)(0);
                units.ptr.p_double[i] = (double)(1);
            }
            minbleiccreate(n, &x, &state, _state);
            minbleicsetcond(&state, epsg, 0.0, 0.0, 0, _state);
            if( ckind==1||ckind==3 )
            {
                ae_vector_set_length(&bl, n, _state);
                ae_vector_set_length(&bu, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    bl.ptr.p_double[i] = (double)(-1);
                    bu.ptr.p_double[i] = (double)(1);
                }
                minbleicsetbc(&state, &bl, &bu, _state);
            }
            if( ckind==2||ckind==3 )
            {
                ae_matrix_set_length(&c, 1, n+1, _state);
                ae_vector_set_length(&ct, 1, _state);
                ct.ptr.p_int[0] = ae_randominteger(3, _state)-1;
                for(i=0; i<=n-1; i++)
                {
                    c.ptr.pp_double[0][i] = 2*ae_randomreal(_state)-1;
                }
                c.ptr.pp_double[0][n] = (double)(0);
                minbleicsetlc(&state, &c, &ct, 1, _state);
            }
            
            /*
             * Test it with default preconditioner VS. perturbed diagonal preconditioner
             */
            minbleicsetprecdefault(&state, _state);
            minbleicsetscale(&state, &units, _state);
            cntb1 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                minbleicrestartfrom(&state, &x, _state);
                while(minbleiciteration(&state, _state))
                {
                    testminbleicunit_calciip2(&state, n, fk, _state);
                }
                minbleicresults(&state, &x, &rep, _state);
                cntb1 = cntb1+rep.inneriterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            ae_vector_set_length(&diagh, n, _state);
            for(i=0; i<=n-1; i++)
            {
                diagh.ptr.p_double[i] = 2*ae_pow((double)(i*i+1), (double)(2*fk), _state)*(0.8+0.4*ae_randomreal(_state));
            }
            minbleicsetprecdiag(&state, &diagh, _state);
            minbleicsetscale(&state, &units, _state);
            cntg1 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                minbleicrestartfrom(&state, &x, _state);
                while(minbleiciteration(&state, _state))
                {
                    testminbleicunit_calciip2(&state, n, fk, _state);
                }
                minbleicresults(&state, &x, &rep, _state);
                cntg1 = cntg1+rep.inneriterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            *err = *err||cntb1<cntg1;
            
            /*
             * Test it with scale-based preconditioner
             */
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = 1/ae_sqrt(2*ae_pow((double)(i*i+1), (double)(2*fk), _state)*(0.8+0.4*ae_randomreal(_state)), _state);
            }
            minbleicsetprecdefault(&state, _state);
            minbleicsetscale(&state, &s, _state);
            cntb2 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                minbleicrestartfrom(&state, &x, _state);
                while(minbleiciteration(&state, _state))
                {
                    testminbleicunit_calciip2(&state, n, fk, _state);
                }
                minbleicresults(&state, &x, &rep, _state);
                cntb2 = cntb2+rep.inneriterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            minbleicsetprecscale(&state, _state);
            minbleicsetscale(&state, &s, _state);
            cntg2 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                minbleicrestartfrom(&state, &x, _state);
                while(minbleiciteration(&state, _state))
                {
                    testminbleicunit_calciip2(&state, n, fk, _state);
                }
                minbleicresults(&state, &x, &rep, _state);
                cntg2 = cntg2+rep.inneriterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            *err = *err||cntb2<cntg2;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function sets random preconditioner:
* unit one, for PrecKind=0
* diagonal-based one, for PrecKind=1
* scale-based one, for PrecKind=2
*************************************************************************/
static void testminbleicunit_setrandompreconditioner(minbleicstate* state,
     ae_int_t n,
     ae_int_t preckind,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector p;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&p, 0, sizeof(p));
    ae_vector_init(&p, 0, DT_REAL, _state, ae_true);

    if( preckind==1 )
    {
        ae_vector_set_length(&p, n, _state);
        for(i=0; i<=n-1; i++)
        {
            p.ptr.p_double[i] = ae_exp(6*ae_randomreal(_state)-3, _state);
        }
        minbleicsetprecdiag(state, &p, _state);
    }
    else
    {
        minbleicsetprecdefault(state, _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests, that gradient verified correctly.
*************************************************************************/
static void testminbleicunit_testgradientcheck(ae_bool* testg,
     ae_state *_state)
{
    ae_frame _frame_block;
    minbleicstate state;
    minbleicreport rep;
    ae_int_t n;
    double a;
    double b;
    double c;
    double d;
    double x0;
    double x1;
    double x2;
    ae_vector x;
    ae_vector bl;
    ae_vector bu;
    ae_int_t infcomp;
    double teststep;
    double noise;
    ae_int_t nbrcomp;
    double spp;
    ae_int_t func;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&x, 0, sizeof(x));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    *testg = ae_false;
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);

    passcount = 35;
    spp = 1.0;
    teststep = 0.01;
    n = 3;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&bl, n, _state);
    ae_vector_set_length(&bu, n, _state);
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Prepare test's parameters
         */
        func = ae_randominteger(3, _state)+1;
        nbrcomp = ae_randominteger(n, _state);
        noise = (double)(10*(2*ae_randominteger(2, _state)-1));
        
        /*
         * Prepare function's parameters
         */
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
        }
        a = 5*ae_randomreal(_state)+1;
        b = 5*ae_randomreal(_state)+1;
        c = 5*ae_randomreal(_state)+1;
        d = 5*ae_randomreal(_state)+1;
        x0 = 5*(2*ae_randomreal(_state)-1);
        x1 = 5*(2*ae_randomreal(_state)-1);
        x2 = 5*(2*ae_randomreal(_state)-1);
        
        /*
         * Prepare boundary parameters
         */
        for(i=0; i<=n-1; i++)
        {
            bl.ptr.p_double[i] = ae_randomreal(_state)-spp;
            bu.ptr.p_double[i] = ae_randomreal(_state)+spp-1;
        }
        infcomp = ae_randominteger(n+1, _state);
        if( infcomp<n )
        {
            bl.ptr.p_double[infcomp] = _state->v_neginf;
        }
        infcomp = ae_randominteger(n+1, _state);
        if( infcomp<n )
        {
            bu.ptr.p_double[infcomp] = _state->v_posinf;
        }
        minbleiccreate(n, &x, &state, _state);
        minbleicsetgradientcheck(&state, teststep, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        
        /*
         * Check that the criterion passes a derivative if it is correct
         */
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                
                /*
                 * Check that .X within the boundaries
                 */
                for(i=0; i<=n-1; i++)
                {
                    if( (ae_isfinite(bl.ptr.p_double[i], _state)&&ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]))||(ae_isfinite(bu.ptr.p_double[i], _state)&&ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i])) )
                    {
                        *testg = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                }
                testminbleicunit_funcderiv(a, b, c, d, x0, x1, x2, &state.x, func, &state.f, &state.g, _state);
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code does not equal to -7 and parameter .VarIdx
         * equal to -1.
         */
        if( rep.terminationtype==-7||rep.varidx!=-1 )
        {
            *testg = ae_true;
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
        }
        minbleicrestartfrom(&state, &x, _state);
        
        /*
         * Check that the criterion does not miss a derivative if
         * it is incorrect
         */
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                for(i=0; i<=n-1; i++)
                {
                    if( (ae_isfinite(bl.ptr.p_double[i], _state)&&ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]))||(ae_isfinite(bu.ptr.p_double[i], _state)&&ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i])) )
                    {
                        *testg = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                }
                testminbleicunit_funcderiv(a, b, c, d, x0, x1, x2, &state.x, func, &state.f, &state.g, _state);
                state.g.ptr.p_double[nbrcomp] = state.g.ptr.p_double[nbrcomp]+noise;
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code equal to -7 and parameter .VarIdx
         * equal to number of incorrect component.
         */
        if( rep.terminationtype!=-7||rep.varidx!=nbrcomp )
        {
            *testg = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    *testg = ae_false;
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests problems which caused bugs in the past.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminbleicunit_testbugs(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_vector x1;
    ae_vector h;
    ae_vector prior;
    ae_vector w;
    ae_matrix a;
    ae_matrix c;
    ae_matrix xy;
    ae_vector ct;
    minbleicstate state;
    minbleicreport rep;
    hqrndstate rs;
    ae_int_t pass;
    double tolx;
    double regterm;
    ae_int_t n;
    ae_int_t ckind;

    ae_frame_make(_state, &_frame_block);
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    memset(&x1, 0, sizeof(x1));
    memset(&h, 0, sizeof(h));
    memset(&prior, 0, sizeof(prior));
    memset(&w, 0, sizeof(w));
    memset(&a, 0, sizeof(a));
    memset(&c, 0, sizeof(c));
    memset(&xy, 0, sizeof(xy));
    memset(&ct, 0, sizeof(ct));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&h, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&prior, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Reproduce situation: optimizer sometimes hangs when starts with
     * gradient orthogonal to the only linear constraint. In most cases
     * it is solved successfully, but sometimes leads to infinite loop
     * in one of the early optimizer versions.
     *
     * The problem is:
     * * f(x)= x'*x + c'*x
     * * linear constraint c'*x=0
     * * initial point is x=0
     * * there are two ways to choose coefficient vector c:
     *   * its components can be long binary fractions
     *   * or they can be either 0 or 1
     *   both ways test different scenarios for accumulation of rounding errors
     *
     * If test fails, it usually hangs
     */
    tolx = 1.0E-10;
    for(pass=1; pass<=10; pass++)
    {
        for(ckind=0; ckind<=1; ckind++)
        {
            for(n=2; n<=10; n++)
            {
                ae_vector_set_length(&x, n, _state);
                ae_matrix_set_length(&c, 1, n+1, _state);
                ae_vector_set_length(&ct, 1, _state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 0.0;
                    if( ckind==0 )
                    {
                        c.ptr.pp_double[0][i] = ae_sqrt(hqrnduniformr(&rs, _state), _state);
                    }
                    else
                    {
                        c.ptr.pp_double[0][i] = (double)(hqrnduniformi(&rs, 2, _state));
                    }
                }
                c.ptr.pp_double[0][n] = 0.0;
                ct.ptr.p_int[0] = 0;
                minbleiccreate(n, &x, &state, _state);
                minbleicsetlc(&state, &c, &ct, 1, _state);
                minbleicsetcond(&state, 0.0, 0.0, 0.0, 99, _state);
                while(minbleiciteration(&state, _state))
                {
                    ae_assert(state.needfg, "Assertion failed", _state);
                    if( state.needfg )
                    {
                        state.f = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+ae_sqr(state.x.ptr.p_double[i], _state)+state.x.ptr.p_double[i]*c.ptr.pp_double[0][i];
                            state.g.ptr.p_double[i] = 2*state.x.ptr.p_double[i]+c.ptr.pp_double[0][i];
                        }
                    }
                }
                minbleicresultsbuf(&state, &x1, &rep, _state);
                ae_set_error_flag(err, rep.terminationtype<=0, __FILE__, __LINE__, "testminbleicunit.ap:3009");
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(err, ae_fp_greater(ae_fabs(x1.ptr.p_double[i], _state),tolx), __FILE__, __LINE__, "testminbleicunit.ap:3011");
                }
            }
        }
    }
    
    /*
     * Reproduce optimization problem which caused bugs (optimizer hangs)
     * when BLEIC was used from MCPD unit. We perform test on specific
     * 9-dimensional problem, no need to try general-case methods.
     *
     * This test hangs if bug is present. Thus, we do not test completion
     * code returned by optimizer - we just test that it was returned :)
     */
    tolx = 1.0E-8;
    regterm = 1.0E-8;
    for(pass=1; pass<=1000; pass++)
    {
        
        /*
         * Prepare constraints:
         * * [0,1] box constraints on all variables
         * * 5 linear constraints, first one is random equality;
         *   second one is random inequality; other ones are "sum-to-one" constraints
         *   for x0-x2, x3-x5, x6-x8.
         */
        ae_vector_set_length(&bl, 9, _state);
        ae_vector_set_length(&bu, 9, _state);
        for(i=0; i<=9-1; i++)
        {
            bl.ptr.p_double[i] = 0.0;
            bu.ptr.p_double[i] = 1.0;
        }
        ae_matrix_set_length(&c, 5, 10, _state);
        ae_vector_set_length(&ct, 5, _state);
        for(i=0; i<=1; i++)
        {
            c.ptr.pp_double[i][9] = (double)(0);
            for(j=0; j<=9-1; j++)
            {
                c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                c.ptr.pp_double[i][9] = c.ptr.pp_double[i][9]+c.ptr.pp_double[i][j]*((double)1/(double)9);
            }
        }
        ct.ptr.p_int[0] = 0;
        ct.ptr.p_int[1] = 1;
        c.ptr.pp_double[1][9] = c.ptr.pp_double[1][9]-0.1;
        for(i=0; i<=3-1; i++)
        {
            for(k=0; k<=9-1; k++)
            {
                c.ptr.pp_double[2+i][k] = (double)(0);
            }
            for(k=0; k<=3-1; k++)
            {
                c.ptr.pp_double[2+i][k*3+i] = (double)(1);
            }
            c.ptr.pp_double[2+i][9] = 1.0;
            ct.ptr.p_int[2+i] = 0;
        }
        
        /*
         * Prepare weights
         */
        ae_vector_set_length(&w, 3, _state);
        for(i=0; i<=w.cnt-1; i++)
        {
            w.ptr.p_double[i] = 1.0;
        }
        
        /*
         * Prepare preconditioner H
         */
        ae_vector_set_length(&h, 9, _state);
        for(i=0; i<=h.cnt-1; i++)
        {
            h.ptr.p_double[i] = 1.0;
        }
        
        /*
         * Prepare prior value for regularization
         */
        ae_vector_set_length(&prior, 9, _state);
        for(i=0; i<=prior.cnt-1; i++)
        {
            prior.ptr.p_double[i] = (double)(0);
        }
        prior.ptr.p_double[0] = 1.0;
        prior.ptr.p_double[4] = 1.0;
        prior.ptr.p_double[8] = 1.0;
        
        /*
         * Prepare dataset XY
         */
        ae_matrix_set_length(&xy, 6, 3, _state);
        for(i=0; i<=xy.rows-1; i++)
        {
            for(j=0; j<=xy.cols-1; j++)
            {
                xy.ptr.pp_double[i][j] = ae_randomreal(_state);
            }
        }
        
        /*
         * Optimize
         */
        ae_vector_set_length(&x, 9, _state);
        for(i=0; i<=9-1; i++)
        {
            x.ptr.p_double[i] = (double)1/(double)9;
        }
        minbleiccreate(9, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetlc(&state, &c, &ct, 5, _state);
        minbleicsetcond(&state, 0.0, 0.0, tolx, 0, _state);
        minbleicsetprecdiag(&state, &h, _state);
        while(minbleiciteration(&state, _state))
        {
            ae_assert(state.needfg, "Assertion failed", _state);
            if( state.needfg )
            {
                
                /*
                 * Calculate regularization term
                 */
                state.f = 0.0;
                for(i=0; i<=9-1; i++)
                {
                    state.f = state.f+regterm*ae_sqr(state.x.ptr.p_double[i]-prior.ptr.p_double[i], _state);
                    state.g.ptr.p_double[i] = 2*regterm*(state.x.ptr.p_double[i]-prior.ptr.p_double[i]);
                }
                
                /*
                 * calculate prediction error/gradient for K-th pair
                 */
                for(k=0; k<=xy.rows-2; k++)
                {
                    for(i=0; i<=3-1; i++)
                    {
                        v = ae_v_dotproduct(&state.x.ptr.p_double[i*3], 1, &xy.ptr.pp_double[k][0], 1, ae_v_len(i*3,i*3+3-1));
                        state.f = state.f+ae_sqr(w.ptr.p_double[i]*(v-xy.ptr.pp_double[k+1][i]), _state);
                        for(j=0; j<=3-1; j++)
                        {
                            state.g.ptr.p_double[i*3+j] = state.g.ptr.p_double[i*3+j]+2*w.ptr.p_double[i]*w.ptr.p_double[i]*(v-xy.ptr.pp_double[k+1][i])*xy.ptr.pp_double[k][j];
                        }
                    }
                }
            }
        }
        minbleicresultsbuf(&state, &x, &rep, _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function return function value and it derivatives. Function dimension
is 3.
    Function's list:
        * funcType=1:
            F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*(Z-Z0)^2+D;
        * funcType=2:
            F(X)=A*sin(X-X0)^2+B*sin(Y-Y0)^2+C*sin(Z-Z0)^2+D;
        * funcType=3:
            F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*((Z-Z0)-(X-X0))^2+D.
*************************************************************************/
static void testminbleicunit_funcderiv(double a,
     double b,
     double c,
     double d,
     double x0,
     double x1,
     double x2,
     /* Real    */ ae_vector* x,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{


    ae_assert(((ae_isfinite(a, _state)&&ae_isfinite(b, _state))&&ae_isfinite(c, _state))&&ae_isfinite(d, _state), "FuncDeriv: A, B, C or D contains NaN or Infinite.", _state);
    ae_assert((ae_isfinite(x0, _state)&&ae_isfinite(x1, _state))&&ae_isfinite(x2, _state), "FuncDeriv: X0, X1 or X2 contains NaN or Infinite.", _state);
    ae_assert(functype>=1&&functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).", _state);
    if( functype==1 )
    {
        *f = a*ae_sqr(x->ptr.p_double[0]-x0, _state)+b*ae_sqr(x->ptr.p_double[1]-x1, _state)+c*ae_sqr(x->ptr.p_double[2]-x2, _state)+d;
        g->ptr.p_double[0] = 2*a*(x->ptr.p_double[0]-x0);
        g->ptr.p_double[1] = 2*b*(x->ptr.p_double[1]-x1);
        g->ptr.p_double[2] = 2*c*(x->ptr.p_double[2]-x2);
        return;
    }
    if( functype==2 )
    {
        *f = a*ae_sqr(ae_sin(x->ptr.p_double[0]-x0, _state), _state)+b*ae_sqr(ae_sin(x->ptr.p_double[1]-x1, _state), _state)+c*ae_sqr(ae_sin(x->ptr.p_double[2]-x2, _state), _state)+d;
        g->ptr.p_double[0] = 2*a*ae_sin(x->ptr.p_double[0]-x0, _state)*ae_cos(x->ptr.p_double[0]-x0, _state);
        g->ptr.p_double[1] = 2*b*ae_sin(x->ptr.p_double[1]-x1, _state)*ae_cos(x->ptr.p_double[1]-x1, _state);
        g->ptr.p_double[2] = 2*c*ae_sin(x->ptr.p_double[2]-x2, _state)*ae_cos(x->ptr.p_double[2]-x2, _state);
        return;
    }
    if( functype==3 )
    {
        *f = a*ae_sqr(x->ptr.p_double[0]-x0, _state)+b*ae_sqr(x->ptr.p_double[1]-x1, _state)+c*ae_sqr(x->ptr.p_double[2]-x2-(x->ptr.p_double[0]-x0), _state)+d;
        g->ptr.p_double[0] = 2*a*(x->ptr.p_double[0]-x0)+2*c*(x->ptr.p_double[0]-x->ptr.p_double[2]-x0+x2);
        g->ptr.p_double[1] = 2*b*(x->ptr.p_double[1]-x1);
        g->ptr.p_double[2] = 2*c*(x->ptr.p_double[2]-x->ptr.p_double[0]-x2+x0);
        return;
    }
}



static void testminqpunit_bcqptest(ae_bool* wereerrors, ae_state *_state);
static ae_bool testminqpunit_ecqptest(ae_state *_state);
static void testminqpunit_icqptest(ae_bool* err, ae_state *_state);
static void testminqpunit_generallcqptest(ae_bool* errorflag,
     ae_state *_state);
static ae_bool testminqpunit_specialicqptests(ae_state *_state);
static void testminqpunit_denseaultests(ae_bool* errorflag,
     ae_state *_state);
static void testminqpunit_spectests(ae_bool* errorflag, ae_state *_state);
static double testminqpunit_projectedantigradnorm(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* g,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     ae_state *_state);
static void testminqpunit_testbcgradandfeasibility(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     ae_int_t n,
     /* Real    */ ae_vector* x,
     double eps,
     ae_bool* errorflag,
     ae_state *_state);
static void testminqpunit_setrandomalgoallmodern(minqpstate* s,
     double* bctol,
     double* lctol,
     ae_state *_state);
static void testminqpunit_setrandomalgononconvex(minqpstate* s,
     ae_state *_state);
static void testminqpunit_setrandomalgosemidefinite(minqpstate* s,
     ae_state *_state);
static void testminqpunit_setrandomalgobc(minqpstate* s, ae_state *_state);
static void testminqpunit_setrandomalgoconvexlc(minqpstate* s,
     ae_state *_state);
static void testminqpunit_setrandomalgononconvexlc(minqpstate* s,
     ae_state *_state);
static void testminqpunit_densetosparse(/* Real    */ ae_matrix* a,
     ae_int_t n,
     sparsematrix* s,
     ae_state *_state);
static void testminqpunit_randomlysplitlc(/* Real    */ ae_matrix* rawc,
     /* Integer */ ae_vector* rawct,
     ae_int_t rawccnt,
     ae_int_t n,
     /* Real    */ ae_matrix* densec,
     /* Integer */ ae_vector* densect,
     sparsematrix* sparsec,
     /* Integer */ ae_vector* sparsect,
     ae_int_t* denseccnt,
     ae_int_t* sparseccnt,
     hqrndstate* rs,
     ae_state *_state);
static void testminqpunit_randomlysplitandsetlc(/* Real    */ ae_matrix* rawc,
     /* Integer */ ae_vector* rawct,
     ae_int_t rawccnt,
     ae_int_t n,
     minqpstate* state,
     hqrndstate* rs,
     ae_state *_state);
static void testminqpunit_randomlyselectconvertandsetquadraticterm(/* Real    */ ae_matrix* a,
     ae_int_t n,
     minqpstate* state,
     hqrndstate* rs,
     ae_state *_state);
static double testminqpunit_getconstraintrcond(/* Real    */ ae_matrix* c,
     ae_int_t k,
     ae_int_t n,
     ae_state *_state);





ae_bool testminqp(ae_bool silent, ae_state *_state)
{
    ae_bool simpleerrors;
    ae_bool func1errors;
    ae_bool func2errors;
    ae_bool bcqperrors;
    ae_bool ecqperrors;
    ae_bool icqperrors;
    ae_bool lcqperrors;
    ae_bool quickqperrors;
    ae_bool bleicerrors;
    ae_bool denseaulerrors;
    ae_bool specerrors;
    ae_bool waserrors;
    ae_bool result;


    
    /*
     * The VERY basic tests for Cholesky and BLEIC
     */
    simpleerrors = simpletest(_state);
    func1errors = functest1(_state);
    func2errors = functest2(_state);
    
    /*
     * Solver-specific tests
     */
    denseaulerrors = ae_false;
    quickqperrors = quickqptests(_state);
    bleicerrors = bleictests(_state);
    testminqpunit_denseaultests(&denseaulerrors, _state);
    
    /*
     * 
     */
    icqperrors = ae_false;
    lcqperrors = ae_false;
    bcqperrors = ae_false;
    testminqpunit_bcqptest(&bcqperrors, _state);
    ecqperrors = testminqpunit_ecqptest(_state);
    testminqpunit_icqptest(&icqperrors, _state);
    icqperrors = icqperrors||testminqpunit_specialicqptests(_state);
    testminqpunit_generallcqptest(&lcqperrors, _state);
    specerrors = ae_false;
    testminqpunit_spectests(&specerrors, _state);
    
    /*
     * report
     */
    waserrors = (((((((((simpleerrors||func1errors)||func2errors)||bcqperrors)||ecqperrors)||icqperrors)||lcqperrors)||quickqperrors)||bleicerrors)||denseaulerrors)||specerrors;
    if( !silent )
    {
        printf("TESTING MinQP\n");
        printf("BASIC TESTS:\n");
        printf("* SimpleTest:                             ");
        if( simpleerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* Func1Test:                              ");
        if( func1errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* Func2Test:                              ");
        if( func2errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("GENERIC QP TESTS:\n");
        printf("* box constrained:                        ");
        if( bcqperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* linearly constrained:                   ");
        if( (ecqperrors||icqperrors)||lcqperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("SOLVER-SPECIFIC TESTS:\n");
        printf("* QuickQP solver tests:                   ");
        if( quickqperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* BLEIC solver tests:                     ");
        if( bleicerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* DENSE-AUL solver tests:                 ");
        if( denseaulerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("SPECIAL PROPERTIES:                       ");
        if( specerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Function to test: 'MinQPCreate', 'MinQPSetQuadraticTerm', 'MinQPSetBC', 
'MinQPSetOrigin', 'MinQPSetStartingPoint', 'MinQPOptimize', 'MinQPResults'.

Test problem:
    A = diag(aii), aii>0 (random)
    b = 0
    random bounds (either no bounds, one bound, two bounds a<b, two bounds a=b)
    random start point
    dimension - from 1 to 5.
    
Returns True on success, False on failure.
*************************************************************************/
ae_bool simpletest(ae_state *_state)
{
    ae_frame _frame_block;
    minqpstate state;
    ae_int_t nexp;
    ae_int_t msn;
    ae_int_t sn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix a;
    ae_vector ub;
    ae_vector db;
    ae_vector x;
    ae_vector tx;
    double maxstb;
    ae_vector stx;
    ae_vector xori;
    ae_int_t infd;
    minqpreport rep;
    double maxn;
    double maxnb;
    double minnb;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&a, 0, sizeof(a));
    memset(&ub, 0, sizeof(ub));
    memset(&db, 0, sizeof(db));
    memset(&x, 0, sizeof(x));
    memset(&tx, 0, sizeof(tx));
    memset(&stx, 0, sizeof(stx));
    memset(&xori, 0, sizeof(xori));
    memset(&rep, 0, sizeof(rep));
    _minqpstate_init(&state, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ub, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&db, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&stx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xori, 0, DT_REAL, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);

    eps = 0.001;
    msn = 5;
    maxstb = (double)(10);
    nexp = 1000;
    maxn = (double)(10);
    maxnb = (double)(1000);
    minnb = (double)(-1000);
    for(sn=1; sn<=msn; sn++)
    {
        ae_vector_set_length(&tx, sn, _state);
        ae_vector_set_length(&xori, sn, _state);
        ae_vector_set_length(&stx, sn, _state);
        ae_vector_set_length(&db, sn, _state);
        ae_vector_set_length(&ub, sn, _state);
        ae_matrix_set_length(&a, sn, sn, _state);
        for(i=0; i<=nexp; i++)
        {
            
            /*
             *create diagonal matrix
             */
            for(k=0; k<=sn-1; k++)
            {
                for(j=0; j<=k; j++)
                {
                    if( j!=k )
                    {
                        a.ptr.pp_double[k][j] = (double)(0);
                    }
                    else
                    {
                        a.ptr.pp_double[k][j] = maxn*ae_randomreal(_state)+1;
                    }
                }
            }
            minqpcreate(sn, &state, _state);
            testminqpunit_setrandomalgobc(&state, _state);
            minqpsetquadraticterm(&state, &a, ae_false, _state);
            for(j=0; j<=sn-1; j++)
            {
                infd = ae_randominteger(5, _state);
                if( infd==0 )
                {
                    db.ptr.p_double[j] = _state->v_neginf;
                    ub.ptr.p_double[j] = _state->v_posinf;
                }
                else
                {
                    if( infd==1 )
                    {
                        db.ptr.p_double[j] = _state->v_neginf;
                        ub.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
                    }
                    else
                    {
                        if( infd==2 )
                        {
                            db.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
                            ub.ptr.p_double[j] = _state->v_posinf;
                        }
                        else
                        {
                            if( infd==3 )
                            {
                                db.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
                                ub.ptr.p_double[j] = db.ptr.p_double[j]+maxstb*ae_randomreal(_state)+0.01;
                            }
                            else
                            {
                                db.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
                                ub.ptr.p_double[j] = db.ptr.p_double[j];
                            }
                        }
                    }
                }
            }
            minqpsetbc(&state, &db, &ub, _state);
            
            /*
             *initialization for shifting
             *initial value for 'XORi'
             *and searching true results
             */
            for(j=0; j<=sn-1; j++)
            {
                xori.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
                tx.ptr.p_double[j] = boundval(xori.ptr.p_double[j], db.ptr.p_double[j], ub.ptr.p_double[j], _state);
            }
            minqpsetorigin(&state, &xori, _state);
            
            /*
             *initialization for starting point
             */
            for(j=0; j<=sn-1; j++)
            {
                stx.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
            }
            minqpsetstartingpoint(&state, &stx, _state);
            
            /*
             *optimize and get result
             */
            minqpoptimize(&state, _state);
            minqpresults(&state, &x, &rep, _state);
            for(j=0; j<=sn-1; j++)
            {
                if( ae_fp_greater(ae_fabs(tx.ptr.p_double[j]-x.ptr.p_double[j], _state),eps)||(ae_fp_less(x.ptr.p_double[j],db.ptr.p_double[j])||ae_fp_greater(x.ptr.p_double[j],ub.ptr.p_double[j])) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', 'MinQPSetQuadraticTerm',
'MinQPSetOrigin', 'MinQPSetStartingPoint', 'MinQPOptimize', 'MinQPResults'.

Test problem:
    A = positive-definite matrix, obtained by 'SPDMatrixRndCond' function
    b <> 0
    without bounds
    random start point
    dimension - from 1 to 5.
*************************************************************************/
ae_bool functest1(ae_state *_state)
{
    ae_frame _frame_block;
    minqpstate state;
    ae_int_t nexp;
    ae_int_t msn;
    ae_int_t sn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix a;
    ae_vector ub;
    ae_vector db;
    ae_vector x;
    ae_vector tx;
    ae_vector stx;
    ae_vector xori;
    ae_vector xoric;
    minqpreport rep;
    double eps;
    ae_vector b;
    ae_int_t c2;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&a, 0, sizeof(a));
    memset(&ub, 0, sizeof(ub));
    memset(&db, 0, sizeof(db));
    memset(&x, 0, sizeof(x));
    memset(&tx, 0, sizeof(tx));
    memset(&stx, 0, sizeof(stx));
    memset(&xori, 0, sizeof(xori));
    memset(&xoric, 0, sizeof(xoric));
    memset(&rep, 0, sizeof(rep));
    memset(&b, 0, sizeof(b));
    _minqpstate_init(&state, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ub, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&db, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&stx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xori, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xoric, 0, DT_REAL, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);

    eps = 0.001;
    msn = 5;
    c2 = 1000;
    nexp = 1000;
    for(sn=1; sn<=msn; sn++)
    {
        ae_vector_set_length(&b, sn, _state);
        ae_vector_set_length(&tx, sn, _state);
        ae_vector_set_length(&xori, sn, _state);
        ae_vector_set_length(&xoric, sn, _state);
        ae_vector_set_length(&stx, sn, _state);
        for(i=0; i<=nexp; i++)
        {
            
            /*
             *create simmetric matrix 'A'
             */
            spdmatrixrndcond(sn, ae_exp(ae_randomreal(_state)*ae_log((double)(c2), _state), _state), &a, _state);
            minqpcreate(sn, &state, _state);
            testminqpunit_setrandomalgobc(&state, _state);
            minqpsetquadraticterm(&state, &a, ae_false, _state);
            for(j=0; j<=sn-1; j++)
            {
                xoric.ptr.p_double[j] = 2*ae_randomreal(_state)-1;
            }
            
            /*
             *create linear part
             */
            for(j=0; j<=sn-1; j++)
            {
                b.ptr.p_double[j] = (double)(0);
                for(k=0; k<=sn-1; k++)
                {
                    b.ptr.p_double[j] = b.ptr.p_double[j]-xoric.ptr.p_double[k]*a.ptr.pp_double[k][j];
                }
            }
            minqpsetlinearterm(&state, &b, _state);
            
            /*
             *initialization for shifting
             *initial value for 'XORi'
             *and searching true results
             */
            for(j=0; j<=sn-1; j++)
            {
                xori.ptr.p_double[j] = 2*ae_randomreal(_state)-1;
                tx.ptr.p_double[j] = xori.ptr.p_double[j]+xoric.ptr.p_double[j];
            }
            minqpsetorigin(&state, &xori, _state);
            
            /*
             *initialization for starting point
             */
            for(j=0; j<=sn-1; j++)
            {
                stx.ptr.p_double[j] = 2*ae_randomreal(_state)-1;
            }
            minqpsetstartingpoint(&state, &stx, _state);
            
            /*
             *optimize and get result
             */
            minqpoptimize(&state, _state);
            minqpresults(&state, &x, &rep, _state);
            for(j=0; j<=sn-1; j++)
            {
                if( ae_fp_greater(ae_fabs(tx.ptr.p_double[j]-x.ptr.p_double[j], _state),eps) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', 'MinQPSetQuadraticTerm',
'MinQPSetBC', 'MinQPSetOrigin', 'MinQPSetStartingPoint', 'MinQPOptimize', 
'MinQPResults'.

Test problem:
    A = positive-definite matrix, obtained by 'SPDMatrixRndCond' function
    b <> 0
    boundary constraints
    random start point
    dimension - from 1 to 5.
*************************************************************************/
ae_bool functest2(ae_state *_state)
{
    ae_frame _frame_block;
    minqpstate state;
    ae_int_t nexp;
    ae_int_t msn;
    ae_int_t sn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix a;
    ae_vector ub;
    ae_vector db;
    ae_vector x;
    ae_vector tmpx;
    double maxstb;
    ae_vector stx;
    ae_vector xori;
    ae_vector xoric;
    ae_int_t infd;
    minqpreport rep;
    double maxnb;
    double minnb;
    double eps;
    ae_vector b;
    ae_vector g;
    ae_vector c;
    ae_vector y0;
    ae_vector y1;
    ae_int_t c2;
    double anti;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&a, 0, sizeof(a));
    memset(&ub, 0, sizeof(ub));
    memset(&db, 0, sizeof(db));
    memset(&x, 0, sizeof(x));
    memset(&tmpx, 0, sizeof(tmpx));
    memset(&stx, 0, sizeof(stx));
    memset(&xori, 0, sizeof(xori));
    memset(&xoric, 0, sizeof(xoric));
    memset(&rep, 0, sizeof(rep));
    memset(&b, 0, sizeof(b));
    memset(&g, 0, sizeof(g));
    memset(&c, 0, sizeof(c));
    memset(&y0, 0, sizeof(y0));
    memset(&y1, 0, sizeof(y1));
    _minqpstate_init(&state, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ub, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&db, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&stx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xori, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xoric, 0, DT_REAL, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);

    eps = 0.001;
    msn = 5;
    c2 = 1000;
    maxstb = (double)(10);
    nexp = 1000;
    maxnb = (double)(1000);
    minnb = (double)(-1000);
    for(sn=1; sn<=msn; sn++)
    {
        ae_vector_set_length(&tmpx, sn, _state);
        ae_vector_set_length(&b, sn, _state);
        ae_vector_set_length(&c, sn, _state);
        ae_vector_set_length(&g, sn, _state);
        ae_vector_set_length(&xori, sn, _state);
        ae_vector_set_length(&xoric, sn, _state);
        ae_vector_set_length(&stx, sn, _state);
        ae_vector_set_length(&db, sn, _state);
        ae_vector_set_length(&ub, sn, _state);
        ae_vector_set_length(&y0, sn, _state);
        ae_vector_set_length(&y1, sn, _state);
        for(i=0; i<=nexp; i++)
        {
            
            /*
             *create simmetric matrix 'A'
             */
            spdmatrixrndcond(sn, ae_exp(ae_randomreal(_state)*ae_log((double)(c2), _state), _state), &a, _state);
            minqpcreate(sn, &state, _state);
            testminqpunit_setrandomalgobc(&state, _state);
            minqpsetquadraticterm(&state, &a, ae_false, _state);
            for(j=0; j<=sn-1; j++)
            {
                xoric.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
            }
            
            /*
             *create linear part
             */
            for(j=0; j<=sn-1; j++)
            {
                b.ptr.p_double[j] = (double)(0);
                for(k=0; k<=sn-1; k++)
                {
                    b.ptr.p_double[j] = b.ptr.p_double[j]-xoric.ptr.p_double[k]*a.ptr.pp_double[k][j];
                }
            }
            minqpsetlinearterm(&state, &b, _state);
            for(j=0; j<=sn-1; j++)
            {
                infd = ae_randominteger(4, _state);
                if( infd==0 )
                {
                    db.ptr.p_double[j] = _state->v_neginf;
                    ub.ptr.p_double[j] = _state->v_posinf;
                }
                else
                {
                    if( infd==1 )
                    {
                        db.ptr.p_double[j] = _state->v_neginf;
                        ub.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
                    }
                    else
                    {
                        if( infd==2 )
                        {
                            db.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
                            ub.ptr.p_double[j] = _state->v_posinf;
                        }
                        else
                        {
                            db.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
                            ub.ptr.p_double[j] = db.ptr.p_double[j]+maxstb*ae_randomreal(_state)+0.01;
                        }
                    }
                }
            }
            minqpsetbc(&state, &db, &ub, _state);
            
            /*
             *initialization for shifting
             *initial value for 'XORi'
             *and searching true results
             */
            for(j=0; j<=sn-1; j++)
            {
                xori.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
            }
            minqpsetorigin(&state, &xori, _state);
            for(j=0; j<=sn-1; j++)
            {
                c.ptr.p_double[j] = (double)(0);
                for(k=0; k<=sn-1; k++)
                {
                    c.ptr.p_double[j] = c.ptr.p_double[j]-xori.ptr.p_double[k]*a.ptr.pp_double[k][j];
                }
            }
            
            /*
             *initialization for starting point
             */
            for(j=0; j<=sn-1; j++)
            {
                stx.ptr.p_double[j] = (maxnb-minnb)*ae_randomreal(_state)+minnb;
            }
            minqpsetstartingpoint(&state, &stx, _state);
            
            /*
             *optimize and get result
             */
            minqpoptimize(&state, _state);
            minqpresults(&state, &x, &rep, _state);
            rmatrixmv(sn, sn, &a, 0, 0, 0, &x, 0, &y0, 0, _state);
            for(j=0; j<=sn-1; j++)
            {
                g.ptr.p_double[j] = y0.ptr.p_double[j]+c.ptr.p_double[j]+b.ptr.p_double[j];
            }
            anti = testminqpunit_projectedantigradnorm(sn, &x, &g, &db, &ub, _state);
            for(j=0; j<=sn-1; j++)
            {
                if( ae_fp_greater(ae_fabs(anti, _state),eps) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
ConsoleTest.
*************************************************************************/
ae_bool consoletest(ae_state *_state)
{
    ae_frame _frame_block;
    minqpstate state;
    ae_int_t nexp;
    ae_int_t msn;
    ae_int_t sn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix a;
    ae_vector ub;
    ae_vector db;
    ae_vector x;
    ae_vector stx;
    ae_vector xori;
    ae_vector xoric;
    minqpreport rep;
    double eps;
    ae_vector b;
    ae_vector g;
    ae_vector y0;
    ae_vector y1;
    double c;
    double anti;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&a, 0, sizeof(a));
    memset(&ub, 0, sizeof(ub));
    memset(&db, 0, sizeof(db));
    memset(&x, 0, sizeof(x));
    memset(&stx, 0, sizeof(stx));
    memset(&xori, 0, sizeof(xori));
    memset(&xoric, 0, sizeof(xoric));
    memset(&rep, 0, sizeof(rep));
    memset(&b, 0, sizeof(b));
    memset(&g, 0, sizeof(g));
    memset(&y0, 0, sizeof(y0));
    memset(&y1, 0, sizeof(y1));
    _minqpstate_init(&state, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ub, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&db, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&stx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xori, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xoric, 0, DT_REAL, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);

    eps = 0.001;
    msn = 2;
    nexp = 0;
    for(sn=2; sn<=msn; sn++)
    {
        ae_vector_set_length(&b, sn, _state);
        ae_vector_set_length(&g, sn, _state);
        ae_vector_set_length(&xori, sn, _state);
        ae_vector_set_length(&xoric, sn, _state);
        ae_vector_set_length(&stx, sn, _state);
        ae_vector_set_length(&db, sn, _state);
        ae_vector_set_length(&ub, sn, _state);
        ae_vector_set_length(&y0, sn, _state);
        ae_vector_set_length(&y1, sn, _state);
        for(i=0; i<=nexp; i++)
        {
            
            /*
             *create simmetric matrix 'A'
             */
            ae_matrix_set_length(&a, sn, sn, _state);
            for(j=0; j<=sn-1; j++)
            {
                for(k=0; k<=sn-1; k++)
                {
                    if( j==k )
                    {
                        a.ptr.pp_double[j][k] = (double)(1);
                    }
                    else
                    {
                        a.ptr.pp_double[j][k] = (double)(0);
                    }
                    printf("%0.5f ",
                        (double)(a.ptr.pp_double[j][k]));
                }
                printf("\n");
            }
            minqpcreate(sn, &state, _state);
            testminqpunit_setrandomalgobc(&state, _state);
            minqpsetquadraticterm(&state, &a, ae_false, _state);
            for(j=0; j<=sn-1; j++)
            {
                xoric.ptr.p_double[j] = (double)(1);
                printf("XoriC=%0.5f \n",
                    (double)(xoric.ptr.p_double[j]));
            }
            
            /*
             *create linear part
             */
            for(j=0; j<=sn-1; j++)
            {
                b.ptr.p_double[j] = (double)(0);
                for(k=0; k<=sn-1; k++)
                {
                    b.ptr.p_double[j] = b.ptr.p_double[j]-xoric.ptr.p_double[k]*a.ptr.pp_double[k][j];
                }
                printf("B[%0d]=%0.5f\n",
                    (int)(j),
                    (double)(b.ptr.p_double[j]));
            }
            minqpsetlinearterm(&state, &b, _state);
            for(j=0; j<=sn-1; j++)
            {
                db.ptr.p_double[j] = (double)(10);
                ub.ptr.p_double[j] = (double)(20);
            }
            minqpsetbc(&state, &db, &ub, _state);
            
            /*
             *initialization for shifting
             *initial value for 'XORi'
             *and searching true results
             */
            for(j=0; j<=sn-1; j++)
            {
                xori.ptr.p_double[j] = (double)(1);
            }
            minqpsetorigin(&state, &xori, _state);
            
            /*
             *optimize and get result
             */
            minqpoptimize(&state, _state);
            minqpresults(&state, &x, &rep, _state);
            rmatrixmv(sn, sn, &a, 0, 0, 0, &x, 0, &y0, 0, _state);
            rmatrixmv(sn, sn, &a, 0, 0, 0, &x, 0, &y1, 0, _state);
            for(j=0; j<=sn-1; j++)
            {
                c = (double)(0);
                for(k=0; k<=sn-1; k++)
                {
                    c = c-xori.ptr.p_double[k]*a.ptr.pp_double[k][j];
                }
                g.ptr.p_double[j] = b.ptr.p_double[j]+c+y0.ptr.p_double[j]+y1.ptr.p_double[j];
            }
            anti = testminqpunit_projectedantigradnorm(sn, &x, &b, &db, &ub, _state);
            printf("SN=%0d\n",
                (int)(sn));
            printf("NEXP=%0d\n",
                (int)(i));
            printf("TermType=%0d\n",
                (int)(rep.terminationtype));
            for(j=0; j<=sn-1; j++)
            {
                printf("X[%0d]=%0.5f;\n",
                    (int)(j),
                    (double)(x.ptr.p_double[j]));
                printf("DB[%0d]=%0.5f; UB[%0d]=%0.5f\n",
                    (int)(j),
                    (double)(db.ptr.p_double[j]),
                    (int)(j),
                    (double)(ub.ptr.p_double[j]));
                printf("XORi[%0d]=%0.5f; XORiC[%0d]=%0.5f;\n",
                    (int)(j),
                    (double)(xori.ptr.p_double[j]),
                    (int)(j),
                    (double)(xoric.ptr.p_double[j]));
                printf("Anti[%0d]=%0.5f;\n",
                    (int)(j),
                    (double)(anti));
                if( ae_fp_greater(ae_fabs(anti, _state),eps) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function performs tests specific for QuickQP solver
    
Returns True on failure.
*************************************************************************/
ae_bool quickqptests(ae_state *_state)
{
    ae_frame _frame_block;
    minqpstate state;
    minqpreport rep;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double g;
    double gnorm;
    ae_bool flag;
    ae_int_t origintype;
    ae_int_t scaletype;
    ae_bool isupper;
    ae_bool issparse;
    ae_int_t itscnt;
    ae_vector nlist;
    ae_int_t nidx;
    ae_matrix a;
    ae_matrix za;
    ae_matrix fulla;
    ae_matrix halfa;
    ae_matrix c;
    sparsematrix sa;
    ae_vector ct;
    ae_vector b;
    ae_vector zb;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector x0;
    ae_vector x1;
    ae_vector xend0;
    ae_vector xend1;
    ae_vector xori;
    ae_vector xz;
    ae_vector s;
    double eps;
    hqrndstate rs;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&nlist, 0, sizeof(nlist));
    memset(&a, 0, sizeof(a));
    memset(&za, 0, sizeof(za));
    memset(&fulla, 0, sizeof(fulla));
    memset(&halfa, 0, sizeof(halfa));
    memset(&c, 0, sizeof(c));
    memset(&sa, 0, sizeof(sa));
    memset(&ct, 0, sizeof(ct));
    memset(&b, 0, sizeof(b));
    memset(&zb, 0, sizeof(zb));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&xend0, 0, sizeof(xend0));
    memset(&xend1, 0, sizeof(xend1));
    memset(&xori, 0, sizeof(xori));
    memset(&xz, 0, sizeof(xz));
    memset(&s, 0, sizeof(s));
    memset(&rs, 0, sizeof(rs));
    _minqpstate_init(&state, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    ae_vector_init(&nlist, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&za, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&halfa, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&zb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xend0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xend1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xori, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xz, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    result = ae_false;
    hqrndrandomize(&rs, _state);
    
    /*
     * Convex unconstrained test:
     * * N dimensions
     * * positive-definite A
     * * algorithm randomly choose dense or sparse A, and for
     *   sparse matrix it randomly choose format.
     * * random B with normal entries
     * * initial point is random, feasible
     * * random origin (zero or non-zero) and scale (unit or
     *   non-unit) are generated
     */
    eps = 1.0E-5;
    for(n=1; n<=10; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            origintype = hqrnduniformi(&rs, 2, _state);
            scaletype = hqrnduniformi(&rs, 2, _state);
            isupper = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            issparse = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            spdmatrixrndcond(n, 1.0E3, &fulla, _state);
            ae_matrix_set_length(&halfa, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j>=i&&isupper)||(j<=i&&!isupper) )
                    {
                        halfa.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j];
                    }
                    else
                    {
                        halfa.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    }
                }
            }
            testminqpunit_densetosparse(&halfa, n, &sa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xori, n, _state);
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                if( origintype==0 )
                {
                    xori.ptr.p_double[i] = (double)(0);
                }
                else
                {
                    xori.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                if( scaletype==0 )
                {
                    s.ptr.p_double[i] = (double)(1);
                }
                else
                {
                    s.ptr.p_double[i] = ae_exp(hqrndnormal(&rs, _state), _state);
                }
            }
            
            /*
             * Solve problem
             */
            minqpcreate(n, &state, _state);
            minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, 0, ae_fp_greater(hqrnduniformr(&rs, _state),0.5), _state);
            minqpsetlinearterm(&state, &b, _state);
            if( issparse )
            {
                minqpsetquadratictermsparse(&state, &sa, isupper, _state);
            }
            else
            {
                minqpsetquadraticterm(&state, &halfa, isupper, _state);
            }
            if( origintype!=0 )
            {
                minqpsetorigin(&state, &xori, _state);
            }
            if( scaletype!=0 )
            {
                minqpsetscale(&state, &s, _state);
            }
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:5111");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            gnorm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                g = b.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    g = g+fulla.ptr.pp_double[i][j]*(x1.ptr.p_double[j]-xori.ptr.p_double[j]);
                }
                gnorm = gnorm+ae_sqr(g, _state);
            }
            gnorm = ae_sqrt(gnorm, _state);
            ae_set_error_flag(&result, ae_fp_greater(gnorm,eps), __FILE__, __LINE__, "testminqpunit.ap:5129");
        }
    }
    
    /*
     * Convex test:
     * * N dimensions
     * * random number (0..N) of random boundary constraints
     * * positive-definite A
     * * algorithm randomly choose dense or sparse A, and for
     *   sparse matrix it randomly choose format.
     * * random B with normal entries
     * * initial point is random, feasible
     * * random origin (zero or non-zero) and scale (unit or
     *   non-unit) are generated
     */
    eps = 1.0E-5;
    for(n=1; n<=10; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            origintype = hqrnduniformi(&rs, 2, _state);
            scaletype = hqrnduniformi(&rs, 2, _state);
            isupper = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            issparse = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            spdmatrixrndcond(n, 1.0E3, &fulla, _state);
            ae_matrix_set_length(&halfa, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j>=i&&isupper)||(j<=i&&!isupper) )
                    {
                        halfa.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j];
                    }
                    else
                    {
                        halfa.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    }
                }
            }
            testminqpunit_densetosparse(&halfa, n, &sa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xori, n, _state);
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bndl.ptr.p_double[i] = _state->v_neginf;
                bndu.ptr.p_double[i] = _state->v_posinf;
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                if( origintype==0 )
                {
                    xori.ptr.p_double[i] = (double)(0);
                }
                else
                {
                    xori.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                if( scaletype==0 )
                {
                    s.ptr.p_double[i] = (double)(1);
                }
                else
                {
                    s.ptr.p_double[i] = ae_exp(hqrndnormal(&rs, _state), _state);
                }
                j = hqrnduniformi(&rs, 5, _state);
                if( j==0 )
                {
                    bndl.ptr.p_double[i] = (double)(0);
                    x0.ptr.p_double[i] = ae_fabs(x0.ptr.p_double[i], _state);
                }
                if( j==1 )
                {
                    bndu.ptr.p_double[i] = (double)(0);
                    x0.ptr.p_double[i] = -ae_fabs(x0.ptr.p_double[i], _state);
                }
                if( j==2 )
                {
                    bndl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    bndu.ptr.p_double[i] = bndl.ptr.p_double[i];
                    x0.ptr.p_double[i] = bndl.ptr.p_double[i];
                }
                if( j==3 )
                {
                    bndl.ptr.p_double[i] = -0.1;
                    bndu.ptr.p_double[i] = 0.1;
                    x0.ptr.p_double[i] = 0.2*hqrnduniformr(&rs, _state)-0.1;
                }
            }
            
            /*
             * Solve problem
             */
            minqpcreate(n, &state, _state);
            minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, 0, ae_fp_greater(hqrnduniformr(&rs, _state),0.5), _state);
            minqpsetlinearterm(&state, &b, _state);
            if( issparse )
            {
                minqpsetquadratictermsparse(&state, &sa, isupper, _state);
            }
            else
            {
                minqpsetquadraticterm(&state, &halfa, isupper, _state);
            }
            if( origintype!=0 )
            {
                minqpsetorigin(&state, &xori, _state);
            }
            if( scaletype!=0 )
            {
                minqpsetscale(&state, &s, _state);
            }
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:5226");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            gnorm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                g = b.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    g = g+fulla.ptr.pp_double[i][j]*(x1.ptr.p_double[j]-xori.ptr.p_double[j]);
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i])&&ae_fp_greater(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i])&&ae_fp_less(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                gnorm = gnorm+ae_sqr(g, _state);
                ae_set_error_flag(&result, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5246");
                ae_set_error_flag(&result, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5247");
            }
            gnorm = ae_sqrt(gnorm, _state);
            ae_set_error_flag(&result, ae_fp_greater(gnorm,eps), __FILE__, __LINE__, "testminqpunit.ap:5250");
        }
    }
    
    /*
     * Strongly non-convex test:
     * * N dimensions, N>=2
     * * box constraints, x[i] in [-1,+1]
     * * A = A0-0.5*I, where A0 is SPD with unit norm and smallest
     *   singular value equal to 1.0E-3, I is identity matrix
     * * random B with normal entries
     * * initial point is random, feasible
     *
     * We perform two tests:
     * * unconstrained problem must be recognized as unbounded
     * * constrained problem can be successfully solved
     *
     * NOTE: it is important to have N>=2, because formula for A
     *       can be applied only to matrix with at least two
     *       singular values
     */
    eps = 1.0E-5;
    for(n=2; n<=10; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            spdmatrixrndcond(n, 1.0E3, &fulla, _state);
            for(i=0; i<=n-1; i++)
            {
                fulla.ptr.pp_double[i][i] = fulla.ptr.pp_double[i][i]-0.5;
            }
            isupper = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            ae_matrix_set_length(&halfa, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j>=i&&isupper)||(j<=i&&!isupper) )
                    {
                        halfa.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j];
                    }
                    else
                    {
                        halfa.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    }
                }
            }
            testminqpunit_densetosparse(&halfa, n, &sa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bndl.ptr.p_double[i] = (double)(-1);
                bndu.ptr.p_double[i] = (double)(1);
                x0.ptr.p_double[i] = 2*hqrnduniformr(&rs, _state)-1;
            }
            
            /*
             * Solve problem:
             * * without constraints we expect failure
             * * with constraints algorithm must succeed
             */
            minqpcreate(n, &state, _state);
            minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, 0, ae_fp_greater(hqrnduniformr(&rs, _state),0.5), _state);
            minqpsetlinearterm(&state, &b, _state);
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                minqpsetquadraticterm(&state, &halfa, isupper, _state);
            }
            else
            {
                minqpsetquadratictermsparse(&state, &sa, isupper, _state);
            }
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype!=-4, __FILE__, __LINE__, "testminqpunit.ap:5315");
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:5319");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            gnorm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&fulla.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                g = v+b.ptr.p_double[i];
                if( ae_fp_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i])&&ae_fp_greater(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i])&&ae_fp_less(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                gnorm = gnorm+ae_sqr(g, _state);
                ae_set_error_flag(&result, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5338");
                ae_set_error_flag(&result, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5339");
            }
            gnorm = ae_sqrt(gnorm, _state);
            ae_set_error_flag(&result, ae_fp_greater(gnorm,eps), __FILE__, __LINE__, "testminqpunit.ap:5342");
        }
    }
    
    /*
     * Basic semi-definite test:
     * * N dimensions, N>=2
     * * box constraints, x[i] in [-1,+1]
     *       [ 1 1 ... 1 1 ]
     * * A = [ ... ... ... ], with one (random) diagonal entry set to -1
     *       [ 1 1 ... 1 1 ]
     * * random B with normal entries
     * * initial point is random, feasible
     *
     * We perform two tests:
     * * unconstrained problem must be recognized as unbounded
     * * constrained problem must be recognized as bounded and
     *   successfully solved
     *
     * Both problems require subtle programming when we work
     * with semidefinite QP.
     *
     * NOTE: unlike BLEIC-QP algorthm, QQP may detect unboundedness
     *       of the problem when started from any x0, with any b.
     *       BLEIC-based solver requires carefully chosen x0 and b
     *       to find direction of zero curvature, but this solver
     *       can find it from any point.
     */
    ae_vector_set_length(&nlist, 12, _state);
    nlist.ptr.p_int[0] = 2;
    nlist.ptr.p_int[1] = 3;
    nlist.ptr.p_int[2] = 4;
    nlist.ptr.p_int[3] = 5;
    nlist.ptr.p_int[4] = 6;
    nlist.ptr.p_int[5] = 7;
    nlist.ptr.p_int[6] = 8;
    nlist.ptr.p_int[7] = 9;
    nlist.ptr.p_int[8] = 10;
    nlist.ptr.p_int[9] = 20;
    nlist.ptr.p_int[10] = 40;
    nlist.ptr.p_int[11] = 80;
    eps = 1.0E-5;
    for(nidx=0; nidx<=nlist.cnt-1; nidx++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            n = nlist.ptr.p_int[nidx];
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                do
                {
                    b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                while(ae_fp_eq(b.ptr.p_double[i],(double)(0)));
                bndl.ptr.p_double[i] = (double)(-1);
                bndu.ptr.p_double[i] = (double)(1);
                x0.ptr.p_double[i] = 2*hqrnduniformr(&rs, _state)-1;
            }
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 1.0;
                }
            }
            j = hqrnduniformi(&rs, n, _state);
            a.ptr.pp_double[j][j] = -1.0;
            testminqpunit_densetosparse(&a, n, &sa, _state);
            
            /*
             * Solve problem:
             * * without constraints we expect failure
             * * with constraints algorithm must succeed
             */
            minqpcreate(n, &state, _state);
            minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, 0, ae_fp_greater(hqrnduniformr(&rs, _state),0.5), _state);
            minqpsetlinearterm(&state, &b, _state);
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                minqpsetquadraticterm(&state, &a, ae_true, _state);
            }
            else
            {
                minqpsetquadratictermsparse(&state, &sa, ae_true, _state);
            }
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype!=-4, __FILE__, __LINE__, "testminqpunit.ap:5425");
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:5429");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            gnorm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                g = b.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    g = g+a.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i])&&ae_fp_greater(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i])&&ae_fp_less(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                gnorm = gnorm+ae_sqr(g, _state);
                ae_set_error_flag(&result, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5450");
                ae_set_error_flag(&result, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5451");
            }
            gnorm = ae_sqrt(gnorm, _state);
            ae_set_error_flag(&result, ae_fp_greater(gnorm,eps), __FILE__, __LINE__, "testminqpunit.ap:5454");
        }
    }
    
    /*
     * Linear (zero-quadratic) test:
     * * N dimensions, N>=1
     * * box constraints, x[i] in [-1,+1]
     * * A = 0
     * * random B with normal entries
     * * initial point is random, feasible
     *
     * We perform two tests:
     * * unconstrained problem must be recognized as unbounded
     * * constrained problem can be successfully solved
     *
     * NOTE: we may explicitly set zero A, or assume that by
     *       default it is zero. During test we will try both
     *       ways.
     */
    eps = 1.0E-5;
    for(n=1; n<=10; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                do
                {
                    b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                while(ae_fp_eq(b.ptr.p_double[i],(double)(0)));
                bndl.ptr.p_double[i] = (double)(-1);
                bndu.ptr.p_double[i] = (double)(1);
                x0.ptr.p_double[i] = 2*hqrnduniformr(&rs, _state)-1;
            }
            
            /*
             * Solve problem:
             * * without constraints we expect failure
             * * with constraints algorithm must succeed
             */
            minqpcreate(n, &state, _state);
            minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, 0, ae_fp_greater(hqrnduniformr(&rs, _state),0.5), _state);
            minqpsetlinearterm(&state, &b, _state);
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                ae_matrix_set_length(&a, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = (double)(0);
                    }
                }
                minqpsetquadraticterm(&state, &a, ae_true, _state);
            }
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype!=-4, __FILE__, __LINE__, "testminqpunit.ap:5512");
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:5516");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(&result, ae_fp_greater(b.ptr.p_double[i],(double)(0))&&ae_fp_greater(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5526");
                ae_set_error_flag(&result, ae_fp_less(b.ptr.p_double[i],(double)(0))&&ae_fp_less(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5527");
            }
        }
    }
    
    /*
     * Test for Newton phase of QQP algorithm - we test that Newton
     * phase can find good solution within one step. In order to do
     * so we:
     * * solve convex QP problem (dense or sparse)
     * * with K<=N equality-only constraints ai=x=bi
     * * with number of outer iterations limited to just 1
     * * and with CG phase turned off (we modify internal structures
     *   of the QQP solver in order to make it)
     */
    eps = 1.0E-5;
    for(pass=1; pass<=10; pass++)
    {
        
        /*
         * Generate problem
         */
        n = 50+hqrnduniformi(&rs, 51, _state);
        spdmatrixrndcond(n, 1.0E3, &a, _state);
        testminqpunit_densetosparse(&a, n, &sa, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = hqrndnormal(&rs, _state);
            x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                bndl.ptr.p_double[i] = _state->v_neginf;
                bndu.ptr.p_double[i] = _state->v_posinf;
            }
            else
            {
                bndl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bndu.ptr.p_double[i] = bndl.ptr.p_double[i];
            }
        }
        
        /*
         * Solve problem
         *
         * NOTE: we modify internal structures of QQP solver in order
         *       to deactivate CG phase
         */
        minqpcreate(n, &state, _state);
        minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, 1, ae_true, _state);
        state.qqpsettingsuser.cgphase = ae_false;
        minqpsetlinearterm(&state, &b, _state);
        if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
        {
            minqpsetquadraticterm(&state, &a, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), _state);
        }
        else
        {
            minqpsetquadratictermsparse(&state, &sa, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), _state);
        }
        minqpsetbc(&state, &bndl, &bndu, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &x1, &rep, _state);
        ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:5587");
        if( rep.terminationtype<=0 )
        {
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Test - calculate constrained gradient at solution,
         * check its norm.
         */
        gnorm = 0.0;
        for(i=0; i<=n-1; i++)
        {
            g = b.ptr.p_double[i];
            for(j=0; j<=n-1; j++)
            {
                g = g+a.ptr.pp_double[i][j]*x1.ptr.p_double[j];
            }
            if( ae_fp_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i])&&ae_fp_greater(g,(double)(0)) )
            {
                g = (double)(0);
            }
            if( ae_fp_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i])&&ae_fp_less(g,(double)(0)) )
            {
                g = (double)(0);
            }
            gnorm = gnorm+ae_sqr(g, _state);
            ae_set_error_flag(&result, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5607");
            ae_set_error_flag(&result, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:5608");
        }
        gnorm = ae_sqrt(gnorm, _state);
        ae_set_error_flag(&result, ae_fp_greater(gnorm,eps), __FILE__, __LINE__, "testminqpunit.ap:5611");
    }
    
    /*
     * Test for Newton phase of QQP algorithm - we test that Newton
     * updates work correctly, i.e. that CNewtonUpdate() internal
     * function correctly updates inverse Hessian matrix.
     *
     * To test it we:
     * * solve ill conditioned convex QP problem
     * * with unconstrained solution XZ whose components are within [-0.5,+0.5]
     * * with one inequality constraint X[k]>=5
     * * with initial point such that:
     *   * X0[i] = 100       for i<>k
     *   * X0[k] = 5+1.0E-5
     * * with number of outer iterations limited to just 1
     * * and with CG phase turned off (we modify internal structures
     *   of the QQP solver in order to make it)
     *
     * The idea is that single Newton step is not enough to find solution,
     * but with just one update we can move exactly to the solution.
     *
     * We perform two tests:
     * * first one with State.QQP.NewtMaxIts set to 1, in order to
     *   make sure that algorithm fails with just one iteration
     * * second one with State.QQP.NewtMaxIts set to 2, in order to
     *   make sure that algorithm converges when it can perform update
     */
    eps = 1.0E-5;
    for(pass=1; pass<=10; pass++)
    {
        
        /*
         * Generate problem
         */
        n = 20+hqrnduniformi(&rs, 20, _state);
        spdmatrixrndcond(n, 1.0E5, &a, _state);
        testminqpunit_densetosparse(&a, n, &sa, _state);
        sparseconverttocrs(&sa, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&xz, n, _state);
        for(i=0; i<=n-1; i++)
        {
            xz.ptr.p_double[i] = hqrnduniformr(&rs, _state)-0.5;
            x0.ptr.p_double[i] = (double)(100);
            bndl.ptr.p_double[i] = _state->v_neginf;
            bndu.ptr.p_double[i] = _state->v_posinf;
        }
        k = hqrnduniformi(&rs, n, _state);
        x0.ptr.p_double[k] = 5.00001;
        bndl.ptr.p_double[k] = 5.0;
        sparsemv(&sa, &xz, &b, _state);
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = -b.ptr.p_double[i];
        }
        
        /*
         * Create solver
         */
        minqpcreate(n, &state, _state);
        minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, 1, ae_true, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetquadraticterm(&state, &a, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), _state);
        minqpsetbc(&state, &bndl, &bndu, _state);
        minqpsetstartingpoint(&state, &x0, _state);
        
        /*
         * Solve problem. First time, with no Newton updates.
         * It must fail.
         *
         * NOTE: we modify internal structures of QQP solver in order
         *       to deactivate CG phase and turn off Newton updates.
         */
        state.qqpsettingsuser.cgphase = ae_false;
        state.qqpsettingsuser.cnphase = ae_true;
        state.qqpsettingsuser.cnmaxupdates = 0;
        minqpoptimize(&state, _state);
        minqpresults(&state, &x1, &rep, _state);
        ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:5690");
        if( result )
        {
            ae_frame_leave(_state);
            return result;
        }
        flag = ae_false;
        testminqpunit_testbcgradandfeasibility(&a, &b, &bndl, &bndu, n, &x1, eps, &flag, _state);
        ae_set_error_flag(&result, !flag, __FILE__, __LINE__, "testminqpunit.ap:5695");
        
        /*
         * Now with Newton updates - it must succeeed.
         */
        state.qqpsettingsuser.cgphase = ae_false;
        state.qqpsettingsuser.cnmaxupdates = n;
        minqpoptimize(&state, _state);
        minqpresults(&state, &x1, &rep, _state);
        ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:5704");
        if( result )
        {
            ae_frame_leave(_state);
            return result;
        }
        flag = ae_false;
        testminqpunit_testbcgradandfeasibility(&a, &b, &bndl, &bndu, n, &x1, eps, &flag, _state);
        ae_set_error_flag(&result, flag, __FILE__, __LINE__, "testminqpunit.ap:5709");
    }
    
    /*
     * Check that problem with general constraints results in
     * correct error code (-5 should be returned).
     */
    ae_matrix_set_length(&c, 1, 3, _state);
    ae_vector_set_length(&ct, 1, _state);
    c.ptr.pp_double[0][0] = 1.0;
    c.ptr.pp_double[0][1] = 1.0;
    c.ptr.pp_double[0][2] = 2.0;
    ct.ptr.p_int[0] = 0;
    minqpcreate(2, &state, _state);
    minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, 0, ae_fp_greater(hqrnduniformr(&rs, _state),0.5), _state);
    minqpsetlc(&state, &c, &ct, 1, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &x1, &rep, _state);
    ae_set_error_flag(&result, rep.terminationtype!=-5, __FILE__, __LINE__, "testminqpunit.ap:5727");
    
    /*
     * Test sparse functionality. QQP solver must perform
     * same steps independently of matrix type (dense or sparse).
     *
     * We generate random unconstrained test problem and solve it
     * twice - first time we solve dense version, second time -
     * sparse version is solved.
     *
     * During this test we:
     * * use stringent stopping criteria (one outer iteration)
     * * turn off Newton phase of the algorithm to slow down
     *   convergence
     */
    eps = 1.0E-3;
    itscnt = 1;
    n = 20;
    isupper = ae_fp_greater(ae_randomreal(_state),0.5);
    spdmatrixrndcond(n, 1.0E3, &za, _state);
    sparsecreate(n, n, 0, &sa, _state);
    ae_matrix_set_length(&a, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( j>=i&&isupper )
            {
                sparseset(&sa, i, j, za.ptr.pp_double[i][j], _state);
                a.ptr.pp_double[i][j] = za.ptr.pp_double[i][j];
            }
            if( j<=i&&!isupper )
            {
                sparseset(&sa, i, j, za.ptr.pp_double[i][j], _state);
                a.ptr.pp_double[i][j] = za.ptr.pp_double[i][j];
            }
        }
    }
    ae_vector_set_length(&b, n, _state);
    ae_vector_set_length(&s, n, _state);
    for(i=0; i<=n-1; i++)
    {
        b.ptr.p_double[i] = randomnormal(_state);
        s.ptr.p_double[i] = ae_pow(10.0, randomnormal(_state)/10, _state);
    }
    minqpcreate(n, &state, _state);
    minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, itscnt, ae_false, _state);
    minqpsetscale(&state, &s, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadraticterm(&state, &a, isupper, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    minqpcreate(n, &state, _state);
    minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, itscnt, ae_false, _state);
    minqpsetscale(&state, &s, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadratictermsparse(&state, &sa, isupper, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend1, &rep, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(xend0.ptr.p_double[i]-xend1.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:5845");
    }
    
    /*
     * Test scale-invariance. QQP performs same steps on scaled and
     * unscaled problems (assuming that scale of the variables is known).
     *
     * We generate random scale matrix S and random well-conditioned and
     * well scaled matrix A. Then we solve two problems:
     *
     *     (1) f = 0.5*x'*A*x+b'*x
     *         (identity scale matrix is used)
     *
     * and
     *
     *     (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
     *         (scale matrix S is used)
     *
     * Solution process is started from X=0, we perform ItsCnt=1 outer
     * iterations with Newton phase turned off (to slow down convergence;
     * we want to prevent algorithm from converging to exact solution which
     * is exactly same for both problems; the idea is to test that same
     * intermediate tests are taken).
     *
     * As result, we must get S*x=y
     */
    eps = 1.0E-3;
    itscnt = 1;
    n = 100;
    ae_vector_set_length(&s, n, _state);
    for(i=0; i<=n-1; i++)
    {
        s.ptr.p_double[i] = ae_pow(10.0, randomnormal(_state)/10, _state);
    }
    spdmatrixrndcond(n, 1.0E3, &a, _state);
    ae_matrix_set_length(&za, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            za.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
        }
    }
    ae_vector_set_length(&b, n, _state);
    ae_vector_set_length(&zb, n, _state);
    for(i=0; i<=n-1; i++)
    {
        b.ptr.p_double[i] = randomnormal(_state);
        zb.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
    }
    minqpcreate(n, &state, _state);
    minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, itscnt, ae_false, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadraticterm(&state, &a, ae_true, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    minqpcreate(n, &state, _state);
    minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, itscnt, ae_false, _state);
    minqpsetlinearterm(&state, &zb, _state);
    minqpsetquadraticterm(&state, &za, ae_true, _state);
    minqpsetscale(&state, &s, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend1, &rep, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(s.ptr.p_double[i]*xend0.ptr.p_double[i]-xend1.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:5902");
    }
    
    /*
     * Test that QQP can efficiently use sparse matrices (i.e. it is
     * not disguised version of some dense QP solver). In order to test
     * it we create very large and very sparse problem (diagonal matrix
     * with N=40.000) and perform 10 iterations of QQP solver.
     *
     * In case QP solver uses some form of dense linear algebra to solve
     * this problem, it will take TOO much time to solve it. And we will
     * notice it by EXTREME slowdown during testing.
     */
    n = 40000;
    sparsecreate(n, n, 0, &sa, _state);
    for(i=0; i<=n-1; i++)
    {
        sparseset(&sa, i, i, ae_pow(10.0, -3*ae_randomreal(_state), _state), _state);
    }
    ae_vector_set_length(&b, n, _state);
    for(i=0; i<=n-1; i++)
    {
        b.ptr.p_double[i] = randomnormal(_state);
    }
    minqpcreate(n, &state, _state);
    minqpsetalgoquickqp(&state, 0.0, 0.0, 0.0, 10, ae_fp_greater(hqrnduniformr(&rs, _state),0.5), _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadratictermsparse(&state, &sa, ae_true, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function performs tests specific for BLEIC solver
    
Returns True on error, False on success.
*************************************************************************/
ae_bool bleictests(ae_state *_state)
{
    ae_frame _frame_block;
    minqpstate state;
    minqpreport rep;
    ae_vector nlist;
    ae_int_t nidx;
    ae_matrix a;
    ae_matrix za;
    ae_matrix c;
    ae_vector b;
    ae_vector zb;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector s;
    ae_vector x;
    ae_vector ct;
    sparsematrix sa;
    ae_int_t n;
    ae_vector x0;
    ae_vector x1;
    hqrndstate rs;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_vector xend0;
    ae_vector xend1;
    double eps;
    double v;
    double g;
    double gnorm;
    ae_int_t itscnt;
    ae_bool isupper;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&nlist, 0, sizeof(nlist));
    memset(&a, 0, sizeof(a));
    memset(&za, 0, sizeof(za));
    memset(&c, 0, sizeof(c));
    memset(&b, 0, sizeof(b));
    memset(&zb, 0, sizeof(zb));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&s, 0, sizeof(s));
    memset(&x, 0, sizeof(x));
    memset(&ct, 0, sizeof(ct));
    memset(&sa, 0, sizeof(sa));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&rs, 0, sizeof(rs));
    memset(&xend0, 0, sizeof(xend0));
    memset(&xend1, 0, sizeof(xend1));
    _minqpstate_init(&state, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    ae_vector_init(&nlist, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&za, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&zb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);
    ae_vector_init(&xend0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xend1, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    hqrndrandomize(&rs, _state);
    
    /*
     * Test sparse functionality. BLEIC-based solver must perform
     * same steps independently of matrix type (dense or sparse).
     *
     * We generate random unconstrained test problem and solve it
     * twice - first time we solve dense version, second time -
     * sparse version is solved.
     */
    eps = 1.0E-3;
    itscnt = 5;
    n = 20;
    isupper = ae_fp_greater(ae_randomreal(_state),0.5);
    spdmatrixrndcond(n, 1.0E3, &za, _state);
    sparsecreate(n, n, 0, &sa, _state);
    ae_matrix_set_length(&a, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( j>=i&&isupper )
            {
                sparseset(&sa, i, j, za.ptr.pp_double[i][j], _state);
                a.ptr.pp_double[i][j] = za.ptr.pp_double[i][j];
            }
            if( j<=i&&!isupper )
            {
                sparseset(&sa, i, j, za.ptr.pp_double[i][j], _state);
                a.ptr.pp_double[i][j] = za.ptr.pp_double[i][j];
            }
        }
    }
    ae_vector_set_length(&b, n, _state);
    ae_vector_set_length(&s, n, _state);
    for(i=0; i<=n-1; i++)
    {
        b.ptr.p_double[i] = randomnormal(_state);
        s.ptr.p_double[i] = ae_pow(10.0, randomnormal(_state)/10, _state);
    }
    minqpcreate(n, &state, _state);
    minqpsetalgobleic(&state, 0.0, 0.0, 0.0, itscnt, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadraticterm(&state, &a, isupper, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    minqpcreate(n, &state, _state);
    minqpsetalgobleic(&state, 0.0, 0.0, 0.0, itscnt, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadratictermsparse(&state, &sa, isupper, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend1, &rep, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(xend0.ptr.p_double[i]-xend1.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:6017");
    }
    
    /*
     * Test scale-invariance. BLEIC performs same steps on scaled and
     * unscaled problems (assuming that scale of the variables is known).
     *
     * We generate random scale matrix S and random well-conditioned and
     * well scaled matrix A. Then we solve two problems:
     *
     *     (1) f = 0.5*x'*A*x+b'*x
     *         (identity scale matrix is used)
     *
     * and
     *
     *     (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
     *         (scale matrix S is used)
     *
     * Solution process is started from X=0, we perform ItsCnt=5 steps.
     * As result, we must get S*x=y
     */
    eps = 1.0E-3;
    itscnt = 5;
    n = 20;
    ae_vector_set_length(&s, n, _state);
    for(i=0; i<=n-1; i++)
    {
        s.ptr.p_double[i] = ae_pow(10.0, randomnormal(_state)/10, _state);
    }
    spdmatrixrndcond(n, 1.0E3, &a, _state);
    ae_matrix_set_length(&za, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            za.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
        }
    }
    ae_vector_set_length(&b, n, _state);
    ae_vector_set_length(&zb, n, _state);
    for(i=0; i<=n-1; i++)
    {
        b.ptr.p_double[i] = randomnormal(_state);
        zb.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
    }
    minqpcreate(n, &state, _state);
    minqpsetalgobleic(&state, 0.0, 0.0, 0.0, itscnt, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadraticterm(&state, &a, ae_true, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    minqpcreate(n, &state, _state);
    minqpsetalgobleic(&state, 0.0, 0.0, 0.0, itscnt, _state);
    minqpsetlinearterm(&state, &zb, _state);
    minqpsetquadraticterm(&state, &za, ae_true, _state);
    minqpsetscale(&state, &s, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend1, &rep, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(s.ptr.p_double[i]*xend0.ptr.p_double[i]-xend1.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:6070");
    }
    
    /*
     * Test that BLEIC can efficiently use sparse matrices (i.e. it is
     * not disguised version of some dense QP solver). In order to test
     * it we create very large and very sparse problem (diagonal matrix
     * with N=20.000) and perform 10 iterations of BLEIC-based QP solver.
     *
     * In case QP solver uses some form of dense linear algebra to solve
     * this problem, it will take TOO much time to solve it. And we will
     * notice it by EXTREME slowdown during testing.
     */
    n = 20000;
    sparsecreate(n, n, 0, &sa, _state);
    for(i=0; i<=n-1; i++)
    {
        sparseset(&sa, i, i, ae_pow(10.0, -3*ae_randomreal(_state), _state), _state);
    }
    ae_vector_set_length(&b, n, _state);
    for(i=0; i<=n-1; i++)
    {
        b.ptr.p_double[i] = randomnormal(_state);
    }
    minqpcreate(n, &state, _state);
    minqpsetalgobleic(&state, 0.0, 0.0, 0.0, 10, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadratictermsparse(&state, &sa, ae_true, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    
    /*
     * Special semi-definite test:
     * * N dimensions, N>=2 (important!)
     * * box constraints, x[i] in [-1,+1]
     *       [ 1 1 ... 1 1 ]
     * * A = [ ... ... ... ]
     *       [ 1 1 ... 1 1 ]
     * * random B such that SUM(b[i])=0.0 (important!)
     * * initial point x0 is chosen in such way that SUM(x[i])=0.0
     *   (important!)
     *
     * We perform two tests:
     * * unconstrained problem must be recognized as unbounded
     *   (when starting from x0!)
     * * constrained problem must be recognized as bounded
     *   and successfully solved
     *
     * Both problems require subtle programming when we work
     * with semidefinite QP.
     *
     * NOTE: it is very important to have N>=2 (otherwise problem
     *       will be bounded from below even without boundary
     *       constraints) and to have x0/b0 such that sum of 
     *       components is zero (such x0 is exact minimum of x'*A*x,
     *       which allows algorithm to find direction of zero curvature
     *       at the very first step). If x0/b are chosen in other way,
     *       algorithm may be unable to find direction of zero
     *       curvature and will cycle forever, slowly decreasing
     *       function value at each iteration.
     *       This is major difference from similar test for QQP solver -
     *       QQP can find direction of zero curvature from almost any
     *       point due to internal CG solver which favors such directions.
     *       BLEIC uses LBFGS, which is less able to find direction of
     *       zero curvature.
     */
    ae_vector_set_length(&nlist, 12, _state);
    nlist.ptr.p_int[0] = 2;
    nlist.ptr.p_int[1] = 3;
    nlist.ptr.p_int[2] = 4;
    nlist.ptr.p_int[3] = 5;
    nlist.ptr.p_int[4] = 6;
    nlist.ptr.p_int[5] = 7;
    nlist.ptr.p_int[6] = 8;
    nlist.ptr.p_int[7] = 9;
    nlist.ptr.p_int[8] = 10;
    nlist.ptr.p_int[9] = 20;
    nlist.ptr.p_int[10] = 40;
    nlist.ptr.p_int[11] = 80;
    eps = 1.0E-5;
    for(nidx=0; nidx<=nlist.cnt-1; nidx++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            n = nlist.ptr.p_int[nidx];
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                do
                {
                    b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                while(ae_fp_eq(b.ptr.p_double[i],(double)(0)));
                bndl.ptr.p_double[i] = (double)(-1);
                bndu.ptr.p_double[i] = (double)(1);
                x0.ptr.p_double[i] = 2*hqrnduniformr(&rs, _state)-1;
            }
            v = 0.0;
            for(i=0; i<=n-1; i++)
            {
                v = v+x0.ptr.p_double[i];
            }
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = x0.ptr.p_double[i]-v/n;
            }
            v = 0.0;
            for(i=0; i<=n-1; i++)
            {
                v = v+b.ptr.p_double[i];
            }
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = b.ptr.p_double[i]-v/n;
            }
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 1.0;
                }
            }
            testminqpunit_densetosparse(&a, n, &sa, _state);
            
            /*
             * Solve problem:
             * * without constraints we expect failure
             * * with constraints algorithm must succeed
             */
            minqpcreate(n, &state, _state);
            minqpsetalgobleic(&state, 0.0, 0.0, 0.0, 0, _state);
            minqpsetlinearterm(&state, &b, _state);
            minqpsetstartingpoint(&state, &x0, _state);
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                minqpsetquadraticterm(&state, &a, ae_true, _state);
            }
            else
            {
                minqpsetquadratictermsparse(&state, &sa, ae_true, _state);
            }
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype!=-4, __FILE__, __LINE__, "testminqpunit.ap:6198");
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:6202");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            gnorm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                g = b.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    g = g+a.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i])&&ae_fp_greater(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i])&&ae_fp_less(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                gnorm = gnorm+ae_sqr(g, _state);
                ae_set_error_flag(&result, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:6222");
                ae_set_error_flag(&result, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:6223");
            }
            gnorm = ae_sqrt(gnorm, _state);
            ae_set_error_flag(&result, ae_fp_greater(gnorm,eps), __FILE__, __LINE__, "testminqpunit.ap:6226");
        }
    }
    
    /*
     * Test that BLEIC-based QP solver can solve non-convex problems
     * which are bounded from below on the feasible set:
     *
     *     min -||x||^2 s.t. x[i] in [-1,+1]
     *
     * We also test ability of the solver to detect unbounded problems
     * (we remove one of the constraints and repeat solution process).
     */
    n = 20;
    eps = 1.0E-14;
    sparsecreate(n, n, 0, &sa, _state);
    for(i=0; i<=n-1; i++)
    {
        sparseset(&sa, i, i, (double)(-1), _state);
    }
    ae_vector_set_length(&bndl, n, _state);
    ae_vector_set_length(&bndu, n, _state);
    ae_vector_set_length(&x, n, _state);
    for(i=0; i<=n-1; i++)
    {
        bndl.ptr.p_double[i] = (double)(-1);
        bndu.ptr.p_double[i] = (double)(1);
        x.ptr.p_double[i] = ae_randomreal(_state)-0.5;
    }
    minqpcreate(n, &state, _state);
    minqpsetalgobleic(&state, eps, 0.0, 0.0, 0, _state);
    minqpsetquadratictermsparse(&state, &sa, ae_true, _state);
    minqpsetbc(&state, &bndl, &bndu, _state);
    minqpsetstartingpoint(&state, &x, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:6259");
    if( rep.terminationtype>0 )
    {
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(&result, ae_fp_neq(xend0.ptr.p_double[i],(double)(-1))&&ae_fp_neq(xend0.ptr.p_double[i],(double)(1)), __FILE__, __LINE__, "testminqpunit.ap:6262");
        }
    }
    i = ae_randominteger(n, _state);
    bndl.ptr.p_double[i] = _state->v_neginf;
    bndu.ptr.p_double[i] = _state->v_posinf;
    minqpsetbc(&state, &bndl, &bndu, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    ae_set_error_flag(&result, rep.terminationtype!=-4, __FILE__, __LINE__, "testminqpunit.ap:6269");
    
    /*
     * Test that BLEIC-based QP solver can solve non-convex problems
     * which are bounded from below on the feasible set:
     *
     *     min -||x||^2 s.t. x[i] in [-1,+1],
     *     with inequality constraints handled as general linear ones
     *
     * We also test ability of the solver to detect unbounded problems
     * (we remove last pair of constraints and try to solve modified
     * problem).
     */
    n = 20;
    eps = 1.0E-14;
    sparsecreate(n, n, 0, &sa, _state);
    for(i=0; i<=n-1; i++)
    {
        sparseset(&sa, i, i, (double)(-1), _state);
    }
    ae_matrix_set_length(&c, 2*n, n+1, _state);
    ae_vector_set_length(&ct, 2*n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n; j++)
        {
            c.ptr.pp_double[2*i+0][j] = (double)(0);
            c.ptr.pp_double[2*i+1][j] = (double)(0);
        }
        c.ptr.pp_double[2*i+0][i] = 1.0;
        c.ptr.pp_double[2*i+0][n] = 1.0;
        ct.ptr.p_int[2*i+0] = -1;
        c.ptr.pp_double[2*i+1][i] = 1.0;
        c.ptr.pp_double[2*i+1][n] = -1.0;
        ct.ptr.p_int[2*i+1] = 1;
    }
    ae_vector_set_length(&x, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = ae_randomreal(_state)-0.5;
    }
    minqpcreate(n, &state, _state);
    minqpsetalgobleic(&state, eps, 0.0, 0.0, 0, _state);
    minqpsetquadratictermsparse(&state, &sa, ae_true, _state);
    minqpsetlc(&state, &c, &ct, 2*n, _state);
    minqpsetstartingpoint(&state, &x, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:6314");
    if( rep.terminationtype>0 )
    {
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(&result, ae_fp_greater(ae_fabs(xend0.ptr.p_double[i]+1, _state),100*ae_machineepsilon)&&ae_fp_greater(ae_fabs(xend0.ptr.p_double[i]-1, _state),100*ae_machineepsilon), __FILE__, __LINE__, "testminqpunit.ap:6317");
        }
    }
    minqpsetlc(&state, &c, &ct, 2*(n-1), _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    ae_set_error_flag(&result, rep.terminationtype!=-4, __FILE__, __LINE__, "testminqpunit.ap:6321");
    
    /*
     * Test that BLEIC-based QP solver can solve QP problems with
     * zero quadratic term:
     *
     *     min b'*x  s.t. x[i] in [-1,+1]
     *
     * It means that QP solver can be used as linear programming solver
     * (altough performance of such solver is worse than that of specialized
     * LP solver).
     *
     * NOTE: we perform this test twice - first time without explicitly setting
     *       quadratic term (we test that default quadratic term is zero), and
     *       second time - with explicitly set quadratic term.
     */
    n = 20;
    sparsecreate(n, n, 0, &sa, _state);
    ae_vector_set_length(&bndl, n, _state);
    ae_vector_set_length(&bndu, n, _state);
    ae_vector_set_length(&b, n, _state);
    for(i=0; i<=n-1; i++)
    {
        bndl.ptr.p_double[i] = (double)(-1);
        bndu.ptr.p_double[i] = (double)(1);
        b.ptr.p_double[i] = randomnormal(_state);
    }
    minqpcreate(n, &state, _state);
    minqpsetalgobleic(&state, eps, 0.0, 0.0, 0, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetbc(&state, &bndl, &bndu, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:6355");
    if( rep.terminationtype>0 )
    {
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(&result, ae_fp_greater(b.ptr.p_double[i],(double)(0))&&ae_fp_neq(xend0.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:6359");
            ae_set_error_flag(&result, ae_fp_less(b.ptr.p_double[i],(double)(0))&&ae_fp_neq(xend0.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:6360");
        }
    }
    minqpcreate(n, &state, _state);
    minqpsetalgobleic(&state, eps, 0.0, 0.0, 0, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetbc(&state, &bndl, &bndu, _state);
    minqpsetquadratictermsparse(&state, &sa, ae_true, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend0, &rep, _state);
    ae_set_error_flag(&result, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:6369");
    if( rep.terminationtype>0 )
    {
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(&result, ae_fp_greater(b.ptr.p_double[i],(double)(0))&&ae_fp_neq(xend0.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:6373");
            ae_set_error_flag(&result, ae_fp_less(b.ptr.p_double[i],(double)(0))&&ae_fp_neq(xend0.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:6374");
        }
    }
    
    /*
     * Test specific problem sent by V.Semenenko, which resulted in
     * the initinite loop in FindFeasiblePoint (before fix). We do
     * not test results returned by solver - simply being able to
     * stop is enough for this test.
     *
     * NOTE: it is important that modifications to problem are applied
     *       sequentially. Test fails after 100-5000 such modifications.
     *       One modification is not enough to cause failure.
     */
    n = 3;
    ae_matrix_set_length(&a, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a.ptr.pp_double[i][j] = 0.0;
        }
    }
    a.ptr.pp_double[0][0] = 1.222990;
    a.ptr.pp_double[1][1] = 1.934900;
    a.ptr.pp_double[2][2] = 0.603924;
    ae_vector_set_length(&b, n, _state);
    b.ptr.p_double[0] = -4.97245;
    b.ptr.p_double[1] = -9.09039;
    b.ptr.p_double[2] = -4.63856;
    ae_matrix_set_length(&c, 8, n+1, _state);
    for(i=0; i<=c.rows-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            c.ptr.pp_double[i][j] = 0.0;
        }
    }
    c.ptr.pp_double[0][0] = (double)(1);
    c.ptr.pp_double[0][n] = 4.94298;
    c.ptr.pp_double[1][0] = (double)(1);
    c.ptr.pp_double[1][n] = 4.79981;
    c.ptr.pp_double[2][1] = (double)(1);
    c.ptr.pp_double[2][n] = -0.4848;
    c.ptr.pp_double[3][1] = (double)(1);
    c.ptr.pp_double[3][n] = -0.73804;
    c.ptr.pp_double[4][2] = (double)(1);
    c.ptr.pp_double[4][n] = 0.575729;
    c.ptr.pp_double[5][2] = (double)(1);
    c.ptr.pp_double[5][n] = 0.458645;
    c.ptr.pp_double[6][0] = (double)(1);
    c.ptr.pp_double[6][2] = (double)(-1);
    c.ptr.pp_double[6][n] = -0.0546574;
    c.ptr.pp_double[7][0] = (double)(1);
    c.ptr.pp_double[7][2] = (double)(-1);
    c.ptr.pp_double[7][n] = -0.5900440;
    ae_vector_set_length(&ct, 8, _state);
    ct.ptr.p_int[0] = -1;
    ct.ptr.p_int[1] = 1;
    ct.ptr.p_int[2] = -1;
    ct.ptr.p_int[3] = 1;
    ct.ptr.p_int[4] = -1;
    ct.ptr.p_int[5] = 1;
    ct.ptr.p_int[6] = -1;
    ct.ptr.p_int[7] = 1;
    ae_vector_set_length(&s, n, _state);
    s.ptr.p_double[0] = 0.143171;
    s.ptr.p_double[1] = 0.253240;
    s.ptr.p_double[2] = 0.117084;
    ae_vector_set_length(&x0, n, _state);
    x0.ptr.p_double[0] = 3.51126;
    x0.ptr.p_double[1] = 4.05731;
    x0.ptr.p_double[2] = 6.63307;
    for(pass=1; pass<=10000; pass++)
    {
        
        /*
         * Apply random distortion
         */
        for(j=0; j<=n-1; j++)
        {
            b.ptr.p_double[j] = b.ptr.p_double[j]+(2*hqrnduniformi(&rs, 2, _state)-1)*0.1;
        }
        for(j=0; j<=6-1; j++)
        {
            c.ptr.pp_double[j][n] = c.ptr.pp_double[j][n]+(2*hqrnduniformi(&rs, 2, _state)-1)*0.1;
        }
        
        /*
         * Solve
         */
        minqpcreate(3, &state, _state);
        minqpsetquadraticterm(&state, &a, ae_true, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetlc(&state, &c, &ct, 8, _state);
        minqpsetalgobleic(&state, 0.0, 0.0, 0.0, 0, _state);
        minqpsetstartingpoint(&state, &x0, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &x1, &rep, _state);
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests bound constrained quadratic programming algorithm.

On failure sets error flag.
*************************************************************************/
static void testminqpunit_bcqptest(ae_bool* wereerrors, ae_state *_state)
{
    ae_frame _frame_block;
    minqpstate state;
    minqpreport rep;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    double v;
    double g;
    double gnorm;
    ae_int_t origintype;
    ae_int_t scaletype;
    ae_bool isupper;
    ae_bool issparse;
    double bctol;
    double lctol;
    ae_matrix a;
    ae_matrix fulla;
    ae_matrix halfa;
    ae_matrix c;
    sparsematrix sa;
    ae_vector ct;
    ae_vector b;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector x0;
    ae_vector x1;
    ae_vector xori;
    ae_vector xz;
    ae_vector s;
    double eps;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&fulla, 0, sizeof(fulla));
    memset(&halfa, 0, sizeof(halfa));
    memset(&c, 0, sizeof(c));
    memset(&sa, 0, sizeof(sa));
    memset(&ct, 0, sizeof(ct));
    memset(&b, 0, sizeof(b));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&xori, 0, sizeof(xori));
    memset(&xz, 0, sizeof(xz));
    memset(&s, 0, sizeof(s));
    memset(&rs, 0, sizeof(rs));
    _minqpstate_init(&state, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&halfa, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xori, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xz, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Convex test:
     * * N dimensions
     * * random number (0..N) of random boundary constraints
     * * positive-definite A
     * * algorithm randomly choose dense or sparse A, and for
     *   sparse matrix it randomly choose format.
     * * random B with normal entries
     * * initial point is random, feasible
     * * random origin (zero or non-zero) and scale (unit or
     *   non-unit) are generated
     */
    eps = 1.0E-4;
    for(n=1; n<=10; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            origintype = hqrnduniformi(&rs, 2, _state);
            scaletype = hqrnduniformi(&rs, 2, _state);
            isupper = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            issparse = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            spdmatrixrndcond(n, 1.0E3, &fulla, _state);
            ae_matrix_set_length(&halfa, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j>=i&&isupper)||(j<=i&&!isupper) )
                    {
                        halfa.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j];
                    }
                    else
                    {
                        halfa.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    }
                }
            }
            testminqpunit_densetosparse(&halfa, n, &sa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xori, n, _state);
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bndl.ptr.p_double[i] = _state->v_neginf;
                bndu.ptr.p_double[i] = _state->v_posinf;
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                if( origintype==0 )
                {
                    xori.ptr.p_double[i] = (double)(0);
                }
                else
                {
                    xori.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                if( scaletype==0 )
                {
                    s.ptr.p_double[i] = (double)(1);
                }
                else
                {
                    s.ptr.p_double[i] = ae_exp(hqrndnormal(&rs, _state), _state);
                }
                j = hqrnduniformi(&rs, 5, _state);
                if( j==0 )
                {
                    bndl.ptr.p_double[i] = (double)(0);
                    x0.ptr.p_double[i] = ae_fabs(x0.ptr.p_double[i], _state);
                }
                if( j==1 )
                {
                    bndu.ptr.p_double[i] = (double)(0);
                    x0.ptr.p_double[i] = -ae_fabs(x0.ptr.p_double[i], _state);
                }
                if( j==2 )
                {
                    bndl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    bndu.ptr.p_double[i] = bndl.ptr.p_double[i];
                    x0.ptr.p_double[i] = bndl.ptr.p_double[i];
                }
                if( j==3 )
                {
                    bndl.ptr.p_double[i] = -0.1;
                    bndu.ptr.p_double[i] = 0.1;
                    x0.ptr.p_double[i] = 0.2*hqrnduniformr(&rs, _state)-0.1;
                }
            }
            
            /*
             * Solve problem
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgoallmodern(&state, &bctol, &lctol, _state);
            minqpsetlinearterm(&state, &b, _state);
            minqpsetstartingpoint(&state, &x0, _state);
            if( issparse )
            {
                minqpsetquadratictermsparse(&state, &sa, isupper, _state);
            }
            else
            {
                minqpsetquadraticterm(&state, &halfa, isupper, _state);
            }
            if( origintype!=0 )
            {
                minqpsetorigin(&state, &xori, _state);
            }
            if( scaletype!=0 )
            {
                minqpsetscale(&state, &s, _state);
            }
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:865");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            gnorm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                g = b.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    g = g+fulla.ptr.pp_double[i][j]*(x1.ptr.p_double[j]-xori.ptr.p_double[j]);
                }
                if( ae_fp_less_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i]+bctol)&&ae_fp_greater(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                if( ae_fp_greater_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i]-bctol)&&ae_fp_less(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                gnorm = gnorm+ae_sqr(g, _state);
                ae_set_error_flag(wereerrors, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:885");
                ae_set_error_flag(wereerrors, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:886");
            }
            gnorm = ae_sqrt(gnorm, _state);
            ae_set_error_flag(wereerrors, ae_fp_greater(gnorm,eps), __FILE__, __LINE__, "testminqpunit.ap:889");
        }
    }
    
    /*
     * Semidefinite test:
     * * N dimensions
     * * nonnegativity constraints
     * * A = [ 1 1 ... 1 1 ; 1 1 ... 1 1 ; .... ; 1 1 ... 1 1 ]
     * * algorithm randomly choose dense or sparse A, and for
     *   sparse matrix it randomly choose format.
     * * random B with normal entries
     * * initial point is random, feasible
     */
    eps = 1.0E-4;
    for(n=1; n<=10; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            isupper = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            issparse = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            ae_matrix_set_length(&fulla, n, n, _state);
            ae_matrix_set_length(&halfa, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    fulla.ptr.pp_double[i][j] = 1.0;
                    if( (j>=i&&isupper)||(j<=i&&!isupper) )
                    {
                        halfa.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j];
                    }
                    else
                    {
                        halfa.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    }
                }
            }
            testminqpunit_densetosparse(&halfa, n, &sa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bndl.ptr.p_double[i] = 0.0;
                bndu.ptr.p_double[i] = _state->v_posinf;
                x0.ptr.p_double[i] = (double)(hqrnduniformi(&rs, 2, _state));
            }
            
            /*
             * Solve problem
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgosemidefinite(&state, _state);
            minqpsetstartingpoint(&state, &x0, _state);
            minqpsetlinearterm(&state, &b, _state);
            if( issparse )
            {
                minqpsetquadratictermsparse(&state, &sa, isupper, _state);
            }
            else
            {
                minqpsetquadraticterm(&state, &halfa, isupper, _state);
            }
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:949");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            gnorm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                g = b.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    g = g+fulla.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i])&&ae_fp_greater(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i])&&ae_fp_less(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                gnorm = gnorm+ae_sqr(g, _state);
                ae_set_error_flag(wereerrors, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:969");
                ae_set_error_flag(wereerrors, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:970");
            }
            gnorm = ae_sqrt(gnorm, _state);
            ae_set_error_flag(wereerrors, ae_fp_greater(gnorm,eps), __FILE__, __LINE__, "testminqpunit.ap:973");
        }
    }
    
    /*
     * Non-convex test:
     * * N dimensions, N>=2
     * * box constraints, x[i] in [-1,+1]
     * * A = A0-0.5*I, where A0 is SPD with unit norm and smallest
     *   singular value equal to 1.0E-3, I is identity matrix
     * * random B with normal entries
     * * initial point is random, feasible
     *
     * We perform two tests:
     * * unconstrained problem must be recognized as unbounded
     * * constrained problem can be successfully solved
     *
     * NOTE: it is important to have N>=2, because formula for A
     *       can be applied only to matrix with at least two
     *       singular values
     */
    eps = 1.0E-4;
    for(n=2; n<=10; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            spdmatrixrndcond(n, 1.0E3, &fulla, _state);
            for(i=0; i<=n-1; i++)
            {
                fulla.ptr.pp_double[i][i] = fulla.ptr.pp_double[i][i]-0.5;
            }
            isupper = ae_fp_less(hqrnduniformr(&rs, _state),0.5);
            ae_matrix_set_length(&halfa, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j>=i&&isupper)||(j<=i&&!isupper) )
                    {
                        halfa.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j];
                    }
                    else
                    {
                        halfa.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    }
                }
            }
            testminqpunit_densetosparse(&halfa, n, &sa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bndl.ptr.p_double[i] = (double)(-1);
                bndu.ptr.p_double[i] = (double)(1);
                x0.ptr.p_double[i] = 2*hqrnduniformr(&rs, _state)-1;
            }
            
            /*
             * Solve problem:
             * * without constraints we expect failure
             * * with constraints algorithm must succeed
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgononconvex(&state, _state);
            minqpsetstartingpoint(&state, &x0, _state);
            minqpsetlinearterm(&state, &b, _state);
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                minqpsetquadraticterm(&state, &halfa, isupper, _state);
            }
            else
            {
                minqpsetquadratictermsparse(&state, &sa, isupper, _state);
            }
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, rep.terminationtype!=-4, __FILE__, __LINE__, "testminqpunit.ap:1040");
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:1044");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            gnorm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&fulla.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                g = v+b.ptr.p_double[i];
                if( ae_fp_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i])&&ae_fp_greater(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                if( ae_fp_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i])&&ae_fp_less(g,(double)(0)) )
                {
                    g = (double)(0);
                }
                gnorm = gnorm+ae_sqr(g, _state);
                ae_set_error_flag(wereerrors, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:1063");
                ae_set_error_flag(wereerrors, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:1064");
            }
            gnorm = ae_sqrt(gnorm, _state);
            ae_set_error_flag(wereerrors, ae_fp_greater(gnorm,eps), __FILE__, __LINE__, "testminqpunit.ap:1067");
        }
    }
    
    /*
     * Linear (zero-quadratic) test:
     * * N dimensions, N>=1
     * * box constraints, x[i] in [-1,+1]
     * * A = 0
     * * random B with normal entries
     * * initial point is random, feasible
     *
     * We perform two tests:
     * * unconstrained problem must be recognized as unbounded
     * * constrained problem can be successfully solved
     *
     * NOTE: we may explicitly set zero A, or assume that by
     *       default it is zero. During test we will try both
     *       ways.
     */
    eps = 1.0E-4;
    for(n=1; n<=10; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Generate problem
             */
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                do
                {
                    b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                while(ae_fp_eq(b.ptr.p_double[i],(double)(0)));
                bndl.ptr.p_double[i] = (double)(-1);
                bndu.ptr.p_double[i] = (double)(1);
                x0.ptr.p_double[i] = 2*hqrnduniformr(&rs, _state)-1;
            }
            
            /*
             * Solve problem:
             * * without constraints we expect failure
             * * with constraints algorithm must succeed
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgononconvex(&state, _state);
            minqpsetlinearterm(&state, &b, _state);
            minqpsetstartingpoint(&state, &x0, _state);
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                ae_matrix_set_length(&a, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = (double)(0);
                    }
                }
                minqpsetquadraticterm(&state, &a, ae_true, _state);
            }
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, rep.terminationtype!=-4, __FILE__, __LINE__, "testminqpunit.ap:1126");
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:1130");
            if( rep.terminationtype<=0 )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Test - calculate constrained gradient at solution,
             * check its norm.
             */
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(wereerrors, ae_fp_greater(b.ptr.p_double[i],(double)(0))&&ae_fp_greater(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:1140");
                ae_set_error_flag(wereerrors, ae_fp_less(b.ptr.p_double[i],(double)(0))&&ae_fp_less(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:1141");
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests equality constrained quadratic programming algorithm.

Returns True on errors.
*************************************************************************/
static ae_bool testminqpunit_ecqptest(ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t k;
    ae_matrix a;
    ae_matrix q;
    ae_matrix c;
    ae_matrix a2;
    ae_vector b;
    ae_vector b2;
    ae_vector xstart;
    ae_vector xstart2;
    ae_vector xend;
    ae_vector xend2;
    ae_vector x0;
    ae_vector x1;
    ae_vector xd;
    ae_vector xs;
    ae_vector tmp;
    ae_vector g;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector xorigin;
    ae_vector ct;
    double eps;
    double theta;
    double f0;
    double f1;
    minqpstate state;
    minqpstate state2;
    minqpreport rep;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_int_t rk;
    double v;
    ae_int_t aulits;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&q, 0, sizeof(q));
    memset(&c, 0, sizeof(c));
    memset(&a2, 0, sizeof(a2));
    memset(&b, 0, sizeof(b));
    memset(&b2, 0, sizeof(b2));
    memset(&xstart, 0, sizeof(xstart));
    memset(&xstart2, 0, sizeof(xstart2));
    memset(&xend, 0, sizeof(xend));
    memset(&xend2, 0, sizeof(xend2));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&xd, 0, sizeof(xd));
    memset(&xs, 0, sizeof(xs));
    memset(&tmp, 0, sizeof(tmp));
    memset(&g, 0, sizeof(g));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&xorigin, 0, sizeof(xorigin));
    memset(&ct, 0, sizeof(ct));
    memset(&state, 0, sizeof(state));
    memset(&state2, 0, sizeof(state2));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xstart, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xstart2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xend, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xend2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xd, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xorigin, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minqpstate_init(&state, _state, ae_true);
    _minqpstate_init(&state2, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    
    /*
     * First test:
     * * N*N identity A
     * * K<N equality constraints Q*x = Q*x0, where Q is random
     *   orthogonal K*N matrix, x0 is some random vector
     * * x1 is some random vector such that Q*x1=0. It is always possible
     *   to find such x1, because K<N
     * * optimization problem has form 0.5*x'*A*x-(x1*A)*x
     * * exact solution must be equal to x0
     */
    eps = 1.0E-4;
    for(n=2; n<=6; n++)
    {
        for(k=1; k<=n-1; k++)
        {
            
            /*
             * Generate problem: A, b, CMatrix, x0, XStart
             */
            rmatrixrndorthogonal(n, &q, _state);
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = (double)(0);
                }
                a.ptr.pp_double[i][i] = (double)(1);
            }
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&x1, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&c, k, n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x1.ptr.p_double[i] = x0.ptr.p_double[i];
                xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=k-1; i++)
            {
                ae_v_move(&c.ptr.pp_double[i][0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                ct.ptr.p_int[i] = 0;
                v = 2*ae_randomreal(_state)-1;
                ae_v_addd(&x1.ptr.p_double[0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = -v;
            }
            
            /*
             * Create optimizer, solve
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgoconvexlc(&state, _state);
            minqpsetlinearterm(&state, &b, _state);
            minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            minqpsetlc(&state, &c, &ct, k, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xend, &rep, _state);
            
            /*
             * Compare with analytic solution
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1247");
                continue;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(xend.ptr.p_double[i]-x0.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:1251");
            }
        }
    }
    
    /*
     * Second test:
     * * N*N SPD A
     * * K<N equality constraints Q*x = Q*x0, where Q is random
     *   orthogonal K*N matrix, x0 is some random vector
     * * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
     *   where x1 is some random vector
     * * we check feasibility properties of the solution
     * * we do not know analytic form of the exact solution,
     *   but we know that projection of gradient into equality constrained
     *   subspace must be zero at the solution
     */
    eps = 1.0E-4;
    for(n=2; n<=6; n++)
    {
        for(k=1; k<=n-1; k++)
        {
            
            /*
             * Generate problem: A, b, CMatrix, x0, XStart
             */
            rmatrixrndorthogonal(n, &q, _state);
            spdmatrixrndcond(n, ae_pow(10.0, 3*ae_randomreal(_state), _state), &a, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&x1, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&c, k, n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=k-1; i++)
            {
                ae_v_move(&c.ptr.pp_double[i][0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                ct.ptr.p_int[i] = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = -v;
            }
            
            /*
             * Create optimizer, solve
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgoconvexlc(&state, _state);
            minqpsetlinearterm(&state, &b, _state);
            minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            minqpsetlc(&state, &c, &ct, k, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xend, &rep, _state);
            
            /*
             * Calculate gradient, check projection
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1319");
                continue;
            }
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&xend.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:1325");
            }
            ae_vector_set_length(&g, n, _state);
            ae_v_move(&g.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xend.ptr.p_double[0], 1, ae_v_len(0,n-1));
                g.ptr.p_double[i] = g.ptr.p_double[i]+v;
            }
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&g.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                ae_v_subd(&g.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
            }
            v = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
            ae_set_error_flag(&waserrors, ae_fp_greater(ae_sqrt(v, _state),eps), __FILE__, __LINE__, "testminqpunit.ap:1340");
        }
    }
    
    /*
     * Boundary and linear equality constrained QP problem:
     * * N*N SPD A with moderate condtion number (up to 100)
     * * boundary constraints 0<=x[i]<=1
     * * K<N equality constraints C*x = C*x0, where Q is random
     *   K*N matrix, x0 is some random vector from the
     *   feasible hypercube (0<=x0[i]<=1)
     * * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
     *   where x1 is some random vector with -1<=x1[i]<=+1.
     *   (sometimes solution is in the inner area, sometimes at the boundary)
     * * every component of the initial point XStart is either 0 or 1
     *   (point is located at the vertices of the feasible hypercube)
     * 
     * Solution of such problem is calculated using two methods:
     * a) boundary and linearly constrained QP
     * b) augmented Lagrangian boundary constrained QP: we add explicit quadratic
     *    penalty to the problem; we also add Lagrangian terms and perform many
     *    subsequent iterations to find good estimates of the Lagrange multipliers.
     *
     * Sometimes augmented Largangian converges to slightly different point
     * (boundary constraints lead to extremely slow, non-smooth convergence of
     * the Lagrange multipliers). In order to correctly handle such situations
     * we compare function values instead of final points - and use relaxed criteria
     * to test for convergence.
     *
     * NOTE: sometimes we need as much as 300 Augmented Lagrangian iterations for
     *       method to converge.
     */
    eps = 5.0E-2;
    theta = 1.0E+4;
    aulits = 300;
    for(n=2; n<=6; n++)
    {
        for(k=1; k<=n-1; k++)
        {
            
            /*
             * Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
             */
            spdmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &a, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&x1, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&c, k, n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = ae_randomreal(_state);
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                bndl.ptr.p_double[i] = 0.0;
                bndu.ptr.p_double[i] = 1.0;
                xstart.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
            }
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                ct.ptr.p_int[i] = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = -v;
            }
            
            /*
             * Create exact optimizer, solve
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgoconvexlc(&state, _state);
            minqpsetlinearterm(&state, &b, _state);
            minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpsetlc(&state, &c, &ct, k, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xend, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1427");
                continue;
            }
            
            /*
             * Solve problem using barrier functions (quadrative objective, boundary constraints,
             * explicit penalty term added to the main quadratic matrix. Lagrangian terms improve
             * solution quality):
             * * A2 := A+C'*C
             * * b2 := b-r'*C
             * * b2 is iteratively updated using augmented Lagrangian update
             * 
             * NOTE: we may need many outer iterations to converge to the optimal values
             *       of Lagrange multipliers. Convergence is slowed down by the presense
             *       of boundary constraints, whose activation/deactivation slows down
             *       process.
             */
            ae_matrix_set_length(&a2, n, n, _state);
            rmatrixcopy(n, n, &a, 0, 0, &a2, 0, 0, _state);
            rmatrixsyrk(n, k, theta, &c, 0, 0, 2, 1.0, &a2, 0, 0, ae_true, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=i+1; j<=n-1; j++)
                {
                    a2.ptr.pp_double[j][i] = a2.ptr.pp_double[i][j];
                }
            }
            ae_vector_set_length(&b2, n, _state);
            ae_v_move(&b2.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            for(i=0; i<=k-1; i++)
            {
                v = c.ptr.pp_double[i][n]*theta;
                ae_v_subd(&b2.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
            }
            minqpcreate(n, &state2, _state);
            testminqpunit_setrandomalgoconvexlc(&state, _state);
            minqpsetquadraticterm(&state2, &a2, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            minqpsetstartingpoint(&state2, &xstart, _state);
            minqpsetbc(&state2, &bndl, &bndu, _state);
            for(i=1; i<=aulits; i++)
            {
                
                /*
                 * Solve, update B2 according to augmented Lagrangian algorithm
                 */
                minqpsetlinearterm(&state2, &b2, _state);
                minqpoptimize(&state2, _state);
                minqpresults(&state2, &xend2, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1474");
                    continue;
                }
                for(j=0; j<=k-1; j++)
                {
                    v = ae_v_dotproduct(&c.ptr.pp_double[j][0], 1, &xend2.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    v = theta*(v-c.ptr.pp_double[j][n]);
                    ae_v_addd(&b2.ptr.p_double[0], 1, &c.ptr.pp_double[j][0], 1, ae_v_len(0,n-1), v);
                }
            }
            
            /*
             * Calculate function value and XEnd and XEnd2
             */
            f0 = 0.0;
            f1 = 0.0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    f0 = f0+0.5*xend.ptr.p_double[i]*a.ptr.pp_double[i][j]*xend.ptr.p_double[j];
                    f1 = f1+0.5*xend2.ptr.p_double[i]*a.ptr.pp_double[i][j]*xend2.ptr.p_double[j];
                }
                f0 = f0+xend.ptr.p_double[i]*b.ptr.p_double[i];
                f1 = f1+xend2.ptr.p_double[i]*b.ptr.p_double[i];
            }
            
            /*
             * Check feasibility properties and compare
             */
            ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(f0-f1, _state),eps), __FILE__, __LINE__, "testminqpunit.ap:1504");
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&xend.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),1.0E6*ae_machineepsilon), __FILE__, __LINE__, "testminqpunit.ap:1508");
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(&waserrors, ae_fp_less(xend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testminqpunit.ap:1512");
                ae_set_error_flag(&waserrors, ae_fp_greater(xend.ptr.p_double[i],(double)(1)), __FILE__, __LINE__, "testminqpunit.ap:1513");
            }
        }
    }
    
    /*
     * Boundary and linear equality constrained QP problem,
     * test for correct handling of non-zero XOrigin:
     * * N*N SPD A with moderate condtion number (up to 100)
     * * boundary constraints 0<=x[i]<=1
     * * K<N equality constraints Q*x = Q*x0, where Q is random
     *   orthogonal K*N matrix, x0 is some random vector from the
     *   inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
     * * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
     *   where b is some random vector with -1<=b[i]<=+1.
     *   (sometimes solution is in the inner area, sometimes at the boundary)
     * * every component of the initial point XStart is random from [-1,1]
     * 
     * Solution of such problem is calculated using two methods:
     * a) QP with SetOrigin() call
     * b) QP with XOrigin explicitly added to the quadratic function,
     *
     * Both methods should give same results; any significant difference is
     * evidence of some error in the QP implementation.
     */
    eps = 1.0E-4;
    for(n=2; n<=6; n++)
    {
        for(k=1; k<=n-1; k++)
        {
            
            /*
             * Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
             * Additionally, we compute modified b: b2 = b-xorigin'*A
             */
            rmatrixrndorthogonal(n, &q, _state);
            spdmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &a, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&b2, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&x1, n, _state);
            ae_vector_set_length(&xorigin, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&c, k, n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                bndl.ptr.p_double[i] = 0.0;
                bndu.ptr.p_double[i] = 1.0;
                xstart.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
                xorigin.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=k-1; i++)
            {
                ae_v_move(&c.ptr.pp_double[i][0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                ct.ptr.p_int[i] = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xorigin.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b2.ptr.p_double[i] = b.ptr.p_double[i]-v;
            }
            
            /*
             * Solve with SetOrigin() call
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgoconvexlc(&state, _state);
            minqpsetlinearterm(&state, &b, _state);
            minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            minqpsetorigin(&state, &xorigin, _state);
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpsetlc(&state, &c, &ct, k, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xend, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1597");
                continue;
            }
            
            /*
             * Solve problem using explicit origin
             */
            minqpcreate(n, &state2, _state);
            testminqpunit_setrandomalgoconvexlc(&state, _state);
            minqpsetlinearterm(&state2, &b2, _state);
            minqpsetquadraticterm(&state2, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            minqpsetstartingpoint(&state2, &xstart, _state);
            minqpsetbc(&state2, &bndl, &bndu, _state);
            minqpsetlc(&state2, &c, &ct, k, _state);
            minqpoptimize(&state2, _state);
            minqpresults(&state2, &xend2, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1615");
                continue;
            }
            
            /*
             * Check feasibility properties and compare solutions
             */
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&xend.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:1625");
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(xend.ptr.p_double[i]-xend2.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:1629");
                ae_set_error_flag(&waserrors, ae_fp_less(xend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testminqpunit.ap:1630");
                ae_set_error_flag(&waserrors, ae_fp_greater(xend.ptr.p_double[i],(double)(1)), __FILE__, __LINE__, "testminqpunit.ap:1631");
            }
        }
    }
    
    /*
     * Boundary and linear equality constrained QP problem with excessive
     * equality constraints:
     * * N*N SPD A with moderate condtion number (up to 100)
     * * boundary constraints 0<=x[i]<=1
     * * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix,
     *   x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
     * * optimization problem has form 0.5*x'*A*x-b*x,
     *   where b is some random vector
     * * because constraints are excessive, the main problem is to find
     *   feasible point; the only existing feasible point is solution,
     *   so we have to check only feasibility
     */
    eps = 1.0E-4;
    for(n=1; n<=6; n++)
    {
        
        /*
         * Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
         */
        k = 2*n;
        spdmatrixrndcond(n, ae_pow(10.0, 3*ae_randomreal(_state), _state), &a, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&x1, n, _state);
        ae_vector_set_length(&xstart, n, _state);
        ae_matrix_set_length(&c, k, n+1, _state);
        ae_vector_set_length(&ct, k, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            bndl.ptr.p_double[i] = 0.0;
            bndu.ptr.p_double[i] = 1.0;
            xstart.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
        }
        for(i=0; i<=k-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            c.ptr.pp_double[i][n] = v;
            ct.ptr.p_int[i] = 0;
        }
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        
        /*
         * Create optimizer, solve
         */
        minqpcreate(n, &state, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        minqpsetstartingpoint(&state, &xstart, _state);
        minqpsetbc(&state, &bndl, &bndu, _state);
        minqpsetlc(&state, &c, &ct, k, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend, &rep, _state);
        
        /*
         * Check feasibility properties of the solution
         */
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1702");
            continue;
        }
        for(i=0; i<=k-1; i++)
        {
            v = ae_v_dotproduct(&xend.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:1708");
        }
    }
    
    /*
     * Boundary and linear equality constrained QP problem,
     * test checks that different starting points yield same final point:
     * * random N from [1..6], random K from [1..2*N]
     * * N*N SPD A with moderate condtion number (up to 100)
     * * boundary constraints 0<=x[i]<=1
     * * K<2*N random linear equality constraints C*x = C*x0,
     *   where x0 is some random vector from the inner area of the
     *   feasible hypercube (0.1<=x0[i]<=0.9)
     * * optimization problem has form 0.5*x'*A*x+b*x,
     *   where b is some random vector with -5<=b[i]<=+5
     * * every component of the initial point XStart is random from [-2,+2]
     * * we perform two starts from random different XStart and compare values
     *   of the target function (although final points may be slightly different,
     *   function values should match each other)
     * 
     * Both points should give same results; any significant difference is
     * evidence of some error in the QP implementation.
     */
    eps = 1.0E-4;
    for(pass=1; pass<=50; pass++)
    {
        
        /*
         * Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, x1, XStart.
         */
        n = ae_randominteger(5, _state)+2;
        k = ae_randominteger(2*n, _state)+1;
        spdmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &a, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&b2, n, _state);
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&xstart, n, _state);
        ae_vector_set_length(&xstart2, n, _state);
        ae_matrix_set_length(&c, k, n+1, _state);
        ae_vector_set_length(&ct, k, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
            b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            bndl.ptr.p_double[i] = 0.0;
            bndu.ptr.p_double[i] = 1.0;
            xstart.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
            xstart2.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
        }
        for(i=0; i<=k-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            c.ptr.pp_double[i][n] = v;
            ct.ptr.p_int[i] = 0;
        }
        
        /*
         * Solve with XStart
         */
        minqpcreate(n, &state, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        minqpsetstartingpoint(&state, &xstart, _state);
        minqpsetbc(&state, &bndl, &bndu, _state);
        minqpsetlc(&state, &c, &ct, k, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1782");
            continue;
        }
        
        /*
         * Solve with XStart2
         */
        minqpsetstartingpoint(&state, &xstart2, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend2, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1794");
            continue;
        }
        
        /*
         * Calculate function value and XEnd and XEnd2, compare solutions
         */
        f0 = 0.0;
        f1 = 0.0;
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                f0 = f0+0.5*xend.ptr.p_double[i]*a.ptr.pp_double[i][j]*xend.ptr.p_double[j];
                f1 = f1+0.5*xend2.ptr.p_double[i]*a.ptr.pp_double[i][j]*xend2.ptr.p_double[j];
            }
            f0 = f0+xend.ptr.p_double[i]*b.ptr.p_double[i];
            f1 = f1+xend2.ptr.p_double[i]*b.ptr.p_double[i];
        }
        ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(f0-f1, _state),eps), __FILE__, __LINE__, "testminqpunit.ap:1813");
    }
    
    /*
     * Test ability to correctly handle situation where algorithm
     * either:
     * (1) starts from point with gradient whose projection to
     *     active set is almost zero (but not exactly zero)
     * (2) performs step to such point
     *
     * In order to do this we solve problem
     * * min 0.5*x'*x - (x0+c)'*x
     * * subject to c'*x = c'*x0, with c and x0 random unit vectors
     * * with initial point xs = x0+r*xd, where r is scalar,
     *   xd is vector which is orthogonal to c.
     * * we try different r=power(2,-rk) for rk=0...70. The idea
     *   is that as we approach closer and closer to x0, which is
     *   a solution of the constrained problem, constrained gradient
     *   of the function rapidly vanishes.
     */
    eps = 1.0E-6;
    for(rk=0; rk<=70; rk++)
    {
        n = 10;
        
        /*
         * Generate x0, c, xd, xs, generate unit A
         */
        randomunit(n, &x0, _state);
        randomunit(n, &xd, _state);
        randomunit(n, &tmp, _state);
        ae_matrix_set_length(&c, 1, n+1, _state);
        ae_vector_set_length(&ct, 1, _state);
        ae_vector_set_length(&xs, n, _state);
        ae_vector_set_length(&b, n, _state);
        c.ptr.pp_double[0][n] = (double)(0);
        ct.ptr.p_int[0] = 0;
        v = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            c.ptr.pp_double[0][i] = tmp.ptr.p_double[i];
            c.ptr.pp_double[0][n] = c.ptr.pp_double[0][n]+tmp.ptr.p_double[i]*x0.ptr.p_double[i];
            b.ptr.p_double[i] = -(x0.ptr.p_double[i]+tmp.ptr.p_double[i]);
            v = v+tmp.ptr.p_double[i]*xd.ptr.p_double[i];
        }
        for(i=0; i<=n-1; i++)
        {
            xd.ptr.p_double[i] = xd.ptr.p_double[i]-v*tmp.ptr.p_double[i];
            xs.ptr.p_double[i] = x0.ptr.p_double[i]+xd.ptr.p_double[i]*ae_pow((double)(2), (double)(-rk), _state);
        }
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = (double)(0);
            }
            a.ptr.pp_double[i][i] = (double)(1);
        }
        
        /*
         * Create and solve optimization problem
         */
        minqpcreate(n, &state, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetquadraticterm(&state, &a, ae_true, _state);
        minqpsetstartingpoint(&state, &xs, _state);
        minqpsetlc(&state, &c, &ct, 1, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(&waserrors, ae_true, __FILE__, __LINE__, "testminqpunit.ap:1884");
            continue;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(xend.ptr.p_double[i]-x0.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:1888");
        }
        v = -c.ptr.pp_double[0][n];
        for(i=0; i<=n-1; i++)
        {
            v = v+xend.ptr.p_double[i]*c.ptr.pp_double[0][i];
        }
        ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(v, _state),1.0E5*ae_machineepsilon), __FILE__, __LINE__, "testminqpunit.ap:1892");
    }
    result = waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests inequality constrained quadratic programming algorithm.

On failure sets Err to True; on success leaves it unchanged.
*************************************************************************/
static void testminqpunit_icqptest(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t k;
    ae_matrix a;
    ae_matrix q;
    ae_matrix c;
    ae_matrix a2;
    ae_matrix t2;
    ae_matrix t3;
    ae_matrix ce;
    ae_vector xs0;
    ae_vector bl;
    ae_vector bu;
    ae_vector tmp;
    ae_vector x;
    ae_vector xstart;
    ae_vector xstart2;
    ae_vector xend;
    ae_vector xend2;
    ae_vector x0;
    ae_vector x1;
    ae_vector b;
    ae_vector b2;
    ae_vector g;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector xorigin;
    ae_vector tmp0;
    ae_vector tmp1;
    ae_vector da;
    ae_vector ct;
    ae_vector nonnegative;
    double eps;
    minqpstate state;
    minqpstate state2;
    minqpreport rep;
    minqpreport rep2;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    double v;
    double vv;
    double f0;
    double f1;
    double tolconstr;
    ae_int_t bscale;
    ae_int_t akind;
    ae_int_t shiftkind;
    ae_int_t ccnt;
    hqrndstate rs;
    snnlssolver nnls;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&q, 0, sizeof(q));
    memset(&c, 0, sizeof(c));
    memset(&a2, 0, sizeof(a2));
    memset(&t2, 0, sizeof(t2));
    memset(&t3, 0, sizeof(t3));
    memset(&ce, 0, sizeof(ce));
    memset(&xs0, 0, sizeof(xs0));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&tmp, 0, sizeof(tmp));
    memset(&x, 0, sizeof(x));
    memset(&xstart, 0, sizeof(xstart));
    memset(&xstart2, 0, sizeof(xstart2));
    memset(&xend, 0, sizeof(xend));
    memset(&xend2, 0, sizeof(xend2));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&b, 0, sizeof(b));
    memset(&b2, 0, sizeof(b2));
    memset(&g, 0, sizeof(g));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&xorigin, 0, sizeof(xorigin));
    memset(&tmp0, 0, sizeof(tmp0));
    memset(&tmp1, 0, sizeof(tmp1));
    memset(&da, 0, sizeof(da));
    memset(&ct, 0, sizeof(ct));
    memset(&nonnegative, 0, sizeof(nonnegative));
    memset(&state, 0, sizeof(state));
    memset(&state2, 0, sizeof(state2));
    memset(&rep, 0, sizeof(rep));
    memset(&rep2, 0, sizeof(rep2));
    memset(&rs, 0, sizeof(rs));
    memset(&nnls, 0, sizeof(nnls));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t3, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ce, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xstart, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xstart2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xend, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xend2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xorigin, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&da, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&nonnegative, 0, DT_BOOL, _state, ae_true);
    _minqpstate_init(&state, _state, ae_true);
    _minqpstate_init(&state2, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    _minqpreport_init(&rep2, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);
    _snnlssolver_init(&nnls, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Inequality constrained problem:
     * * N*N diagonal A
     * * one inequality constraint q'*x>=0, where q is random unit vector
     * * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
     *   where x1 is some random vector
     * * either:
     *   a) x1 is feasible => we must stop at x1
     *   b) x1 is infeasible => we must stop at the boundary q'*x=0 and
     *      projection of gradient onto q*x=0 must be zero
     *
     * NOTE: we make several passes because some specific kind of errors is rarely
     *       caught by this test, so we need several repetitions.
     */
    eps = 1.0E-4;
    for(n=2; n<=6; n++)
    {
        for(pass=0; pass<=4; pass++)
        {
            
            /*
             * Generate problem: A, b, CMatrix, x0, XStart
             */
            spdmatrixrndcond(n, ae_pow(10.0, 3*ae_randomreal(_state), _state), &a, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x1, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&c, 1, n+1, _state);
            ae_vector_set_length(&ct, 1, _state);
            for(i=0; i<=n-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            do
            {
                v = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    c.ptr.pp_double[0][i] = 2*ae_randomreal(_state)-1;
                    v = v+ae_sqr(c.ptr.pp_double[0][i], _state);
                }
                v = ae_sqrt(v, _state);
            }
            while(ae_fp_eq(v,(double)(0)));
            for(i=0; i<=n-1; i++)
            {
                c.ptr.pp_double[0][i] = c.ptr.pp_double[0][i]/v;
            }
            c.ptr.pp_double[0][n] = (double)(0);
            ct.ptr.p_int[0] = 1;
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = -v;
            }
            
            /*
             * Create optimizer, solve
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgoconvexlc(&state, _state);
            minqpsetlinearterm(&state, &b, _state);
            minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            minqpsetlc(&state, &c, &ct, 1, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xend, &rep, _state);
            
            /*
             * Test
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2003");
                continue;
            }
            v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
            if( ae_fp_greater_eq(v,(double)(0)) )
            {
                
                /*
                 * X1 is feasible
                 */
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(err, ae_fp_greater(ae_fabs(xend.ptr.p_double[i]-x1.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:2013");
                }
            }
            else
            {
                
                /*
                 * X1 is infeasible:
                 * * XEnd must be approximately feasible
                 * * gradient projection onto c'*x=0 must be zero
                 */
                v = ae_v_dotproduct(&xend.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                ae_set_error_flag(err, ae_fp_less(v,-eps), __FILE__, __LINE__, "testminqpunit.ap:2023");
                ae_vector_set_length(&g, n, _state);
                ae_v_move(&g.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xend.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    g.ptr.p_double[i] = g.ptr.p_double[i]+v;
                }
                v = ae_v_dotproduct(&g.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                ae_v_subd(&g.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1), v);
                v = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                ae_set_error_flag(err, ae_fp_greater(ae_sqrt(v, _state),eps), __FILE__, __LINE__, "testminqpunit.ap:2034");
            }
        }
    }
    
    /*
     * Boundary and linear equality/inequality constrained QP problem,
     * test for correct handling of non-zero XOrigin:
     * * N*N SPD A with moderate condtion number (up to 100)
     * * boundary constraints 0<=x[i]<=1
     * * K<N linear equality/inequality constraints Q*x = Q*x0, where
     *   Q is random orthogonal K*N matrix, x0 is some random vector from the
     *   inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
     * * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
     *   where b is some random vector with -1<=b[i]<=+1.
     *   (sometimes solution is in the inner area, sometimes at the boundary)
     * * every component of the initial point XStart is random from [-1,1]
     * 
     * Solution of such problem is calculated using two methods:
     * a) QP with SetOrigin() call
     * b) QP with XOrigin explicitly added to the quadratic function,
     *
     * Both methods should give same results; any significant difference is
     * evidence of some error in the QP implementation.
     */
    eps = 1.0E-4;
    for(n=2; n<=6; n++)
    {
        for(k=1; k<=n-1; k++)
        {
            
            /*
             * Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
             * Additionally, we compute modified b: b2 = b-xorigin'*A
             */
            rmatrixrndorthogonal(n, &q, _state);
            spdmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &a, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&b2, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&x1, n, _state);
            ae_vector_set_length(&xorigin, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&c, k, n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                bndl.ptr.p_double[i] = 0.0;
                bndu.ptr.p_double[i] = 1.0;
                xstart.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
                xorigin.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=k-1; i++)
            {
                ae_v_move(&c.ptr.pp_double[i][0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                ct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xorigin.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b2.ptr.p_double[i] = b.ptr.p_double[i]-v;
            }
            
            /*
             * Solve with SetOrigin() call
             */
            minqpcreate(n, &state, _state);
            testminqpunit_setrandomalgoconvexlc(&state, _state);
            minqpsetlinearterm(&state, &b, _state);
            minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            minqpsetorigin(&state, &xorigin, _state);
            minqpsetbc(&state, &bndl, &bndu, _state);
            minqpsetlc(&state, &c, &ct, k, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xend, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2118");
                continue;
            }
            
            /*
             * Solve problem using explicit origin
             */
            minqpcreate(n, &state2, _state);
            testminqpunit_setrandomalgoconvexlc(&state, _state);
            minqpsetlinearterm(&state2, &b2, _state);
            minqpsetquadraticterm(&state2, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            minqpsetstartingpoint(&state2, &xstart, _state);
            minqpsetbc(&state2, &bndl, &bndu, _state);
            minqpsetlc(&state2, &c, &ct, k, _state);
            minqpoptimize(&state2, _state);
            minqpresults(&state2, &xend2, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2136");
                continue;
            }
            
            /*
             * Calculate function value and XEnd and XEnd2
             */
            f0 = 0.0;
            f1 = 0.0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    f0 = f0+0.5*(xend.ptr.p_double[i]-xorigin.ptr.p_double[i])*a.ptr.pp_double[i][j]*(xend.ptr.p_double[j]-xorigin.ptr.p_double[j]);
                    f1 = f1+0.5*(xend2.ptr.p_double[i]-xorigin.ptr.p_double[i])*a.ptr.pp_double[i][j]*(xend2.ptr.p_double[j]-xorigin.ptr.p_double[j]);
                }
                f0 = f0+(xend.ptr.p_double[i]-xorigin.ptr.p_double[i])*b.ptr.p_double[i];
                f1 = f1+(xend2.ptr.p_double[i]-xorigin.ptr.p_double[i])*b.ptr.p_double[i];
            }
            
            /*
             * Check feasibility properties and compare solutions
             */
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&xend.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                if( ct.ptr.p_int[i]==0 )
                {
                    ae_set_error_flag(err, ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:2163");
                }
                if( ct.ptr.p_int[i]>0 )
                {
                    ae_set_error_flag(err, ae_fp_less(v,c.ptr.pp_double[i][n]-eps), __FILE__, __LINE__, "testminqpunit.ap:2165");
                }
                if( ct.ptr.p_int[i]<0 )
                {
                    ae_set_error_flag(err, ae_fp_greater(v,c.ptr.pp_double[i][n]+eps), __FILE__, __LINE__, "testminqpunit.ap:2167");
                }
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(xend.ptr.p_double[i]-xend2.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:2171");
                ae_set_error_flag(err, ae_fp_less(xend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testminqpunit.ap:2172");
                ae_set_error_flag(err, ae_fp_greater(xend.ptr.p_double[i],(double)(1)), __FILE__, __LINE__, "testminqpunit.ap:2173");
            }
        }
    }
    
    /*
     * Boundary constraints vs linear ones:
     * * N*N SPD A
     * * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
     *   where x1 is some random vector from [-1,+1]
     * * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
     *   with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
     * * initial point xstart is from [-1,+2]
     * * we solve two related QP problems:
     *   a) one with constraints posed as boundary ones
     *   b) another one with same constraints posed as general linear ones
     * both problems must have same solution.
     * Here we test that boundary constrained and linear inequality constrained
     * solvers give same results.
     */
    eps = 1.0E-3;
    for(n=1; n<=6; n++)
    {
        
        /*
         * Generate problem: A, b, x0, XStart, C, CT
         */
        spdmatrixrndcond(n, ae_pow(10.0, 3*ae_randomreal(_state), _state), &a, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x1, n, _state);
        ae_vector_set_length(&xstart, n, _state);
        ae_matrix_set_length(&c, 2*n, n+1, _state);
        ae_vector_set_length(&ct, 2*n, _state);
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            xstart.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
            bndl.ptr.p_double[i] = -(0.1+0.9*ae_randomreal(_state));
            bndu.ptr.p_double[i] = 0.1+0.9*ae_randomreal(_state);
            for(j=0; j<=n-1; j++)
            {
                c.ptr.pp_double[2*i+0][j] = (double)(0);
                c.ptr.pp_double[2*i+1][j] = (double)(0);
            }
            c.ptr.pp_double[2*i+0][i] = (double)(1);
            c.ptr.pp_double[2*i+0][n] = bndl.ptr.p_double[i];
            ct.ptr.p_int[2*i+0] = 1;
            c.ptr.pp_double[2*i+1][i] = (double)(1);
            c.ptr.pp_double[2*i+1][n] = bndu.ptr.p_double[i];
            ct.ptr.p_int[2*i+1] = -1;
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
            b.ptr.p_double[i] = -v;
        }
        
        /*
         * Solve linear inequality constrained problem
         */
        minqpcreate(n, &state, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        minqpsetstartingpoint(&state, &xstart, _state);
        minqpsetlc(&state, &c, &ct, 2*n, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend, &rep, _state);
        
        /*
         * Solve boundary constrained problem
         */
        minqpcreate(n, &state2, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlinearterm(&state2, &b, _state);
        minqpsetquadraticterm(&state2, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        minqpsetstartingpoint(&state2, &xstart, _state);
        minqpsetbc(&state2, &bndl, &bndu, _state);
        minqpoptimize(&state2, _state);
        minqpresults(&state2, &xend2, &rep2, _state);
        
        /*
         * Calculate gradient, check projection
         */
        if( rep.terminationtype<=0||rep2.terminationtype<=0 )
        {
            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2261");
            continue;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_less(xend.ptr.p_double[i],bndl.ptr.p_double[i]-eps), __FILE__, __LINE__, "testminqpunit.ap:2266");
            ae_set_error_flag(err, ae_fp_greater(xend.ptr.p_double[i],bndu.ptr.p_double[i]+eps), __FILE__, __LINE__, "testminqpunit.ap:2267");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(xend.ptr.p_double[i]-xend2.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:2268");
        }
    }
    
    /*
     * Boundary constraints posed as general linear ones:
     * * no bound constraints
     * * 2*N linear constraints 0 <= x[i] <= 1
     * * preconditioner is chosen at random (we just want to be
     *   sure that preconditioning won't prevent us from converging
     *   to the feasible point):
     *   * unit preconditioner
     *   * random diagonal-based preconditioner
     *   * random scale-based preconditioner
     * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
     * * with such simple constraints and function it is easy to find
     *   analytic form of solution: S[i] = bound(x0[i], 0, 1).
     * * however, we can't guarantee that solution is strictly feasible
     *   with respect to nonlinearity constraint, so we check
     *   for approximate feasibility.
     */
    for(n=1; n<=5; n++)
    {
        
        /*
         * Generate X, BL, BU.
         */
        ae_matrix_set_length(&a, n, n, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&xstart, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_matrix_set_length(&c, 2*n, n+1, _state);
        ae_vector_set_length(&ct, 2*n, _state);
        for(i=0; i<=n-1; i++)
        {
            xstart.ptr.p_double[i] = ae_randomreal(_state);
            x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
            b.ptr.p_double[i] = -x0.ptr.p_double[i];
            for(j=0; j<=n; j++)
            {
                c.ptr.pp_double[2*i+0][j] = (double)(0);
                c.ptr.pp_double[2*i+1][j] = (double)(0);
            }
            c.ptr.pp_double[2*i+0][i] = (double)(1);
            c.ptr.pp_double[2*i+0][n] = (double)(0);
            ct.ptr.p_int[2*i+0] = 1;
            c.ptr.pp_double[2*i+1][i] = (double)(1);
            c.ptr.pp_double[2*i+1][n] = (double)(1);
            ct.ptr.p_int[2*i+1] = -1;
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    a.ptr.pp_double[i][j] = (double)(1);
                }
                else
                {
                    a.ptr.pp_double[i][j] = (double)(0);
                }
            }
        }
        
        /*
         * Create and optimize
         */
        minqpcreate(n, &state, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlc(&state, &c, &ct, 2*n, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        minqpsetstartingpoint(&state, &xstart, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2337");
            continue;
        }
        
        /*
         * * compare solution with analytic one
         * * check feasibility
         */
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(xend.ptr.p_double[i]-boundval(x0.ptr.p_double[i], 0.0, 1.0, _state), _state),0.05), __FILE__, __LINE__, "testminqpunit.ap:2347");
            ae_set_error_flag(err, ae_fp_less(xend.ptr.p_double[i],0.0-1.0E-6), __FILE__, __LINE__, "testminqpunit.ap:2348");
            ae_set_error_flag(err, ae_fp_greater(xend.ptr.p_double[i],1.0+1.0E-6), __FILE__, __LINE__, "testminqpunit.ap:2349");
        }
    }
    
    /*
     * Boundary and linear equality/inequality constrained QP problem with
     * excessive constraints:
     * * N*N SPD A with moderate condtion number (up to 100)
     * * boundary constraints 0<=x[i]<=1
     * * K=2*N equality/inequality constraints Q*x = Q*x0, where Q is random matrix,
     *   x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
     * * optimization problem has form 0.5*x'*A*x-b*x,
     *   where b is some random vector
     * * because constraints are excessive, the main problem is to find
     *   feasible point; usually, the only existing feasible point is solution,
     *   so we have to check only feasibility
     */
    eps = 1.0E-4;
    for(n=1; n<=6; n++)
    {
        
        /*
         * Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
         */
        k = 2*n;
        spdmatrixrndcond(n, ae_pow(10.0, 3*ae_randomreal(_state), _state), &a, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&x1, n, _state);
        ae_vector_set_length(&xstart, n, _state);
        ae_matrix_set_length(&c, k, n+1, _state);
        ae_vector_set_length(&ct, k, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            bndl.ptr.p_double[i] = 0.0;
            bndu.ptr.p_double[i] = 1.0;
            xstart.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
        }
        for(i=0; i<=k-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            ct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
            if( ct.ptr.p_int[i]==0 )
            {
                c.ptr.pp_double[i][n] = v;
            }
            if( ct.ptr.p_int[i]>0 )
            {
                c.ptr.pp_double[i][n] = v-1.0E-3;
            }
            if( ct.ptr.p_int[i]<0 )
            {
                c.ptr.pp_double[i][n] = v+1.0E-3;
            }
        }
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        
        /*
         * Create optimizer, solve
         */
        minqpcreate(n, &state, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        minqpsetstartingpoint(&state, &xstart, _state);
        minqpsetbc(&state, &bndl, &bndu, _state);
        minqpsetlc(&state, &c, &ct, k, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend, &rep, _state);
        
        /*
         * Check feasibility properties of the solution
         */
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2425");
            continue;
        }
        for(i=0; i<=k-1; i++)
        {
            v = ae_v_dotproduct(&xend.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            if( ct.ptr.p_int[i]==0 )
            {
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),eps), __FILE__, __LINE__, "testminqpunit.ap:2432");
            }
            if( ct.ptr.p_int[i]>0 )
            {
                ae_set_error_flag(err, ae_fp_less(v,c.ptr.pp_double[i][n]-eps), __FILE__, __LINE__, "testminqpunit.ap:2434");
            }
            if( ct.ptr.p_int[i]<0 )
            {
                ae_set_error_flag(err, ae_fp_greater(v,c.ptr.pp_double[i][n]+eps), __FILE__, __LINE__, "testminqpunit.ap:2436");
            }
        }
    }
    
    /*
     * General inequality constrained problem:
     * * N*N SPD diagonal A with moderate condtion number
     * * no boundary constraints
     * * K=N inequality constraints C*x >= C*x0, where C is N*N well conditioned
     *   matrix, x0 is some random vector [-1,+1]
     * * optimization problem has form 0.5*x'*A*x-b'*x,
     *   where b is random vector from [-1,+1]
     * * using duality, we can obtain solution of QP problem as follows:
     *   a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
     *      is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
     *   b) after we got dual solution ys, we calculate primal solution
     *      xs = inv(A)*(C'*ys-b)
     */
    eps = 1.0E-3;
    for(n=1; n<=6; n++)
    {
        
        /*
         * Generate problem
         */
        ae_vector_set_length(&da, n, _state);
        ae_matrix_set_length(&a, n, n, _state);
        rmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &t2, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&xstart, n, _state);
        ae_matrix_set_length(&c, n, n+1, _state);
        ae_vector_set_length(&ct, n, _state);
        for(i=0; i<=n-1; i++)
        {
            da.ptr.p_double[i] = ae_exp(8*ae_randomreal(_state)-4, _state);
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = (double)(0);
            }
            a.ptr.pp_double[i][i] = da.ptr.p_double[i];
        }
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_v_move(&c.ptr.pp_double[i][0], 1, &t2.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            c.ptr.pp_double[i][n] = v;
            ct.ptr.p_int[i] = 1;
        }
        
        /*
         * Solve primal problem, check feasibility
         */
        minqpcreate(n, &state, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        minqpsetstartingpoint(&state, &xstart, _state);
        minqpsetlc(&state, &c, &ct, n, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2502");
            continue;
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&xend.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            ae_set_error_flag(err, ae_fp_less(v,c.ptr.pp_double[i][n]-eps), __FILE__, __LINE__, "testminqpunit.ap:2508");
        }
        
        /*
         * Generate dual problem:
         * * A2 stores new quadratic term
         * * B2 stores new linear term
         * * BndL/BndU store boundary constraints
         */
        ae_matrix_set_length(&t3, n, n, _state);
        ae_matrix_set_length(&a2, n, n, _state);
        rmatrixtranspose(n, n, &c, 0, 0, &t3, 0, 0, _state);
        for(i=0; i<=n-1; i++)
        {
            v = 1/ae_sqrt(da.ptr.p_double[i], _state);
            ae_v_muld(&t3.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
        }
        rmatrixsyrk(n, n, 1.0, &t3, 0, 0, 2, 0.0, &a2, 0, 0, ae_true, _state);
        ae_vector_set_length(&tmp0, n, _state);
        ae_v_move(&tmp0.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
        for(i=0; i<=n-1; i++)
        {
            tmp0.ptr.p_double[i] = tmp0.ptr.p_double[i]/da.ptr.p_double[i];
        }
        ae_vector_set_length(&b2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &tmp0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            b2.ptr.p_double[i] = -(v+c.ptr.pp_double[i][n]);
        }
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        for(i=0; i<=n-1; i++)
        {
            bndl.ptr.p_double[i] = 0.0;
            bndu.ptr.p_double[i] = _state->v_posinf;
        }
        minqpcreate(n, &state2, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlinearterm(&state2, &b2, _state);
        minqpsetquadraticterm(&state2, &a2, ae_true, _state);
        minqpsetbc(&state2, &bndl, &bndu, _state);
        minqpoptimize(&state2, _state);
        minqpresults(&state2, &xend2, &rep2, _state);
        if( rep2.terminationtype<=0 )
        {
            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2563");
            continue;
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&c.ptr.pp_double[0][i], c.stride, &xend2.ptr.p_double[0], 1, ae_v_len(0,n-1));
            tmp0.ptr.p_double[i] = v-b.ptr.p_double[i];
        }
        for(i=0; i<=n-1; i++)
        {
            tmp0.ptr.p_double[i] = tmp0.ptr.p_double[i]/da.ptr.p_double[i];
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(tmp0.ptr.p_double[i]-xend.ptr.p_double[i], _state),eps*ae_maxreal(ae_fabs(tmp0.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testminqpunit.ap:2576");
        }
    }
    
    /*
     * Boundary and linear equality/inequality constrained QP problem,
     * test checks that different starting points yield same final point:
     * * random N from [1..6], random K from [1..2*N]
     * * N*N SPD A with moderate condtion number (up to 100)
     * * boundary constraints 0<=x[i]<=1
     * * K<2*N linear inequality constraints Q*x <= Q*x0, where
     *   Q is random K*N matrix, x0 is some random vector from the
     *   inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
     * * optimization problem has form 0.5*x'*A*x+b*x,
     *   where b is some random vector with -5<=b[i]<=+5
     * * every component of the initial point XStart is random from [-2,+2]
     * * we perform two starts from random different XStart and compare values
     *   of the target function (although final points may be slightly different,
     *   function values should match each other)
     */
    eps = 1.0E-4;
    for(pass=1; pass<=50; pass++)
    {
        
        /*
         * Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, x1, XStart.
         */
        n = ae_randominteger(5, _state)+2;
        k = ae_randominteger(2*n, _state)+1;
        spdmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &a, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&b2, n, _state);
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&xstart, n, _state);
        ae_vector_set_length(&xstart2, n, _state);
        ae_matrix_set_length(&c, k, n+1, _state);
        ae_vector_set_length(&ct, k, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
            b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            bndl.ptr.p_double[i] = 0.0;
            bndu.ptr.p_double[i] = 1.0;
            xstart.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
            xstart2.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
        }
        for(i=0; i<=k-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            c.ptr.pp_double[i][n] = v;
            ct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
        }
        
        /*
         * Solve with XStart
         */
        minqpcreate(n, &state, _state);
        testminqpunit_setrandomalgoconvexlc(&state, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        minqpsetstartingpoint(&state, &xstart, _state);
        minqpsetbc(&state, &bndl, &bndu, _state);
        minqpsetlc(&state, &c, &ct, k, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2646");
            continue;
        }
        
        /*
         * Solve with XStart2
         */
        minqpsetstartingpoint(&state, &xstart2, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xend2, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminqpunit.ap:2658");
            continue;
        }
        
        /*
         * Calculate function value and XEnd and XEnd2, compare solutions
         */
        f0 = 0.0;
        f1 = 0.0;
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                f0 = f0+0.5*xend.ptr.p_double[i]*a.ptr.pp_double[i][j]*xend.ptr.p_double[j];
                f1 = f1+0.5*xend2.ptr.p_double[i]*a.ptr.pp_double[i][j]*xend2.ptr.p_double[j];
            }
            f0 = f0+xend.ptr.p_double[i]*b.ptr.p_double[i];
            f1 = f1+xend2.ptr.p_double[i]*b.ptr.p_double[i];
        }
        ae_set_error_flag(err, ae_fp_greater(ae_fabs(f0-f1, _state),eps), __FILE__, __LINE__, "testminqpunit.ap:2677");
    }
    
    /*
     * Convex/nonconvex optimization problem with excessive
     * (degenerate constraints):
     *
     * * N=2..5
     * * f = 0.5*x'*A*x+b'*x
     * * b has normally distributed entries with scale 10^BScale
     * * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank
     * * box constraints: x[i] in [-1,+1]
     * * 2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
     *   where v_k is one of 2^N vertices of feasible hypercube, v_shift is
     *   a shift parameter:
     *   * with zero v_shift such constraints are degenerate (each vertex has
     *     N box constraints and one "redundant" linear constraint)
     *   * with positive v_shift linear constraint is always inactive
     *   * with small (about machine epsilon) but negative v_shift,
     *     constraint is close to degenerate - but not exactly
     *
     * We check that constrained gradient is close to zero at solution.
     * Box constraint is considered active if distance to boundary is less
     * than TolConstr.
     *
     * NOTE: TolConstr must be large enough so it won't conflict with
     *       perturbation introduced by v_shift
     */
    tolconstr = 1.0E-8;
    for(n=2; n<=8; n++)
    {
        for(akind=0; akind<=3; akind++)
        {
            for(shiftkind=-5; shiftkind<=1; shiftkind++)
            {
                for(bscale=0; bscale>=-2; bscale--)
                {
                    
                    /*
                     * Generate A, B and initial point
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    ae_vector_set_length(&b, n, _state);
                    ae_vector_set_length(&x, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        b.ptr.p_double[i] = ae_pow((double)(10), (double)(bscale), _state)*hqrndnormal(&rs, _state);
                        x.ptr.p_double[i] = hqrnduniformr(&rs, _state)-0.5;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                    if( akind==1 )
                    {
                        
                        /*
                         * Dense well conditioned SPD
                         */
                        spdmatrixrndcond(n, 50.0, &a, _state);
                    }
                    if( akind==2 )
                    {
                        
                        /*
                         * Dense well conditioned indefinite
                         */
                        smatrixrndcond(n, 50.0, &a, _state);
                    }
                    if( akind==3 )
                    {
                        
                        /*
                         * Low rank
                         */
                        ae_vector_set_length(&tmp, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 0.0;
                            }
                        }
                        for(k=1; k<=ae_minint(3, n-1, _state); k++)
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                tmp.ptr.p_double[i] = hqrndnormal(&rs, _state);
                            }
                            v = hqrndnormal(&rs, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]+v*tmp.ptr.p_double[i]*tmp.ptr.p_double[j];
                                }
                            }
                        }
                    }
                    
                    /*
                     * Generate constraints
                     */
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        bl.ptr.p_double[i] = -1.0;
                        bu.ptr.p_double[i] = 1.0;
                    }
                    ccnt = ae_round(ae_pow((double)(2), (double)(n), _state), _state);
                    ae_matrix_set_length(&c, ccnt, n+1, _state);
                    ae_vector_set_length(&ct, ccnt, _state);
                    for(i=0; i<=ccnt-1; i++)
                    {
                        ct.ptr.p_int[i] = -1;
                        k = i;
                        c.ptr.pp_double[i][n] = ae_sign((double)(shiftkind), _state)*ae_pow((double)(10), ae_fabs((double)(shiftkind), _state), _state)*ae_machineepsilon;
                        for(j=0; j<=n-1; j++)
                        {
                            c.ptr.pp_double[i][j] = (double)(2*(k%2)-1);
                            c.ptr.pp_double[i][n] = c.ptr.pp_double[i][n]+c.ptr.pp_double[i][j]*c.ptr.pp_double[i][j];
                            k = k/2;
                        }
                    }
                    
                    /*
                     * Create and optimize
                     */
                    minqpcreate(n, &state, _state);
                    minqpsetstartingpoint(&state, &x, _state);
                    testminqpunit_setrandomalgononconvexlc(&state, _state);
                    minqpsetbc(&state, &bl, &bu, _state);
                    minqpsetlc(&state, &c, &ct, ccnt, _state);
                    minqpsetlinearterm(&state, &b, _state);
                    minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
                    minqpoptimize(&state, _state);
                    minqpresults(&state, &xs0, &rep, _state);
                    ae_set_error_flag(err, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:2800");
                    if( *err )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    
                    /*
                     * Evaluate gradient at solution and test
                     */
                    vv = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xs0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        v = v+b.ptr.p_double[i];
                        if( ae_fp_less_eq(xs0.ptr.p_double[i],bl.ptr.p_double[i]+tolconstr)&&ae_fp_greater(v,(double)(0)) )
                        {
                            v = 0.0;
                        }
                        if( ae_fp_greater_eq(xs0.ptr.p_double[i],bu.ptr.p_double[i]-tolconstr)&&ae_fp_less(v,(double)(0)) )
                        {
                            v = 0.0;
                        }
                        vv = vv+ae_sqr(v, _state);
                    }
                    vv = ae_sqrt(vv, _state);
                    ae_set_error_flag(err, ae_fp_greater(vv,1.0E-5), __FILE__, __LINE__, "testminqpunit.ap:2824");
                }
            }
        }
    }
    
    /*
     * Convex/nonconvex optimization problem with combination of
     * box and linear constraints:
     *
     * * N=2..8
     * * f = 0.5*x'*A*x+b'*x
     * * b has normally distributed entries with scale 10^BScale
     * * several kinds of A are tried: zero, well conditioned SPD,
     *   well conditioned indefinite, low rank
     * * box constraints: x[i] in [-1,+1]
     * * initial point x0 = [0 0 ... 0 0]
     * * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
     *   random mix of equality/inequality constraints is tried.
     *   x0 is guaranteed to be feasible.
     *
     * We check that constrained gradient is close to zero at solution.
     * Inequality constraint is considered active if distance to boundary
     * is less than TolConstr. We use nonnegative least squares solver
     * in order to compute constrained gradient.
     */
    tolconstr = 1.0E-8;
    for(n=2; n<=8; n++)
    {
        for(akind=0; akind<=3; akind++)
        {
            for(bscale=0; bscale>=-2; bscale--)
            {
                
                /*
                 * Generate A, B and initial point
                 */
                ae_matrix_set_length(&a, n, n, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&x, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    b.ptr.p_double[i] = ae_pow((double)(10), (double)(bscale), _state)*hqrndnormal(&rs, _state);
                    x.ptr.p_double[i] = 0.0;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = 0.0;
                    }
                }
                if( akind==1 )
                {
                    
                    /*
                     * Dense well conditioned SPD
                     */
                    spdmatrixrndcond(n, 50.0, &a, _state);
                }
                if( akind==2 )
                {
                    
                    /*
                     * Dense well conditioned indefinite
                     */
                    smatrixrndcond(n, 50.0, &a, _state);
                }
                if( akind==3 )
                {
                    
                    /*
                     * Low rank
                     */
                    ae_vector_set_length(&tmp, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                    for(k=1; k<=ae_minint(3, n-1, _state); k++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            tmp.ptr.p_double[i] = hqrndnormal(&rs, _state);
                        }
                        v = hqrndnormal(&rs, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]+v*tmp.ptr.p_double[i]*tmp.ptr.p_double[j];
                            }
                        }
                    }
                }
                
                /*
                 * Generate constraints
                 */
                ae_vector_set_length(&bl, n, _state);
                ae_vector_set_length(&bu, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    bl.ptr.p_double[i] = -1.0;
                    bu.ptr.p_double[i] = 1.0;
                }
                ccnt = ae_minint(3, n-1, _state);
                ae_matrix_set_length(&c, ccnt, n+1, _state);
                ae_vector_set_length(&ct, ccnt, _state);
                for(i=0; i<=ccnt-1; i++)
                {
                    ct.ptr.p_int[i] = hqrnduniformi(&rs, 3, _state)-1;
                    c.ptr.pp_double[i][n] = 0.0;
                    for(j=0; j<=n-1; j++)
                    {
                        c.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                    }
                }
                
                /*
                 * Create and optimize
                 */
                minqpcreate(n, &state, _state);
                minqpsetstartingpoint(&state, &x, _state);
                testminqpunit_setrandomalgononconvexlc(&state, _state);
                minqpsetbc(&state, &bl, &bu, _state);
                minqpsetlc(&state, &c, &ct, ccnt, _state);
                minqpsetlinearterm(&state, &b, _state);
                minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
                minqpoptimize(&state, _state);
                minqpresults(&state, &xs0, &rep, _state);
                ae_set_error_flag(err, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:2934");
                if( *err )
                {
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * 1. evaluate unconstrained gradient at solution
                 *
                 * 2. calculate constrained gradient (NNLS solver is used
                 *    to evaluate gradient subject to active constraints).
                 *    In order to do this we form CE matrix, matrix of active
                 *    constraints (columns store constraint vectors). Then
                 *    we try to approximate gradient vector by columns of CE,
                 *    subject to non-negativity restriction placed on variables
                 *    corresponding to inequality constraints.
                 *
                 *    Residual from such regression is a constrained gradient vector.
                 */
                ae_vector_set_length(&g, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xs0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    g.ptr.p_double[i] = v+b.ptr.p_double[i];
                }
                ae_matrix_set_length(&ce, n, n+ccnt, _state);
                ae_vector_set_length(&nonnegative, n+ccnt, _state);
                k = 0;
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(err, ae_fp_less(xs0.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:2962");
                    ae_set_error_flag(err, ae_fp_greater(xs0.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:2963");
                    if( ae_fp_less_eq(xs0.ptr.p_double[i],bl.ptr.p_double[i]+tolconstr) )
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ce.ptr.pp_double[j][k] = 0.0;
                        }
                        ce.ptr.pp_double[i][k] = 1.0;
                        nonnegative.ptr.p_bool[k] = ae_true;
                        inc(&k, _state);
                        continue;
                    }
                    if( ae_fp_greater_eq(xs0.ptr.p_double[i],bu.ptr.p_double[i]-tolconstr) )
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ce.ptr.pp_double[j][k] = 0.0;
                        }
                        ce.ptr.pp_double[i][k] = -1.0;
                        nonnegative.ptr.p_bool[k] = ae_true;
                        inc(&k, _state);
                        continue;
                    }
                }
                for(i=0; i<=ccnt-1; i++)
                {
                    v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &xs0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    v = v-c.ptr.pp_double[i][n];
                    ae_set_error_flag(err, ct.ptr.p_int[i]==0&&ae_fp_greater(ae_fabs(v, _state),tolconstr), __FILE__, __LINE__, "testminqpunit.ap:2987");
                    ae_set_error_flag(err, ct.ptr.p_int[i]>0&&ae_fp_less(v,-tolconstr), __FILE__, __LINE__, "testminqpunit.ap:2988");
                    ae_set_error_flag(err, ct.ptr.p_int[i]<0&&ae_fp_greater(v,tolconstr), __FILE__, __LINE__, "testminqpunit.ap:2989");
                    if( ct.ptr.p_int[i]==0 )
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ce.ptr.pp_double[j][k] = c.ptr.pp_double[i][j];
                        }
                        nonnegative.ptr.p_bool[k] = ae_false;
                        inc(&k, _state);
                        continue;
                    }
                    if( (ct.ptr.p_int[i]>0&&ae_fp_less_eq(v,tolconstr))||(ct.ptr.p_int[i]<0&&ae_fp_greater_eq(v,-tolconstr)) )
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ce.ptr.pp_double[j][k] = ae_sign((double)(ct.ptr.p_int[i]), _state)*c.ptr.pp_double[i][j];
                        }
                        nonnegative.ptr.p_bool[k] = ae_true;
                        inc(&k, _state);
                        continue;
                    }
                }
                snnlsinit(0, 0, 0, &nnls, _state);
                snnlssetproblem(&nnls, &ce, &g, 0, k, n, _state);
                for(i=0; i<=k-1; i++)
                {
                    if( !nonnegative.ptr.p_bool[i] )
                    {
                        snnlsdropnnc(&nnls, i, _state);
                    }
                }
                snnlssolve(&nnls, &tmp, _state);
                for(i=0; i<=k-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        g.ptr.p_double[j] = g.ptr.p_double[j]-tmp.ptr.p_double[i]*ce.ptr.pp_double[j][i];
                    }
                }
                vv = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                vv = ae_sqrt(vv, _state);
                ae_set_error_flag(err, ae_fp_greater(vv,1.0E-5), __FILE__, __LINE__, "testminqpunit.ap:3018");
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests linearly constrained QP solvers.

On failure sets Err to True; on success leaves it unchanged.
*************************************************************************/
static void testminqpunit_generallcqptest(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t solvertype;
    double v;
    double vv;
    ae_bool bflag;
    ae_int_t pass;
    ae_matrix rawa;
    ae_matrix a;
    sparsematrix sa;
    ae_vector b;
    ae_vector b2;
    ae_vector xs;
    ae_vector x0;
    ae_vector x1;
    ae_vector x2;
    ae_vector xorigin;
    ae_vector s;
    ae_vector da;
    ae_vector xstart;
    ae_vector g;
    ae_vector gs;
    ae_vector tmp;
    ae_vector tmp0;
    ae_vector tmp1;
    ae_vector tmp2;
    ae_vector lagrange;
    ae_vector bndl;
    ae_vector bndu;
    ae_matrix rawc;
    ae_vector rawct;
    ae_int_t rawccnt;
    ae_matrix densec;
    ae_vector densect;
    ae_int_t denseccnt;
    sparsematrix sparsec;
    ae_vector sparsect;
    ae_int_t sparseccnt;
    ae_matrix activeset;
    ae_vector activeeq;
    ae_int_t nactive;
    snnlssolver nnls;
    double constraintsrcond;
    ae_vector svdw;
    ae_matrix svdu;
    ae_matrix svdvt;
    minqpstate state;
    minqpreport rep;
    minqpstate state2;
    minqpreport rep2;
    double f0;
    double f1;
    double xtol;
    double ftol;
    double gtol;
    double tolconstr;
    ae_int_t bscale;
    ae_int_t akind;
    ae_matrix ce;
    ae_matrix q;
    ae_matrix t2;
    ae_matrix t3;
    ae_matrix a2;
    ae_vector nonnegative;
    double mx;
    ae_int_t shiftkind;
    double bleicepsx;
    double aulepsx;
    double aulrho;
    ae_int_t aulits;
    ae_matrix kkt;
    ae_vector kktright;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&rawa, 0, sizeof(rawa));
    memset(&a, 0, sizeof(a));
    memset(&sa, 0, sizeof(sa));
    memset(&b, 0, sizeof(b));
    memset(&b2, 0, sizeof(b2));
    memset(&xs, 0, sizeof(xs));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&xorigin, 0, sizeof(xorigin));
    memset(&s, 0, sizeof(s));
    memset(&da, 0, sizeof(da));
    memset(&xstart, 0, sizeof(xstart));
    memset(&g, 0, sizeof(g));
    memset(&gs, 0, sizeof(gs));
    memset(&tmp, 0, sizeof(tmp));
    memset(&tmp0, 0, sizeof(tmp0));
    memset(&tmp1, 0, sizeof(tmp1));
    memset(&tmp2, 0, sizeof(tmp2));
    memset(&lagrange, 0, sizeof(lagrange));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&rawc, 0, sizeof(rawc));
    memset(&rawct, 0, sizeof(rawct));
    memset(&densec, 0, sizeof(densec));
    memset(&densect, 0, sizeof(densect));
    memset(&sparsec, 0, sizeof(sparsec));
    memset(&sparsect, 0, sizeof(sparsect));
    memset(&activeset, 0, sizeof(activeset));
    memset(&activeeq, 0, sizeof(activeeq));
    memset(&nnls, 0, sizeof(nnls));
    memset(&svdw, 0, sizeof(svdw));
    memset(&svdu, 0, sizeof(svdu));
    memset(&svdvt, 0, sizeof(svdvt));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&state2, 0, sizeof(state2));
    memset(&rep2, 0, sizeof(rep2));
    memset(&ce, 0, sizeof(ce));
    memset(&q, 0, sizeof(q));
    memset(&t2, 0, sizeof(t2));
    memset(&t3, 0, sizeof(t3));
    memset(&a2, 0, sizeof(a2));
    memset(&nonnegative, 0, sizeof(nonnegative));
    memset(&kkt, 0, sizeof(kkt));
    memset(&kktright, 0, sizeof(kktright));
    _hqrndstate_init(&rs, _state, ae_true);
    ae_matrix_init(&rawa, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xorigin, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&da, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xstart, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lagrange, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rawc, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rawct, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&densec, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&densect, 0, DT_INT, _state, ae_true);
    _sparsematrix_init(&sparsec, _state, ae_true);
    ae_vector_init(&sparsect, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&activeset, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&activeeq, 0, DT_BOOL, _state, ae_true);
    _snnlssolver_init(&nnls, _state, ae_true);
    ae_vector_init(&svdw, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&svdu, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&svdvt, 0, 0, DT_REAL, _state, ae_true);
    _minqpstate_init(&state, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    _minqpstate_init(&state2, _state, ae_true);
    _minqpreport_init(&rep2, _state, ae_true);
    ae_matrix_init(&ce, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t3, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&nonnegative, 0, DT_BOOL, _state, ae_true);
    ae_matrix_init(&kkt, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&kktright, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    bleicepsx = 1.0E-9;
    aulepsx = 1.0E-12;
    aulrho = 1.0E3;
    aulits = 15;
    
    /*
     * SMALL-SCALE TESTS: many tests for small N's
     */
    for(solvertype=0; solvertype<=1; solvertype++)
    {
        
        /*
         * Test random linearly constrained QP problem with known answer:
         * * generate random A and b
         * * generate random solution XS
         * * calculate unconstrained gradient GS at XS
         * * generate random box/linear constraints C, with some of them being
         *   active at XS, and some being inactive. Calculate residual gradient
         *   GP after projection of GS onto active set, add one more constraint
         *   equal to +-(GP-GS).
         *
         * We test here BLEIC and Dense-AUL solvers, with A being passed
         * in dense or sparse format, and linear constraints C being passed
         * as dense, sparse or mixed ones.
         */
        for(n=1; n<=10; n++)
        {
            
            /*
             * Generate random A, b and xs
             */
            spdmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &rawa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&xs, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                xs.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            
            /*
             * Generate well conditioned "raw" constraints:
             * * generate random box and CCnt-1 linear constraints
             * * determine active set, calculate its condition number
             * * repeat until condition number is good enough
             *   (better than 1E2; larger values sometimes result in
             *   spurious failures)
             */
            ae_vector_set_length(&gs, n, _state);
            ae_vector_set_length(&tmp, n, _state);
            rmatrixmv(n, n, &rawa, 0, 0, 0, &xs, 0, &gs, 0, _state);
            for(i=0; i<=n-1; i++)
            {
                gs.ptr.p_double[i] = gs.ptr.p_double[i]+b.ptr.p_double[i];
            }
            rawccnt = 1+hqrnduniformi(&rs, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_matrix_set_length(&activeset, n, n+rawccnt, _state);
            ae_vector_set_length(&activeeq, n+rawccnt, _state);
            ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
            ae_vector_set_length(&rawct, rawccnt, _state);
            do
            {
                nactive = 0;
                for(i=0; i<=n-1; i++)
                {
                    bndl.ptr.p_double[i] = xs.ptr.p_double[i]-1-hqrnduniformr(&rs, _state);
                    bndu.ptr.p_double[i] = xs.ptr.p_double[i]+1+hqrnduniformr(&rs, _state);
                    if( ae_fp_less(hqrnduniformr(&rs, _state),0.66) )
                    {
                        
                        /*
                         * I-th box constraint is inactive
                         */
                        continue;
                    }
                    if( ae_fp_greater(hqrnduniformr(&rs, _state),0.50) )
                    {
                        
                        /*
                         * I-th box constraint is equality one
                         */
                        bndl.ptr.p_double[i] = xs.ptr.p_double[i];
                        bndu.ptr.p_double[i] = xs.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            activeset.ptr.pp_double[j][nactive] = (double)(0);
                        }
                        activeset.ptr.pp_double[i][nactive] = (double)(1);
                        activeeq.ptr.p_bool[nactive] = ae_true;
                        nactive = nactive+1;
                    }
                    else
                    {
                        
                        /*
                         * I-th box constraint is inequality one
                         */
                        for(j=0; j<=n-1; j++)
                        {
                            activeset.ptr.pp_double[j][nactive] = (double)(0);
                        }
                        if( ae_fp_greater(gs.ptr.p_double[i],(double)(0)) )
                        {
                            bndl.ptr.p_double[i] = xs.ptr.p_double[i];
                            activeset.ptr.pp_double[i][nactive] = (double)(-1);
                            if( ae_fp_greater(hqrnduniformr(&rs, _state),0.50) )
                            {
                                bndu.ptr.p_double[i] = _state->v_posinf;
                            }
                        }
                        else
                        {
                            bndu.ptr.p_double[i] = xs.ptr.p_double[i];
                            activeset.ptr.pp_double[i][nactive] = (double)(1);
                            if( ae_fp_greater(hqrnduniformr(&rs, _state),0.50) )
                            {
                                bndl.ptr.p_double[i] = _state->v_neginf;
                            }
                        }
                        activeeq.ptr.p_bool[nactive] = ae_false;
                        nactive = nactive+1;
                    }
                }
                for(i=0; i<=rawccnt-2; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ae_fp_less(hqrnduniformr(&rs, _state),0.50) )
                        {
                            rawc.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                        }
                        else
                        {
                            rawc.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                    rawc.ptr.pp_double[i][hqrnduniformi(&rs, n, _state)] = hqrndnormal(&rs, _state);
                    rawc.ptr.pp_double[i][n] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        rawc.ptr.pp_double[i][n] = rawc.ptr.pp_double[i][n]+rawc.ptr.pp_double[i][j]*xs.ptr.p_double[j];
                    }
                    rawct.ptr.p_int[i] = -1;
                    if( ae_fp_less(hqrnduniformr(&rs, _state),0.66) )
                    {
                        
                        /*
                         * I-th box constraint is inactive
                         */
                        rawc.ptr.pp_double[i][n] = rawc.ptr.pp_double[i][n]+(1+hqrnduniformr(&rs, _state));
                        if( ae_fp_greater(hqrnduniformr(&rs, _state),0.50) )
                        {
                            ae_v_muld(&rawc.ptr.pp_double[i][0], 1, ae_v_len(0,n), -1);
                            rawct.ptr.p_int[i] = -rawct.ptr.p_int[i];
                        }
                        continue;
                    }
                    if( ae_fp_greater(hqrnduniformr(&rs, _state),0.50) )
                    {
                        
                        /*
                         * I-th box constraint is equality one
                         */
                        rawct.ptr.p_int[i] = 0;
                        for(j=0; j<=n-1; j++)
                        {
                            activeset.ptr.pp_double[j][nactive] = rawc.ptr.pp_double[i][j];
                        }
                        activeeq.ptr.p_bool[nactive] = ae_true;
                        nactive = nactive+1;
                    }
                    else
                    {
                        
                        /*
                         * I-th box constraint is inequality one
                         */
                        v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &gs.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        if( ae_fp_greater(v,(double)(0)) )
                        {
                            rawct.ptr.p_int[i] = 1;
                            for(j=0; j<=n-1; j++)
                            {
                                activeset.ptr.pp_double[j][nactive] = -rawc.ptr.pp_double[i][j];
                            }
                        }
                        else
                        {
                            rawct.ptr.p_int[i] = -1;
                            for(j=0; j<=n-1; j++)
                            {
                                activeset.ptr.pp_double[j][nactive] = rawc.ptr.pp_double[i][j];
                            }
                        }
                        activeeq.ptr.p_bool[nactive] = ae_false;
                        nactive = nactive+1;
                    }
                }
                ae_v_moveneg(&tmp.ptr.p_double[0], 1, &gs.ptr.p_double[0], 1, ae_v_len(0,n-1));
                snnlsinit(0, 0, 0, &nnls, _state);
                snnlssetproblem(&nnls, &activeset, &tmp, 0, nactive, n, _state);
                for(i=0; i<=nactive-1; i++)
                {
                    if( activeeq.ptr.p_bool[i] )
                    {
                        snnlsdropnnc(&nnls, i, _state);
                    }
                }
                snnlssolve(&nnls, &lagrange, _state);
                ae_v_moveneg(&tmp.ptr.p_double[0], 1, &gs.ptr.p_double[0], 1, ae_v_len(0,n-1));
                for(i=0; i<=nactive-1; i++)
                {
                    v = lagrange.ptr.p_double[i];
                    ae_v_subd(&tmp.ptr.p_double[0], 1, &activeset.ptr.pp_double[0][i], activeset.stride, ae_v_len(0,n-1), v);
                }
                ae_v_move(&rawc.ptr.pp_double[rawccnt-1][0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,n-1));
                v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &xs.ptr.p_double[0], 1, ae_v_len(0,n-1));
                rawc.ptr.pp_double[rawccnt-1][n] = v;
                rawct.ptr.p_int[rawccnt-1] = -1;
                
                /*
                 * Calculate reciprocal condition number
                 */
                if( nactive>0 )
                {
                    bflag = rmatrixsvd(&activeset, n, nactive, 0, 0, 0, &svdw, &svdu, &svdvt, _state);
                    ae_assert(bflag, "MinQPTest: integrity failure", _state);
                    constraintsrcond = svdw.ptr.p_double[ae_minint(nactive, n, _state)-1]/svdw.ptr.p_double[0];
                }
                else
                {
                    constraintsrcond = (double)(1);
                }
                
                /*
                 * Check RCond
                 */
            }
            while(ae_fp_less(constraintsrcond,0.01));
            
            /*
             * Create optimizer, solve
             */
            minqpcreate(n, &state, _state);
            if( solvertype==0 )
            {
                minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                }
                else
                {
                    ae_assert(ae_false, "unexpected solver type", _state);
                }
            }
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&rawa, n, &state, &rs, _state);
            minqpsetbc(&state, &bndl, &bndu, _state);
            testminqpunit_randomlysplitandsetlc(&rawc, &rawct, rawccnt, n, &state, &rs, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            
            /*
             * Test
             *
             * Because constrained problems are often ill-conditioned,
             * we do NOT compare X1 with XS directly. Instead, we:
             * a) compare function values at X1 and XS with good precision
             * b) check constraint violation with good precision
             * c) perform comparison for |X1-XS| with LOW precision
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:3327");
                ae_frame_leave(_state);
                return;
            }
            f0 = (double)(0);
            f1 = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                f0 = f0+b.ptr.p_double[i]*xs.ptr.p_double[i];
                f1 = f1+b.ptr.p_double[i]*x1.ptr.p_double[i];
                for(j=0; j<=n-1; j++)
                {
                    f0 = f0+0.5*xs.ptr.p_double[i]*rawa.ptr.pp_double[i][j]*xs.ptr.p_double[j];
                    f1 = f1+0.5*x1.ptr.p_double[i]*rawa.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                }
            }
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(f0-f1, _state),1.0E-3), __FILE__, __LINE__, "testminqpunit.ap:3342");
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-xs.ptr.p_double[i], _state),1.0E-2), __FILE__, __LINE__, "testminqpunit.ap:3344");
            }
        }
        
        /*
         * Inequality constrained problem:
         * * N*N diagonal A
         * * one inequality constraint q'*x>=0, where q is random unit vector
         * * optimization problem has form 0.5*x'*A*x-(xs*A)*x,
         *   where xs is some random vector
         * * either:
         *   a) xs is feasible => we must stop at xs
         *   b) xs is infeasible => we must stop at the boundary q'*x=0 and
         *      projection of gradient onto q*x=0 must be zero
         *
         * NOTE: we make several passes because some specific kind of errors is rarely
         *       caught by this test, so we need several repetitions.
         */
        xtol = 1.0E-4;
        gtol = 1.0E-4;
        for(n=2; n<=6; n++)
        {
            for(pass=0; pass<=4; pass++)
            {
                
                /*
                 * Generate problem: A, b, CMatrix, x0, XStart
                 */
                spdmatrixrndcond(n, ae_pow(10.0, 3*ae_randomreal(_state), _state), &a, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&xs, n, _state);
                ae_vector_set_length(&xstart, n, _state);
                ae_matrix_set_length(&rawc, 1, n+1, _state);
                ae_vector_set_length(&rawct, 1, _state);
                for(i=0; i<=n-1; i++)
                {
                    xs.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                do
                {
                    v = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        rawc.ptr.pp_double[0][i] = 2*ae_randomreal(_state)-1;
                        v = v+ae_sqr(rawc.ptr.pp_double[0][i], _state);
                    }
                    v = ae_sqrt(v, _state);
                }
                while(ae_fp_eq(v,(double)(0)));
                for(i=0; i<=n-1; i++)
                {
                    rawc.ptr.pp_double[0][i] = rawc.ptr.pp_double[0][i]/v;
                }
                rawc.ptr.pp_double[0][n] = (double)(0);
                rawct.ptr.p_int[0] = 1;
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xs.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    b.ptr.p_double[i] = -v;
                }
                
                /*
                 * Create optimizer, solve
                 */
                minqpcreate(n, &state, _state);
                if( solvertype==0 )
                {
                    minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "unexpected solver type", _state);
                    }
                }
                minqpsetlinearterm(&state, &b, _state);
                testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
                minqpsetstartingpoint(&state, &xstart, _state);
                testminqpunit_randomlysplitandsetlc(&rawc, &rawct, 1, n, &state, &rs, _state);
                minqpoptimize(&state, _state);
                minqpresults(&state, &x1, &rep, _state);
                
                /*
                 * Test
                 */
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:3423");
                    ae_frame_leave(_state);
                    return;
                }
                v = ae_v_dotproduct(&xs.ptr.p_double[0], 1, &rawc.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                if( ae_fp_greater_eq(v,(double)(0)) )
                {
                    
                    /*
                     * XS is feasible
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-xs.ptr.p_double[i], _state),xtol), __FILE__, __LINE__, "testminqpunit.ap:3433");
                    }
                }
                else
                {
                    
                    /*
                     * XS is infeasible:
                     * * X1 must be approximately feasible
                     * * gradient projection onto c'*x=0 must be zero
                     */
                    v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &rawc.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                    ae_set_error_flag(errorflag, ae_fp_less(v,-xtol), __FILE__, __LINE__, "testminqpunit.ap:3443");
                    ae_vector_set_length(&g, n, _state);
                    ae_v_move(&g.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        g.ptr.p_double[i] = g.ptr.p_double[i]+v;
                    }
                    v = ae_v_dotproduct(&g.ptr.p_double[0], 1, &rawc.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                    ae_v_subd(&g.ptr.p_double[0], 1, &rawc.ptr.pp_double[0][0], 1, ae_v_len(0,n-1), v);
                    v = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_sqrt(v, _state),gtol), __FILE__, __LINE__, "testminqpunit.ap:3454");
                }
            }
        }
        
        /*
         * Box constraints vs linear ones:
         * * N*N SPD A
         * * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
         *   where x1 is some random vector from [-1,+1]
         * * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
         *   with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
         * * initial point xstart is from [-1,+2]
         * * we solve two related QP problems:
         *   a) one with constraints posed as boundary ones
         *   b) another one with same constraints posed as general linear ones
         * both problems must have same solution.
         * Here we test that boundary constrained and linear inequality constrained
         * solvers give same results.
         */
        xtol = 1.0E-5;
        for(n=1; n<=6; n++)
        {
            
            /*
             * Generate problem: A, b, x0, XStart, C, CT
             */
            spdmatrixrndcond(n, ae_pow(10.0, 3*ae_randomreal(_state), _state), &a, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x1, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&rawc, 2*n, n+1, _state);
            ae_vector_set_length(&rawct, 2*n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xstart.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                bndl.ptr.p_double[i] = -(0.1+0.9*ae_randomreal(_state));
                bndu.ptr.p_double[i] = 0.1+0.9*ae_randomreal(_state);
                for(j=0; j<=n-1; j++)
                {
                    rawc.ptr.pp_double[2*i+0][j] = (double)(0);
                    rawc.ptr.pp_double[2*i+1][j] = (double)(0);
                }
                rawc.ptr.pp_double[2*i+0][i] = (double)(1);
                rawc.ptr.pp_double[2*i+0][n] = bndl.ptr.p_double[i];
                rawct.ptr.p_int[2*i+0] = 1;
                rawc.ptr.pp_double[2*i+1][i] = (double)(1);
                rawc.ptr.pp_double[2*i+1][n] = bndu.ptr.p_double[i];
                rawct.ptr.p_int[2*i+1] = -1;
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = -v;
            }
            
            /*
             * Solve linear inequality constrained problem
             */
            minqpcreate(n, &state, _state);
            if( solvertype==0 )
            {
                minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                }
                else
                {
                    ae_assert(ae_false, "unexpected solver type", _state);
                }
            }
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            testminqpunit_randomlysplitandsetlc(&rawc, &rawct, 2*n, n, &state, &rs, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            
            /*
             * Solve boundary constrained problem
             */
            minqpcreate(n, &state2, _state);
            if( solvertype==0 )
            {
                minqpsetalgobleic(&state2, 0.0, 0.0, bleicepsx, 0, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minqpsetalgodenseaul(&state2, aulepsx, aulrho, aulits, _state);
                }
                else
                {
                    ae_assert(ae_false, "unexpected solver type", _state);
                }
            }
            minqpsetlinearterm(&state2, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state2, &rs, _state);
            minqpsetstartingpoint(&state2, &xstart, _state);
            minqpsetbc(&state2, &bndl, &bndu, _state);
            minqpoptimize(&state2, _state);
            minqpresults(&state2, &x2, &rep2, _state);
            
            /*
             * Calculate gradient, check projection
             */
            if( rep.terminationtype<=0||rep2.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:3552");
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]-xtol), __FILE__, __LINE__, "testminqpunit.ap:3557");
                ae_set_error_flag(errorflag, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]+xtol), __FILE__, __LINE__, "testminqpunit.ap:3558");
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x2.ptr.p_double[i], _state),xtol), __FILE__, __LINE__, "testminqpunit.ap:3559");
            }
        }
        
        /*
         * Convex/nonconvex optimization problem with combination of
         * box and linear constraints:
         *
         * * N=2..8
         * * f = 0.5*x'*A*x+b'*x
         * * b has normally distributed entries with scale 10^BScale
         * * several kinds of A are tried: zero, well conditioned SPD,
         *   well conditioned indefinite, low rank
         * * box constraints: x[i] in [-1,+1]
         * * initial point x0 = [0 0 ... 0 0]
         * * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
         *   random mix of equality/inequality constraints is tried, moderate
         *   condition number is guaranteed, x0 is guaranteed to be feasible.
         *
         * We check that constrained gradient is close to zero at solution.
         * Inequality constraint is considered active if distance to boundary
         * is less than TolConstr. We use nonnegative least squares solver
         * in order to compute constrained gradient.
         */
        tolconstr = (double)(-99999);
        for(n=2; n<=8; n++)
        {
            for(akind=0; akind<=3; akind++)
            {
                for(bscale=1; bscale>=-1; bscale--)
                {
                    
                    /*
                     * Dense-AUL solver has lower precision on rank-deficient
                     * problems, so we skip AKind=3
                     */
                    if( solvertype==1&&akind==3 )
                    {
                        continue;
                    }
                    
                    /*
                     * Set up tolerances
                     */
                    if( solvertype==0 )
                    {
                        tolconstr = 1.0E-8;
                    }
                    else
                    {
                        if( solvertype==1 )
                        {
                            tolconstr = 1.0E-3;
                            if( akind==3 )
                            {
                                tolconstr = tolconstr*5;
                            }
                        }
                        else
                        {
                            ae_assert(ae_false, "unexpected solver type", _state);
                        }
                    }
                    gtol = 1.0E-4;
                    
                    /*
                     * Generate A, B and initial point
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    ae_vector_set_length(&b, n, _state);
                    ae_vector_set_length(&xstart, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        b.ptr.p_double[i] = ae_pow((double)(10), (double)(bscale), _state)*hqrndnormal(&rs, _state);
                        xstart.ptr.p_double[i] = 0.0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                    if( akind==1 )
                    {
                        
                        /*
                         * Dense well conditioned SPD
                         */
                        spdmatrixrndcond(n, 50.0, &a, _state);
                    }
                    if( akind==2 )
                    {
                        
                        /*
                         * Dense well conditioned indefinite
                         */
                        smatrixrndcond(n, 50.0, &a, _state);
                    }
                    if( akind==3 )
                    {
                        
                        /*
                         * Low rank
                         */
                        ae_vector_set_length(&tmp, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 0.0;
                            }
                        }
                        for(k=1; k<=ae_minint(3, n-1, _state); k++)
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                tmp.ptr.p_double[i] = hqrndnormal(&rs, _state);
                            }
                            v = hqrndnormal(&rs, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]+v*tmp.ptr.p_double[i]*tmp.ptr.p_double[j];
                                }
                            }
                        }
                    }
                    
                    /*
                     * Generate constraints
                     */
                    ae_vector_set_length(&bndl, n, _state);
                    ae_vector_set_length(&bndu, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        bndl.ptr.p_double[i] = -1.0;
                        bndu.ptr.p_double[i] = 1.0;
                    }
                    rawccnt = ae_minint(3, n-1, _state);
                    ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
                    ae_vector_set_length(&rawct, rawccnt, _state);
                    do
                    {
                        for(i=0; i<=rawccnt-1; i++)
                        {
                            rawct.ptr.p_int[i] = hqrnduniformi(&rs, 3, _state)-1;
                            rawc.ptr.pp_double[i][n] = 0.0;
                            for(j=0; j<=n-1; j++)
                            {
                                rawc.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                            }
                        }
                    }
                    while(ae_fp_less_eq(testminqpunit_getconstraintrcond(&rawc, rawccnt, n, _state),0.01));
                    
                    /*
                     * Create and optimize
                     */
                    minqpcreate(n, &state, _state);
                    minqpsetstartingpoint(&state, &xstart, _state);
                    if( solvertype==0 )
                    {
                        minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
                    }
                    else
                    {
                        if( solvertype==1 )
                        {
                            minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                        }
                        else
                        {
                            ae_assert(ae_false, "unexpected solver type", _state);
                        }
                    }
                    minqpsetbc(&state, &bndl, &bndu, _state);
                    testminqpunit_randomlysplitandsetlc(&rawc, &rawct, rawccnt, n, &state, &rs, _state);
                    minqpsetlinearterm(&state, &b, _state);
                    testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
                    minqpoptimize(&state, _state);
                    minqpresults(&state, &x1, &rep, _state);
                    ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:3701");
                    if( *errorflag )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    
                    /*
                     * 1. evaluate unconstrained gradient at solution
                     *
                     * 2. calculate constrained gradient (NNLS solver is used
                     *    to evaluate gradient subject to active constraints).
                     *    In order to do this we form CE matrix, matrix of active
                     *    constraints (columns store constraint vectors). Then
                     *    we try to approximate gradient vector by columns of CE,
                     *    subject to non-negativity restriction placed on variables
                     *    corresponding to inequality constraints.
                     *
                     *    Residual from such regression is a constrained gradient vector.
                     */
                    ae_vector_set_length(&g, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        g.ptr.p_double[i] = v+b.ptr.p_double[i];
                    }
                    ae_matrix_set_length(&ce, n, n+rawccnt, _state);
                    ae_vector_set_length(&nonnegative, n+rawccnt, _state);
                    k = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:3729");
                        ae_set_error_flag(errorflag, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminqpunit.ap:3730");
                        if( ae_fp_less_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i]+tolconstr) )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ce.ptr.pp_double[j][k] = 0.0;
                            }
                            ce.ptr.pp_double[i][k] = 1.0;
                            nonnegative.ptr.p_bool[k] = ae_true;
                            inc(&k, _state);
                            continue;
                        }
                        if( ae_fp_greater_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i]-tolconstr) )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ce.ptr.pp_double[j][k] = 0.0;
                            }
                            ce.ptr.pp_double[i][k] = -1.0;
                            nonnegative.ptr.p_bool[k] = ae_true;
                            inc(&k, _state);
                            continue;
                        }
                    }
                    for(i=0; i<=rawccnt-1; i++)
                    {
                        v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        v = v-rawc.ptr.pp_double[i][n];
                        ae_set_error_flag(errorflag, rawct.ptr.p_int[i]==0&&ae_fp_greater(ae_fabs(v, _state),tolconstr), __FILE__, __LINE__, "testminqpunit.ap:3754");
                        ae_set_error_flag(errorflag, rawct.ptr.p_int[i]>0&&ae_fp_less(v,-tolconstr), __FILE__, __LINE__, "testminqpunit.ap:3755");
                        ae_set_error_flag(errorflag, rawct.ptr.p_int[i]<0&&ae_fp_greater(v,tolconstr), __FILE__, __LINE__, "testminqpunit.ap:3756");
                        if( rawct.ptr.p_int[i]==0 )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ce.ptr.pp_double[j][k] = rawc.ptr.pp_double[i][j];
                            }
                            nonnegative.ptr.p_bool[k] = ae_false;
                            inc(&k, _state);
                            continue;
                        }
                        if( (rawct.ptr.p_int[i]>0&&ae_fp_less_eq(v,tolconstr))||(rawct.ptr.p_int[i]<0&&ae_fp_greater_eq(v,-tolconstr)) )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ce.ptr.pp_double[j][k] = ae_sign((double)(rawct.ptr.p_int[i]), _state)*rawc.ptr.pp_double[i][j];
                            }
                            nonnegative.ptr.p_bool[k] = ae_true;
                            inc(&k, _state);
                            continue;
                        }
                    }
                    snnlsinit(0, 0, 0, &nnls, _state);
                    snnlssetproblem(&nnls, &ce, &g, 0, k, n, _state);
                    for(i=0; i<=k-1; i++)
                    {
                        if( !nonnegative.ptr.p_bool[i] )
                        {
                            snnlsdropnnc(&nnls, i, _state);
                        }
                    }
                    snnlssolve(&nnls, &tmp, _state);
                    for(i=0; i<=k-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            g.ptr.p_double[j] = g.ptr.p_double[j]-tmp.ptr.p_double[i]*ce.ptr.pp_double[j][i];
                        }
                    }
                    vv = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    vv = ae_sqrt(vv, _state);
                    ae_set_error_flag(errorflag, ae_fp_greater(vv,gtol), __FILE__, __LINE__, "testminqpunit.ap:3785");
                }
            }
        }
        
        /*
         * Boundary and linear equality/inequality constrained QP problem,
         * test for correct handling of non-zero XOrigin:
         * * N*N SPD A with moderate condtion number (up to 100)
         * * boundary constraints 0<=x[i]<=1
         * * K<N linear equality/inequality constraints Q*x = Q*x0, where
         *   Q is random orthogonal K*N matrix, x0 is some random vector from the
         *   inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
         * * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
         *   where b is some random vector with -1<=b[i]<=+1.
         *   (sometimes solution is in the inner area, sometimes at the boundary)
         * * every component of the initial point XStart is random from [-1,1]
         * 
         * Solution of such problem is calculated using two methods:
         * a) QP with SetOrigin() call
         * b) QP with XOrigin explicitly added to the quadratic function,
         *
         * Both methods should give same results; any significant difference is
         * evidence of some error in the QP implementation.
         */
        ftol = (double)(-99999);
        xtol = (double)(-99999);
        for(n=2; n<=6; n++)
        {
            for(k=1; k<=n-1; k++)
            {
                
                /*
                 * Set up tolerances
                 */
                if( solvertype==0 )
                {
                    ftol = 1.0E-5;
                    xtol = 1.0E-5;
                }
                else
                {
                    if( solvertype==1 )
                    {
                        ftol = 1.0E-3;
                        xtol = 1.0E-3;
                    }
                    else
                    {
                        ae_assert(ae_false, "unexpected solver type", _state);
                    }
                }
                
                /*
                 * Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
                 * Additionally, we compute modified b: b2 = b-xorigin'*A
                 */
                rmatrixrndorthogonal(n, &q, _state);
                spdmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &a, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&b2, n, _state);
                ae_vector_set_length(&bndl, n, _state);
                ae_vector_set_length(&bndu, n, _state);
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&x1, n, _state);
                ae_vector_set_length(&xorigin, n, _state);
                ae_vector_set_length(&xstart, n, _state);
                ae_matrix_set_length(&rawc, k, n+1, _state);
                ae_vector_set_length(&rawct, k, _state);
                for(i=0; i<=n-1; i++)
                {
                    x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
                    b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    bndl.ptr.p_double[i] = 0.0;
                    bndu.ptr.p_double[i] = 1.0;
                    xstart.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
                    xorigin.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                for(i=0; i<=k-1; i++)
                {
                    ae_v_move(&rawc.ptr.pp_double[i][0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                    v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    rawc.ptr.pp_double[i][n] = v;
                    rawct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xorigin.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    b2.ptr.p_double[i] = b.ptr.p_double[i]-v;
                }
                
                /*
                 * Solve with SetOrigin() call
                 */
                minqpcreate(n, &state, _state);
                if( solvertype==0 )
                {
                    minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "unexpected solver type", _state);
                    }
                }
                minqpsetlinearterm(&state, &b, _state);
                testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
                minqpsetstartingpoint(&state, &xstart, _state);
                minqpsetorigin(&state, &xorigin, _state);
                minqpsetbc(&state, &bndl, &bndu, _state);
                testminqpunit_randomlysplitandsetlc(&rawc, &rawct, k, n, &state, &rs, _state);
                minqpoptimize(&state, _state);
                minqpresults(&state, &x1, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:3889");
                    continue;
                }
                
                /*
                 * Solve problem using explicit origin
                 */
                minqpcreate(n, &state2, _state);
                if( solvertype==0 )
                {
                    minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "unexpected solver type", _state);
                    }
                }
                minqpsetlinearterm(&state2, &b2, _state);
                testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state2, &rs, _state);
                minqpsetstartingpoint(&state2, &xstart, _state);
                minqpsetbc(&state2, &bndl, &bndu, _state);
                testminqpunit_randomlysplitandsetlc(&rawc, &rawct, k, n, &state2, &rs, _state);
                minqpoptimize(&state2, _state);
                minqpresults(&state2, &x2, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:3912");
                    continue;
                }
                
                /*
                 * Calculate function value at X1/X2.
                 * Compare solutions.
                 */
                f0 = 0.0;
                f1 = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        f0 = f0+0.5*(x1.ptr.p_double[i]-xorigin.ptr.p_double[i])*a.ptr.pp_double[i][j]*(x1.ptr.p_double[j]-xorigin.ptr.p_double[j]);
                        f1 = f1+0.5*(x2.ptr.p_double[i]-xorigin.ptr.p_double[i])*a.ptr.pp_double[i][j]*(x2.ptr.p_double[j]-xorigin.ptr.p_double[j]);
                    }
                    f0 = f0+(x1.ptr.p_double[i]-xorigin.ptr.p_double[i])*b.ptr.p_double[i];
                    f1 = f1+(x2.ptr.p_double[i]-xorigin.ptr.p_double[i])*b.ptr.p_double[i];
                }
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(f0-f1, _state),ftol), __FILE__, __LINE__, "testminqpunit.ap:3932");
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x2.ptr.p_double[i], _state),xtol), __FILE__, __LINE__, "testminqpunit.ap:3934");
                }
            }
        }
        
        /*
         * Test scale-invariance. Solver performs same steps on scaled and
         * unscaled problems (assuming that scale of the variables is known
         * and is 2^k for some integer k; latter guarantees that DENSE-AUL solver
         * performs EXACTLY same steps even under finite-precision arithmetics).
         *
         * We generate random scale matrix S and random well-conditioned and
         * well scaled matrix A. Then we solve two problems:
         *
         *     (1) f = 0.5*x'*A*x+b'*x
         *         (identity scale matrix is used)
         *
         * and
         *
         *     (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
         *         (scale matrix S is used)
         *
         * with correspondingly scaled constraints, origin and starting point.
         *
         * As result, we must get S*x=y
         */
        for(n=2; n<=6; n++)
        {
            for(k=1; k<=n-1; k++)
            {
                
                /*
                 * Set up tolerances:
                 * * BLEIC does not guarantee that we perform steps exactly even
                 *   when scale is 2^k, so we use non-zero XTol=1E-3
                 * * DENSE-AUL provides such guarantee, so XTol=0
                 */
                if( solvertype==0 )
                {
                    xtol = 1.0E-3;
                }
                else
                {
                    if( solvertype==1 )
                    {
                        xtol = (double)(0);
                    }
                    else
                    {
                        ae_assert(ae_false, "unexpected solver type", _state);
                    }
                }
                
                /*
                 * Generate "raw" problem: A, b, BndL, BndU, C/CT, XOrigin, XStart.
                 * Solve it immediately.
                 *
                 * NOTE: we make sure that there exists at least one feasible point X0.
                 */
                spdmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &a, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&bndl, n, _state);
                ae_vector_set_length(&bndu, n, _state);
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&xstart, n, _state);
                ae_vector_set_length(&xorigin, n, _state);
                ae_matrix_set_length(&rawc, k, n+1, _state);
                ae_vector_set_length(&rawct, k, _state);
                for(i=0; i<=n-1; i++)
                {
                    x0.ptr.p_double[i] = hqrnduniformr(&rs, _state)-0.5;
                    b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    bndl.ptr.p_double[i] = -1-hqrnduniformr(&rs, _state);
                    bndu.ptr.p_double[i] = 1+hqrnduniformr(&rs, _state);
                    xstart.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    xorigin.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                for(i=0; i<=k-1; i++)
                {
                    v = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        rawc.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                        v = v+rawc.ptr.pp_double[i][j]*x0.ptr.p_double[j];
                    }
                    rawc.ptr.pp_double[i][n] = v;
                    rawct.ptr.p_int[i] = hqrnduniformi(&rs, 3, _state)-1;
                }
                testminqpunit_randomlysplitlc(&rawc, &rawct, k, n, &densec, &densect, &sparsec, &sparsect, &denseccnt, &sparseccnt, &rs, _state);
                minqpcreate(n, &state, _state);
                state.dbgskipconstraintnormalization = ae_true;
                if( solvertype==0 )
                {
                    minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "unexpected solver type", _state);
                    }
                }
                minqpsetlinearterm(&state, &b, _state);
                testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
                minqpsetstartingpoint(&state, &xstart, _state);
                minqpsetorigin(&state, &xorigin, _state);
                minqpsetbc(&state, &bndl, &bndu, _state);
                minqpsetlcmixed(&state, &densec, &densect, denseccnt, &sparsec, &sparsect, sparseccnt, _state);
                minqpoptimize(&state, _state);
                minqpresults(&state, &x1, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:4030");
                    continue;
                }
                
                /*
                 * Scale problem and solve one more time.
                 * Randomly choose between dense and sparse versions.
                 */
                ae_vector_set_length(&s, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    s.ptr.p_double[i] = ae_pow((double)(2), (double)(hqrnduniformi(&rs, 11, _state)-5), _state);
                }
                akind = hqrnduniformi(&rs, 2, _state);
                if( akind==0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
                        }
                    }
                }
                else
                {
                    sparsecreate(n, n, 0, &sa, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            sparseset(&sa, i, j, a.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]), _state);
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                    xstart.ptr.p_double[i] = xstart.ptr.p_double[i]*s.ptr.p_double[i];
                    xorigin.ptr.p_double[i] = xorigin.ptr.p_double[i]*s.ptr.p_double[i];
                    bndl.ptr.p_double[i] = bndl.ptr.p_double[i]*s.ptr.p_double[i];
                    bndu.ptr.p_double[i] = bndu.ptr.p_double[i]*s.ptr.p_double[i];
                }
                for(i=0; i<=denseccnt-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        densec.ptr.pp_double[i][j] = densec.ptr.pp_double[i][j]/s.ptr.p_double[j];
                    }
                }
                for(i=0; i<=sparseccnt-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        sparseset(&sparsec, i, j, sparseget(&sparsec, i, j, _state)/s.ptr.p_double[j], _state);
                    }
                }
                minqpcreate(n, &state, _state);
                state.dbgskipconstraintnormalization = ae_true;
                if( solvertype==0 )
                {
                    minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "unexpected solver type", _state);
                    }
                }
                minqpsetlinearterm(&state, &b, _state);
                if( akind==0 )
                {
                    minqpsetquadraticterm(&state, &a, ae_fp_greater(ae_randomreal(_state),0.5), _state);
                }
                if( akind==1 )
                {
                    minqpsetquadratictermsparse(&state, &sa, ae_fp_greater(ae_randomreal(_state),0.5), _state);
                }
                minqpsetstartingpoint(&state, &xstart, _state);
                minqpsetorigin(&state, &xorigin, _state);
                minqpsetbc(&state, &bndl, &bndu, _state);
                minqpsetlcmixed(&state, &densec, &densect, denseccnt, &sparsec, &sparsect, sparseccnt, _state);
                minqpsetscale(&state, &s, _state);
                minqpoptimize(&state, _state);
                minqpresults(&state, &x2, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:4091");
                    continue;
                }
                
                /*
                 * Compare
                 */
                mx = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    mx = ae_maxreal(mx, ae_fabs(x1.ptr.p_double[i], _state), _state);
                }
                mx = ae_maxreal(mx, (double)(1), _state);
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x2.ptr.p_double[i]/s.ptr.p_double[i], _state),xtol*mx), __FILE__, __LINE__, "testminqpunit.ap:4103");
                }
            }
        }
        
        /*
         * General inequality constrained problem:
         * * N*N SPD diagonal A with moderate condtion number
         * * no boundary constraints
         * * K=N inequality constraints C*x >= C*x0, where C is N*N well conditioned
         *   matrix, x0 is some random vector [-1,+1]
         * * optimization problem has form 0.5*x'*A*x-b'*x,
         *   where b is random vector from [-1,+1]
         * * using duality, we can obtain solution of QP problem as follows:
         *   a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
         *      is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
         *   b) after we got dual solution ys, we calculate primal solution
         *      xs = inv(A)*(C'*ys-b)
         */
        for(n=1; n<=6; n++)
        {
            
            /*
             * Set up tolerances
             */
            if( solvertype==0 )
            {
                xtol = 1.0E-5;
            }
            else
            {
                if( solvertype==1 )
                {
                    xtol = 1.0E-3;
                }
                else
                {
                    ae_assert(ae_false, "unexpected solver type", _state);
                }
            }
            
            /*
             * Generate problem
             */
            ae_vector_set_length(&da, n, _state);
            ae_matrix_set_length(&a, n, n, _state);
            rmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &t2, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&rawc, n, n+1, _state);
            ae_vector_set_length(&rawct, n, _state);
            for(i=0; i<=n-1; i++)
            {
                da.ptr.p_double[i] = ae_exp(6*ae_randomreal(_state)-3, _state);
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = (double)(0);
                }
                a.ptr.pp_double[i][i] = da.ptr.p_double[i];
            }
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_v_move(&rawc.ptr.pp_double[i][0], 1, &t2.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                rawc.ptr.pp_double[i][n] = v;
                rawct.ptr.p_int[i] = 1;
            }
            
            /*
             * Solve primal problem, check feasibility
             */
            minqpcreate(n, &state, _state);
            if( solvertype==0 )
            {
                minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                }
                else
                {
                    ae_assert(ae_false, "unexpected solver type", _state);
                }
            }
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            minqpsetlc(&state, &rawc, &rawct, n, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:4187");
                continue;
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &rawc.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                ae_set_error_flag(errorflag, ae_fp_less(v,rawc.ptr.pp_double[i][n]-xtol), __FILE__, __LINE__, "testminqpunit.ap:4193");
            }
            
            /*
             * Generate dual problem:
             * * A2 stores new quadratic term
             * * B2 stores new linear term
             * * BndL/BndU store boundary constraints
             */
            ae_matrix_set_length(&t3, n, n, _state);
            ae_matrix_set_length(&a2, n, n, _state);
            rmatrixtranspose(n, n, &rawc, 0, 0, &t3, 0, 0, _state);
            for(i=0; i<=n-1; i++)
            {
                v = 1/ae_sqrt(da.ptr.p_double[i], _state);
                ae_v_muld(&t3.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
            }
            rmatrixsyrk(n, n, 1.0, &t3, 0, 0, 2, 0.0, &a2, 0, 0, ae_true, _state);
            ae_vector_set_length(&tmp0, n, _state);
            ae_v_move(&tmp0.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            for(i=0; i<=n-1; i++)
            {
                tmp0.ptr.p_double[i] = tmp0.ptr.p_double[i]/da.ptr.p_double[i];
            }
            ae_vector_set_length(&b2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &tmp0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b2.ptr.p_double[i] = -(v+rawc.ptr.pp_double[i][n]);
            }
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            for(i=0; i<=n-1; i++)
            {
                bndl.ptr.p_double[i] = 0.0;
                bndu.ptr.p_double[i] = _state->v_posinf;
            }
            minqpcreate(n, &state2, _state);
            minqpsetalgoquickqp(&state2, 0.0, 0.0, 1.0E-9, 0, ae_true, _state);
            minqpsetlinearterm(&state2, &b2, _state);
            minqpsetquadraticterm(&state2, &a2, ae_true, _state);
            minqpsetbc(&state2, &bndl, &bndu, _state);
            minqpoptimize(&state2, _state);
            minqpresults(&state2, &x2, &rep2, _state);
            if( rep2.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:4248");
                continue;
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&rawc.ptr.pp_double[0][i], rawc.stride, &x2.ptr.p_double[0], 1, ae_v_len(0,n-1));
                tmp0.ptr.p_double[i] = v-b.ptr.p_double[i];
            }
            for(i=0; i<=n-1; i++)
            {
                tmp0.ptr.p_double[i] = tmp0.ptr.p_double[i]/da.ptr.p_double[i];
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(tmp0.ptr.p_double[i]-x1.ptr.p_double[i], _state),xtol*ae_maxreal(ae_fabs(tmp0.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testminqpunit.ap:4261");
            }
        }
        
        /*
         * Boundary and linear equality/inequality constrained QP problem with
         * excessive constraints:
         * * N*N SPD A with moderate condtion number (up to 100)
         * * boundary constraints 0<=x[i]<=1
         * * K=2*N equality/inequality constraints Q*x = Q*x0, where Q is random matrix,
         *   x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
         * * optimization problem has form 0.5*x'*A*x-b*x,
         *   where b is some random vector
         * * because constraints are excessive, the main problem is to find
         *   feasible point; usually, the only existing feasible point is solution,
         *   so we have to check only feasibility
         */
        for(n=1; n<=6; n++)
        {
            
            /*
             * Set up tolerances
             */
            if( solvertype==0 )
            {
                xtol = 1.0E-5;
            }
            else
            {
                if( solvertype==1 )
                {
                    xtol = 5.0E-3;
                }
                else
                {
                    ae_assert(ae_false, "unexpected solver type", _state);
                }
            }
            
            /*
             * Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
             */
            k = 2*n;
            spdmatrixrndcond(n, ae_pow(10.0, 3*ae_randomreal(_state), _state), &a, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&x1, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&rawc, k, n+1, _state);
            ae_vector_set_length(&rawct, k, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                bndl.ptr.p_double[i] = 0.0;
                bndu.ptr.p_double[i] = 1.0;
                xstart.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
            }
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawc.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                rawct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
                if( rawct.ptr.p_int[i]==0 )
                {
                    rawc.ptr.pp_double[i][n] = v;
                }
                if( rawct.ptr.p_int[i]>0 )
                {
                    rawc.ptr.pp_double[i][n] = v-50*xtol;
                }
                if( rawct.ptr.p_int[i]<0 )
                {
                    rawc.ptr.pp_double[i][n] = v+50*xtol;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            
            /*
             * Create optimizer, solve
             */
            minqpcreate(n, &state, _state);
            if( solvertype==0 )
            {
                minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                }
                else
                {
                    ae_assert(ae_false, "unexpected solver type", _state);
                }
            }
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            minqpsetbc(&state, &bndl, &bndu, _state);
            testminqpunit_randomlysplitandsetlc(&rawc, &rawct, k, n, &state, &rs, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            
            /*
             * Check feasibility properties of the solution
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:4355");
                continue;
            }
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &rawc.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                if( rawct.ptr.p_int[i]==0 )
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(v-rawc.ptr.pp_double[i][n], _state),xtol), __FILE__, __LINE__, "testminqpunit.ap:4362");
                }
                if( rawct.ptr.p_int[i]>0 )
                {
                    ae_set_error_flag(errorflag, ae_fp_less(v,rawc.ptr.pp_double[i][n]-xtol), __FILE__, __LINE__, "testminqpunit.ap:4364");
                }
                if( rawct.ptr.p_int[i]<0 )
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(v,rawc.ptr.pp_double[i][n]+xtol), __FILE__, __LINE__, "testminqpunit.ap:4366");
                }
            }
        }
        
        /*
         * Boundary constraints posed as general linear ones:
         * * no bound constraints
         * * 2*N linear constraints 0 <= x[i] <= 1
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * with such simple constraints and function it is easy to find
         *   analytic form of solution: S[i] = bound(x0[i], 0, 1).
         * * however, we can't guarantee that solution is strictly feasible
         *   with respect to nonlinearity constraint, so we check
         *   for approximate feasibility.
         */
        for(n=1; n<=5; n++)
        {
            
            /*
             * Set up tolerances
             */
            if( solvertype==0 )
            {
                xtol = 1.0E-3;
            }
            else
            {
                if( solvertype==1 )
                {
                    xtol = 1.0E-3;
                }
                else
                {
                    ae_assert(ae_false, "unexpected solver type", _state);
                }
            }
            
            /*
             * Generate X, BL, BU.
             */
            ae_matrix_set_length(&a, n, n, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_matrix_set_length(&rawc, 2*n, n+1, _state);
            ae_vector_set_length(&rawct, 2*n, _state);
            k = 2*n;
            for(i=0; i<=n-1; i++)
            {
                xstart.ptr.p_double[i] = ae_randomreal(_state);
                x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                b.ptr.p_double[i] = -x0.ptr.p_double[i];
                for(j=0; j<=n; j++)
                {
                    rawc.ptr.pp_double[2*i+0][j] = (double)(0);
                    rawc.ptr.pp_double[2*i+1][j] = (double)(0);
                }
                rawc.ptr.pp_double[2*i+0][i] = (double)(1);
                rawc.ptr.pp_double[2*i+0][n] = (double)(0);
                rawct.ptr.p_int[2*i+0] = 1;
                rawc.ptr.pp_double[2*i+1][i] = (double)(1);
                rawc.ptr.pp_double[2*i+1][n] = (double)(1);
                rawct.ptr.p_int[2*i+1] = -1;
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( i==j )
                    {
                        a.ptr.pp_double[i][j] = (double)(1);
                    }
                    else
                    {
                        a.ptr.pp_double[i][j] = (double)(0);
                    }
                }
            }
            
            /*
             * Create and optimize
             */
            minqpcreate(n, &state, _state);
            if( solvertype==0 )
            {
                minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                }
                else
                {
                    ae_assert(ae_false, "unexpected solver type", _state);
                }
            }
            testminqpunit_randomlysplitandsetlc(&rawc, &rawct, k, n, &state, &rs, _state);
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
            minqpsetstartingpoint(&state, &xstart, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &x1, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:4449");
                continue;
            }
            
            /*
             * * compare solution with analytic one
             * * check feasibility
             */
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-boundval(x0.ptr.p_double[i], 0.0, 1.0, _state), _state),xtol), __FILE__, __LINE__, "testminqpunit.ap:4459");
                ae_set_error_flag(errorflag, ae_fp_less(x1.ptr.p_double[i],0.0-xtol), __FILE__, __LINE__, "testminqpunit.ap:4460");
                ae_set_error_flag(errorflag, ae_fp_greater(x1.ptr.p_double[i],1.0+xtol), __FILE__, __LINE__, "testminqpunit.ap:4461");
            }
        }
        
        /*
         * Convex/nonconvex optimization problem with excessive
         * (degenerate constraints):
         *
         * * N=2..5
         * * f = 0.5*x'*A*x+b'*x
         * * b has normally distributed entries
         * * A is diagonal with log-normally distributed entries
         * * box constraints: x[i] in [-1,+1]
         * * 2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
         *   where v_k is one of 2^N vertices of feasible hypercube, v_shift is
         *   a shift parameter:
         *   * with zero v_shift such constraints are degenerate (each vertex has
         *     N box constraints and one "redundant" linear constraint)
         *   * with positive v_shift linear constraint is always inactive
         *   * with small (about machine epsilon) but negative v_shift,
         *     constraint is close to degenerate - but not exactly
         *
         * Because A is diagonal, we can easily find out solution analytically.
         *
         * NOTE: TolConstr must be large enough so it won't conflict with
         *       perturbation introduced by v_shift
         */
        for(n=2; n<=8; n++)
        {
            for(shiftkind=-5; shiftkind<=1; shiftkind++)
            {
                
                /*
                 * Set up tolerances
                 */
                if( solvertype==0 )
                {
                    tolconstr = 1.0E-6;
                    gtol = 1.0E-6;
                }
                else
                {
                    if( solvertype==1 )
                    {
                        tolconstr = 1.0E-4;
                        gtol = 1.0E-4;
                    }
                    else
                    {
                        ae_assert(ae_false, "unexpected solver type", _state);
                    }
                }
                
                /*
                 * Generate A, B and initial point
                 */
                ae_matrix_set_length(&a, n, n, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&xstart, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    xstart.ptr.p_double[i] = hqrnduniformr(&rs, _state)-0.5;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = 0.0;
                    }
                    a.ptr.pp_double[i][i] = ae_pow((double)(2), hqrndnormal(&rs, _state), _state);
                }
                
                /*
                 * Generate constraints
                 */
                ae_vector_set_length(&bndl, n, _state);
                ae_vector_set_length(&bndu, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    bndl.ptr.p_double[i] = -1.0;
                    bndu.ptr.p_double[i] = 1.0;
                }
                rawccnt = ae_round(ae_pow((double)(2), (double)(n), _state), _state);
                ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
                ae_vector_set_length(&rawct, rawccnt, _state);
                for(i=0; i<=rawccnt-1; i++)
                {
                    rawct.ptr.p_int[i] = -1;
                    k = i;
                    rawc.ptr.pp_double[i][n] = ae_sign((double)(shiftkind), _state)*ae_pow((double)(10), ae_fabs((double)(shiftkind), _state), _state)*ae_machineepsilon;
                    for(j=0; j<=n-1; j++)
                    {
                        rawc.ptr.pp_double[i][j] = (double)(2*(k%2)-1);
                        rawc.ptr.pp_double[i][n] = rawc.ptr.pp_double[i][n]+rawc.ptr.pp_double[i][j]*rawc.ptr.pp_double[i][j];
                        k = k/2;
                    }
                }
                
                /*
                 * Create and optimize
                 */
                minqpcreate(n, &state, _state);
                minqpsetstartingpoint(&state, &xstart, _state);
                if( solvertype==0 )
                {
                    minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "unexpected solver type", _state);
                    }
                }
                minqpsetbc(&state, &bndl, &bndu, _state);
                testminqpunit_randomlysplitandsetlc(&rawc, &rawct, rawccnt, n, &state, &rs, _state);
                minqpsetlinearterm(&state, &b, _state);
                testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
                minqpoptimize(&state, _state);
                minqpresults(&state, &x1, &rep, _state);
                ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:4569");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Evaluate gradient at solution and test
                 */
                vv = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    v = v+b.ptr.p_double[i];
                    if( ae_fp_less_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i]+tolconstr)&&ae_fp_greater(v,(double)(0)) )
                    {
                        v = 0.0;
                    }
                    if( ae_fp_greater_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i]-tolconstr)&&ae_fp_less(v,(double)(0)) )
                    {
                        v = 0.0;
                    }
                    vv = vv+ae_sqr(v, _state);
                }
                vv = ae_sqrt(vv, _state);
                ae_set_error_flag(errorflag, ae_fp_greater(vv,gtol), __FILE__, __LINE__, "testminqpunit.ap:4593");
            }
        }
    }
    
    /*
     * Large-scale tests: a few selected tests for large N's
     */
    for(solvertype=0; solvertype<=1; solvertype++)
    {
        
        /*
         * General equality constrained problem:
         * * N*N SPD (non-diagonal) A with moderate condition number
         * * no box constraints
         * * K<N equality constraints C*x = C*x0, where C is K*N well conditioned
         *   matrix, x0 is some random vector [-1,+1]
         * * optimization problem has form 0.5*x'*A*x-b'*x,
         *   where b is random vector from [-1,+1]
         * * true solution of QP problem is obtained with KKT matrix;
         *   we compare value returned by solver against one calculated
         *   via KKT conditions.
         */
        n = 60;
        rawccnt = 40;
        if( solvertype==0 )
        {
            xtol = 1.0E-3;
        }
        else
        {
            if( solvertype==1 )
            {
                xtol = 1.0E-3;
            }
            else
            {
                ae_assert(ae_false, "unexpected solver type", _state);
            }
        }
        spdmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &a, _state);
        rmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &t2, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&xstart, n, _state);
        ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
        ae_vector_set_length(&rawct, rawccnt, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=rawccnt-1; i++)
        {
            ae_v_move(&rawc.ptr.pp_double[i][0], 1, &t2.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            rawc.ptr.pp_double[i][n] = v;
            rawct.ptr.p_int[i] = 0;
        }
        minqpcreate(n, &state, _state);
        if( solvertype==0 )
        {
            minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
        }
        else
        {
            if( solvertype==1 )
            {
                minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
            }
            else
            {
                ae_assert(ae_false, "unexpected solver type", _state);
            }
        }
        minqpsetlinearterm(&state, &b, _state);
        testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
        minqpsetstartingpoint(&state, &xstart, _state);
        testminqpunit_randomlysplitandsetlc(&rawc, &rawct, rawccnt, n, &state, &rs, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &x1, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:4667");
            continue;
        }
        for(i=0; i<=rawccnt-1; i++)
        {
            v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &rawc.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            ae_set_error_flag(errorflag, ae_fp_less(v,rawc.ptr.pp_double[i][n]-xtol), __FILE__, __LINE__, "testminqpunit.ap:4673");
        }
        ae_matrix_set_length(&kkt, n+rawccnt, n+rawccnt, _state);
        ae_vector_set_length(&kktright, n+rawccnt, _state);
        for(i=0; i<=n+rawccnt-1; i++)
        {
            for(j=0; j<=n+rawccnt-1; j++)
            {
                kkt.ptr.pp_double[i][j] = (double)(0);
            }
        }
        rmatrixcopy(n, n, &a, 0, 0, &kkt, 0, 0, _state);
        rmatrixcopy(rawccnt, n, &rawc, 0, 0, &kkt, n, 0, _state);
        rmatrixtranspose(rawccnt, n, &rawc, 0, 0, &kkt, 0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            kktright.ptr.p_double[i] = -b.ptr.p_double[i];
        }
        for(i=0; i<=rawccnt-1; i++)
        {
            kktright.ptr.p_double[n+i] = rawc.ptr.pp_double[i][n];
        }
        fblssolvels(&kkt, &kktright, n+rawccnt, n+rawccnt, &tmp0, &tmp1, &tmp2, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(kktright.ptr.p_double[i]-x1.ptr.p_double[i], _state),xtol*ae_maxreal(ae_fabs(kktright.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testminqpunit.ap:4692");
        }
        
        /*
         * General inequality constrained problem:
         * * N*N SPD diagonal A with moderate condtion number
         * * no box constraints
         * * K<N inequality constraints C*x >= C*x0, where C is N*N well conditioned
         *   matrix, x0 is some random vector [-1,+1]
         * * optimization problem has form 0.5*x'*A*x-b'*x,
         *   where b is random vector from [-1,+1]
         * * using duality, we can obtain solution of QP problem as follows:
         *   a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
         *      is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
         *   b) after we got dual solution ys, we calculate primal solution
         *      xs = inv(A)*(C'*ys-b)
         */
        n = 60;
        rawccnt = 40;
        if( solvertype==0 )
        {
            xtol = 1.0E-3;
        }
        else
        {
            if( solvertype==1 )
            {
                xtol = 1.0E-3;
            }
            else
            {
                ae_assert(ae_false, "unexpected solver type", _state);
            }
        }
        ae_vector_set_length(&da, n, _state);
        ae_matrix_set_length(&a, n, n, _state);
        rmatrixrndcond(n, ae_pow(10.0, 2*ae_randomreal(_state), _state), &t2, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&xstart, n, _state);
        ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
        ae_vector_set_length(&rawct, rawccnt, _state);
        for(i=0; i<=n-1; i++)
        {
            da.ptr.p_double[i] = ae_exp(8*ae_randomreal(_state)-4, _state);
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = (double)(0);
            }
            a.ptr.pp_double[i][i] = da.ptr.p_double[i];
        }
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=rawccnt-1; i++)
        {
            ae_v_move(&rawc.ptr.pp_double[i][0], 1, &t2.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            rawc.ptr.pp_double[i][n] = v;
            rawct.ptr.p_int[i] = 1;
        }
        minqpcreate(n, &state, _state);
        if( solvertype==0 )
        {
            minqpsetalgobleic(&state, 0.0, 0.0, bleicepsx, 0, _state);
        }
        else
        {
            if( solvertype==1 )
            {
                minqpsetalgodenseaul(&state, aulepsx, aulrho, aulits, _state);
            }
            else
            {
                ae_assert(ae_false, "unexpected solver type", _state);
            }
        }
        minqpsetlinearterm(&state, &b, _state);
        testminqpunit_randomlyselectconvertandsetquadraticterm(&a, n, &state, &rs, _state);
        minqpsetstartingpoint(&state, &xstart, _state);
        testminqpunit_randomlysplitandsetlc(&rawc, &rawct, rawccnt, n, &state, &rs, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &x1, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:4770");
            continue;
        }
        for(i=0; i<=rawccnt-1; i++)
        {
            v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &rawc.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            ae_set_error_flag(errorflag, ae_fp_less(v,rawc.ptr.pp_double[i][n]-xtol), __FILE__, __LINE__, "testminqpunit.ap:4776");
        }
        ae_matrix_set_length(&t3, n, rawccnt, _state);
        ae_matrix_set_length(&a2, rawccnt, rawccnt, _state);
        rmatrixtranspose(rawccnt, n, &rawc, 0, 0, &t3, 0, 0, _state);
        for(i=0; i<=n-1; i++)
        {
            v = 1/ae_sqrt(da.ptr.p_double[i], _state);
            ae_v_muld(&t3.ptr.pp_double[i][0], 1, ae_v_len(0,rawccnt-1), v);
        }
        rmatrixsyrk(rawccnt, n, 1.0, &t3, 0, 0, 2, 0.0, &a2, 0, 0, ae_true, _state);
        ae_vector_set_length(&tmp0, n, _state);
        ae_v_move(&tmp0.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
        for(i=0; i<=n-1; i++)
        {
            tmp0.ptr.p_double[i] = tmp0.ptr.p_double[i]/da.ptr.p_double[i];
        }
        ae_vector_set_length(&b2, rawccnt, _state);
        for(i=0; i<=rawccnt-1; i++)
        {
            v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &tmp0.ptr.p_double[0], 1, ae_v_len(0,n-1));
            b2.ptr.p_double[i] = -(v+rawc.ptr.pp_double[i][n]);
        }
        ae_vector_set_length(&bndl, rawccnt, _state);
        ae_vector_set_length(&bndu, rawccnt, _state);
        for(i=0; i<=rawccnt-1; i++)
        {
            bndl.ptr.p_double[i] = 0.0;
            bndu.ptr.p_double[i] = _state->v_posinf;
        }
        minqpcreate(rawccnt, &state2, _state);
        minqpsetalgoquickqp(&state2, 0.0, 0.0, 1.0E-9, 0, ae_true, _state);
        minqpsetlinearterm(&state2, &b2, _state);
        minqpsetquadraticterm(&state2, &a2, ae_true, _state);
        minqpsetbc(&state2, &bndl, &bndu, _state);
        minqpoptimize(&state2, _state);
        minqpresults(&state2, &x2, &rep2, _state);
        if( rep2.terminationtype<=0 )
        {
            ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:4829");
            continue;
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&rawc.ptr.pp_double[0][i], rawc.stride, &x2.ptr.p_double[0], 1, ae_v_len(0,rawccnt-1));
            tmp0.ptr.p_double[i] = v-b.ptr.p_double[i];
        }
        for(i=0; i<=n-1; i++)
        {
            tmp0.ptr.p_double[i] = tmp0.ptr.p_double[i]/da.ptr.p_double[i];
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(tmp0.ptr.p_double[i]-x1.ptr.p_double[i], _state),xtol*ae_maxreal(ae_fabs(tmp0.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testminqpunit.ap:4842");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests special inequality constrained QP problems.

Returns True on errors.
*************************************************************************/
static ae_bool testminqpunit_specialicqptests(ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix c;
    ae_vector xstart;
    ae_vector xend;
    ae_vector xexact;
    ae_vector b;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector ct;
    minqpstate state;
    minqpreport rep;
    ae_bool waserrors;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&c, 0, sizeof(c));
    memset(&xstart, 0, sizeof(xstart));
    memset(&xend, 0, sizeof(xend));
    memset(&xexact, 0, sizeof(xexact));
    memset(&b, 0, sizeof(b));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&ct, 0, sizeof(ct));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xstart, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xend, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xexact, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minqpstate_init(&state, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    
    /*
     * Test 1: reported by Vanderlande Industries.
     *         Tests algorithm ability to handle degenerate constraints.
     */
    ae_matrix_set_length(&a, 3, 3, _state);
    for(i=0; i<=2; i++)
    {
        for(j=0; j<=2; j++)
        {
            a.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=2; i++)
    {
        a.ptr.pp_double[i][i] = (double)(1);
    }
    ae_vector_set_length(&b, 3, _state);
    b.ptr.p_double[0] = (double)(-50);
    b.ptr.p_double[1] = (double)(-50);
    b.ptr.p_double[2] = (double)(-75);
    ae_vector_set_length(&bndl, 3, _state);
    bndl.ptr.p_double[0] = (double)(0);
    bndl.ptr.p_double[1] = (double)(0);
    bndl.ptr.p_double[2] = (double)(0);
    ae_vector_set_length(&bndu, 3, _state);
    bndu.ptr.p_double[0] = (double)(100);
    bndu.ptr.p_double[1] = (double)(100);
    bndu.ptr.p_double[2] = (double)(150);
    ae_vector_set_length(&xstart, 3, _state);
    xstart.ptr.p_double[0] = (double)(0);
    xstart.ptr.p_double[1] = (double)(100);
    xstart.ptr.p_double[2] = (double)(0);
    ae_vector_set_length(&xexact, 3, _state);
    xexact.ptr.p_double[0] = (double)(0);
    xexact.ptr.p_double[1] = (double)(100);
    xexact.ptr.p_double[2] = (double)(50);
    ae_matrix_set_length(&c, 3, 4, _state);
    c.ptr.pp_double[0][0] = (double)(1);
    c.ptr.pp_double[0][1] = (double)(-1);
    c.ptr.pp_double[0][2] = (double)(0);
    c.ptr.pp_double[0][3] = (double)(-100);
    c.ptr.pp_double[1][0] = (double)(1);
    c.ptr.pp_double[1][1] = (double)(0);
    c.ptr.pp_double[1][2] = (double)(-1);
    c.ptr.pp_double[1][3] = (double)(0);
    c.ptr.pp_double[2][0] = (double)(-1);
    c.ptr.pp_double[2][1] = (double)(0);
    c.ptr.pp_double[2][2] = (double)(1);
    c.ptr.pp_double[2][3] = (double)(50);
    ae_vector_set_length(&ct, 3, _state);
    ct.ptr.p_int[0] = -1;
    ct.ptr.p_int[1] = -1;
    ct.ptr.p_int[2] = -1;
    minqpcreate(3, &state, _state);
    testminqpunit_setrandomalgoconvexlc(&state, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadraticterm(&state, &a, ae_true, _state);
    minqpsetstartingpoint(&state, &xstart, _state);
    minqpsetbc(&state, &bndl, &bndu, _state);
    minqpsetlc(&state, &c, &ct, 3, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend, &rep, _state);
    if( rep.terminationtype>0 )
    {
        for(i=0; i<=2; i++)
        {
            waserrors = waserrors||ae_fp_greater(ae_fabs(xend.ptr.p_double[i]-xexact.ptr.p_double[i], _state),1.0E6*ae_machineepsilon);
        }
    }
    else
    {
        waserrors = ae_true;
    }
    
    /*
     * Test 2: reported by Vanderlande Industries.
     *         Tests algorithm ability to handle degenerate constraints.
     */
    ae_matrix_set_length(&a, 3, 3, _state);
    for(i=0; i<=2; i++)
    {
        for(j=0; j<=2; j++)
        {
            a.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=2; i++)
    {
        a.ptr.pp_double[i][i] = (double)(1);
    }
    ae_vector_set_length(&b, 3, _state);
    b.ptr.p_double[0] = (double)(-50);
    b.ptr.p_double[1] = (double)(-50);
    b.ptr.p_double[2] = (double)(-75);
    ae_vector_set_length(&bndl, 3, _state);
    bndl.ptr.p_double[0] = (double)(0);
    bndl.ptr.p_double[1] = (double)(0);
    bndl.ptr.p_double[2] = (double)(0);
    ae_vector_set_length(&bndu, 3, _state);
    bndu.ptr.p_double[0] = (double)(100);
    bndu.ptr.p_double[1] = (double)(100);
    bndu.ptr.p_double[2] = (double)(150);
    ae_vector_set_length(&xstart, 3, _state);
    xstart.ptr.p_double[0] = (double)(0);
    xstart.ptr.p_double[1] = (double)(100);
    xstart.ptr.p_double[2] = (double)(150);
    ae_vector_set_length(&xexact, 3, _state);
    xexact.ptr.p_double[0] = (double)(0);
    xexact.ptr.p_double[1] = (double)(100);
    xexact.ptr.p_double[2] = (double)(100);
    ae_matrix_set_length(&c, 3, 4, _state);
    c.ptr.pp_double[0][0] = (double)(1);
    c.ptr.pp_double[0][1] = (double)(-1);
    c.ptr.pp_double[0][2] = (double)(0);
    c.ptr.pp_double[0][3] = (double)(-100);
    c.ptr.pp_double[1][0] = (double)(0);
    c.ptr.pp_double[1][1] = (double)(1);
    c.ptr.pp_double[1][2] = (double)(-1);
    c.ptr.pp_double[1][3] = (double)(0);
    c.ptr.pp_double[2][0] = (double)(0);
    c.ptr.pp_double[2][1] = (double)(-1);
    c.ptr.pp_double[2][2] = (double)(1);
    c.ptr.pp_double[2][3] = (double)(50);
    ae_vector_set_length(&ct, 3, _state);
    ct.ptr.p_int[0] = -1;
    ct.ptr.p_int[1] = -1;
    ct.ptr.p_int[2] = -1;
    minqpcreate(3, &state, _state);
    testminqpunit_setrandomalgoconvexlc(&state, _state);
    minqpsetlinearterm(&state, &b, _state);
    minqpsetquadraticterm(&state, &a, ae_true, _state);
    minqpsetstartingpoint(&state, &xstart, _state);
    minqpsetbc(&state, &bndl, &bndu, _state);
    minqpsetlc(&state, &c, &ct, 3, _state);
    minqpoptimize(&state, _state);
    minqpresults(&state, &xend, &rep, _state);
    if( rep.terminationtype>0 )
    {
        for(i=0; i<=2; i++)
        {
            waserrors = waserrors||ae_fp_greater(ae_fabs(xend.ptr.p_double[i]-xexact.ptr.p_double[i], _state),1.0E6*ae_machineepsilon);
        }
    }
    else
    {
        waserrors = ae_true;
    }
    result = waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests linearly constrained DENSE-AUL solver

On failure sets Err to True; on success leaves it unchanged.
*************************************************************************/
static void testminqpunit_denseaultests(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double vv;
    ae_int_t scaletype;
    ae_matrix rawa;
    ae_matrix z;
    ae_vector bndl;
    ae_vector bndu;
    ae_matrix rawc;
    ae_vector rawct;
    ae_int_t rawccnt;
    ae_vector b;
    ae_vector x0;
    ae_vector xf;
    ae_vector r;
    ae_vector xsol;
    ae_vector s;
    minqpstate state;
    minqpreport rep;
    double epsx;
    double xtol;
    double rho;
    ae_int_t outerits;
    densesolverreport svrep;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&rawa, 0, sizeof(rawa));
    memset(&z, 0, sizeof(z));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&rawc, 0, sizeof(rawc));
    memset(&rawct, 0, sizeof(rawct));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    memset(&xf, 0, sizeof(xf));
    memset(&r, 0, sizeof(r));
    memset(&xsol, 0, sizeof(xsol));
    memset(&s, 0, sizeof(s));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&svrep, 0, sizeof(svrep));
    _hqrndstate_init(&rs, _state, ae_true);
    ae_matrix_init(&rawa, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rawc, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rawct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xsol, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    _minqpstate_init(&state, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    _densesolverreport_init(&svrep, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Test that unconstrained problem is solved with high precision,
     * independently of Rho and/or outer iterations count.
     *
     * 50% of problems are rescaled wildly (with scale being passed to
     * the solver).
     */
    epsx = 1.0E-12;
    xtol = 1.0E-7;
    for(n=1; n<=10; n++)
    {
        for(scaletype=0; scaletype<=1; scaletype++)
        {
            
            /*
             * Generate random A, b, X0 and XSOL
             */
            spdmatrixrndcond(n, ae_pow(10.0, 3*hqrnduniformr(&rs, _state), _state), &rawa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&xsol, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                xsol.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&rawa.ptr.pp_double[i][0], 1, &xsol.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = -v;
            }
            
            /*
             * Generate scale vector, apply it
             */
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( scaletype>0 )
                {
                    s.ptr.p_double[i] = ae_pow((double)(10), hqrnduniformr(&rs, _state)*20-10, _state);
                }
                else
                {
                    s.ptr.p_double[i] = (double)(1);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawa.ptr.pp_double[i][j] = rawa.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
                }
                b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                x0.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                xsol.ptr.p_double[i] = xsol.ptr.p_double[i]*s.ptr.p_double[i];
            }
            
            /*
             * Create optimizer, solve
             */
            rho = ae_pow((double)(10), 2*hqrnduniformr(&rs, _state), _state);
            outerits = hqrnduniformi(&rs, 5, _state);
            minqpcreate(n, &state, _state);
            minqpsetalgodenseaul(&state, epsx, rho, outerits, _state);
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&rawa, n, &state, &rs, _state);
            minqpsetscale(&state, &s, _state);
            minqpsetstartingpoint(&state, &x0, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xf, &rep, _state);
            
            /*
             * Compare against analytically known solution
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:6564");
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xf.ptr.p_double[i]-xsol.ptr.p_double[i], _state)/s.ptr.p_double[i],xtol), __FILE__, __LINE__, "testminqpunit.ap:6568");
            }
        }
    }
    
    /*
     * Test that problem with zero constraint matrix can be solved
     * (with high precision). We do not perform any additional "tweaks"
     * like scaling of variables, just want to test ability to handle
     * zero matrices.
     */
    epsx = 1.0E-12;
    xtol = 1.0E-7;
    for(n=1; n<=10; n++)
    {
        
        /*
         * Generate random A, b, X0 and XSOL
         */
        spdmatrixrndcond(n, ae_pow(10.0, 3*hqrnduniformr(&rs, _state), _state), &rawa, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&xsol, n, _state);
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
            xsol.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&rawa.ptr.pp_double[i][0], 1, &xsol.ptr.p_double[0], 1, ae_v_len(0,n-1));
            b.ptr.p_double[i] = -v;
        }
        rawccnt = hqrnduniformi(&rs, 2*n, _state);
        ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
        ae_vector_set_length(&rawct, rawccnt, _state);
        for(i=0; i<=rawccnt-1; i++)
        {
            for(j=0; j<=n; j++)
            {
                rawc.ptr.pp_double[i][j] = (double)(0);
            }
            rawct.ptr.p_int[i] = hqrnduniformi(&rs, 3, _state)-1;
        }
        
        /*
         * Create optimizer, solve
         */
        rho = ae_pow((double)(10), 2*hqrnduniformr(&rs, _state), _state);
        outerits = hqrnduniformi(&rs, 5, _state);
        minqpcreate(n, &state, _state);
        minqpsetalgodenseaul(&state, epsx, rho, outerits, _state);
        minqpsetlinearterm(&state, &b, _state);
        testminqpunit_randomlyselectconvertandsetquadraticterm(&rawa, n, &state, &rs, _state);
        minqpsetlc(&state, &rawc, &rawct, rawccnt, _state);
        minqpsetstartingpoint(&state, &x0, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xf, &rep, _state);
        
        /*
         * Compare against analytically known solution
         */
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:6628");
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xf.ptr.p_double[i]-xsol.ptr.p_double[i], _state),xtol), __FILE__, __LINE__, "testminqpunit.ap:6632");
        }
    }
    
    /*
     * Test that box/linearly inequality constrained problem with ALL constraints
     * being inactive at BOTH initial and final points is solved with high precision.
     *
     * 50% of problems are rescaled wildly (with scale being passed to
     * the solver).
     */
    epsx = 1.0E-12;
    xtol = 1.0E-7;
    for(n=1; n<=10; n++)
    {
        for(scaletype=0; scaletype<=1; scaletype++)
        {
            
            /*
             * Generate random A, b, X0 and XSOL
             */
            spdmatrixrndcond(n, ae_pow(10.0, 3*hqrnduniformr(&rs, _state), _state), &rawa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&xsol, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                xsol.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&rawa.ptr.pp_double[i][0], 1, &xsol.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = -v;
            }
            
            /*
             * Generate such set of inequality constraints that ALL
             * constraints are inactive at both X0 and XSOL.
             */
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            for(i=0; i<=n-1; i++)
            {
                bndl.ptr.p_double[i] = ae_minreal(x0.ptr.p_double[i], xsol.ptr.p_double[i], _state)-1-hqrnduniformr(&rs, _state);
                bndu.ptr.p_double[i] = ae_maxreal(x0.ptr.p_double[i], xsol.ptr.p_double[i], _state)+1+hqrnduniformr(&rs, _state);
            }
            rawccnt = hqrnduniformi(&rs, 2*n, _state);
            ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
            ae_vector_set_length(&rawct, rawccnt, _state);
            for(i=0; i<=rawccnt-1; i++)
            {
                v = (double)(0);
                vv = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    rawc.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                    v = v+rawc.ptr.pp_double[i][j]*x0.ptr.p_double[j];
                    vv = vv+rawc.ptr.pp_double[i][j]*xsol.ptr.p_double[j];
                }
                if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
                {
                    rawct.ptr.p_int[i] = 1;
                    rawc.ptr.pp_double[i][n] = ae_minreal(v, vv, _state)-1-hqrnduniformr(&rs, _state);
                }
                else
                {
                    rawct.ptr.p_int[i] = -1;
                    rawc.ptr.pp_double[i][n] = ae_maxreal(v, vv, _state)+1+hqrnduniformr(&rs, _state);
                }
            }
            
            /*
             * Generate scale vector, apply it
             */
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( scaletype>0 )
                {
                    s.ptr.p_double[i] = ae_pow((double)(10), hqrnduniformr(&rs, _state)*20-10, _state);
                }
                else
                {
                    s.ptr.p_double[i] = (double)(1);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawa.ptr.pp_double[i][j] = rawa.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
                }
                b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                x0.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                xsol.ptr.p_double[i] = xsol.ptr.p_double[i]*s.ptr.p_double[i];
                bndl.ptr.p_double[i] = bndl.ptr.p_double[i]*s.ptr.p_double[i];
                bndu.ptr.p_double[i] = bndu.ptr.p_double[i]*s.ptr.p_double[i];
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawc.ptr.pp_double[i][j] = rawc.ptr.pp_double[i][j]/s.ptr.p_double[j];
                }
            }
            
            /*
             * Create optimizer, solve
             */
            rho = 100.0;
            outerits = 1;
            minqpcreate(n, &state, _state);
            minqpsetstartingpoint(&state, &x0, _state);
            minqpsetalgodenseaul(&state, epsx, rho, outerits, _state);
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&rawa, n, &state, &rs, _state);
            minqpsetbc(&state, &bndl, &bndu, _state);
            testminqpunit_randomlysplitandsetlc(&rawc, &rawct, rawccnt, n, &state, &rs, _state);
            minqpsetscale(&state, &s, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xf, &rep, _state);
            
            /*
             * Compare against analytically known solution
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:6749");
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xf.ptr.p_double[i]-xsol.ptr.p_double[i], _state)/s.ptr.p_double[i],xtol), __FILE__, __LINE__, "testminqpunit.ap:6753");
            }
        }
    }
    
    /*
     * Test that linear equality constrained problem is solved with high precision.
     *
     * 50% of problems are rescaled wildly (variable scaling, with scale being
     * passed to the solver).
     */
    epsx = 1.0E-12;
    xtol = 1.0E-6;
    for(n=1; n<=10; n++)
    {
        for(scaletype=0; scaletype<=1; scaletype++)
        {
            
            /*
             * Generate random A, b, X0, constraints
             */
            spdmatrixrndcond(n, ae_pow(10.0, 3*hqrnduniformr(&rs, _state), _state), &rawa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&xsol, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            rmatrixrndcond(n, (double)(10), &z, _state);
            rawccnt = ae_maxint(n-2, 0, _state);
            if( rawccnt>0 )
            {
                ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
                ae_vector_set_length(&rawct, rawccnt, _state);
                for(i=0; i<=rawccnt-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        rawc.ptr.pp_double[i][j] = z.ptr.pp_double[i][j];
                    }
                    rawct.ptr.p_int[i] = 0;
                    rawc.ptr.pp_double[i][n] = hqrndnormal(&rs, _state);
                }
            }
            
            /*
             * Generate scale vector, apply it
             */
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( scaletype>0 )
                {
                    s.ptr.p_double[i] = ae_pow((double)(10), hqrnduniformr(&rs, _state)*20-10, _state);
                }
                else
                {
                    s.ptr.p_double[i] = (double)(1);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawa.ptr.pp_double[i][j] = rawa.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
                }
                b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                x0.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                xsol.ptr.p_double[i] = xsol.ptr.p_double[i]*s.ptr.p_double[i];
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawc.ptr.pp_double[i][j] = rawc.ptr.pp_double[i][j]/s.ptr.p_double[j];
                }
            }
            
            /*
             * Create optimizer, solve
             */
            rho = 100.0;
            outerits = 3;
            minqpcreate(n, &state, _state);
            minqpsetalgodenseaul(&state, epsx, rho, outerits, _state);
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&rawa, n, &state, &rs, _state);
            testminqpunit_randomlysplitandsetlc(&rawc, &rawct, rawccnt, n, &state, &rs, _state);
            minqpsetscale(&state, &s, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xf, &rep, _state);
            
            /*
             * Solve problem analytically using Lagrangian approach
             */
            ae_matrix_set_length(&z, n+rawccnt, n+rawccnt, _state);
            ae_vector_set_length(&r, n+rawccnt, _state);
            for(i=0; i<=n+rawccnt-1; i++)
            {
                for(j=0; j<=n+rawccnt-1; j++)
                {
                    z.ptr.pp_double[i][j] = (double)(0);
                }
                r.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    z.ptr.pp_double[i][j] = rawa.ptr.pp_double[i][j];
                }
            }
            for(i=0; i<=n-1; i++)
            {
                r.ptr.p_double[i] = -b.ptr.p_double[i];
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    z.ptr.pp_double[n+i][j] = rawc.ptr.pp_double[i][j];
                    z.ptr.pp_double[j][n+i] = rawc.ptr.pp_double[i][j];
                }
                r.ptr.p_double[n+i] = rawc.ptr.pp_double[i][n];
            }
            rmatrixsolve(&z, n+rawccnt, &r, &k, &svrep, &xsol, _state);
            ae_assert(k>0, "MinQPTest: integrity check failed", _state);
            
            /*
             * Compare against analytically known solution
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:6863");
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xf.ptr.p_double[i]-xsol.ptr.p_double[i], _state)/s.ptr.p_double[i],xtol), __FILE__, __LINE__, "testminqpunit.ap:6867");
            }
        }
    }
    
    /*
     * MEDIUM-SCALE VERSION OF PREVIOUS TEST.
     *
     * Test that linear equality constrained problem is solved with high precision.
     *
     * 50% of problems are rescaled wildly (variable scaling, with scale being
     * passed to the solver).
     */
    epsx = 1.0E-12;
    xtol = 1.0E-6;
    for(n=99; n<=101; n++)
    {
        for(scaletype=0; scaletype<=1; scaletype++)
        {
            
            /*
             * Generate random A, b, X0, constraints
             */
            spdmatrixrndcond(n, ae_pow(10.0, 3*hqrnduniformr(&rs, _state), _state), &rawa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&xsol, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            rmatrixrndcond(n, (double)(10), &z, _state);
            rawccnt = ae_maxint(n-2, 0, _state);
            if( rawccnt>0 )
            {
                ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
                ae_vector_set_length(&rawct, rawccnt, _state);
                for(i=0; i<=rawccnt-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        rawc.ptr.pp_double[i][j] = z.ptr.pp_double[i][j];
                    }
                    rawct.ptr.p_int[i] = 0;
                    rawc.ptr.pp_double[i][n] = hqrndnormal(&rs, _state);
                }
            }
            
            /*
             * Generate scale vector, apply it
             */
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( scaletype>0 )
                {
                    s.ptr.p_double[i] = ae_pow((double)(10), hqrnduniformr(&rs, _state)*20-10, _state);
                }
                else
                {
                    s.ptr.p_double[i] = (double)(1);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawa.ptr.pp_double[i][j] = rawa.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
                }
                b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                x0.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                xsol.ptr.p_double[i] = xsol.ptr.p_double[i]*s.ptr.p_double[i];
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawc.ptr.pp_double[i][j] = rawc.ptr.pp_double[i][j]/s.ptr.p_double[j];
                }
            }
            
            /*
             * Create optimizer, solve
             */
            rho = 100.0;
            outerits = 3;
            minqpcreate(n, &state, _state);
            minqpsetalgodenseaul(&state, epsx, rho, outerits, _state);
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&rawa, n, &state, &rs, _state);
            testminqpunit_randomlysplitandsetlc(&rawc, &rawct, rawccnt, n, &state, &rs, _state);
            minqpsetscale(&state, &s, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xf, &rep, _state);
            
            /*
             * Solve problem analytically using Lagrangian approach
             */
            ae_matrix_set_length(&z, n+rawccnt, n+rawccnt, _state);
            ae_vector_set_length(&r, n+rawccnt, _state);
            for(i=0; i<=n+rawccnt-1; i++)
            {
                for(j=0; j<=n+rawccnt-1; j++)
                {
                    z.ptr.pp_double[i][j] = (double)(0);
                }
                r.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    z.ptr.pp_double[i][j] = rawa.ptr.pp_double[i][j];
                }
            }
            for(i=0; i<=n-1; i++)
            {
                r.ptr.p_double[i] = -b.ptr.p_double[i];
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    z.ptr.pp_double[n+i][j] = rawc.ptr.pp_double[i][j];
                    z.ptr.pp_double[j][n+i] = rawc.ptr.pp_double[i][j];
                }
                r.ptr.p_double[n+i] = rawc.ptr.pp_double[i][n];
            }
            rmatrixsolve(&z, n+rawccnt, &r, &k, &svrep, &xsol, _state);
            ae_assert(k>0, "MinQPTest: integrity check failed", _state);
            
            /*
             * Compare against analytically known solution
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:6979");
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xf.ptr.p_double[i]-xsol.ptr.p_double[i], _state)/s.ptr.p_double[i],xtol), __FILE__, __LINE__, "testminqpunit.ap:6983");
            }
        }
    }
    
    /*
     * Test that constraints are automatically scaled to adapt to problem curvature
     * (that multiplication of A and b by some large/small number does not affect
     * solver).
     *
     * We generate random well-scaled problem, and multiply A/b by some large/small number,
     * and test that problem is still solved with high precision.
     *
     * NOTE: just to make things worse, we rescale variables randomly, but primary
     *       idea of this test is to check for multiplication of A/B
     */
    epsx = 1.0E-12;
    xtol = 1.0E-6;
    for(n=1; n<=10; n++)
    {
        for(scaletype=-1; scaletype<=1; scaletype++)
        {
            
            /*
             * Generate random A, b, X0, constraints
             */
            spdmatrixrndcond(n, ae_pow(10.0, 3*hqrnduniformr(&rs, _state), _state), &rawa, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&xsol, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            rmatrixrndcond(n, (double)(10), &z, _state);
            rawccnt = ae_maxint(n-2, 0, _state);
            if( rawccnt>0 )
            {
                ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
                ae_vector_set_length(&rawct, rawccnt, _state);
                for(i=0; i<=rawccnt-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        rawc.ptr.pp_double[i][j] = z.ptr.pp_double[i][j];
                    }
                    rawct.ptr.p_int[i] = 0;
                    rawc.ptr.pp_double[i][n] = hqrndnormal(&rs, _state);
                }
            }
            
            /*
             * Solve problem analytically using Lagrangian approach
             */
            ae_matrix_set_length(&z, n+rawccnt, n+rawccnt, _state);
            ae_vector_set_length(&r, n+rawccnt, _state);
            for(i=0; i<=n+rawccnt-1; i++)
            {
                for(j=0; j<=n+rawccnt-1; j++)
                {
                    z.ptr.pp_double[i][j] = (double)(0);
                }
                r.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    z.ptr.pp_double[i][j] = rawa.ptr.pp_double[i][j];
                }
            }
            for(i=0; i<=n-1; i++)
            {
                r.ptr.p_double[i] = -b.ptr.p_double[i];
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    z.ptr.pp_double[n+i][j] = rawc.ptr.pp_double[i][j];
                    z.ptr.pp_double[j][n+i] = rawc.ptr.pp_double[i][j];
                }
                r.ptr.p_double[n+i] = rawc.ptr.pp_double[i][n];
            }
            rmatrixsolve(&z, n+rawccnt, &r, &k, &svrep, &xsol, _state);
            ae_assert(k>0, "MinQPTest: integrity check failed", _state);
            
            /*
             * Generate scale vector, apply it
             */
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = ae_pow((double)(10), hqrnduniformr(&rs, _state)*20-10, _state);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawa.ptr.pp_double[i][j] = rawa.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
                }
                b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                x0.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                xsol.ptr.p_double[i] = xsol.ptr.p_double[i]*s.ptr.p_double[i];
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawc.ptr.pp_double[i][j] = rawc.ptr.pp_double[i][j]/s.ptr.p_double[j];
                }
            }
            
            /*
             * Multiply A/B by some coefficient with wild magnitude
             */
            v = ae_pow((double)(10), (double)(10*scaletype), _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rawa.ptr.pp_double[i][j] = rawa.ptr.pp_double[i][j]*v;
                }
                b.ptr.p_double[i] = b.ptr.p_double[i]*v;
            }
            
            /*
             * Create optimizer, solve
             */
            rho = 100.0;
            outerits = 3;
            minqpcreate(n, &state, _state);
            minqpsetalgodenseaul(&state, epsx, rho, outerits, _state);
            minqpsetlinearterm(&state, &b, _state);
            testminqpunit_randomlyselectconvertandsetquadraticterm(&rawa, n, &state, &rs, _state);
            testminqpunit_randomlysplitandsetlc(&rawc, &rawct, rawccnt, n, &state, &rs, _state);
            minqpsetscale(&state, &s, _state);
            minqpoptimize(&state, _state);
            minqpresults(&state, &xf, &rep, _state);
            
            /*
             * Compare against analytically known solution
             */
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminqpunit.ap:7106");
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xf.ptr.p_double[i]-xsol.ptr.p_double[i], _state)/s.ptr.p_double[i],xtol), __FILE__, __LINE__, "testminqpunit.ap:7110");
            }
        }
    }
    
    /*
     * Test that problem with slight negative curvature f = -0.001*|x|^2
     * subject to general linear (!) constraints -1 <= x[i] <= +1 is
     * correctly solved. Initial point is in [-0.001,+0.001] range.
     *
     * NOTE: this test is mostly intended for working set selection algorithm;
     *       it must correctly detect non-SPD problems and select full working
     *       set; starting with zero working set will result in recognition of
     *       problem as unbounded one and premature termination of algorithm.
     */
    epsx = 1.0E-12;
    xtol = 1.0E-6;
    for(n=1; n<=10; n++)
    {
        
        /*
         * Generate A, b, X0, constraints
         */
        ae_matrix_set_length(&rawa, n, n, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                rawa.ptr.pp_double[i][j] = (double)(0);
            }
            rawa.ptr.pp_double[i][i] = -0.001;
            b.ptr.p_double[i] = (double)(0);
            x0.ptr.p_double[i] = 0.001*hqrndnormal(&rs, _state);
        }
        ae_matrix_set_length(&rawc, 2*n, n+1, _state);
        ae_vector_set_length(&rawct, 2*n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n; j++)
            {
                rawc.ptr.pp_double[2*i+0][j] = (double)(0);
                rawc.ptr.pp_double[2*i+1][j] = (double)(0);
            }
            rawc.ptr.pp_double[2*i+0][i] = (double)(1);
            rawc.ptr.pp_double[2*i+1][i] = (double)(1);
            rawc.ptr.pp_double[2*i+0][n] = (double)(-1);
            rawc.ptr.pp_double[2*i+1][n] = (double)(1);
            rawct.ptr.p_int[2*i+0] = 1;
            rawct.ptr.p_int[2*i+1] = -1;
        }
        
        /*
         * Create optimizer, solve
         */
        rho = 100.0;
        outerits = 5;
        minqpcreate(n, &state, _state);
        minqpsetalgodenseaul(&state, epsx, rho, outerits, _state);
        minqpsetstartingpoint(&state, &x0, _state);
        minqpsetquadraticterm(&state, &rawa, ae_true, _state);
        minqpsetlinearterm(&state, &b, _state);
        minqpsetlc(&state, &rawc, &rawct, 2*n, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xf, &rep, _state);
        
        /*
         * Test
         */
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:7175");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(ae_fabs(xf.ptr.p_double[i], _state)-1, _state),xtol), __FILE__, __LINE__, "testminqpunit.ap:7179");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests various special properties

On failure sets Err to True; on success leaves it unchanged.
*************************************************************************/
static void testminqpunit_spectests(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    minqpstate state;
    minqpreport rep;
    ae_matrix rawa;
    ae_vector b;
    ae_vector s;
    ae_vector x0;
    ae_vector xs;
    ae_vector xs2;
    ae_vector bndl;
    ae_vector bndu;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    double xtol;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&rawa, 0, sizeof(rawa));
    memset(&b, 0, sizeof(b));
    memset(&s, 0, sizeof(s));
    memset(&x0, 0, sizeof(x0));
    memset(&xs, 0, sizeof(xs));
    memset(&xs2, 0, sizeof(xs2));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    _hqrndstate_init(&rs, _state, ae_true);
    _minqpstate_init(&state, _state, ae_true);
    _minqpreport_init(&rep, _state, ae_true);
    ae_matrix_init(&rawa, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Test correctness of MinQPSetScaleDiagAuto():
     * * that it correctly handles matrices with positive diagonals
     * * that it correctly handles non-positive elements
     *
     * First test is performed by comparing one step of automatically
     * scaled QP-BLEIC with one step of manually scaled QP-BLEIC.
     */
    xtol = 1.0E-6;
    for(n=1; n<=10; n++)
    {
        
        /*
         * Generate random box constrained QP problem with specially
         * crafted diagonal.
         */
        ae_matrix_set_length(&rawa, n, n, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                rawa.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
            }
            rawa.ptr.pp_double[i][i] = ae_pow((double)(2), (double)(hqrnduniformi(&rs, 9, _state)-4), _state);
            b.ptr.p_double[i] = hqrndnormal(&rs, _state);
            bndl.ptr.p_double[i] = (double)(-1);
            bndu.ptr.p_double[i] = (double)(1);
            x0.ptr.p_double[i] = (double)(0);
        }
        
        /*
         * Create solver
         */
        minqpcreate(n, &state, _state);
        minqpsetalgobleic(&state, (double)(0), (double)(0), (double)(0), 1, _state);
        minqpsetlinearterm(&state, &b, _state);
        testminqpunit_randomlyselectconvertandsetquadraticterm(&rawa, n, &state, &rs, _state);
        minqpsetbc(&state, &bndl, &bndu, _state);
        minqpsetstartingpoint(&state, &x0, _state);
        
        /*
         * Solve with automatic scaling
         * Solve with manual scaling
         * Compare
         */
        ae_vector_set_length(&xs, 0, _state);
        minqpsetscaleautodiag(&state, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xs, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:7257");
        ae_set_error_flag(errorflag, xs.cnt!=n, __FILE__, __LINE__, "testminqpunit.ap:7258");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        ae_vector_set_length(&s, n, _state);
        for(i=0; i<=n-1; i++)
        {
            s.ptr.p_double[i] = 1/ae_sqrt(rawa.ptr.pp_double[i][i], _state);
        }
        minqpsetscale(&state, &s, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xs2, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminqpunit.ap:7267");
        ae_set_error_flag(errorflag, xs2.cnt!=n, __FILE__, __LINE__, "testminqpunit.ap:7268");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xs.ptr.p_double[i]-xs2.ptr.p_double[i], _state),xtol), __FILE__, __LINE__, "testminqpunit.ap:7272");
        }
        
        /*
         * Check that automatic scaling fails for matrices with zero or negative elements
         */
        i = hqrnduniformi(&rs, n, _state);
        j = hqrnduniformi(&rs, 2, _state)-1;
        rawa.ptr.pp_double[i][i] = (double)(j);
        testminqpunit_randomlyselectconvertandsetquadraticterm(&rawa, n, &state, &rs, _state);
        minqpsetscaleautodiag(&state, _state);
        minqpoptimize(&state, _state);
        minqpresults(&state, &xs, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype!=-9, __FILE__, __LINE__, "testminqpunit.ap:7285");
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Function normal
*************************************************************************/
static double testminqpunit_projectedantigradnorm(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* g,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     ae_state *_state)
{
    ae_int_t i;
    double r;
    double result;


    r = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        ae_assert(ae_fp_greater_eq(x->ptr.p_double[i],bndl->ptr.p_double[i])&&ae_fp_less_eq(x->ptr.p_double[i],bndu->ptr.p_double[i]), "ProjectedAntiGradNormal: boundary constraints violation", _state);
        if( ((ae_fp_greater(x->ptr.p_double[i],bndl->ptr.p_double[i])&&ae_fp_less(x->ptr.p_double[i],bndu->ptr.p_double[i]))||(ae_fp_eq(x->ptr.p_double[i],bndl->ptr.p_double[i])&&ae_fp_greater(-g->ptr.p_double[i],(double)(0))))||(ae_fp_eq(x->ptr.p_double[i],bndu->ptr.p_double[i])&&ae_fp_less(-g->ptr.p_double[i],(double)(0))) )
        {
            r = r+g->ptr.p_double[i]*g->ptr.p_double[i];
        }
    }
    result = ae_sqrt(r, _state);
    return result;
}


/*************************************************************************
This function tests that norm of bound-constrained gradient at point X is
less than Eps:
* unconstrained gradient is A*x+b
* if I-th component is at the boundary, and antigradient points outside of
  the feasible area, I-th component of constrained gradient is zero

This function accepts QP terms A and B, bound constraints, current point,
and performs test. Additionally, it checks that point is feasible w.r.t.
boundary constraints.

In case of failure, error flag is set. Otherwise, it is not modified.

IMPORTANT: this function does NOT use SetErrorFlag() to modify flag.
           If you want to use SetErrorFlag() for easier tracking of errors,
           you should store flag returned by this function into separate
           variable TmpFlag and call SetErrorFlag(ErrorFlag, TmpFlag) yourself.
*************************************************************************/
static void testminqpunit_testbcgradandfeasibility(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     ae_int_t n,
     /* Real    */ ae_vector* x,
     double eps,
     ae_bool* errorflag,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double g;
    double gnorm;


    gnorm = 0.0;
    for(i=0; i<=n-1; i++)
    {
        g = b->ptr.p_double[i];
        for(j=0; j<=n-1; j++)
        {
            g = g+a->ptr.pp_double[i][j]*x->ptr.p_double[j];
        }
        if( ae_fp_eq(x->ptr.p_double[i],bndl->ptr.p_double[i])&&ae_fp_greater(g,(double)(0)) )
        {
            g = (double)(0);
        }
        if( ae_fp_eq(x->ptr.p_double[i],bndu->ptr.p_double[i])&&ae_fp_less(g,(double)(0)) )
        {
            g = (double)(0);
        }
        gnorm = gnorm+ae_sqr(g, _state);
        if( ae_fp_less(x->ptr.p_double[i],bndl->ptr.p_double[i]) )
        {
            *errorflag = ae_true;
        }
        if( ae_fp_greater(x->ptr.p_double[i],bndu->ptr.p_double[i]) )
        {
            *errorflag = ae_true;
        }
    }
    gnorm = ae_sqrt(gnorm, _state);
    if( ae_fp_greater(gnorm,eps) )
    {
        *errorflag = ae_true;
    }
}


/*************************************************************************
set random type of the QP solver.
All "modern" solvers can be chosen.

OUTPUT PARAMETERS:
    BCTol   -   expected precision of box  constraints  handling  assuming
                unit scale of variables.
    LCTol   -   expected precinion of linear constraints handling assuming
                unit scale of variables.
                
BCTol and LCTol have following meaning - if some constraint is active,  it
means that we should be at most TOL units away from boundary. It is possible
that zero value is returned.

From definition it follows that if we stopped at more than TOL  units away
from the boundary, gradient in corresponding direction is nearly zero.
*************************************************************************/
static void testminqpunit_setrandomalgoallmodern(minqpstate* s,
     double* bctol,
     double* lctol,
     ae_state *_state)
{
    ae_int_t i;

    *bctol = 0;
    *lctol = 0;

    i = 1+ae_randominteger(3, _state);
    if( i==1 )
    {
        minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0, _state);
        *bctol = (double)(0);
        *lctol = 1.0E-8;
    }
    if( i==2 )
    {
        minqpsetalgoquickqp(s, 1.0E-12, 0.0, 0.0, 0, ae_fp_greater(ae_randomreal(_state),0.5), _state);
        *bctol = (double)(0);
        *lctol = (double)(0);
    }
    if( i==3 )
    {
        minqpsetalgodenseaul(s, 1.0E-12, 1000.0, 10, _state);
        *bctol = 1.0E-3;
        *lctol = 1.0E-3;
    }
}


/*************************************************************************
set random type of theQP solver
*************************************************************************/
static void testminqpunit_setrandomalgononconvex(minqpstate* s,
     ae_state *_state)
{
    ae_int_t i;


    i = 1+ae_randominteger(2, _state);
    if( i==1 )
    {
        minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0, _state);
    }
    if( i==2 )
    {
        minqpsetalgoquickqp(s, 1.0E-12, 0.0, 0.0, 0, ae_fp_greater(ae_randomreal(_state),0.5), _state);
    }
}


/*************************************************************************
set random type of theQP solver
*************************************************************************/
static void testminqpunit_setrandomalgosemidefinite(minqpstate* s,
     ae_state *_state)
{
    ae_int_t i;


    i = 1+ae_randominteger(2, _state);
    if( i==1 )
    {
        minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0, _state);
    }
    if( i==2 )
    {
        minqpsetalgoquickqp(s, 1.0E-12, 0.0, 0.0, 0, ae_fp_greater(ae_randomreal(_state),0.5), _state);
    }
}


/*************************************************************************
set random type of the QP solver, must support boundary constraints
*************************************************************************/
static void testminqpunit_setrandomalgobc(minqpstate* s, ae_state *_state)
{
    ae_int_t i;


    i = ae_randominteger(2, _state);
    if( i==0 )
    {
        minqpsetalgoquickqp(s, 0.0, 0.0, 1.0E-13, 0, ae_true, _state);
    }
    if( i==1 )
    {
        minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0, _state);
    }
}


/*************************************************************************
set random type of the QP solver,
must support convex problems with boundary/linear constraints
*************************************************************************/
static void testminqpunit_setrandomalgoconvexlc(minqpstate* s,
     ae_state *_state)
{
    ae_int_t i;


    i = ae_randominteger(2, _state);
    if( i==0 )
    {
        minqpsetalgodenseaul(s, 1.0E-12, (double)(10000), 15, _state);
    }
    if( i==1 )
    {
        minqpsetalgobleic(s, 0.0, 0.0, 1.0E-12, 0, _state);
    }
}


/*************************************************************************
set random type of the QP solver,
must support nonconvex problems with boundary/linear constraints
*************************************************************************/
static void testminqpunit_setrandomalgononconvexlc(minqpstate* s,
     ae_state *_state)
{
    ae_int_t i;


    i = ae_randominteger(1, _state);
    if( i==0 )
    {
        minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0, _state);
    }
}


/*************************************************************************
Convert dense matrix to sparse matrix using random format
*************************************************************************/
static void testminqpunit_densetosparse(/* Real    */ ae_matrix* a,
     ae_int_t n,
     sparsematrix* s,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    sparsematrix s0;

    ae_frame_make(_state, &_frame_block);
    memset(&s0, 0, sizeof(s0));
    _sparsematrix_clear(s);
    _sparsematrix_init(&s0, _state, ae_true);

    sparsecreate(n, n, n*n, &s0, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            sparseset(&s0, i, j, a->ptr.pp_double[i][j], _state);
        }
    }
    sparsecopytobuf(&s0, ae_randominteger(3, _state), s, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Randomly split constraints into dense and sparse parts
*************************************************************************/
static void testminqpunit_randomlysplitlc(/* Real    */ ae_matrix* rawc,
     /* Integer */ ae_vector* rawct,
     ae_int_t rawccnt,
     ae_int_t n,
     /* Real    */ ae_matrix* densec,
     /* Integer */ ae_vector* densect,
     sparsematrix* sparsec,
     /* Integer */ ae_vector* sparsect,
     ae_int_t* denseccnt,
     ae_int_t* sparseccnt,
     hqrndstate* rs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(densec);
    ae_vector_clear(densect);
    _sparsematrix_clear(sparsec);
    ae_vector_clear(sparsect);
    *denseccnt = 0;
    *sparseccnt = 0;

    
    /*
     * Split "raw" constraints into dense and sparse parts
     */
    *denseccnt = hqrnduniformi(rs, rawccnt+1, _state);
    *sparseccnt = rawccnt-(*denseccnt);
    if( *denseccnt>0 )
    {
        ae_matrix_set_length(densec, *denseccnt, n+1, _state);
        ae_vector_set_length(densect, *denseccnt, _state);
        for(i=0; i<=*denseccnt-1; i++)
        {
            for(j=0; j<=n; j++)
            {
                densec->ptr.pp_double[i][j] = rawc->ptr.pp_double[i][j];
            }
            densect->ptr.p_int[i] = rawct->ptr.p_int[i];
        }
    }
    if( *sparseccnt>0 )
    {
        sparsecreate(*sparseccnt, n+1, 0, sparsec, _state);
        ae_vector_set_length(sparsect, *sparseccnt, _state);
        for(i=0; i<=*sparseccnt-1; i++)
        {
            for(j=0; j<=n; j++)
            {
                sparseset(sparsec, i, j, rawc->ptr.pp_double[*denseccnt+i][j], _state);
            }
            sparsect->ptr.p_int[i] = rawct->ptr.p_int[*denseccnt+i];
        }
    }
}


/*************************************************************************
Randomly split constraints into dense and sparse parts and set them
*************************************************************************/
static void testminqpunit_randomlysplitandsetlc(/* Real    */ ae_matrix* rawc,
     /* Integer */ ae_vector* rawct,
     ae_int_t rawccnt,
     ae_int_t n,
     minqpstate* state,
     hqrndstate* rs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix densec;
    ae_vector densect;
    sparsematrix sparsec;
    ae_vector sparsect;
    ae_int_t denseccnt;
    ae_int_t sparseccnt;

    ae_frame_make(_state, &_frame_block);
    memset(&densec, 0, sizeof(densec));
    memset(&densect, 0, sizeof(densect));
    memset(&sparsec, 0, sizeof(sparsec));
    memset(&sparsect, 0, sizeof(sparsect));
    ae_matrix_init(&densec, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&densect, 0, DT_INT, _state, ae_true);
    _sparsematrix_init(&sparsec, _state, ae_true);
    ae_vector_init(&sparsect, 0, DT_INT, _state, ae_true);

    testminqpunit_randomlysplitlc(rawc, rawct, rawccnt, n, &densec, &densect, &sparsec, &sparsect, &denseccnt, &sparseccnt, rs, _state);
    if( ae_fp_greater(hqrnduniformr(rs, _state),0.5)||denseccnt*sparseccnt>0 )
    {
        minqpsetlcmixed(state, &densec, &densect, denseccnt, &sparsec, &sparsect, sparseccnt, _state);
    }
    else
    {
        if( denseccnt>0 )
        {
            minqpsetlc(state, &densec, &densect, denseccnt, _state);
        }
        if( sparseccnt>0 )
        {
            minqpsetlcsparse(state, &sparsec, &sparsect, sparseccnt, _state);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Randomly selects triangle of full symmetric matrix, converts it to one of
the matrix storage formats (dense or sparse) and sets.
*************************************************************************/
static void testminqpunit_randomlyselectconvertandsetquadraticterm(/* Real    */ ae_matrix* a,
     ae_int_t n,
     minqpstate* state,
     hqrndstate* rs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_matrix densea;
    sparsematrix sparsea;
    ae_bool isupper;
    ae_bool isdense;

    ae_frame_make(_state, &_frame_block);
    memset(&densea, 0, sizeof(densea));
    memset(&sparsea, 0, sizeof(sparsea));
    ae_matrix_init(&densea, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sparsea, _state, ae_true);

    isupper = ae_fp_greater(hqrnduniformr(rs, _state),0.5);
    isdense = ae_fp_greater(hqrnduniformr(rs, _state),0.5);
    if( isupper&&isdense )
    {
        ae_matrix_set_length(&densea, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                densea.ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
            }
        }
        minqpsetquadraticterm(state, &densea, isupper, _state);
        ae_frame_leave(_state);
        return;
    }
    if( !isupper&&isdense )
    {
        ae_matrix_set_length(&densea, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                densea.ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
            }
        }
        minqpsetquadraticterm(state, &densea, isupper, _state);
        ae_frame_leave(_state);
        return;
    }
    if( isupper&&!isdense )
    {
        sparsecreate(n, n, 0, &sparsea, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                sparseset(&sparsea, i, j, a->ptr.pp_double[i][j], _state);
            }
        }
        minqpsetquadratictermsparse(state, &sparsea, isupper, _state);
        ae_frame_leave(_state);
        return;
    }
    if( !isupper&&!isdense )
    {
        sparsecreate(n, n, 0, &sparsea, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                sparseset(&sparsea, i, j, a->ptr.pp_double[i][j], _state);
            }
        }
        minqpsetquadratictermsparse(state, &sparsea, isupper, _state);
        ae_frame_leave(_state);
        return;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function returns reciprocal of condition  number  of  general  linear
constraints.
*************************************************************************/
static double testminqpunit_getconstraintrcond(/* Real    */ ae_matrix* c,
     ae_int_t k,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool bflag;
    ae_vector svdw;
    ae_matrix svdu;
    ae_matrix svdvt;
    double result;

    ae_frame_make(_state, &_frame_block);
    memset(&svdw, 0, sizeof(svdw));
    memset(&svdu, 0, sizeof(svdu));
    memset(&svdvt, 0, sizeof(svdvt));
    ae_vector_init(&svdw, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&svdu, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&svdvt, 0, 0, DT_REAL, _state, ae_true);

    bflag = rmatrixsvd(c, k, n, 0, 0, 0, &svdw, &svdu, &svdvt, _state);
    ae_assert(bflag, "MinQPTest: integrity failure", _state);
    if( ae_fp_greater(svdw.ptr.p_double[0],(double)(0)) )
    {
        result = svdw.ptr.p_double[ae_minint(k, n, _state)-1]/svdw.ptr.p_double[0];
    }
    else
    {
        result = (double)(1);
    }
    ae_frame_leave(_state);
    return result;
}



static void testminbcunit_calciip2(minbcstate* state,
     ae_int_t n,
     ae_int_t fk,
     ae_state *_state);
static void testminbcunit_testfeasibility(ae_bool* feaserr,
     ae_bool* converr,
     ae_bool* interr,
     ae_state *_state);
static void testminbcunit_testother(ae_bool* err, ae_state *_state);
static void testminbcunit_testpreconditioning(ae_bool* err,
     ae_state *_state);
static void testminbcunit_setrandompreconditioner(minbcstate* state,
     ae_int_t n,
     ae_int_t preckind,
     ae_state *_state);
static void testminbcunit_testgradientcheck(ae_bool* testg,
     ae_state *_state);
static void testminbcunit_funcderiv(double a,
     double b,
     double c,
     double d,
     double x0,
     double x1,
     double x2,
     /* Real    */ ae_vector* x,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);





ae_bool testminbc(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool feasibilityerrors;
    ae_bool othererrors;
    ae_bool precerrors;
    ae_bool interrors;
    ae_bool converrors;
    ae_bool graderrors;
    ae_bool result;


    waserrors = ae_false;
    feasibilityerrors = ae_false;
    othererrors = ae_false;
    precerrors = ae_false;
    interrors = ae_false;
    converrors = ae_false;
    graderrors = ae_false;
    testminbcunit_testfeasibility(&feasibilityerrors, &converrors, &interrors, _state);
    testminbcunit_testother(&othererrors, _state);
    testminbcunit_testpreconditioning(&precerrors, _state);
    testminbcunit_testgradientcheck(&graderrors, _state);
    
    /*
     * end
     */
    waserrors = ((((feasibilityerrors||othererrors)||converrors)||interrors)||precerrors)||graderrors;
    if( !silent )
    {
        printf("TESTING BC OPTIMIZATION\n");
        printf("FEASIBILITY PROPERTIES:                   ");
        if( feasibilityerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("PRECONDITIONING:                          ");
        if( precerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE PROPERTIES:                   ");
        if( converrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("INTERNAL ERRORS:                          ");
        if( interrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TEST FOR VERIFICATION OF THE GRADIENT:    ");
        if( graderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Calculate test function IIP2

f(x) = sum( ((i*i+1)^FK*x[i])^2, i=0..N-1)

It has high condition number which makes fast convergence unlikely without
good preconditioner.

*************************************************************************/
static void testminbcunit_calciip2(minbcstate* state,
     ae_int_t n,
     ae_int_t fk,
     ae_state *_state)
{
    ae_int_t i;


    if( state->needfg )
    {
        state->f = (double)(0);
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->needfg )
        {
            state->f = state->f+ae_pow((double)(i*i+1), (double)(2*fk), _state)*ae_sqr(state->x.ptr.p_double[i], _state);
            state->g.ptr.p_double[i] = ae_pow((double)(i*i+1), (double)(2*fk), _state)*2*state->x.ptr.p_double[i];
        }
    }
}


/*************************************************************************
This function test feasibility properties.
It launches a sequence of problems and examines their solutions.
Most of the attention is directed towards feasibility properties,
although we make some quick checks to ensure that actual solution is found.

On failure sets FeasErr (or ConvErr, depending on failure type) to True,
or leaves it unchanged otherwise.

IntErr is set to True on internal errors (errors in the control flow).
*************************************************************************/
static void testminbcunit_testfeasibility(ae_bool* feaserr,
     ae_bool* converr,
     ae_bool* interr,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pkind;
    ae_int_t preckind;
    ae_int_t passcount;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t nmax;
    ae_int_t i;
    ae_int_t p;
    double v;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_vector g;
    ae_vector x0;
    ae_vector xc;
    ae_vector xs;
    ae_vector svdw;
    ae_matrix csvdu;
    ae_matrix svdvt;
    minbcstate state;
    double weakepsg;
    minbcreport rep;
    ae_int_t dkind;
    double diffstep;

    ae_frame_make(_state, &_frame_block);
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    memset(&g, 0, sizeof(g));
    memset(&x0, 0, sizeof(x0));
    memset(&xc, 0, sizeof(xc));
    memset(&xs, 0, sizeof(xs));
    memset(&svdw, 0, sizeof(svdw));
    memset(&csvdu, 0, sizeof(csvdu));
    memset(&svdvt, 0, sizeof(svdvt));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&svdw, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&csvdu, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&svdvt, 0, 0, DT_REAL, _state, ae_true);
    _minbcstate_init(&state, _state, ae_true);
    _minbcreport_init(&rep, _state, ae_true);

    nmax = 5;
    weakepsg = 1.0E-4;
    passcount = 10;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Another simple problem:
         * * bound constraints 0 <= x[i] <= 1
         * * no linear constraints
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * with such simple boundaries and function it is easy to find
         *   analytic form of solution: S[i] = bound(x0[i], 0, 1)
         * * we also check that both final solution and subsequent iterates
         *   are strictly feasible
         */
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            for(preckind=0; preckind<=2; preckind++)
            {
                for(pkind=1; pkind<=2; pkind++)
                {
                    for(n=1; n<=nmax; n++)
                    {
                        
                        /*
                         * Generate X, BL, BU.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&bl, n, _state);
                        ae_vector_set_length(&bu, n, _state);
                        ae_vector_set_length(&x, n, _state);
                        ae_vector_set_length(&x0, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            bl.ptr.p_double[i] = (double)(0);
                            bu.ptr.p_double[i] = (double)(1);
                            x.ptr.p_double[i] = ae_randomreal(_state);
                            x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                        }
                        
                        /*
                         * Create and optimize
                         */
                        if( dkind==0 )
                        {
                            minbccreate(n, &x, &state, _state);
                        }
                        if( dkind==1 )
                        {
                            minbccreatef(n, &x, diffstep, &state, _state);
                        }
                        minbcsetbc(&state, &bl, &bu, _state);
                        minbcsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                        testminbcunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbciteration(&state, _state))
                        {
                            if( state.needf||state.needfg )
                            {
                                state.f = (double)(0);
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                if( state.needf||state.needfg )
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p), _state);
                                }
                                if( state.needfg )
                                {
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state);
                                }
                                *feaserr = *feaserr||ae_fp_less(state.x.ptr.p_double[i],0.0);
                                *feaserr = *feaserr||ae_fp_greater(state.x.ptr.p_double[i],1.0);
                            }
                        }
                        minbcresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * * compare solution with analytic one
                         * * check feasibility
                         */
                        v = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            if( ae_fp_greater(x.ptr.p_double[i],(double)(0))&&ae_fp_less(x.ptr.p_double[i],(double)(1)) )
                            {
                                v = v+ae_sqr(p*ae_pow(x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state), _state);
                            }
                            *feaserr = *feaserr||ae_fp_less(x.ptr.p_double[i],0.0);
                            *feaserr = *feaserr||ae_fp_greater(x.ptr.p_double[i],1.0);
                        }
                        *converr = *converr||ae_fp_greater(ae_sqrt(v, _state),weakepsg);
                    }
                }
            }
        }
        
        /*
         * Same as previous problem, but with minor modifications:
         * * some bound constraints are 0<=x[i]<=1, some are Ci=x[i]=Ci
         * * no linear constraints
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * with such simple boundaries and function it is easy to find
         *   analytic form of solution: S[i] = bound(x0[i], 0, 1)
         * * we also check that both final solution and subsequent iterates
         *   are strictly feasible
         */
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            for(preckind=0; preckind<=2; preckind++)
            {
                for(pkind=1; pkind<=2; pkind++)
                {
                    for(n=1; n<=nmax; n++)
                    {
                        
                        /*
                         * Generate X, BL, BU.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&bl, n, _state);
                        ae_vector_set_length(&bu, n, _state);
                        ae_vector_set_length(&x, n, _state);
                        ae_vector_set_length(&x0, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            if( ae_fp_greater(ae_randomreal(_state),0.5) )
                            {
                                bl.ptr.p_double[i] = (double)(0);
                                bu.ptr.p_double[i] = (double)(1);
                            }
                            else
                            {
                                bl.ptr.p_double[i] = ae_randomreal(_state);
                                bu.ptr.p_double[i] = bl.ptr.p_double[i];
                            }
                            x.ptr.p_double[i] = ae_randomreal(_state);
                            x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                        }
                        
                        /*
                         * Create and optimize
                         */
                        if( dkind==0 )
                        {
                            minbccreate(n, &x, &state, _state);
                        }
                        if( dkind==1 )
                        {
                            minbccreatef(n, &x, diffstep, &state, _state);
                        }
                        minbcsetbc(&state, &bl, &bu, _state);
                        minbcsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                        testminbcunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbciteration(&state, _state))
                        {
                            if( state.needf||state.needfg )
                            {
                                state.f = (double)(0);
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                if( state.needf||state.needfg )
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p), _state);
                                }
                                if( state.needfg )
                                {
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state);
                                }
                                *feaserr = *feaserr||ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]);
                                *feaserr = *feaserr||ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i]);
                            }
                        }
                        minbcresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * * compare solution with analytic one
                         * * check feasibility
                         */
                        v = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            if( ae_fp_greater(x.ptr.p_double[i],bl.ptr.p_double[i])&&ae_fp_less(x.ptr.p_double[i],bu.ptr.p_double[i]) )
                            {
                                v = v+ae_sqr(p*ae_pow(x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state), _state);
                            }
                            *feaserr = *feaserr||ae_fp_less(x.ptr.p_double[i],bl.ptr.p_double[i]);
                            *feaserr = *feaserr||ae_fp_greater(x.ptr.p_double[i],bu.ptr.p_double[i]);
                        }
                        *converr = *converr||ae_fp_greater(ae_sqrt(v, _state),weakepsg);
                    }
                }
            }
        }
        
        /*
         * Infeasible problem:
         * * all bound constraints are 0 <= x[i] <= 1 except for one
         * * that one is 0 >= x[i] >= 1
         * * no linear constraints
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from detecting
         *   infeasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * algorithm must return correct error code on such problem
         */
        for(preckind=0; preckind<=2; preckind++)
        {
            for(pkind=1; pkind<=2; pkind++)
            {
                for(n=1; n<=nmax; n++)
                {
                    
                    /*
                     * Generate X, BL, BU.
                     */
                    p = 2*pkind;
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&x0, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        bl.ptr.p_double[i] = (double)(0);
                        bu.ptr.p_double[i] = (double)(1);
                        x.ptr.p_double[i] = ae_randomreal(_state);
                        x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                    }
                    i = ae_randominteger(n, _state);
                    bl.ptr.p_double[i] = (double)(1);
                    bu.ptr.p_double[i] = (double)(0);
                    
                    /*
                     * Create and optimize
                     */
                    minbccreate(n, &x, &state, _state);
                    minbcsetbc(&state, &bl, &bu, _state);
                    minbcsetcond(&state, weakepsg, 0.0, 0.0, 0, _state);
                    testminbcunit_setrandompreconditioner(&state, n, preckind, _state);
                    while(minbciteration(&state, _state))
                    {
                        if( state.needfg )
                        {
                            state.f = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p), _state);
                                state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], (double)(p-1), _state);
                            }
                            continue;
                        }
                        
                        /*
                         * Unknown protocol specified
                         */
                        *interr = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    minbcresults(&state, &x, &rep, _state);
                    *feaserr = *feaserr||rep.terminationtype!=-3;
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function additional properties.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminbcunit_testother(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t passcount;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_vector xf;
    ae_vector x0;
    ae_vector x1;
    ae_vector b;
    ae_vector xlast;
    ae_vector a;
    ae_vector s;
    ae_vector h;
    ae_matrix fulla;
    double fprev;
    double xprev;
    double stpmax;
    double v;
    ae_int_t pkind;
    ae_int_t ckind;
    ae_int_t mkind;
    double vc;
    double vm;
    minbcstate state;
    double epsx;
    double epsg;
    double eps;
    double tmpeps;
    minbcreport rep;
    double diffstep;
    ae_int_t dkind;
    ae_bool wasf;
    ae_bool wasfg;
    double r;
    hqrndstate rs;
    ae_int_t spoiliteration;
    ae_int_t stopiteration;
    ae_int_t spoilvar;
    double spoilval;
    double ss;
    ae_int_t stopcallidx;
    ae_int_t callidx;
    ae_int_t maxits;
    ae_bool terminationrequested;

    ae_frame_make(_state, &_frame_block);
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    memset(&xf, 0, sizeof(xf));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&b, 0, sizeof(b));
    memset(&xlast, 0, sizeof(xlast));
    memset(&a, 0, sizeof(a));
    memset(&s, 0, sizeof(s));
    memset(&h, 0, sizeof(h));
    memset(&fulla, 0, sizeof(fulla));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&h, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    _minbcstate_init(&state, _state, ae_true);
    _minbcreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    epsx = 1.0E-4;
    epsg = 1.0E-8;
    passcount = 10;
    
    /*
     * Try to reproduce bug 570 (optimizer hangs on problems where it is required
     * to perform very small step - less than 1E-50 - in order to activate constraints).
     *
     * The problem being solved is:
     *
     *     min x[0]+x[1]+...+x[n-1]
     *
     * subject to
     *
     *     x[i]>=0, for i=0..n-1
     *
     * with initial point
     *
     *     x[0] = 1.0E-100, x[1]=x[2]=...=0.5
     *
     * We try to reproduce this problem in different settings:
     * * boundary-only constraints - we test that completion code is positive,
     *   and all x[] are EXACTLY zero
     * * boundary constraints posed as general linear ones - we test that
     *   completion code is positive, and all x[] are APPROXIMATELY zero.
     */
    n = 10;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&bl, n, _state);
    ae_vector_set_length(&bu, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = 0.5;
        bl.ptr.p_double[i] = 0.0;
        bu.ptr.p_double[i] = _state->v_posinf;
    }
    x.ptr.p_double[0] = 1.0E-100;
    minbccreate(n, &x, &state, _state);
    minbcsetbc(&state, &bl, &bu, _state);
    minbcsetcond(&state, (double)(0), (double)(0), (double)(0), 2*n, _state);
    while(minbciteration(&state, _state))
    {
        if( state.needfg )
        {
            state.f = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                state.f = state.f+state.x.ptr.p_double[i];
                state.g.ptr.p_double[i] = 1.0;
            }
        }
    }
    minbcresults(&state, &xf, &rep, _state);
    ae_set_error_flag(err, rep.terminationtype<=0, __FILE__, __LINE__, "testminbcunit.ap:491");
    if( rep.terminationtype>0 )
    {
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_neq(xf.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testminbcunit.ap:494");
        }
    }
    
    /*
     * Test reports:
     * * first value must be starting point
     * * last value must be last point
     */
    for(pass=1; pass<=passcount; pass++)
    {
        n = 50;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)(10);
            bl.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            bu.ptr.p_double[i] = _state->v_posinf;
        }
        minbccreate(n, &x, &state, _state);
        minbcsetbc(&state, &bl, &bu, _state);
        minbcsetcond(&state, 1.0E-64, (double)(0), (double)(0), 10, _state);
        minbcsetxrep(&state, ae_true, _state);
        fprev = ae_maxrealnumber;
        while(minbciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                }
            }
            if( state.xupdated )
            {
                if( ae_fp_eq(fprev,ae_maxrealnumber) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(err, ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]), __FILE__, __LINE__, "testminbcunit.ap:535");
                    }
                }
                fprev = state.f;
                ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            }
        }
        minbcresults(&state, &x, &rep, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]), __FILE__, __LINE__, "testminbcunit.ap:542");
        }
    }
    
    /*
     * Test differentiation vs. analytic gradient
     * (first one issues NeedF requests, second one issues NeedFG requests)
     */
    for(pass=1; pass<=passcount; pass++)
    {
        n = 10;
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (double)(1);
            }
            if( dkind==0 )
            {
                minbccreate(n, &x, &state, _state);
            }
            if( dkind==1 )
            {
                minbccreatef(n, &x, diffstep, &state, _state);
            }
            minbcsetcond(&state, 1.0E-6, (double)(0), epsx, 0, _state);
            wasf = ae_false;
            wasfg = ae_false;
            while(minbciteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = (double)(0);
                }
                for(i=0; i<=n-1; i++)
                {
                    if( state.needf||state.needfg )
                    {
                        state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    }
                    if( state.needfg )
                    {
                        state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                    }
                }
                wasf = wasf||state.needf;
                wasfg = wasfg||state.needfg;
            }
            minbcresults(&state, &x, &rep, _state);
            if( dkind==0 )
            {
                ae_set_error_flag(err, wasf||!wasfg, __FILE__, __LINE__, "testminbcunit.ap:582");
            }
            if( dkind==1 )
            {
                ae_set_error_flag(err, !wasf||wasfg, __FILE__, __LINE__, "testminbcunit.ap:584");
            }
        }
    }
    
    /*
     * Test that numerical differentiation uses scaling.
     *
     * In order to test that we solve simple optimization
     * problem: min(x^2) with initial x equal to 0.0.
     *
     * We choose random DiffStep and S, then we check that
     * optimizer evaluates function at +-DiffStep*S only.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        ae_vector_set_length(&x, 1, _state);
        ae_vector_set_length(&s, 1, _state);
        diffstep = ae_randomreal(_state)*1.0E-6;
        s.ptr.p_double[0] = ae_exp(ae_randomreal(_state)*4-2, _state);
        x.ptr.p_double[0] = (double)(0);
        minbccreatef(1, &x, diffstep, &state, _state);
        minbcsetcond(&state, 1.0E-6, (double)(0), epsx, 0, _state);
        minbcsetscale(&state, &s, _state);
        v = (double)(0);
        while(minbciteration(&state, _state))
        {
            state.f = ae_sqr(state.x.ptr.p_double[0], _state);
            v = ae_maxreal(v, ae_fabs(state.x.ptr.p_double[0], _state), _state);
        }
        minbcresults(&state, &x, &rep, _state);
        r = v/(s.ptr.p_double[0]*diffstep);
        ae_set_error_flag(err, ae_fp_greater(ae_fabs(ae_log(r, _state), _state),ae_log(1+1000*ae_machineepsilon, _state)), __FILE__, __LINE__, "testminbcunit.ap:615");
    }
    
    /*
     * Test stpmax
     */
    for(pass=1; pass<=passcount; pass++)
    {
        n = 1;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        x.ptr.p_double[0] = (double)(100);
        bl.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
        bu.ptr.p_double[0] = _state->v_posinf;
        stpmax = 0.05+0.05*ae_randomreal(_state);
        minbccreate(n, &x, &state, _state);
        minbcsetbc(&state, &bl, &bu, _state);
        minbcsetcond(&state, epsg, (double)(0), epsx, 0, _state);
        minbcsetxrep(&state, ae_true, _state);
        minbcsetstpmax(&state, stpmax, _state);
        xprev = x.ptr.p_double[0];
        while(minbciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = ae_exp(state.x.ptr.p_double[0], _state)-ae_exp(-state.x.ptr.p_double[0], _state);
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax), __FILE__, __LINE__, "testminbcunit.ap:643");
            }
            if( state.xupdated )
            {
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax), __FILE__, __LINE__, "testminbcunit.ap:647");
                xprev = state.x.ptr.p_double[0];
            }
        }
    }
    
    /*
     * Ability to solve problems with function which is unbounded from below
     */
    for(pass=1; pass<=passcount; pass++)
    {
        n = 1;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        bl.ptr.p_double[0] = 4*ae_randomreal(_state)+1;
        bu.ptr.p_double[0] = bl.ptr.p_double[0]+1;
        x.ptr.p_double[0] = 0.5*(bl.ptr.p_double[0]+bu.ptr.p_double[0]);
        minbccreate(n, &x, &state, _state);
        minbcsetbc(&state, &bl, &bu, _state);
        minbcsetcond(&state, epsg, (double)(0), epsx, 0, _state);
        while(minbciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = -1.0E8*ae_sqr(state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = -2.0E8*state.x.ptr.p_double[0];
            }
        }
        minbcresults(&state, &x, &rep, _state);
        ae_set_error_flag(err, ae_fp_greater(ae_fabs(x.ptr.p_double[0]-bu.ptr.p_double[0], _state),epsx), __FILE__, __LINE__, "testminbcunit.ap:677");
    }
    
    /*
     * Test correctness of the scaling:
     * * initial point is random point from [+1,+2]^N
     * * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
     * * function is EFFECTIVELY unconstrained; it has formal constraints,
     *   but they are inactive at the solution; we try different variants
     *   in order to explore different control paths of the optimizer:
     *   0) absense of constraints
     *   1) bound constraints -100000<=x[i]<=100000
     * * we use random scaling matrix
     * * we test different variants of the preconditioning:
     *   0) unit preconditioner
     *   1) random diagonal from [0.01,100]
     *   2) scale preconditioner
     * * we set very stringent stopping conditions
     * * and we test that in the extremum stopping conditions are
     *   satisfied subject to the current scaling coefficients.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        tmpeps = 1.0E-5;
        for(n=1; n<=10; n++)
        {
            for(ckind=0; ckind<=1; ckind++)
            {
                for(pkind=0; pkind<=2; pkind++)
                {
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&a, n, _state);
                    ae_vector_set_length(&s, n, _state);
                    ae_vector_set_length(&h, n, _state);
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = ae_randomreal(_state)+1;
                        bl.ptr.p_double[i] = (double)(-100000);
                        bu.ptr.p_double[i] = (double)(100000);
                        a.ptr.p_double[i] = ae_exp(ae_log((double)(10), _state)*(2*ae_randomreal(_state)-1), _state);
                        s.ptr.p_double[i] = ae_exp(ae_log((double)(10), _state)*(2*ae_randomreal(_state)-1), _state);
                        h.ptr.p_double[i] = ae_exp(ae_log((double)(10), _state)*(2*ae_randomreal(_state)-1), _state);
                    }
                    minbccreate(n, &x, &state, _state);
                    if( ckind==1 )
                    {
                        minbcsetbc(&state, &bl, &bu, _state);
                    }
                    if( pkind==1 )
                    {
                        minbcsetprecdiag(&state, &h, _state);
                    }
                    if( pkind==2 )
                    {
                        minbcsetprecscale(&state, _state);
                    }
                    minbcsetcond(&state, tmpeps, (double)(0), (double)(0), 0, _state);
                    minbcsetscale(&state, &s, _state);
                    while(minbciteration(&state, _state))
                    {
                        if( state.needfg )
                        {
                            state.f = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+a.ptr.p_double[i]*ae_sqr(state.x.ptr.p_double[i], _state);
                                state.g.ptr.p_double[i] = 2*a.ptr.p_double[i]*state.x.ptr.p_double[i];
                            }
                        }
                    }
                    minbcresults(&state, &x, &rep, _state);
                    if( rep.terminationtype<=0 )
                    {
                        ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminbcunit.ap:744");
                        ae_frame_leave(_state);
                        return;
                    }
                    v = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+ae_sqr(s.ptr.p_double[i]*2*a.ptr.p_double[i]*x.ptr.p_double[i], _state);
                    }
                    v = ae_sqrt(v, _state);
                    ae_set_error_flag(err, ae_fp_greater(v,tmpeps), __FILE__, __LINE__, "testminbcunit.ap:751");
                }
            }
        }
    }
    
    /*
     * Check correctness of the "trimming".
     *
     * Trimming is a technique which is used to help algorithm
     * cope with unbounded functions. In order to check this
     * technique we will try to solve following optimization
     * problem:
     *
     *     min f(x) subject to no constraints on X
     *            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
     *     f(x) = {
     *            { M, if x<=-0.999999 or x>=0.999999
     *
     * where c is either 1.0 or 1.0E+4, M is either 1.0E8, 1.0E20 or +INF
     * (we try different combinations)
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(ckind=0; ckind<=1; ckind++)
        {
            for(mkind=0; mkind<=2; mkind++)
            {
                
                /*
                 * Choose c and M
                 */
                vc = (double)(1);
                vm = (double)(1);
                if( ckind==0 )
                {
                    vc = 1.0;
                }
                if( ckind==1 )
                {
                    vc = 1.0E+4;
                }
                if( mkind==0 )
                {
                    vm = 1.0E+8;
                }
                if( mkind==1 )
                {
                    vm = 1.0E+20;
                }
                if( mkind==2 )
                {
                    vm = _state->v_posinf;
                }
                
                /*
                 * Create optimizer, solve optimization problem
                 */
                epsg = 1.0E-6*vc;
                ae_vector_set_length(&x, 1, _state);
                x.ptr.p_double[0] = 0.0;
                minbccreate(1, &x, &state, _state);
                minbcsetcond(&state, epsg, (double)(0), (double)(0), 0, _state);
                while(minbciteration(&state, _state))
                {
                    if( state.needfg )
                    {
                        if( ae_fp_less(-0.999999,state.x.ptr.p_double[0])&&ae_fp_less(state.x.ptr.p_double[0],0.999999) )
                        {
                            state.f = 1/(1-state.x.ptr.p_double[0])+1/(1+state.x.ptr.p_double[0])+vc*state.x.ptr.p_double[0];
                            state.g.ptr.p_double[0] = 1/ae_sqr(1-state.x.ptr.p_double[0], _state)-1/ae_sqr(1+state.x.ptr.p_double[0], _state)+vc;
                        }
                        else
                        {
                            state.f = vm;
                            state.g.ptr.p_double[0] = (double)(0);
                        }
                    }
                }
                minbcresults(&state, &x, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminbcunit.ap:819");
                    ae_frame_leave(_state);
                    return;
                }
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(1/ae_sqr(1-x.ptr.p_double[0], _state)-1/ae_sqr(1+x.ptr.p_double[0], _state)+vc, _state),epsg), __FILE__, __LINE__, "testminbcunit.ap:822");
            }
        }
    }
    
    /*
     * Test behaviour on noisy functions.
     *
     * Consider following problem:
     * * f(x,y) = (x+1)^2 + (y+1)^2 + 10000*MachineEpsilon*RandomReal()
     * * boundary constraints x>=0, y>=0
     * * starting point (x0,y0)=(10*MachineEpsilon,1.0)
     *
     * Such problem contains small numerical noise. Without noise its
     * solution is (xs,ys)=(0,0), which is easy to find. However, presence
     * of the noise makes it hard to solve:
     * * noisy f(x,y) is monotonically decreasing only when we perform
     *   steps orders of magnitude larger than 10000*MachineEpsilon
     * * at small scales f(x,y) is non-monotonic and non-convex
     * * however, our first step must be done towards
     *   (x1,y1) = (0,1-some_small_value), and length of such step is
     *   many times SMALLER than 10000*MachineEpsilon
     * * second step, from (x1,y1) to (xs,ys), will be large enough to
     *   ignore numerical noise, so the only problem is to perform
     *   first step
     *
     * Naive implementation of BC should fail sometimes (sometimes -
     * due to non-deterministic nature of noise) on such problem. However,
     * our improved implementation should solve it correctly. We test
     * several variations of inner stopping criteria.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        eps = 1.0E-9;
        ae_vector_set_length(&x, 2, _state);
        ae_vector_set_length(&bl, 2, _state);
        ae_vector_set_length(&bu, 2, _state);
        x.ptr.p_double[0] = 10*ae_machineepsilon;
        x.ptr.p_double[1] = 1.0;
        bl.ptr.p_double[0] = 0.0;
        bu.ptr.p_double[0] = _state->v_posinf;
        bl.ptr.p_double[1] = 0.0;
        bu.ptr.p_double[1] = _state->v_posinf;
        for(ckind=0; ckind<=2; ckind++)
        {
            minbccreate(2, &x, &state, _state);
            minbcsetbc(&state, &bl, &bu, _state);
            if( ckind==0 )
            {
                minbcsetcond(&state, eps, (double)(0), (double)(0), 0, _state);
            }
            if( ckind==1 )
            {
                minbcsetcond(&state, (double)(0), eps, (double)(0), 0, _state);
            }
            if( ckind==2 )
            {
                minbcsetcond(&state, (double)(0), (double)(0), eps, 0, _state);
            }
            while(minbciteration(&state, _state))
            {
                if( state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0]+1, _state)+ae_sqr(state.x.ptr.p_double[1]+1, _state)+10000*ae_machineepsilon*ae_randomreal(_state);
                    state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]+1);
                    state.g.ptr.p_double[1] = 2*(state.x.ptr.p_double[1]+1);
                }
            }
            minbcresults(&state, &xf, &rep, _state);
            if( (rep.terminationtype<=0||ae_fp_neq(xf.ptr.p_double[0],(double)(0)))||ae_fp_neq(xf.ptr.p_double[1],(double)(0)) )
            {
                ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminbcunit.ap:886");
                ae_frame_leave(_state);
                return;
            }
        }
    }
    
    /*
     * Deterministic variation of the previous problem.
     *
     * Consider following problem:
     * * boundary constraints x>=0, y>=0
     * * starting point (x0,y0)=(10*MachineEpsilon,1.0)
     *            / (x+1)^2 + (y+1)^2,        for (x,y)<>(x0,y0)
     * * f(x,y) = |
     *            \ (x+1)^2 + (y+1)^2 - 0.1,  for (x,y)=(x0,y0)
     *
     * Such problem contains deterministic numerical noise (-0.1 at
     * starting point). Without noise its solution is easy to find.
     * However, presence of the noise makes it hard to solve:
     * * our first step must be done towards (x1,y1) = (0,1-some_small_value),
     *   but such step will increase function valye by approximately 0.1  -
     *   instead of decreasing it.
     *
     * Naive implementation of BC should fail on such problem. However,
     * our improved implementation should solve it correctly. We test
     * several variations of inner stopping criteria.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        eps = 1.0E-9;
        ae_vector_set_length(&x, 2, _state);
        ae_vector_set_length(&bl, 2, _state);
        ae_vector_set_length(&bu, 2, _state);
        x.ptr.p_double[0] = 10*ae_machineepsilon;
        x.ptr.p_double[1] = 1.0;
        bl.ptr.p_double[0] = 0.0;
        bu.ptr.p_double[0] = _state->v_posinf;
        bl.ptr.p_double[1] = 0.0;
        bu.ptr.p_double[1] = _state->v_posinf;
        for(ckind=0; ckind<=2; ckind++)
        {
            minbccreate(2, &x, &state, _state);
            minbcsetbc(&state, &bl, &bu, _state);
            if( ckind==0 )
            {
                minbcsetcond(&state, eps, (double)(0), (double)(0), 0, _state);
            }
            if( ckind==1 )
            {
                minbcsetcond(&state, (double)(0), eps, (double)(0), 0, _state);
            }
            if( ckind==2 )
            {
                minbcsetcond(&state, (double)(0), (double)(0), eps, 0, _state);
            }
            while(minbciteration(&state, _state))
            {
                if( state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0]+1, _state)+ae_sqr(state.x.ptr.p_double[1]+1, _state);
                    if( ae_fp_eq(state.x.ptr.p_double[0],x.ptr.p_double[0])&&ae_fp_eq(state.x.ptr.p_double[1],x.ptr.p_double[1]) )
                    {
                        state.f = state.f-0.1;
                    }
                    state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]+1);
                    state.g.ptr.p_double[1] = 2*(state.x.ptr.p_double[1]+1);
                }
            }
            minbcresults(&state, &xf, &rep, _state);
            if( (rep.terminationtype<=0||ae_fp_neq(xf.ptr.p_double[0],(double)(0)))||ae_fp_neq(xf.ptr.p_double[1],(double)(0)) )
            {
                ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testminbcunit.ap:949");
                ae_frame_leave(_state);
                return;
            }
        }
    }
    
    /*
     * Test integrity checks for NAN/INF:
     * * algorithm solves optimization problem, which is normal for some time (quadratic)
     * * after 5-th step we choose random component of gradient and consistently spoil
     *   it by NAN or INF.
     * * we check that correct termination code is returned (-8)
     */
    n = 100;
    for(pass=1; pass<=10; pass++)
    {
        spoiliteration = 5;
        stopiteration = 8;
        if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
        {
            
            /*
             * Gradient can be spoiled by +INF, -INF, NAN
             */
            spoilvar = hqrnduniformi(&rs, n, _state);
            i = hqrnduniformi(&rs, 3, _state);
            spoilval = _state->v_nan;
            if( i==0 )
            {
                spoilval = _state->v_neginf;
            }
            if( i==1 )
            {
                spoilval = _state->v_posinf;
            }
        }
        else
        {
            
            /*
             * Function value can be spoiled only by NAN
             * (+INF can be recognized as legitimate value during optimization)
             */
            spoilvar = -1;
            spoilval = _state->v_nan;
        }
        spdmatrixrndcond(n, 1.0E5, &fulla, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = hqrndnormal(&rs, _state);
            x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        minbccreate(n, &x0, &state, _state);
        minbcsetcond(&state, 0.0, 0.0, 0.0, stopiteration, _state);
        minbcsetxrep(&state, ae_true, _state);
        k = -1;
        while(minbciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                    state.g.ptr.p_double[i] = b.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        state.f = state.f+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        state.g.ptr.p_double[i] = state.g.ptr.p_double[i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                    }
                }
                if( k>=spoiliteration )
                {
                    if( spoilvar<0 )
                    {
                        state.f = spoilval;
                    }
                    else
                    {
                        state.g.ptr.p_double[spoilvar] = spoilval;
                    }
                }
                continue;
            }
            if( state.xupdated )
            {
                inc(&k, _state);
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minbcresults(&state, &x1, &rep, _state);
        ae_set_error_flag(err, rep.terminationtype!=-8, __FILE__, __LINE__, "testminbcunit.ap:1034");
    }
    
    /*
     * Check algorithm ability to handle request for termination:
     * * to terminate with correct return code = 8
     * * to return point which was "current" at the moment of termination
     *
     * NOTE: we solve problem with "corrupted" preconditioner which makes it hard
     *       to converge in less than StopCallIdx iterations
     */
    for(pass=1; pass<=50; pass++)
    {
        n = 3;
        ss = (double)(100);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 6+ae_randomreal(_state);
        }
        ae_vector_set_length(&s, 3, _state);
        s.ptr.p_double[0] = 0.00001;
        s.ptr.p_double[1] = 0.00001;
        s.ptr.p_double[2] = 10000.0;
        stopcallidx = ae_randominteger(20, _state);
        maxits = 25;
        minbccreate(n, &x, &state, _state);
        minbcsetcond(&state, (double)(0), (double)(0), (double)(0), maxits, _state);
        minbcsetxrep(&state, ae_true, _state);
        minbcsetprecdiag(&state, &s, _state);
        callidx = 0;
        terminationrequested = ae_false;
        ae_v_move(&xlast.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        while(minbciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ss*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = 2*ss*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0])*(-1);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
                if( callidx==stopcallidx )
                {
                    minbcrequesttermination(&state, _state);
                    terminationrequested = ae_true;
                }
                inc(&callidx, _state);
                continue;
            }
            if( state.xupdated )
            {
                if( !terminationrequested )
                {
                    ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minbcresults(&state, &x, &rep, _state);
        ae_set_error_flag(err, rep.terminationtype!=8, __FILE__, __LINE__, "testminbcunit.ap:1092");
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(err, ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]), __FILE__, __LINE__, "testminbcunit.ap:1094");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests preconditioning

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminbcunit_testpreconditioning(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t n;
    ae_vector x;
    ae_vector x0;
    ae_int_t i;
    ae_int_t k;
    ae_matrix v;
    ae_vector bl;
    ae_vector bu;
    ae_vector vd;
    ae_vector d;
    ae_vector units;
    ae_vector s;
    ae_int_t cntb1;
    ae_int_t cntb2;
    ae_int_t cntg1;
    ae_int_t cntg2;
    double epsg;
    ae_vector diagh;
    minbcstate state;
    minbcreport rep;
    ae_int_t ckind;
    ae_int_t fk;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&x0, 0, sizeof(x0));
    memset(&v, 0, sizeof(v));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&vd, 0, sizeof(vd));
    memset(&d, 0, sizeof(d));
    memset(&units, 0, sizeof(units));
    memset(&s, 0, sizeof(s));
    memset(&diagh, 0, sizeof(diagh));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vd, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&units, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);
    _minbcstate_init(&state, _state, ae_true);
    _minbcreport_init(&rep, _state, ae_true);

    
    /*
     * Preconditioner test 1.
     *
     * If
     * * B1 is default preconditioner with unit scale
     * * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix
     * * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * then B1 is worse than G1, B2 is worse than G2.
     * "Worse" means more iterations to converge.
     *
     * Test problem setup:
     * * f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1)
     * * constraints:
     *   0) absent
     *   1) box
     *
     * N        - problem size
     * K        - number of repeated passes (should be large enough to average out random factors)
     */
    k = 100;
    epsg = 1.0E-8;
    for(n=10; n<=10; n++)
    {
        for(ckind=0; ckind<=1; ckind++)
        {
            fk = 1;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&units, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (double)(0);
                units.ptr.p_double[i] = (double)(1);
            }
            minbccreate(n, &x, &state, _state);
            minbcsetcond(&state, epsg, 0.0, 0.0, 0, _state);
            if( ckind==1 )
            {
                ae_vector_set_length(&bl, n, _state);
                ae_vector_set_length(&bu, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    bl.ptr.p_double[i] = (double)(-1);
                    bu.ptr.p_double[i] = (double)(1);
                }
                minbcsetbc(&state, &bl, &bu, _state);
            }
            
            /*
             * Test it with default preconditioner VS. perturbed diagonal preconditioner
             */
            minbcsetprecdefault(&state, _state);
            minbcsetscale(&state, &units, _state);
            cntb1 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                minbcrestartfrom(&state, &x, _state);
                while(minbciteration(&state, _state))
                {
                    testminbcunit_calciip2(&state, n, fk, _state);
                }
                minbcresults(&state, &x, &rep, _state);
                cntb1 = cntb1+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            ae_vector_set_length(&diagh, n, _state);
            for(i=0; i<=n-1; i++)
            {
                diagh.ptr.p_double[i] = 2*ae_pow((double)(i*i+1), (double)(2*fk), _state)*(0.8+0.4*ae_randomreal(_state));
            }
            minbcsetprecdiag(&state, &diagh, _state);
            minbcsetscale(&state, &units, _state);
            cntg1 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                minbcrestartfrom(&state, &x, _state);
                while(minbciteration(&state, _state))
                {
                    testminbcunit_calciip2(&state, n, fk, _state);
                }
                minbcresults(&state, &x, &rep, _state);
                cntg1 = cntg1+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            *err = *err||cntb1<cntg1;
            
            /*
             * Test it with scale-based preconditioner
             */
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = 1/ae_sqrt(2*ae_pow((double)(i*i+1), (double)(2*fk), _state)*(0.8+0.4*ae_randomreal(_state)), _state);
            }
            minbcsetprecdefault(&state, _state);
            minbcsetscale(&state, &s, _state);
            cntb2 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                minbcrestartfrom(&state, &x, _state);
                while(minbciteration(&state, _state))
                {
                    testminbcunit_calciip2(&state, n, fk, _state);
                }
                minbcresults(&state, &x, &rep, _state);
                cntb2 = cntb2+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            minbcsetprecscale(&state, _state);
            minbcsetscale(&state, &s, _state);
            cntg2 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                minbcrestartfrom(&state, &x, _state);
                while(minbciteration(&state, _state))
                {
                    testminbcunit_calciip2(&state, n, fk, _state);
                }
                minbcresults(&state, &x, &rep, _state);
                cntg2 = cntg2+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            *err = *err||cntb2<cntg2;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function sets random preconditioner:
* unit one, for PrecKind=0
* diagonal-based one, for PrecKind=1
* scale-based one, for PrecKind=2
*************************************************************************/
static void testminbcunit_setrandompreconditioner(minbcstate* state,
     ae_int_t n,
     ae_int_t preckind,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector p;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&p, 0, sizeof(p));
    ae_vector_init(&p, 0, DT_REAL, _state, ae_true);

    if( preckind==1 )
    {
        ae_vector_set_length(&p, n, _state);
        for(i=0; i<=n-1; i++)
        {
            p.ptr.p_double[i] = ae_exp(6*ae_randomreal(_state)-3, _state);
        }
        minbcsetprecdiag(state, &p, _state);
    }
    else
    {
        minbcsetprecdefault(state, _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests, that gradient verified correctly.
*************************************************************************/
static void testminbcunit_testgradientcheck(ae_bool* testg,
     ae_state *_state)
{
    ae_frame _frame_block;
    minbcstate state;
    minbcreport rep;
    ae_int_t n;
    double a;
    double b;
    double c;
    double d;
    double x0;
    double x1;
    double x2;
    ae_vector x;
    ae_vector bl;
    ae_vector bu;
    ae_int_t infcomp;
    double teststep;
    double noise;
    ae_int_t nbrcomp;
    double spp;
    ae_int_t func;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&x, 0, sizeof(x));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    *testg = ae_false;
    _minbcstate_init(&state, _state, ae_true);
    _minbcreport_init(&rep, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);

    passcount = 35;
    spp = 1.0;
    teststep = 0.01;
    n = 3;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&bl, n, _state);
    ae_vector_set_length(&bu, n, _state);
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Prepare test's parameters
         */
        func = ae_randominteger(3, _state)+1;
        nbrcomp = ae_randominteger(n, _state);
        noise = (double)(10*(2*ae_randominteger(2, _state)-1));
        
        /*
         * Prepare function's parameters
         */
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
        }
        a = 5*ae_randomreal(_state)+1;
        b = 5*ae_randomreal(_state)+1;
        c = 5*ae_randomreal(_state)+1;
        d = 5*ae_randomreal(_state)+1;
        x0 = 5*(2*ae_randomreal(_state)-1);
        x1 = 5*(2*ae_randomreal(_state)-1);
        x2 = 5*(2*ae_randomreal(_state)-1);
        
        /*
         * Prepare boundary parameters
         */
        for(i=0; i<=n-1; i++)
        {
            bl.ptr.p_double[i] = ae_randomreal(_state)-spp;
            bu.ptr.p_double[i] = ae_randomreal(_state)+spp-1;
        }
        infcomp = ae_randominteger(n+1, _state);
        if( infcomp<n )
        {
            bl.ptr.p_double[infcomp] = _state->v_neginf;
        }
        infcomp = ae_randominteger(n+1, _state);
        if( infcomp<n )
        {
            bu.ptr.p_double[infcomp] = _state->v_posinf;
        }
        minbccreate(n, &x, &state, _state);
        minbcsetgradientcheck(&state, teststep, _state);
        minbcsetbc(&state, &bl, &bu, _state);
        
        /*
         * Check that the criterion passes a derivative if it is correct
         */
        while(minbciteration(&state, _state))
        {
            if( state.needfg )
            {
                
                /*
                 * Check that .X within the boundaries
                 */
                for(i=0; i<=n-1; i++)
                {
                    if( (ae_isfinite(bl.ptr.p_double[i], _state)&&ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]))||(ae_isfinite(bu.ptr.p_double[i], _state)&&ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i])) )
                    {
                        *testg = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                }
                testminbcunit_funcderiv(a, b, c, d, x0, x1, x2, &state.x, func, &state.f, &state.g, _state);
            }
        }
        minbcresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code does not equal to -7 and parameter .VarIdx
         * equal to -1.
         */
        if( rep.terminationtype==-7||rep.varidx!=-1 )
        {
            *testg = ae_true;
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
        }
        minbcrestartfrom(&state, &x, _state);
        
        /*
         * Check that the criterion does not miss a derivative if
         * it is incorrect
         */
        while(minbciteration(&state, _state))
        {
            if( state.needfg )
            {
                for(i=0; i<=n-1; i++)
                {
                    if( (ae_isfinite(bl.ptr.p_double[i], _state)&&ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]))||(ae_isfinite(bu.ptr.p_double[i], _state)&&ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i])) )
                    {
                        *testg = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                }
                testminbcunit_funcderiv(a, b, c, d, x0, x1, x2, &state.x, func, &state.f, &state.g, _state);
                state.g.ptr.p_double[nbrcomp] = state.g.ptr.p_double[nbrcomp]+noise;
            }
        }
        minbcresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code equal to -7 and parameter .VarIdx
         * equal to number of incorrect component.
         */
        if( rep.terminationtype!=-7||rep.varidx!=nbrcomp )
        {
            *testg = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    *testg = ae_false;
    ae_frame_leave(_state);
}


/*************************************************************************
This function return function value and it derivatives. Function dimension
is 3.
    Function's list:
        * funcType=1:
            F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*(Z-Z0)^2+D;
        * funcType=2:
            F(X)=A*sin(X-X0)^2+B*sin(Y-Y0)^2+C*sin(Z-Z0)^2+D;
        * funcType=3:
            F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*((Z-Z0)-(X-X0))^2+D.
*************************************************************************/
static void testminbcunit_funcderiv(double a,
     double b,
     double c,
     double d,
     double x0,
     double x1,
     double x2,
     /* Real    */ ae_vector* x,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{


    ae_assert(((ae_isfinite(a, _state)&&ae_isfinite(b, _state))&&ae_isfinite(c, _state))&&ae_isfinite(d, _state), "FuncDeriv: A, B, C or D contains NaN or Infinite.", _state);
    ae_assert((ae_isfinite(x0, _state)&&ae_isfinite(x1, _state))&&ae_isfinite(x2, _state), "FuncDeriv: X0, X1 or X2 contains NaN or Infinite.", _state);
    ae_assert(functype>=1&&functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).", _state);
    if( functype==1 )
    {
        *f = a*ae_sqr(x->ptr.p_double[0]-x0, _state)+b*ae_sqr(x->ptr.p_double[1]-x1, _state)+c*ae_sqr(x->ptr.p_double[2]-x2, _state)+d;
        g->ptr.p_double[0] = 2*a*(x->ptr.p_double[0]-x0);
        g->ptr.p_double[1] = 2*b*(x->ptr.p_double[1]-x1);
        g->ptr.p_double[2] = 2*c*(x->ptr.p_double[2]-x2);
        return;
    }
    if( functype==2 )
    {
        *f = a*ae_sqr(ae_sin(x->ptr.p_double[0]-x0, _state), _state)+b*ae_sqr(ae_sin(x->ptr.p_double[1]-x1, _state), _state)+c*ae_sqr(ae_sin(x->ptr.p_double[2]-x2, _state), _state)+d;
        g->ptr.p_double[0] = 2*a*ae_sin(x->ptr.p_double[0]-x0, _state)*ae_cos(x->ptr.p_double[0]-x0, _state);
        g->ptr.p_double[1] = 2*b*ae_sin(x->ptr.p_double[1]-x1, _state)*ae_cos(x->ptr.p_double[1]-x1, _state);
        g->ptr.p_double[2] = 2*c*ae_sin(x->ptr.p_double[2]-x2, _state)*ae_cos(x->ptr.p_double[2]-x2, _state);
        return;
    }
    if( functype==3 )
    {
        *f = a*ae_sqr(x->ptr.p_double[0]-x0, _state)+b*ae_sqr(x->ptr.p_double[1]-x1, _state)+c*ae_sqr(x->ptr.p_double[2]-x2-(x->ptr.p_double[0]-x0), _state)+d;
        g->ptr.p_double[0] = 2*a*(x->ptr.p_double[0]-x0)+2*c*(x->ptr.p_double[0]-x->ptr.p_double[2]-x0+x2);
        g->ptr.p_double[1] = 2*b*(x->ptr.p_double[1]-x1);
        g->ptr.p_double[2] = 2*c*(x->ptr.p_double[2]-x->ptr.p_double[0]-x2+x0);
        return;
    }
}



static void testminnlcunit_testbc(ae_bool* wereerrors, ae_state *_state);
static void testminnlcunit_testlc(ae_bool* wereerrors, ae_state *_state);
static void testminnlcunit_testnlc(ae_bool* wereerrors, ae_state *_state);
static void testminnlcunit_testother(ae_bool* wereerrors,
     ae_state *_state);
static void testminnlcunit_testbugs(ae_bool* wereerrors, ae_state *_state);





ae_bool testminnlc(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool bcerr;
    ae_bool lcerr;
    ae_bool nlcerr;
    ae_bool othererr;
    ae_bool bugs;
    ae_bool result;


    waserrors = ae_false;
    bcerr = ae_false;
    lcerr = ae_false;
    nlcerr = ae_false;
    othererr = ae_false;
    bugs = ae_false;
    testminnlcunit_testbugs(&bugs, _state);
    testminnlcunit_testbc(&bcerr, _state);
    testminnlcunit_testlc(&lcerr, _state);
    testminnlcunit_testnlc(&nlcerr, _state);
    testminnlcunit_testother(&othererr, _state);
    
    /*
     * end
     */
    waserrors = (((bcerr||lcerr)||nlcerr)||othererr)||bugs;
    if( !silent )
    {
        printf("TESTING MINNLC OPTIMIZATION\n");
        printf("TESTS:\n");
        printf("* BOUND CONSTRAINED                       ");
        if( bcerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* LINEARLY CONSTRAINED                    ");
        if( lcerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* NONLINEARLY CONSTRAINED                 ");
        if( nlcerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* OTHER PROPERTIES                        ");
        if( othererr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* FIXED BUGS:                             ");
        if( bugs )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
This function tests bound constrained quadratic programming algorithm.

On failure sets error flag.
*************************************************************************/
static void testminnlcunit_testbc(ae_bool* wereerrors, ae_state *_state)
{
    ae_frame _frame_block;
    minnlcstate state;
    minnlcreport rep;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aulits;
    double tolx;
    double tolg;
    ae_int_t scaletype;
    double rho;
    ae_int_t solvertype;
    ae_matrix fulla;
    ae_vector b;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector s;
    ae_vector x0;
    ae_vector x1;
    double gnorm;
    double g;
    ae_int_t prectype;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&fulla, 0, sizeof(fulla));
    memset(&b, 0, sizeof(b));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&s, 0, sizeof(s));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&rs, 0, sizeof(rs));
    _minnlcstate_init(&state, _state, ae_true);
    _minnlcreport_init(&rep, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    for(solvertype=0; solvertype<=1; solvertype++)
    {
        
        /*
         * Convex test:
         * * N dimensions
         * * random number (0..N) of random boundary constraints
         * * positive-definite quadratic programming problem
         * * initial point is random (maybe infeasible!)
         * * random scale (unit or non-unit)
         */
        aulits = 50;
        rho = 200.0;
        tolx = 0.0005;
        tolg = 0.01;
        for(n=1; n<=10; n++)
        {
            for(pass=1; pass<=10; pass++)
            {
                for(prectype=-1; prectype<=2; prectype++)
                {
                    
                    /*
                     * Generate well-conditioned problem with unit scale
                     */
                    spdmatrixrndcond(n, 1.0E2, &fulla, _state);
                    ae_vector_set_length(&b, n, _state);
                    ae_vector_set_length(&bndl, n, _state);
                    ae_vector_set_length(&bndu, n, _state);
                    ae_vector_set_length(&x0, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                        bndl.ptr.p_double[i] = _state->v_neginf;
                        bndu.ptr.p_double[i] = _state->v_posinf;
                        x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                        j = hqrnduniformi(&rs, 5, _state);
                        if( j==0 )
                        {
                            bndl.ptr.p_double[i] = (double)(0);
                        }
                        if( j==1 )
                        {
                            bndu.ptr.p_double[i] = (double)(0);
                        }
                        if( j==2 )
                        {
                            bndl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                            bndu.ptr.p_double[i] = bndl.ptr.p_double[i];
                        }
                        if( j==3 )
                        {
                            bndl.ptr.p_double[i] = -0.1;
                            bndu.ptr.p_double[i] = 0.1;
                        }
                    }
                    
                    /*
                     * Apply scaling to quadratic/linear term, so problem becomes
                     * well-conditioned in the scaled coordinates.
                     */
                    scaletype = hqrnduniformi(&rs, 2, _state);
                    ae_vector_set_length(&s, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        if( scaletype==0 )
                        {
                            s.ptr.p_double[i] = (double)(1);
                        }
                        else
                        {
                            s.ptr.p_double[i] = ae_exp(20*hqrndnormal(&rs, _state), _state);
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        x0.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                        bndl.ptr.p_double[i] = bndl.ptr.p_double[i]*s.ptr.p_double[i];
                        bndu.ptr.p_double[i] = bndu.ptr.p_double[i]*s.ptr.p_double[i];
                        b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            fulla.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
                        }
                    }
                    
                    /*
                     * Solve problem
                     */
                    minnlccreate(n, &x0, &state, _state);
                    if( solvertype==0 )
                    {
                        minnlcsetalgoaul(&state, rho, aulits, _state);
                        if( prectype==0 )
                        {
                            minnlcsetprecinexact(&state, _state);
                        }
                        if( prectype==1 )
                        {
                            minnlcsetprecexactlowrank(&state, 0, _state);
                        }
                        if( prectype==2 )
                        {
                            minnlcsetprecexactrobust(&state, 0, _state);
                        }
                    }
                    else
                    {
                        if( solvertype==1 )
                        {
                            minnlcsetalgoslp(&state, _state);
                        }
                        else
                        {
                            ae_assert(ae_false, "Assertion failed", _state);
                        }
                    }
                    if( scaletype!=0 )
                    {
                        minnlcsetscale(&state, &s, _state);
                    }
                    minnlcsetbc(&state, &bndl, &bndu, _state);
                    minnlcsetcond(&state, 1.0E-9, 0, _state);
                    while(minnlciteration(&state, _state))
                    {
                        if( state.needfij )
                        {
                            state.fi.ptr.p_double[0] = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                                state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                                    state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                                }
                            }
                            continue;
                        }
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                    minnlcresults(&state, &x1, &rep, _state);
                    ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:225");
                    ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:226");
                    if( *wereerrors )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    
                    /*
                     * Check feasibility properties
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(wereerrors, ae_isfinite(bndl.ptr.p_double[i], _state)&&ae_fp_less_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i]-tolx*s.ptr.p_double[i]), __FILE__, __LINE__, "testminnlcunit.ap:235");
                        ae_set_error_flag(wereerrors, ae_isfinite(bndu.ptr.p_double[i], _state)&&ae_fp_greater_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i]+tolx*s.ptr.p_double[i]), __FILE__, __LINE__, "testminnlcunit.ap:236");
                    }
                    
                    /*
                     * Test - calculate scaled constrained gradient at solution,
                     * check its norm.
                     */
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g = g+fulla.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                        }
                        g = s.ptr.p_double[i]*g;
                        if( (ae_isfinite(bndl.ptr.p_double[i], _state)&&ae_fp_less(ae_fabs(x1.ptr.p_double[i]-bndl.ptr.p_double[i], _state),tolx*s.ptr.p_double[i]))&&ae_fp_greater(g,(double)(0)) )
                        {
                            g = (double)(0);
                        }
                        if( (ae_isfinite(bndu.ptr.p_double[i], _state)&&ae_fp_less(ae_fabs(x1.ptr.p_double[i]-bndu.ptr.p_double[i], _state),tolx*s.ptr.p_double[i]))&&ae_fp_less(g,(double)(0)) )
                        {
                            g = (double)(0);
                        }
                        gnorm = gnorm+ae_sqr(g, _state);
                    }
                    gnorm = ae_sqrt(gnorm, _state);
                    ae_set_error_flag(wereerrors, ae_fp_greater(gnorm,tolg), __FILE__, __LINE__, "testminnlcunit.ap:258");
                }
            }
        }
        
        /*
         * Non-convex test:
         * * N dimensions, N>=2
         * * box constraints, x[i] in [-1,+1]
         * * A is symmetric indefinite with condition number 50.0
         * * random B with normal entries
         * * initial point is random, feasible
         * * scale is always unit
         *
         * We check that constrained problem can be successfully solved.
         * We do not check ability to detect unboundedness of unconstrained
         * problem because there is such functionality in MinNLC.
         */
        aulits = 50;
        rho = 200.0;
        tolx = 0.0005;
        tolg = 0.01;
        for(n=2; n<=10; n++)
        {
            for(pass=1; pass<=10; pass++)
            {
                for(prectype=-1; prectype<=2; prectype++)
                {
                    
                    /*
                     * Generate problem
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            fulla.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        fulla.ptr.pp_double[i][i] = -1-hqrnduniformr(&rs, _state);
                    }
                    ae_vector_set_length(&b, n, _state);
                    ae_vector_set_length(&bndl, n, _state);
                    ae_vector_set_length(&bndu, n, _state);
                    ae_vector_set_length(&x0, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        b.ptr.p_double[i] = 0.05*hqrndnormal(&rs, _state);
                        bndl.ptr.p_double[i] = (double)(-1);
                        bndu.ptr.p_double[i] = (double)(1);
                        x0.ptr.p_double[i] = 2*hqrnduniformr(&rs, _state)-1;
                    }
                    
                    /*
                     * Solve problem:
                     * * without constraints we expect failure
                     * * with constraints algorithm must succeed
                     */
                    minnlccreate(n, &x0, &state, _state);
                    if( solvertype==0 )
                    {
                        minnlcsetalgoaul(&state, rho, aulits, _state);
                        if( prectype==0 )
                        {
                            minnlcsetprecinexact(&state, _state);
                        }
                        if( prectype==1 )
                        {
                            minnlcsetprecexactlowrank(&state, 0, _state);
                        }
                        if( prectype==2 )
                        {
                            minnlcsetprecexactrobust(&state, 0, _state);
                        }
                    }
                    else
                    {
                        if( solvertype==1 )
                        {
                            minnlcsetalgoslp(&state, _state);
                        }
                        else
                        {
                            ae_assert(ae_false, "Assertion failed", _state);
                        }
                    }
                    minnlcsetbc(&state, &bndl, &bndu, _state);
                    minnlcsetcond(&state, 1.0E-9, 0, _state);
                    while(minnlciteration(&state, _state))
                    {
                        if( state.needfij )
                        {
                            state.fi.ptr.p_double[0] = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                                state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                                    state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                                }
                            }
                            continue;
                        }
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                    minnlcresults(&state, &x1, &rep, _state);
                    ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:346");
                    ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:347");
                    if( *wereerrors )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    
                    /*
                     * Check feasibility properties
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(wereerrors, ae_isfinite(bndl.ptr.p_double[i], _state)&&ae_fp_less_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i]-tolx), __FILE__, __LINE__, "testminnlcunit.ap:356");
                        ae_set_error_flag(wereerrors, ae_isfinite(bndu.ptr.p_double[i], _state)&&ae_fp_greater_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i]+tolx), __FILE__, __LINE__, "testminnlcunit.ap:357");
                    }
                    
                    /*
                     * Test - calculate scaled constrained gradient at solution,
                     * check its norm.
                     */
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g = g+fulla.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                        }
                        if( (ae_isfinite(bndl.ptr.p_double[i], _state)&&ae_fp_less(ae_fabs(x1.ptr.p_double[i]-bndl.ptr.p_double[i], _state),tolx))&&ae_fp_greater(g,(double)(0)) )
                        {
                            g = (double)(0);
                        }
                        if( (ae_isfinite(bndu.ptr.p_double[i], _state)&&ae_fp_less(ae_fabs(x1.ptr.p_double[i]-bndu.ptr.p_double[i], _state),tolx))&&ae_fp_less(g,(double)(0)) )
                        {
                            g = (double)(0);
                        }
                        gnorm = gnorm+ae_sqr(g, _state);
                    }
                    gnorm = ae_sqrt(gnorm, _state);
                    ae_set_error_flag(wereerrors, ae_fp_greater(gnorm,tolg), __FILE__, __LINE__, "testminnlcunit.ap:378");
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests linearly constrained quadratic programming algorithm.

Sets error flag on failure.
*************************************************************************/
static void testminnlcunit_testlc(ae_bool* wereerrors, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t k;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_matrix q;
    ae_matrix fulla;
    double v;
    double vv;
    ae_vector tmp;
    ae_vector bl;
    ae_vector bu;
    ae_vector b;
    ae_vector xs0;
    ae_vector xstart;
    ae_vector x;
    ae_vector x0;
    ae_vector x1;
    ae_vector x2;
    ae_vector xm;
    ae_vector s;
    ae_vector g;
    ae_vector bndl;
    ae_vector bndu;
    ae_matrix a;
    ae_matrix c;
    ae_matrix ce;
    ae_vector ct;
    ae_vector nonnegative;
    double tolx;
    double tolg;
    double tolf;
    ae_int_t aulits;
    double rho;
    minnlcstate state;
    minnlcreport rep;
    ae_int_t scaletype;
    double f0;
    double f1;
    double tolconstr;
    ae_int_t bscale;
    ae_int_t akind;
    ae_int_t ccnt;
    ae_int_t shiftkind;
    ae_int_t prectype;
    ae_int_t solvertype;
    hqrndstate rs;
    snnlssolver nnls;

    ae_frame_make(_state, &_frame_block);
    memset(&q, 0, sizeof(q));
    memset(&fulla, 0, sizeof(fulla));
    memset(&tmp, 0, sizeof(tmp));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&b, 0, sizeof(b));
    memset(&xs0, 0, sizeof(xs0));
    memset(&xstart, 0, sizeof(xstart));
    memset(&x, 0, sizeof(x));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&xm, 0, sizeof(xm));
    memset(&s, 0, sizeof(s));
    memset(&g, 0, sizeof(g));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&a, 0, sizeof(a));
    memset(&c, 0, sizeof(c));
    memset(&ce, 0, sizeof(ce));
    memset(&ct, 0, sizeof(ct));
    memset(&nonnegative, 0, sizeof(nonnegative));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    memset(&nnls, 0, sizeof(nnls));
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xstart, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xm, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ce, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&nonnegative, 0, DT_BOOL, _state, ae_true);
    _minnlcstate_init(&state, _state, ae_true);
    _minnlcreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);
    _snnlssolver_init(&nnls, _state, ae_true);

    hqrndrandomize(&rs, _state);
    for(solvertype=0; solvertype<=1; solvertype++)
    {
        
        /*
         * First test:
         * * K<N equality constraints Q*x = Q*x0, where Q is random
         *   orthogonal K*N matrix, x0 is some random vector
         * * quadratic programming problem with identity quadratic term A and
         *   linear term equal to xm*A, where xm is some random vector such
         *   that Q*xm=0. It is always possible to find such xm, because K<N
         *   Thus, optimization problem has form 0.5*x'*I*x-xm'*x.
         * * exact solution must be equal to x0
         *
         * NOTE: this test is important because it is the only linearly constrained one
         *       which uses non-unit scaling!
         */
        rho = 200.0;
        tolx = 0.0005;
        aulits = 50;
        for(n=2; n<=6; n++)
        {
            for(k=1; k<=n-1; k++)
            {
                for(prectype=-1; prectype<=2; prectype++)
                {
                    
                    /*
                     * Generate problem: A, b, CMatrix, x0, XStart
                     */
                    rmatrixrndorthogonal(n, &q, _state);
                    ae_vector_set_length(&b, n, _state);
                    ae_vector_set_length(&x0, n, _state);
                    ae_vector_set_length(&xm, n, _state);
                    ae_vector_set_length(&xstart, n, _state);
                    ae_matrix_set_length(&c, k, n+1, _state);
                    ae_vector_set_length(&ct, k, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        xm.ptr.p_double[i] = x0.ptr.p_double[i];
                        xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        ae_v_move(&c.ptr.pp_double[i][0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                        v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        c.ptr.pp_double[i][n] = v;
                        ct.ptr.p_int[i] = 0;
                        v = 2*ae_randomreal(_state)-1;
                        ae_v_addd(&xm.ptr.p_double[0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        b.ptr.p_double[i] = -xm.ptr.p_double[i];
                    }
                    
                    /*
                     * Apply scaling to linear term and known solution,
                     * so problem becomes well-conditioned in the scaled coordinates.
                     */
                    scaletype = hqrnduniformi(&rs, 2, _state);
                    ae_vector_set_length(&s, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        if( scaletype==0 )
                        {
                            s.ptr.p_double[i] = (double)(1);
                        }
                        else
                        {
                            s.ptr.p_double[i] = ae_exp(20*hqrndnormal(&rs, _state), _state);
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        x0.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                        xstart.ptr.p_double[i] = xstart.ptr.p_double[i]*s.ptr.p_double[i];
                        b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            c.ptr.pp_double[i][j] = c.ptr.pp_double[i][j]/s.ptr.p_double[j];
                        }
                    }
                    
                    /*
                     * Create optimizer, solve
                     */
                    minnlccreate(n, &xstart, &state, _state);
                    minnlcsetscale(&state, &s, _state);
                    minnlcsetcond(&state, 1.0E-9, 0, _state);
                    minnlcsetlc(&state, &c, &ct, k, _state);
                    if( solvertype==0 )
                    {
                        minnlcsetalgoaul(&state, rho, aulits, _state);
                        if( prectype==0 )
                        {
                            minnlcsetprecinexact(&state, _state);
                        }
                        if( prectype==1 )
                        {
                            minnlcsetprecexactlowrank(&state, 0, _state);
                        }
                        if( prectype==2 )
                        {
                            minnlcsetprecexactrobust(&state, 0, _state);
                        }
                    }
                    else
                    {
                        if( solvertype==1 )
                        {
                            minnlcsetalgoslp(&state, _state);
                        }
                        else
                        {
                            ae_assert(ae_false, "Assertion failed", _state);
                        }
                    }
                    while(minnlciteration(&state, _state))
                    {
                        if( state.needfij )
                        {
                            state.fi.ptr.p_double[0] = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i]+0.5*ae_sqr(state.x.ptr.p_double[i], _state)/ae_sqr(s.ptr.p_double[i], _state);
                                state.j.ptr.pp_double[0][i] = b.ptr.p_double[i]+state.x.ptr.p_double[i]/ae_sqr(s.ptr.p_double[i], _state);
                            }
                            continue;
                        }
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                    minnlcresults(&state, &x1, &rep, _state);
                    ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:536");
                    ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:537");
                    if( *wereerrors )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    
                    /*
                     * Compare with analytic solution
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x0.ptr.p_double[i], _state),tolx*s.ptr.p_double[i]), __FILE__, __LINE__, "testminnlcunit.ap:545");
                    }
                }
            }
        }
        
        /*
         * Inequality constrained problem:
         * * N*N diagonal A
         * * one inequality constraint q'*x>=0, where q is random unit vector
         * * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
         *   where x1 is some random vector
         * * either:
         *   a) x1 is feasible => we must stop at x1
         *   b) x1 is infeasible => we must stop at the boundary q'*x=0 and
         *      projection of gradient onto q*x=0 must be zero
         *
         * NOTE: we make several passes because some specific kind of errors is rarely
         *       caught by this test, so we need several repetitions.
         */
        rho = 200.0;
        tolx = 0.0005;
        tolg = 0.01;
        aulits = 50;
        for(n=2; n<=6; n++)
        {
            for(pass=0; pass<=4; pass++)
            {
                
                /*
                 * Generate problem: A, b, CMatrix, x0, XStart
                 */
                spdmatrixrndcond(n, 1.0E2, &fulla, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&xm, n, _state);
                ae_vector_set_length(&xstart, n, _state);
                ae_matrix_set_length(&c, 1, n+1, _state);
                ae_vector_set_length(&ct, 1, _state);
                for(i=0; i<=n-1; i++)
                {
                    xm.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                do
                {
                    v = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        c.ptr.pp_double[0][i] = 2*ae_randomreal(_state)-1;
                        v = v+ae_sqr(c.ptr.pp_double[0][i], _state);
                    }
                    v = ae_sqrt(v, _state);
                }
                while(ae_fp_eq(v,(double)(0)));
                for(i=0; i<=n-1; i++)
                {
                    c.ptr.pp_double[0][i] = c.ptr.pp_double[0][i]/v;
                }
                c.ptr.pp_double[0][n] = (double)(0);
                ct.ptr.p_int[0] = 1;
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&fulla.ptr.pp_double[i][0], 1, &xm.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    b.ptr.p_double[i] = -v;
                }
                
                /*
                 * Apply scaling to linear term and known solution,
                 * so problem becomes well-conditioned in the scaled coordinates.
                 */
                scaletype = hqrnduniformi(&rs, 2, _state);
                ae_vector_set_length(&s, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    if( scaletype==0 )
                    {
                        s.ptr.p_double[i] = (double)(1);
                    }
                    else
                    {
                        s.ptr.p_double[i] = ae_exp(20*hqrndnormal(&rs, _state), _state);
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    xm.ptr.p_double[i] = xm.ptr.p_double[i]*s.ptr.p_double[i];
                    xstart.ptr.p_double[i] = xstart.ptr.p_double[i]*s.ptr.p_double[i];
                    b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        fulla.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
                    }
                }
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[0][j] = c.ptr.pp_double[0][j]/s.ptr.p_double[j];
                }
                
                /*
                 * Create optimizer, solve
                 */
                minnlccreate(n, &xstart, &state, _state);
                if( solvertype==0 )
                {
                    minnlcsetalgoaul(&state, rho, aulits, _state);
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minnlcsetalgoslp(&state, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
                minnlcsetlc(&state, &c, &ct, 1, _state);
                minnlcsetscale(&state, &s, _state);
                while(minnlciteration(&state, _state))
                {
                    if( state.needfij )
                    {
                        state.fi.ptr.p_double[0] = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                            state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                                state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            }
                        }
                        continue;
                    }
                    ae_assert(ae_false, "Assertion failed", _state);
                }
                minnlcresults(&state, &x1, &rep, _state);
                ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:663");
                ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:664");
                if( *wereerrors )
                {
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Test solution
                 */
                ae_vector_set_length(&g, n, _state);
                ae_v_move(&g.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&fulla.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    g.ptr.p_double[i] = g.ptr.p_double[i]+v;
                }
                v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                ae_set_error_flag(wereerrors, ae_fp_less(v,-tolx), __FILE__, __LINE__, "testminnlcunit.ap:679");
                if( ae_fp_less(v,tolx) )
                {
                    
                    /*
                     * Point at the boundary, project gradient into
                     * equality-constrained subspace.
                     */
                    v = 0.0;
                    vv = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+g.ptr.p_double[i]*c.ptr.pp_double[0][i];
                        vv = vv+c.ptr.pp_double[0][i]*c.ptr.pp_double[0][i];
                    }
                    v = v/vv;
                    ae_v_subd(&g.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1), v);
                }
                v = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+ae_sqr(g.ptr.p_double[i]*s.ptr.p_double[i], _state);
                }
                ae_set_error_flag(wereerrors, ae_fp_greater(ae_sqrt(v, _state),tolg), __FILE__, __LINE__, "testminnlcunit.ap:699");
            }
        }
        
        /*
         * Equality-constrained test:
         * * N*N SPD A
         * * K<N equality constraints Q*x = Q*x0, where Q is random
         *   orthogonal K*N matrix, x0 is some random vector
         * * optimization problem has form 0.5*x'*A*x-(xm*A)*x,
         *   where xm is some random vector
         * * we check feasibility properties of the solution
         * * we do not know analytic form of the exact solution,
         *   but we know that projection of gradient into equality constrained
         *   subspace must be zero at the solution
         */
        rho = 200.0;
        tolx = 0.0005;
        tolg = 0.01;
        aulits = 50;
        for(n=2; n<=6; n++)
        {
            for(k=1; k<=n-1; k++)
            {
                
                /*
                 * Generate problem: A, b, CMatrix, x0, XStart
                 */
                rmatrixrndorthogonal(n, &q, _state);
                spdmatrixrndcond(n, 1.0E2, &fulla, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&xm, n, _state);
                ae_vector_set_length(&xstart, n, _state);
                ae_matrix_set_length(&c, k, n+1, _state);
                ae_vector_set_length(&ct, k, _state);
                for(i=0; i<=n-1; i++)
                {
                    x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    xm.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    xstart.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                for(i=0; i<=k-1; i++)
                {
                    ae_v_move(&c.ptr.pp_double[i][0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                    v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    c.ptr.pp_double[i][n] = v;
                    ct.ptr.p_int[i] = 0;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&fulla.ptr.pp_double[i][0], 1, &xm.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    b.ptr.p_double[i] = -v;
                }
                
                /*
                 * Create optimizer, solve
                 */
                minnlccreate(n, &xstart, &state, _state);
                if( solvertype==0 )
                {
                    minnlcsetalgoaul(&state, rho, aulits, _state);
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minnlcsetalgoslp(&state, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
                minnlcsetcond(&state, 1.0E-9, 0, _state);
                minnlcsetlc(&state, &c, &ct, k, _state);
                while(minnlciteration(&state, _state))
                {
                    if( state.needfij )
                    {
                        state.fi.ptr.p_double[0] = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                            state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                                state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            }
                        }
                        continue;
                    }
                    ae_assert(ae_false, "Assertion failed", _state);
                }
                minnlcresults(&state, &x1, &rep, _state);
                ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:790");
                ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:791");
                if( *wereerrors )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=k-1; i++)
                {
                    v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                    ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:797");
                }
                ae_vector_set_length(&g, n, _state);
                ae_v_move(&g.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&fulla.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    g.ptr.p_double[i] = g.ptr.p_double[i]+v;
                }
                for(i=0; i<=k-1; i++)
                {
                    v = ae_v_dotproduct(&g.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                    ae_v_subd(&g.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
                }
                v = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                ae_set_error_flag(wereerrors, ae_fp_greater(ae_sqrt(v, _state),tolg), __FILE__, __LINE__, "testminnlcunit.ap:812");
            }
        }
        
        /*
         * Boundary constraints vs linear ones:
         * * N*N SPD A
         * * optimization problem has form 0.5*x'*A*x-(xm*A)*x,
         *   where xm is some random vector from [-1,+1]
         * * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
         *   with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
         * * initial point xstart is from [-1,+2]
         * * we solve two related QP problems:
         *   a) one with constraints posed as boundary ones
         *   b) another one with same constraints posed as general linear ones
         * both problems must have same solution.
         * Here we test that boundary constrained and linear inequality constrained
         * solvers give same results.
         */
        rho = 200.0;
        tolx = 0.0005;
        aulits = 50;
        for(n=1; n<=6; n++)
        {
            
            /*
             * Generate problem: A, b, x0, XStart, C, CT
             */
            spdmatrixrndcond(n, 1.0E2, &fulla, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&xm, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_matrix_set_length(&c, 2*n, n+1, _state);
            ae_vector_set_length(&ct, 2*n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            for(i=0; i<=n-1; i++)
            {
                xm.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                bndl.ptr.p_double[i] = -(0.1+0.9*ae_randomreal(_state));
                bndu.ptr.p_double[i] = 0.1+0.9*ae_randomreal(_state);
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[2*i+0][j] = (double)(0);
                    c.ptr.pp_double[2*i+1][j] = (double)(0);
                }
                c.ptr.pp_double[2*i+0][i] = (double)(1);
                c.ptr.pp_double[2*i+0][n] = bndl.ptr.p_double[i];
                ct.ptr.p_int[2*i+0] = 1;
                c.ptr.pp_double[2*i+1][i] = (double)(1);
                c.ptr.pp_double[2*i+1][n] = bndu.ptr.p_double[i];
                ct.ptr.p_int[2*i+1] = -1;
            }
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&fulla.ptr.pp_double[i][0], 1, &xm.ptr.p_double[0], 1, ae_v_len(0,n-1));
                b.ptr.p_double[i] = -v;
            }
            
            /*
             * Solve linear inequality constrained problem
             */
            minnlccreate(n, &x0, &state, _state);
            if( solvertype==0 )
            {
                minnlcsetalgoaul(&state, rho, aulits, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minnlcsetalgoslp(&state, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            minnlcsetlc(&state, &c, &ct, 2*n, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                        state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:907");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:908");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Solve boundary constrained problem
             */
            minnlccreate(n, &x0, &state, _state);
            if( solvertype==0 )
            {
                minnlcsetalgoaul(&state, rho, aulits, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minnlcsetalgoslp(&state, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            minnlcsetbc(&state, &bndl, &bndu, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                        state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x2, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:947");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:948");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Compare solutions
             */
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x2.ptr.p_double[i], _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:956");
            }
        }
        
        /*
         * Boundary and linear equality constrained QP problem with excessive
         * equality constraints:
         * * N*N SPD A with moderate condtion number (up to 100)
         * * boundary constraints 0<=x[i]<=1
         * * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix,
         *   x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
         * * optimization problem has form 0.5*x'*A*x-b*x,
         *   where b is some random vector
         * * because constraints are excessive, the main problem is to find
         *   feasible point; the only existing feasible point is solution,
         *   so we have to check only feasibility
         */
        rho = 200.0;
        tolx = 0.0005;
        aulits = 50;
        for(n=1; n<=6; n++)
        {
            
            /*
             * Generate problem: A, b, BndL, BndU, CMatrix, x0, xm, XStart
             */
            k = 2*n;
            spdmatrixrndcond(n, 1.0E2, &fulla, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xm, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&c, k, n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
                xm.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                bndl.ptr.p_double[i] = 0.0;
                bndu.ptr.p_double[i] = 1.0;
                xstart.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
            }
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                ct.ptr.p_int[i] = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            
            /*
             * Create optimizer, solve
             */
            minnlccreate(n, &xstart, &state, _state);
            if( solvertype==0 )
            {
                minnlcsetalgoaul(&state, rho, aulits, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minnlcsetalgoslp(&state, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            minnlcsetcond(&state, 1.0E-9, 0, _state);
            minnlcsetlc(&state, &c, &ct, k, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                        state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:1047");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:1048");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:1054");
            }
        }
        
        /*
         * Boundary and linear equality/inequality constrained QP problem with
         * excessive constraints:
         * * N*N SPD A with moderate condtion number (up to 100)
         * * boundary constraints 0<=x[i]<=1
         * * K=2*N equality/inequality constraints Q*x = Q*xm, where Q is random matrix,
         *   xm is some random vector from the feasible hypercube (0.1<=xm[i]<=0.9)
         * * optimization problem has form 0.5*x'*A*x-b*x,
         *   where b is some random vector
         * * because constraints are excessive, the main problem is to find
         *   feasible point; usually, the only existing feasible point is solution,
         *   so we have to check only feasibility
         *
         * NOTE: this problem is difficult one (estimates of Lagrange multipliers converge
         *       slowly), so we use relaxed tolerances - 0.010 for AUL solver
         */
        rho = 1000.0;
        aulits = 50;
        for(n=1; n<=6; n++)
        {
            
            /*
             * Generate problem: A, b, BndL, BndU, CMatrix, xm, x1, XStart
             */
            k = 2*n;
            spdmatrixrndcond(n, 1.0E2, &fulla, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&xm, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_matrix_set_length(&c, k, n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=n-1; i++)
            {
                xm.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
                bndl.ptr.p_double[i] = 0.0;
                bndu.ptr.p_double[i] = 1.0;
                x0.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &xm.ptr.p_double[0], 1, ae_v_len(0,n-1));
                ct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
                if( ct.ptr.p_int[i]==0 )
                {
                    c.ptr.pp_double[i][n] = v;
                }
                if( ct.ptr.p_int[i]>0 )
                {
                    c.ptr.pp_double[i][n] = v;
                }
                if( ct.ptr.p_int[i]<0 )
                {
                    c.ptr.pp_double[i][n] = v;
                }
            }
            
            /*
             * Create optimizer, solve
             */
            minnlccreate(n, &x0, &state, _state);
            if( solvertype==0 )
            {
                minnlcsetalgoaul(&state, rho, aulits, _state);
                tolx = 0.0100;
            }
            else
            {
                if( solvertype==1 )
                {
                    minnlcsetalgoslp(&state, _state);
                    tolx = 0.0010;
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            minnlcsetbc(&state, &bndl, &bndu, _state);
            minnlcsetlc(&state, &c, &ct, k, _state);
            minnlcsetcond(&state, 1.0E-9, 0, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                        state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:1153");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:1154");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=k-1; i++)
            {
                v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                if( ct.ptr.p_int[i]==0 )
                {
                    ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:1161");
                }
                if( ct.ptr.p_int[i]>0 )
                {
                    ae_set_error_flag(wereerrors, ae_fp_less(v,c.ptr.pp_double[i][n]-tolx), __FILE__, __LINE__, "testminnlcunit.ap:1163");
                }
                if( ct.ptr.p_int[i]<0 )
                {
                    ae_set_error_flag(wereerrors, ae_fp_greater(v,c.ptr.pp_double[i][n]+tolx), __FILE__, __LINE__, "testminnlcunit.ap:1165");
                }
            }
        }
        
        /*
         * Boundary and linear equality constrained QP problem,
         * test checks that different starting points yield same final point:
         * * random N from [1..6], random K from [1..N-1]
         * * N*N SPD A with moderate condtion number (important!)
         * * boundary constraints 0<=x[i]<=1
         * * K<N random linear equality constraints C*x = C*x0,
         *   where x0 is some random vector from the inner area of the
         *   feasible hypercube (0.1<=x0[i]<=0.9)
         * * optimization problem has form 0.5*x'*A*x+b*x,
         *   where b is some random vector with -5<=b[i]<=+5
         *
         * We solve this problem two times:
         * * each time from different initial point XStart in [-2,+2]
         * * we compare values of the target function (although final points
         *   may be slightly different, function values should match each other)
         * 
         * Both points should give same results; any significant difference is
         * evidence of some error in the QP implementation.
         */
        rho = 1000.0;
        tolf = 0.01;
        aulits = 50;
        for(pass=1; pass<=50; pass++)
        {
            
            /*
             * Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, xm, XStart.
             */
            n = ae_randominteger(5, _state)+2;
            k = 1+ae_randominteger(n-1, _state);
            spdmatrixrndcond(n, 1.0E2, &fulla, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xstart, n, _state);
            ae_matrix_set_length(&c, k, n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 0.1+0.8*ae_randomreal(_state);
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                bndl.ptr.p_double[i] = 0.0;
                bndu.ptr.p_double[i] = 1.0;
            }
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                ct.ptr.p_int[i] = 0;
            }
            
            /*
             * Start from first point
             */
            for(i=0; i<=n-1; i++)
            {
                xstart.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
            }
            minnlccreate(n, &xstart, &state, _state);
            if( solvertype==0 )
            {
                minnlcsetalgoaul(&state, rho, aulits, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minnlcsetalgoslp(&state, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            minnlcsetcond(&state, 1.0E-9, 0, _state);
            minnlcsetbc(&state, &bndl, &bndu, _state);
            minnlcsetlc(&state, &c, &ct, k, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                        state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x0, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x0, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:1264");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:1265");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Start from another point
             */
            for(i=0; i<=n-1; i++)
            {
                xstart.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
            }
            minnlcrestartfrom(&state, &xstart, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                        state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:1295");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:1296");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Calculate function value at X0 and X1, compare solutions
             */
            f0 = 0.0;
            f1 = 0.0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    f0 = f0+0.5*x0.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*x0.ptr.p_double[j];
                    f1 = f1+0.5*x1.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                }
                f0 = f0+x0.ptr.p_double[i]*b.ptr.p_double[i];
                f1 = f1+x1.ptr.p_double[i]*b.ptr.p_double[i];
            }
            ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(f0-f1, _state),tolf), __FILE__, __LINE__, "testminnlcunit.ap:1315");
        }
        
        /*
         * Convex/nonconvex optimization problem with excessive
         * (degenerate constraints):
         *
         * * N=2..5
         * * f = 0.5*x'*A*x+b'*x
         * * b has normally distributed entries with scale 10^BScale
         * * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank
         * * box constraints: x[i] in [-1,+1]
         * * 2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
         *   where v_k is one of 2^N vertices of feasible hypercube, v_shift is
         *   a shift parameter:
         *   * with zero v_shift such constraints are degenerate (each vertex has
         *     N box constraints and one "redundant" linear constraint)
         *   * with positive v_shift linear constraint is always inactive
         *   * with small (about machine epsilon) but negative v_shift,
         *     constraint is close to degenerate - but not exactly
         *
         * We check that constrained gradient is close to zero at solution.
         * Box constraint is considered active if distance to boundary is less
         * than TolConstr.
         *
         * NOTE: because AUL algorithm is less exact than its active set counterparts,
         *       VERY loose tolerances are used for this test.
         */
        tolconstr = 1.0E-2;
        for(n=2; n<=8; n++)
        {
            for(akind=0; akind<=3; akind++)
            {
                for(shiftkind=-5; shiftkind<=1; shiftkind++)
                {
                    for(bscale=0; bscale>=-2; bscale--)
                    {
                        
                        /*
                         * Generate A, B and initial point
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        ae_vector_set_length(&b, n, _state);
                        ae_vector_set_length(&x, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            b.ptr.p_double[i] = ae_pow((double)(10), (double)(bscale), _state)*hqrndnormal(&rs, _state);
                            x.ptr.p_double[i] = hqrnduniformr(&rs, _state)-0.5;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 0.0;
                            }
                        }
                        if( akind==1 )
                        {
                            
                            /*
                             * Dense well conditioned SPD
                             */
                            spdmatrixrndcond(n, 50.0, &a, _state);
                        }
                        if( akind==2 )
                        {
                            
                            /*
                             * Dense well conditioned indefinite
                             */
                            smatrixrndcond(n, 50.0, &a, _state);
                        }
                        if( akind==3 )
                        {
                            
                            /*
                             * Low rank
                             */
                            ae_vector_set_length(&tmp, n, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a.ptr.pp_double[i][j] = 0.0;
                                }
                                a.ptr.pp_double[i][i] = 1.0E-9;
                            }
                            for(k=1; k<=ae_minint(3, n-1, _state); k++)
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    tmp.ptr.p_double[i] = hqrndnormal(&rs, _state);
                                }
                                v = hqrndnormal(&rs, _state);
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]+v*tmp.ptr.p_double[i]*tmp.ptr.p_double[j];
                                    }
                                }
                            }
                        }
                        
                        /*
                         * Generate constraints
                         */
                        ae_vector_set_length(&bl, n, _state);
                        ae_vector_set_length(&bu, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            bl.ptr.p_double[i] = -1.0;
                            bu.ptr.p_double[i] = 1.0;
                        }
                        ccnt = ae_round(ae_pow((double)(2), (double)(n), _state), _state);
                        ae_matrix_set_length(&c, ccnt, n+1, _state);
                        ae_vector_set_length(&ct, ccnt, _state);
                        for(i=0; i<=ccnt-1; i++)
                        {
                            ct.ptr.p_int[i] = -1;
                            k = i;
                            c.ptr.pp_double[i][n] = ae_sign((double)(shiftkind), _state)*ae_pow((double)(10), ae_fabs((double)(shiftkind), _state), _state)*ae_machineepsilon;
                            for(j=0; j<=n-1; j++)
                            {
                                c.ptr.pp_double[i][j] = (double)(2*(k%2)-1);
                                c.ptr.pp_double[i][n] = c.ptr.pp_double[i][n]+c.ptr.pp_double[i][j]*c.ptr.pp_double[i][j];
                                k = k/2;
                            }
                        }
                        
                        /*
                         * Create and optimize
                         */
                        minnlccreate(n, &x, &state, _state);
                        minnlcsetbc(&state, &bl, &bu, _state);
                        minnlcsetlc(&state, &c, &ct, ccnt, _state);
                        minnlcsetcond(&state, 1.0E-12, 0, _state);
                        if( solvertype==0 )
                        {
                            minnlcsetalgoaul(&state, rho, aulits, _state);
                        }
                        else
                        {
                            if( solvertype==1 )
                            {
                                minnlcsetalgoslp(&state, _state);
                            }
                            else
                            {
                                ae_assert(ae_false, "Assertion failed", _state);
                            }
                        }
                        while(minnlciteration(&state, _state))
                        {
                            ae_assert(state.needfij, "Assertion failed", _state);
                            state.fi.ptr.p_double[0] = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+state.x.ptr.p_double[i]*b.ptr.p_double[i];
                                state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*v;
                                state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+v;
                            }
                        }
                        minnlcresults(&state, &xs0, &rep, _state);
                        ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:1462");
                        if( *wereerrors )
                        {
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * Evaluate gradient at solution and test
                         */
                        vv = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xs0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            v = v+b.ptr.p_double[i];
                            if( ae_fp_less_eq(xs0.ptr.p_double[i],bl.ptr.p_double[i]+tolconstr)&&ae_fp_greater(v,(double)(0)) )
                            {
                                v = 0.0;
                            }
                            if( ae_fp_greater_eq(xs0.ptr.p_double[i],bu.ptr.p_double[i]-tolconstr)&&ae_fp_less(v,(double)(0)) )
                            {
                                v = 0.0;
                            }
                            vv = vv+ae_sqr(v, _state);
                        }
                        vv = ae_sqrt(vv, _state);
                        ae_set_error_flag(wereerrors, ae_fp_greater(vv,1.0E-3), __FILE__, __LINE__, "testminnlcunit.ap:1486");
                    }
                }
            }
        }
        
        /*
         * Linear/convex optimization problem with combination of
         * box and linear constraints:
         *
         * * N=2..8
         * * f = 0.5*x'*A*x+b'*x
         * * b has normally distributed entries with scale 10^BScale
         * * several kinds of A are tried: zero, well conditioned SPD
         * * box constraints: x[i] in [-1,+1]
         * * initial point x0 = [0 0 ... 0 0]
         * * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
         *   random mix of equality/inequality constraints is tried.
         *   x0 is guaranteed to be feasible.
         *
         * We check that constrained gradient is close to zero at solution.
         * Inequality constraint is considered active if distance to boundary
         * is less than TolConstr. We use nonnegative least squares solver
         * in order to compute constrained gradient.
         */
        tolconstr = 1.0E-2;
        for(n=2; n<=8; n++)
        {
            for(akind=0; akind<=1; akind++)
            {
                for(bscale=0; bscale>=-2; bscale--)
                {
                    
                    /*
                     * Generate A, B and initial point
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    ae_vector_set_length(&b, n, _state);
                    ae_vector_set_length(&x, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        b.ptr.p_double[i] = ae_pow((double)(10), (double)(bscale), _state)*hqrndnormal(&rs, _state);
                        x.ptr.p_double[i] = 0.0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 0.0;
                        }
                    }
                    if( akind==1 )
                    {
                        
                        /*
                         * Dense well conditioned SPD
                         */
                        spdmatrixrndcond(n, 50.0, &a, _state);
                    }
                    if( solvertype!=0 )
                    {
                        
                        /*
                         * AUL performs poorly on such problems,
                         * but SLP works goo.
                         */
                        if( akind==2 )
                        {
                            
                            /*
                             * Dense well conditioned indefinite
                             */
                            smatrixrndcond(n, 50.0, &a, _state);
                        }
                        if( akind==3 )
                        {
                            
                            /*
                             * Low rank
                             */
                            ae_vector_set_length(&tmp, n, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a.ptr.pp_double[i][j] = 0.0;
                                }
                                a.ptr.pp_double[i][i] = 1.0E-9;
                            }
                            for(k=1; k<=ae_minint(3, n-1, _state); k++)
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    tmp.ptr.p_double[i] = hqrndnormal(&rs, _state);
                                }
                                v = hqrndnormal(&rs, _state);
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a.ptr.pp_double[i][j] = a.ptr.pp_double[i][j]+v*tmp.ptr.p_double[i]*tmp.ptr.p_double[j];
                                    }
                                }
                            }
                        }
                    }
                    
                    /*
                     * Generate constraints
                     */
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        bl.ptr.p_double[i] = -1.0;
                        bu.ptr.p_double[i] = 1.0;
                    }
                    ccnt = ae_minint(3, n-1, _state);
                    ae_matrix_set_length(&c, ccnt, n+1, _state);
                    ae_vector_set_length(&ct, ccnt, _state);
                    for(i=0; i<=ccnt-1; i++)
                    {
                        ct.ptr.p_int[i] = hqrnduniformi(&rs, 3, _state)-1;
                        c.ptr.pp_double[i][n] = 0.0;
                        for(j=0; j<=n-1; j++)
                        {
                            c.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)-0.5;
                        }
                    }
                    
                    /*
                     * Create and optimize
                     */
                    minnlccreate(n, &x, &state, _state);
                    minnlcsetbc(&state, &bl, &bu, _state);
                    minnlcsetlc(&state, &c, &ct, ccnt, _state);
                    minnlcsetcond(&state, 1.0E-9, 0, _state);
                    if( solvertype==0 )
                    {
                        minnlcsetalgoaul(&state, 1000.0, 10, _state);
                    }
                    else
                    {
                        if( solvertype==1 )
                        {
                            minnlcsetalgoslp(&state, _state);
                        }
                        else
                        {
                            ae_assert(ae_false, "Assertion failed", _state);
                        }
                    }
                    while(minnlciteration(&state, _state))
                    {
                        ae_assert(state.needfij, "Assertion failed", _state);
                        state.fi.ptr.p_double[0] = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+state.x.ptr.p_double[i]*b.ptr.p_double[i];
                            state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*v;
                            state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+v;
                        }
                    }
                    minnlcresults(&state, &xs0, &rep, _state);
                    ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:1627");
                    if( *wereerrors )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    
                    /*
                     * 1. evaluate unconstrained gradient at solution
                     *
                     * 2. calculate constrained gradient (NNLS solver is used
                     *    to evaluate gradient subject to active constraints).
                     *    In order to do this we form CE matrix, matrix of active
                     *    constraints (columns store constraint vectors). Then
                     *    we try to approximate gradient vector by columns of CE,
                     *    subject to non-negativity restriction placed on variables
                     *    corresponding to inequality constraints.
                     *
                     *    Residual from such regression is a constrained gradient vector.
                     */
                    ae_vector_set_length(&g, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xs0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        g.ptr.p_double[i] = v+b.ptr.p_double[i];
                    }
                    ae_matrix_set_length(&ce, n, n+ccnt, _state);
                    ae_vector_set_length(&nonnegative, n+ccnt, _state);
                    k = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(wereerrors, ae_fp_less(xs0.ptr.p_double[i],bl.ptr.p_double[i]-tolconstr), __FILE__, __LINE__, "testminnlcunit.ap:1655");
                        ae_set_error_flag(wereerrors, ae_fp_greater(xs0.ptr.p_double[i],bu.ptr.p_double[i]+tolconstr), __FILE__, __LINE__, "testminnlcunit.ap:1656");
                        if( ae_fp_less_eq(xs0.ptr.p_double[i],bl.ptr.p_double[i]+tolconstr) )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ce.ptr.pp_double[j][k] = 0.0;
                            }
                            ce.ptr.pp_double[i][k] = 1.0;
                            nonnegative.ptr.p_bool[k] = ae_true;
                            inc(&k, _state);
                            continue;
                        }
                        if( ae_fp_greater_eq(xs0.ptr.p_double[i],bu.ptr.p_double[i]-tolconstr) )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ce.ptr.pp_double[j][k] = 0.0;
                            }
                            ce.ptr.pp_double[i][k] = -1.0;
                            nonnegative.ptr.p_bool[k] = ae_true;
                            inc(&k, _state);
                            continue;
                        }
                    }
                    for(i=0; i<=ccnt-1; i++)
                    {
                        v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &xs0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        v = v-c.ptr.pp_double[i][n];
                        ae_set_error_flag(wereerrors, ct.ptr.p_int[i]==0&&ae_fp_greater(ae_fabs(v, _state),tolconstr), __FILE__, __LINE__, "testminnlcunit.ap:1680");
                        ae_set_error_flag(wereerrors, ct.ptr.p_int[i]>0&&ae_fp_less(v,-tolconstr), __FILE__, __LINE__, "testminnlcunit.ap:1681");
                        ae_set_error_flag(wereerrors, ct.ptr.p_int[i]<0&&ae_fp_greater(v,tolconstr), __FILE__, __LINE__, "testminnlcunit.ap:1682");
                        if( ct.ptr.p_int[i]==0 )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ce.ptr.pp_double[j][k] = c.ptr.pp_double[i][j];
                            }
                            nonnegative.ptr.p_bool[k] = ae_false;
                            inc(&k, _state);
                            continue;
                        }
                        if( (ct.ptr.p_int[i]>0&&ae_fp_less_eq(v,tolconstr))||(ct.ptr.p_int[i]<0&&ae_fp_greater_eq(v,-tolconstr)) )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ce.ptr.pp_double[j][k] = ae_sign((double)(ct.ptr.p_int[i]), _state)*c.ptr.pp_double[i][j];
                            }
                            nonnegative.ptr.p_bool[k] = ae_true;
                            inc(&k, _state);
                            continue;
                        }
                    }
                    snnlsinit(0, 0, 0, &nnls, _state);
                    snnlssetproblem(&nnls, &ce, &g, 0, k, n, _state);
                    for(i=0; i<=k-1; i++)
                    {
                        if( !nonnegative.ptr.p_bool[i] )
                        {
                            snnlsdropnnc(&nnls, i, _state);
                        }
                    }
                    snnlssolve(&nnls, &tmp, _state);
                    for(i=0; i<=k-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            g.ptr.p_double[j] = g.ptr.p_double[j]-tmp.ptr.p_double[i]*ce.ptr.pp_double[j][i];
                        }
                    }
                    vv = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    vv = ae_sqrt(vv, _state);
                    ae_set_error_flag(wereerrors, ae_fp_greater(vv,1.0E-3), __FILE__, __LINE__, "testminnlcunit.ap:1711");
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests nonlinearly constrained quadratic programming algorithm.

Sets error flag on failure.
*************************************************************************/
static void testminnlcunit_testnlc(ae_bool* wereerrors, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t n2;
    double tolx;
    double tolg;
    ae_int_t aulits;
    double rho;
    minnlcstate state;
    minnlcreport rep;
    ae_int_t scaletype;
    ae_vector x0;
    ae_vector x1;
    ae_vector b;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector s;
    ae_vector g;
    ae_vector rnlc;
    ae_vector ckind;
    ae_matrix fulla;
    ae_matrix c;
    ae_vector ct;
    ae_int_t cntbc;
    ae_int_t cntlc;
    ae_int_t cntnlec;
    ae_int_t cntnlic;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t pass;
    ae_int_t klc;
    ae_int_t knlec;
    ae_int_t knlic;
    double v;
    double vv;
    double vx;
    double vy;
    double gnorm;
    hqrndstate rs;
    ae_int_t prectype;
    ae_int_t solvertype;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&b, 0, sizeof(b));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&s, 0, sizeof(s));
    memset(&g, 0, sizeof(g));
    memset(&rnlc, 0, sizeof(rnlc));
    memset(&ckind, 0, sizeof(ckind));
    memset(&fulla, 0, sizeof(fulla));
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    memset(&rs, 0, sizeof(rs));
    _minnlcstate_init(&state, _state, ae_true);
    _minnlcreport_init(&rep, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rnlc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ckind, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    for(solvertype=0; solvertype<=1; solvertype++)
    {
        
        /*
         * Basic test:
         * * 2-dimensional problem
         * * target function F(x0,x1) = (x0-1)^2 + (x1-1)^2
         * * one nonlinear constraint Z(x0,x1) = x0^2+x1^2-1,
         *   which is tried as equality and inequality one
         */
        rho = 200.0;
        tolx = 0.0005;
        aulits = 50;
        n = 2;
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        minnlccreate(n, &x0, &state, _state);
        if( solvertype==0 )
        {
            minnlcsetalgoaul(&state, rho, aulits, _state);
        }
        else
        {
            if( solvertype==1 )
            {
                minnlcsetalgoslp(&state, _state);
            }
            else
            {
                ae_assert(ae_false, "Assertion failed", _state);
            }
        }
        minnlcsetcond(&state, 1.0E-9, 0, _state);
        minnlcsetnlc(&state, 0, 1, _state);
        while(minnlciteration(&state, _state))
        {
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = ae_sqr(state.x.ptr.p_double[0]-1, _state)+ae_sqr(state.x.ptr.p_double[1]-1, _state);
                state.j.ptr.pp_double[0][0] = 2*(state.x.ptr.p_double[0]-1);
                state.j.ptr.pp_double[0][1] = 2*(state.x.ptr.p_double[1]-1);
                state.fi.ptr.p_double[1] = ae_sqr(state.x.ptr.p_double[0], _state)+ae_sqr(state.x.ptr.p_double[1], _state)-1;
                state.j.ptr.pp_double[1][0] = 2*state.x.ptr.p_double[0];
                state.j.ptr.pp_double[1][1] = 2*state.x.ptr.p_double[1];
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnlcresults(&state, &x1, &rep, _state);
        ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:1793");
        ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:1794");
        if( *wereerrors )
        {
            ae_frame_leave(_state);
            return;
        }
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[0]-ae_sqrt((double)(2), _state)/2, _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:1797");
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[1]-ae_sqrt((double)(2), _state)/2, _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:1798");
        minnlcsetnlc(&state, 1, 0, _state);
        minnlcrestartfrom(&state, &x0, _state);
        while(minnlciteration(&state, _state))
        {
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = ae_sqr(state.x.ptr.p_double[0]-1, _state)+ae_sqr(state.x.ptr.p_double[1]-1, _state);
                state.j.ptr.pp_double[0][0] = 2*(state.x.ptr.p_double[0]-1);
                state.j.ptr.pp_double[0][1] = 2*(state.x.ptr.p_double[1]-1);
                state.fi.ptr.p_double[1] = ae_sqr(state.x.ptr.p_double[0], _state)+ae_sqr(state.x.ptr.p_double[1], _state)-1;
                state.j.ptr.pp_double[1][0] = 2*state.x.ptr.p_double[0];
                state.j.ptr.pp_double[1][1] = 2*state.x.ptr.p_double[1];
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnlcresults(&state, &x1, &rep, _state);
        ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:1816");
        ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:1817");
        if( *wereerrors )
        {
            ae_frame_leave(_state);
            return;
        }
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[0]-ae_sqrt((double)(2), _state)/2, _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:1820");
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[1]-ae_sqrt((double)(2), _state)/2, _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:1821");
        
        /*
         * This test checks correctness of scaling being applied to nonlinear
         * constraints. We solve bound constrained scaled problem and check
         * that solution is correct.
         */
        aulits = 50;
        rho = 200.0;
        tolx = 0.0005;
        tolg = 0.01;
        for(n=1; n<=10; n++)
        {
            for(pass=1; pass<=10; pass++)
            {
                
                /*
                 * Generate well-conditioned problem with unit scale
                 */
                spdmatrixrndcond(n, 1.0E2, &fulla, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&bndl, n, _state);
                ae_vector_set_length(&bndu, n, _state);
                ae_vector_set_length(&x0, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                cntnlec = hqrnduniformi(&rs, n, _state);
                cntnlic = n-cntnlec;
                for(i=0; i<=cntnlec-1; i++)
                {
                    bndl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    bndu.ptr.p_double[i] = bndl.ptr.p_double[i];
                }
                for(i=cntnlec; i<=n-1; i++)
                {
                    bndl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    bndu.ptr.p_double[i] = bndl.ptr.p_double[i]+0.5;
                }
                
                /*
                 * Apply scaling to quadratic/linear term, so problem becomes
                 * well-conditioned in the scaled coordinates.
                 */
                scaletype = hqrnduniformi(&rs, 2, _state);
                ae_vector_set_length(&s, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    if( scaletype==0 )
                    {
                        s.ptr.p_double[i] = (double)(1);
                    }
                    else
                    {
                        s.ptr.p_double[i] = ae_exp(20*hqrndnormal(&rs, _state), _state);
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    x0.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                    bndl.ptr.p_double[i] = bndl.ptr.p_double[i]*s.ptr.p_double[i];
                    bndu.ptr.p_double[i] = bndu.ptr.p_double[i]*s.ptr.p_double[i];
                    b.ptr.p_double[i] = b.ptr.p_double[i]/s.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        fulla.ptr.pp_double[i][j] = fulla.ptr.pp_double[i][j]/(s.ptr.p_double[i]*s.ptr.p_double[j]);
                    }
                }
                
                /*
                 * Solve problem with boundary constraints posed as nonlinear ones
                 */
                minnlccreate(n, &x0, &state, _state);
                if( solvertype==0 )
                {
                    minnlcsetalgoaul(&state, rho, aulits, _state);
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minnlcsetalgoslp(&state, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
                minnlcsetscale(&state, &s, _state);
                minnlcsetnlc(&state, cntnlec, 2*cntnlic, _state);
                minnlcsetcond(&state, 1.0E-9, 0, _state);
                while(minnlciteration(&state, _state))
                {
                    if( state.needfij )
                    {
                        for(i=0; i<=cntnlec+2*cntnlic; i++)
                        {
                            state.fi.ptr.p_double[i] = (double)(0);
                            for(j=0; j<=n-1; j++)
                            {
                                state.j.ptr.pp_double[i][j] = (double)(0);
                            }
                        }
                        
                        /*
                         * Function itself
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                            state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                                state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            }
                        }
                        
                        /*
                         * Equality constraints
                         */
                        for(i=0; i<=cntnlec-1; i++)
                        {
                            state.fi.ptr.p_double[1+i] = (state.x.ptr.p_double[i]-bndl.ptr.p_double[i])/s.ptr.p_double[i];
                            state.j.ptr.pp_double[1+i][i] = 1/s.ptr.p_double[i];
                        }
                        
                        /*
                         * Inequality constraints
                         */
                        for(i=0; i<=cntnlic-1; i++)
                        {
                            k = cntnlec+i;
                            state.fi.ptr.p_double[1+cntnlec+2*i+0] = (bndl.ptr.p_double[k]-state.x.ptr.p_double[k])/s.ptr.p_double[k];
                            state.j.ptr.pp_double[1+cntnlec+2*i+0][k] = -1/s.ptr.p_double[k];
                            state.fi.ptr.p_double[1+cntnlec+2*i+1] = (state.x.ptr.p_double[k]-bndu.ptr.p_double[k])/s.ptr.p_double[k];
                            state.j.ptr.pp_double[1+cntnlec+2*i+1][k] = 1/s.ptr.p_double[k];
                        }
                        continue;
                    }
                    ae_assert(ae_false, "Assertion failed", _state);
                }
                minnlcresults(&state, &x1, &rep, _state);
                ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:1951");
                ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:1952");
                if( *wereerrors )
                {
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Check feasibility properties
                 */
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(wereerrors, ae_isfinite(bndl.ptr.p_double[i], _state)&&ae_fp_less_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i]-tolx*s.ptr.p_double[i]), __FILE__, __LINE__, "testminnlcunit.ap:1961");
                    ae_set_error_flag(wereerrors, ae_isfinite(bndu.ptr.p_double[i], _state)&&ae_fp_greater_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i]+tolx*s.ptr.p_double[i]), __FILE__, __LINE__, "testminnlcunit.ap:1962");
                }
                
                /*
                 * Test - calculate scaled constrained gradient at solution,
                 * check its norm.
                 */
                ae_vector_set_length(&g, n, _state);
                gnorm = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    g.ptr.p_double[i] = b.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        g.ptr.p_double[i] = g.ptr.p_double[i]+fulla.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                    }
                    g.ptr.p_double[i] = s.ptr.p_double[i]*g.ptr.p_double[i];
                    if( (ae_isfinite(bndl.ptr.p_double[i], _state)&&ae_fp_less(ae_fabs(x1.ptr.p_double[i]-bndl.ptr.p_double[i], _state),tolx*s.ptr.p_double[i]))&&ae_fp_greater(g.ptr.p_double[i],(double)(0)) )
                    {
                        g.ptr.p_double[i] = (double)(0);
                    }
                    if( (ae_isfinite(bndu.ptr.p_double[i], _state)&&ae_fp_less(ae_fabs(x1.ptr.p_double[i]-bndu.ptr.p_double[i], _state),tolx*s.ptr.p_double[i]))&&ae_fp_less(g.ptr.p_double[i],(double)(0)) )
                    {
                        g.ptr.p_double[i] = (double)(0);
                    }
                    gnorm = gnorm+ae_sqr(g.ptr.p_double[i], _state);
                }
                gnorm = ae_sqrt(gnorm, _state);
                ae_set_error_flag(wereerrors, ae_fp_greater(gnorm,tolg), __FILE__, __LINE__, "testminnlcunit.ap:1985");
            }
        }
        
        /*
         * Complex problem with mix of boundary, linear and nonlinear constraints:
         * * quadratic target function f(x) = 0.5*x'*A*x + b'*x
         * * unit scaling is used
         * * problem size N is even
         * * all variables are divided into pairs: x[0] and x[1], x[2] and x[3], ...
         * * constraints are set for pairs of variables, i.e. each constraint involves
         *   only pair of adjacent variables (x0/x1, x2/x3, x4/x5 and so on), and each
         *   pair of variables has at most one constraint which binds them
         * * for variables u and v following kinds of constraints can be randomly set:
         *   * CKind=0      no constraint
         *   * CKind=1      boundary equality constraint:    u=a, v=b
         *   * CKind=2      boundary inequality constraint:  a0<=u<=b0, a1<=v<=b1
         *   * CKind=3      linear equality constraint:      a*u+b*v  = c
         *   * CKind=4      linear inequality constraint:    a*u+b*v <= c
         *   * CKind=5      nonlinear equality constraint:   u^2+v^2  = 1
         *   * CKind=6      nonlinear inequality constraint: u^2+v^2 <= 1
         * * it is relatively easy to calculated projected gradient for such problem
         */
        aulits = 50;
        rho = 200.0;
        tolx = 0.0005;
        tolg = 0.01;
        n = 20;
        n2 = n/2;
        for(pass=1; pass<=10; pass++)
        {
            for(prectype=-1; prectype<=2; prectype++)
            {
                
                /*
                 * Generate well-conditioned problem with unit scale
                 */
                spdmatrixrndcond(n, 1.0E2, &fulla, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&bndl, n, _state);
                ae_vector_set_length(&bndu, n, _state);
                ae_matrix_set_length(&c, n, n+1, _state);
                ae_vector_set_length(&ct, n, _state);
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&ckind, n2, _state);
                ae_vector_set_length(&rnlc, n2, _state);
                cntbc = 0;
                cntlc = 0;
                cntnlec = 0;
                cntnlic = 0;
                for(i=0; i<=n-1; i++)
                {
                    bndl.ptr.p_double[i] = _state->v_neginf;
                    bndu.ptr.p_double[i] = _state->v_posinf;
                    x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                for(i=0; i<=n2-1; i++)
                {
                    ckind.ptr.p_int[i] = hqrnduniformi(&rs, 7, _state);
                    rnlc.ptr.p_double[i] = (double)(0);
                    if( ckind.ptr.p_int[i]==0 )
                    {
                        
                        /*
                         * Unconstrained
                         */
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==1 )
                    {
                        
                        /*
                         * Bound equality constrained
                         */
                        bndl.ptr.p_double[2*i+0] = hqrnduniformr(&rs, _state)-0.5;
                        bndu.ptr.p_double[2*i+0] = bndl.ptr.p_double[2*i+0];
                        bndl.ptr.p_double[2*i+1] = hqrnduniformr(&rs, _state)-0.5;
                        bndu.ptr.p_double[2*i+1] = bndl.ptr.p_double[2*i+1];
                        inc(&cntbc, _state);
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==2 )
                    {
                        
                        /*
                         * Bound inequality constrained
                         */
                        bndl.ptr.p_double[2*i+0] = hqrnduniformr(&rs, _state)-0.5;
                        bndu.ptr.p_double[2*i+0] = bndl.ptr.p_double[2*i+0]+0.5;
                        bndl.ptr.p_double[2*i+1] = hqrnduniformr(&rs, _state)-0.5;
                        bndu.ptr.p_double[2*i+1] = bndl.ptr.p_double[2*i+1]+0.5;
                        inc(&cntbc, _state);
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==3 )
                    {
                        
                        /*
                         * Linear equality constrained
                         */
                        for(j=0; j<=n; j++)
                        {
                            c.ptr.pp_double[cntlc][j] = 0.0;
                        }
                        vx = hqrnduniformr(&rs, _state)-0.5;
                        vy = hqrnduniformr(&rs, _state)-0.5;
                        c.ptr.pp_double[cntlc][2*i+0] = vx;
                        c.ptr.pp_double[cntlc][2*i+1] = vy;
                        c.ptr.pp_double[cntlc][n] = hqrnduniformr(&rs, _state)-0.5;
                        ct.ptr.p_int[cntlc] = 0;
                        inc(&cntlc, _state);
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==4 )
                    {
                        
                        /*
                         * Linear inequality constrained
                         */
                        for(j=0; j<=n; j++)
                        {
                            c.ptr.pp_double[cntlc][j] = 0.0;
                        }
                        vx = hqrnduniformr(&rs, _state)-0.5;
                        vy = hqrnduniformr(&rs, _state)-0.5;
                        c.ptr.pp_double[cntlc][2*i+0] = vx;
                        c.ptr.pp_double[cntlc][2*i+1] = vy;
                        c.ptr.pp_double[cntlc][n] = hqrnduniformr(&rs, _state)-0.5;
                        ct.ptr.p_int[cntlc] = -1;
                        inc(&cntlc, _state);
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==5 )
                    {
                        
                        /*
                         * Nonlinear equality constrained
                         */
                        rnlc.ptr.p_double[i] = 0.5+hqrnduniformr(&rs, _state);
                        inc(&cntnlec, _state);
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==6 )
                    {
                        
                        /*
                         * Nonlinear inequality constrained
                         */
                        rnlc.ptr.p_double[i] = 0.5+hqrnduniformr(&rs, _state);
                        inc(&cntnlic, _state);
                        continue;
                    }
                    ae_assert(ae_false, "Assertion failed", _state);
                }
                
                /*
                 * Solve problem
                 */
                minnlccreate(n, &x0, &state, _state);
                if( solvertype==0 )
                {
                    minnlcsetalgoaul(&state, rho, aulits, _state);
                    minnlcsetcond(&state, 1.0E-9, 0, _state);
                    if( prectype==0 )
                    {
                        minnlcsetprecinexact(&state, _state);
                    }
                    if( prectype==1 )
                    {
                        minnlcsetprecexactlowrank(&state, 0, _state);
                    }
                    if( prectype==2 )
                    {
                        minnlcsetprecexactrobust(&state, 0, _state);
                    }
                }
                else
                {
                    if( solvertype==1 )
                    {
                        minnlcsetalgoslp(&state, _state);
                        minnlcsetcond(&state, 1.0E-7, 0, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
                minnlcsetbc(&state, &bndl, &bndu, _state);
                minnlcsetlc(&state, &c, &ct, cntlc, _state);
                minnlcsetnlc(&state, cntnlec, cntnlic, _state);
                while(minnlciteration(&state, _state))
                {
                    if( state.needfij )
                    {
                        
                        /*
                         * Evaluate target function
                         */
                        state.fi.ptr.p_double[0] = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                            state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                                state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            }
                        }
                        
                        /*
                         * Evaluate constraint functions
                         */
                        knlec = 1;
                        knlic = 1+cntnlec;
                        for(i=0; i<=n2-1; i++)
                        {
                            if( ckind.ptr.p_int[i]==5 )
                            {
                                state.fi.ptr.p_double[knlec] = (double)(0);
                                for(j=0; j<=n-1; j++)
                                {
                                    state.j.ptr.pp_double[knlec][j] = 0.0;
                                }
                                state.fi.ptr.p_double[knlec] = ae_sqr(state.x.ptr.p_double[2*i+0], _state)+ae_sqr(state.x.ptr.p_double[2*i+1], _state)-rnlc.ptr.p_double[i];
                                state.j.ptr.pp_double[knlec][2*i+0] = 2*state.x.ptr.p_double[2*i+0];
                                state.j.ptr.pp_double[knlec][2*i+1] = 2*state.x.ptr.p_double[2*i+1];
                                inc(&knlec, _state);
                                continue;
                            }
                            if( ckind.ptr.p_int[i]==6 )
                            {
                                state.fi.ptr.p_double[knlic] = (double)(0);
                                for(j=0; j<=n-1; j++)
                                {
                                    state.j.ptr.pp_double[knlic][j] = 0.0;
                                }
                                state.fi.ptr.p_double[knlic] = ae_sqr(state.x.ptr.p_double[2*i+0], _state)+ae_sqr(state.x.ptr.p_double[2*i+1], _state)-rnlc.ptr.p_double[i];
                                state.j.ptr.pp_double[knlic][2*i+0] = 2*state.x.ptr.p_double[2*i+0];
                                state.j.ptr.pp_double[knlic][2*i+1] = 2*state.x.ptr.p_double[2*i+1];
                                inc(&knlic, _state);
                                continue;
                            }
                        }
                        ae_assert(knlec==1+cntnlec, "Assertion failed", _state);
                        ae_assert(knlic==1+cntnlec+cntnlic, "Assertion failed", _state);
                        continue;
                    }
                    ae_assert(ae_false, "Assertion failed", _state);
                }
                minnlcresults(&state, &x1, &rep, _state);
                ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:2209");
                ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:2210");
                if( *wereerrors )
                {
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Check feasibility properties
                 */
                klc = 0;
                for(i=0; i<=n2-1; i++)
                {
                    if( ckind.ptr.p_int[i]==0 )
                    {
                        
                        /*
                         * Unconstrained
                         */
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==1 )
                    {
                        
                        /*
                         * Bound equality constrained
                         */
                        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[2*i+0]-bndl.ptr.p_double[2*i+0], _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:2232");
                        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[2*i+1]-bndl.ptr.p_double[2*i+1], _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:2233");
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==2 )
                    {
                        
                        /*
                         * Bound inequality constrained
                         */
                        ae_set_error_flag(wereerrors, ae_fp_less(x1.ptr.p_double[2*i+0],bndl.ptr.p_double[2*i+0]-tolx), __FILE__, __LINE__, "testminnlcunit.ap:2241");
                        ae_set_error_flag(wereerrors, ae_fp_greater(x1.ptr.p_double[2*i+0],bndu.ptr.p_double[2*i+0]+tolx), __FILE__, __LINE__, "testminnlcunit.ap:2242");
                        ae_set_error_flag(wereerrors, ae_fp_less(x1.ptr.p_double[2*i+1],bndl.ptr.p_double[2*i+1]-tolx), __FILE__, __LINE__, "testminnlcunit.ap:2243");
                        ae_set_error_flag(wereerrors, ae_fp_greater(x1.ptr.p_double[2*i+1],bndu.ptr.p_double[2*i+1]+tolx), __FILE__, __LINE__, "testminnlcunit.ap:2244");
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==3 )
                    {
                        
                        /*
                         * Linear equality constrained
                         */
                        v = x1.ptr.p_double[2*i+0]*c.ptr.pp_double[klc][2*i+0]+x1.ptr.p_double[2*i+1]*c.ptr.pp_double[klc][2*i+1]-c.ptr.pp_double[klc][n];
                        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(v, _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:2253");
                        inc(&klc, _state);
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==4 )
                    {
                        
                        /*
                         * Linear inequality constrained
                         */
                        v = x1.ptr.p_double[2*i+0]*c.ptr.pp_double[klc][2*i+0]+x1.ptr.p_double[2*i+1]*c.ptr.pp_double[klc][2*i+1]-c.ptr.pp_double[klc][n];
                        ae_set_error_flag(wereerrors, ae_fp_greater(v,tolx), __FILE__, __LINE__, "testminnlcunit.ap:2263");
                        inc(&klc, _state);
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==5 )
                    {
                        
                        /*
                         * Nonlinear equality constrained
                         */
                        v = ae_sqr(x1.ptr.p_double[2*i+0], _state)+ae_sqr(x1.ptr.p_double[2*i+1], _state)-rnlc.ptr.p_double[i];
                        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(v, _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:2273");
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==6 )
                    {
                        
                        /*
                         * Nonlinear inequality constrained
                         */
                        v = ae_sqr(x1.ptr.p_double[2*i+0], _state)+ae_sqr(x1.ptr.p_double[2*i+1], _state)-rnlc.ptr.p_double[i];
                        ae_set_error_flag(wereerrors, ae_fp_greater(v,tolx), __FILE__, __LINE__, "testminnlcunit.ap:2282");
                        continue;
                    }
                    ae_assert(ae_false, "Assertion failed", _state);
                }
                
                /*
                 * Test - calculate scaled constrained gradient at solution,
                 * check its norm.
                 */
                ae_vector_set_length(&g, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    v = b.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        v = v+fulla.ptr.pp_double[i][j]*x1.ptr.p_double[j];
                    }
                    g.ptr.p_double[i] = v;
                }
                klc = 0;
                for(i=0; i<=n2-1; i++)
                {
                    if( ckind.ptr.p_int[i]==0 )
                    {
                        
                        /*
                         * Unconstrained
                         */
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==1 )
                    {
                        
                        /*
                         * Bound equality constrained, unconditionally set gradient to zero
                         */
                        g.ptr.p_double[2*i+0] = 0.0;
                        g.ptr.p_double[2*i+1] = 0.0;
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==2 )
                    {
                        
                        /*
                         * Bound inequality constrained, conditionally set gradient to zero
                         * (when constraint is active)
                         */
                        if( ae_fp_less(x1.ptr.p_double[2*i+0],bndl.ptr.p_double[2*i+0]+tolx)||ae_fp_greater(x1.ptr.p_double[2*i+0],bndu.ptr.p_double[2*i+0]-tolx) )
                        {
                            g.ptr.p_double[2*i+0] = 0.0;
                        }
                        if( ae_fp_less(x1.ptr.p_double[2*i+1],bndl.ptr.p_double[2*i+1]+tolx)||ae_fp_greater(x1.ptr.p_double[2*i+1],bndu.ptr.p_double[2*i+1]-tolx) )
                        {
                            g.ptr.p_double[2*i+1] = 0.0;
                        }
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==3 )
                    {
                        
                        /*
                         * Linear equality constrained, unconditionally project gradient into
                         * equality constrained subspace
                         */
                        v = g.ptr.p_double[2*i+0]*c.ptr.pp_double[klc][2*i+0]+g.ptr.p_double[2*i+1]*c.ptr.pp_double[klc][2*i+1];
                        vv = ae_sqr(c.ptr.pp_double[klc][2*i+0], _state)+ae_sqr(c.ptr.pp_double[klc][2*i+1], _state);
                        g.ptr.p_double[2*i+0] = g.ptr.p_double[2*i+0]-c.ptr.pp_double[klc][2*i+0]*(v/vv);
                        g.ptr.p_double[2*i+1] = g.ptr.p_double[2*i+1]-c.ptr.pp_double[klc][2*i+1]*(v/vv);
                        inc(&klc, _state);
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==4 )
                    {
                        
                        /*
                         * Linear inequality constrained, conditionally project gradient
                         * (when constraint is active)
                         */
                        v = x1.ptr.p_double[2*i+0]*c.ptr.pp_double[klc][2*i+0]+x1.ptr.p_double[2*i+1]*c.ptr.pp_double[klc][2*i+1]-c.ptr.pp_double[klc][n];
                        if( ae_fp_greater(v,-tolx) )
                        {
                            v = g.ptr.p_double[2*i+0]*c.ptr.pp_double[klc][2*i+0]+g.ptr.p_double[2*i+1]*c.ptr.pp_double[klc][2*i+1];
                            vv = ae_sqr(c.ptr.pp_double[klc][2*i+0], _state)+ae_sqr(c.ptr.pp_double[klc][2*i+1], _state);
                            g.ptr.p_double[2*i+0] = g.ptr.p_double[2*i+0]-c.ptr.pp_double[klc][2*i+0]*(v/vv);
                            g.ptr.p_double[2*i+1] = g.ptr.p_double[2*i+1]-c.ptr.pp_double[klc][2*i+1]*(v/vv);
                        }
                        inc(&klc, _state);
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==5 )
                    {
                        
                        /*
                         * Nonlinear equality constrained, unconditionally project gradient
                         *
                         * NOTE: here we rely on the fact that corresponding components of X
                         *       sum to one.
                         */
                        v = coalesce(ae_sqrt(ae_sqr(x1.ptr.p_double[2*i+0], _state)+ae_sqr(x1.ptr.p_double[2*i+1], _state), _state), 1.0, _state);
                        vx = x1.ptr.p_double[2*i+0]/v;
                        vy = x1.ptr.p_double[2*i+1]/v;
                        v = g.ptr.p_double[2*i+0]*vx+g.ptr.p_double[2*i+1]*vy;
                        g.ptr.p_double[2*i+0] = g.ptr.p_double[2*i+0]-vx*v;
                        g.ptr.p_double[2*i+1] = g.ptr.p_double[2*i+1]-vy*v;
                        continue;
                    }
                    if( ckind.ptr.p_int[i]==6 )
                    {
                        
                        /*
                         * Nonlinear inequality constrained, conditionally project gradient
                         * (when constraint is active)
                         *
                         * NOTE: here we rely on the fact that corresponding components of X
                         *       sum to one.
                         */
                        v = ae_sqr(x1.ptr.p_double[2*i+0], _state)+ae_sqr(x1.ptr.p_double[2*i+1], _state)-rnlc.ptr.p_double[i];
                        if( ae_fp_greater(v,-tolx) )
                        {
                            v = coalesce(ae_sqrt(ae_sqr(x1.ptr.p_double[2*i+0], _state)+ae_sqr(x1.ptr.p_double[2*i+1], _state), _state), 1.0, _state);
                            vx = x1.ptr.p_double[2*i+0]/v;
                            vy = x1.ptr.p_double[2*i+1]/v;
                            v = g.ptr.p_double[2*i+0]*vx+g.ptr.p_double[2*i+1]*vy;
                            g.ptr.p_double[2*i+0] = g.ptr.p_double[2*i+0]-vx*v;
                            g.ptr.p_double[2*i+1] = g.ptr.p_double[2*i+1]-vy*v;
                        }
                        continue;
                    }
                    ae_assert(ae_false, "Assertion failed", _state);
                }
                gnorm = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    gnorm = gnorm+ae_sqr(g.ptr.p_double[i], _state);
                }
                gnorm = ae_sqrt(gnorm, _state);
                ae_set_error_flag(wereerrors, ae_fp_greater(gnorm,tolg), __FILE__, __LINE__, "testminnlcunit.ap:2404");
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function performs additional tests

On failure sets error flag.
*************************************************************************/
static void testminnlcunit_testother(ae_bool* wereerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    double v;
    double h;
    double fl;
    double fr;
    double fl2;
    double fr2;
    double dfl;
    double dfr;
    double dfl2;
    double dfr2;
    double d2fl;
    double d2fr;
    double d2fl2;
    double d2fr2;
    double f0;
    double df;
    double d2f;
    double ndf;
    double nd2f;
    double dtol;
    double diffstep;
    minnlcstate state;
    minnlcreport rep;
    double rho;
    ae_int_t aulits;
    double tolx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_vector b;
    ae_vector x0;
    ae_vector x1;
    ae_vector x2;
    ae_vector x3;
    ae_vector xlast;
    ae_vector bndl;
    ae_vector bndu;
    double condv;
    ae_matrix a;
    ae_matrix c;
    ae_matrix fulla;
    ae_vector ct;
    ae_int_t nlbfgs;
    ae_int_t nexactlowrank;
    ae_int_t nexactrobust;
    ae_int_t nnone;
    ae_int_t prectype;
    ae_int_t ctype;
    ae_int_t trialidx;
    ae_int_t blocksize;
    ae_int_t blockcnt;
    ae_int_t maxits;
    ae_int_t spoiliteration;
    ae_int_t stopiteration;
    ae_int_t spoilvar;
    double spoilval;
    ae_int_t pass;
    ae_int_t solvertype;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&x3, 0, sizeof(x3));
    memset(&xlast, 0, sizeof(xlast));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&a, 0, sizeof(a));
    memset(&c, 0, sizeof(c));
    memset(&fulla, 0, sizeof(fulla));
    memset(&ct, 0, sizeof(ct));
    _hqrndstate_init(&rs, _state, ae_true);
    _minnlcstate_init(&state, _state, ae_true);
    _minnlcreport_init(&rep, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x3, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fulla, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Test equality penalty function (correctly calculated and smooth)
     */
    h = 1.0E-4;
    v = -0.98;
    dtol = 1.0E-3;
    while(ae_fp_less_eq(v,0.98))
    {
        
        /*
         * Test numerical derivative; this test also checks continuity of the
         * function
         */
        minnlcequalitypenaltyfunction(v-2*h, &fl2, &dfl2, &d2fl2, _state);
        minnlcequalitypenaltyfunction(v-h, &fl, &dfl, &d2fl, _state);
        minnlcequalitypenaltyfunction(v+h, &fr, &dfr, &d2fr, _state);
        minnlcequalitypenaltyfunction(v+2*h, &fr2, &dfr2, &d2fr2, _state);
        minnlcequalitypenaltyfunction(v, &f0, &df, &d2f, _state);
        ndf = (-fr2+8*fr-8*fl+fl2)/(12*h);
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(ndf-df, _state),dtol*ae_maxreal(ae_fabs(ndf, _state), (double)(1), _state)), __FILE__, __LINE__, "testminnlcunit.ap:2474");
        nd2f = (-dfr2+8*dfr-8*dfl+dfl2)/(12*h);
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(nd2f-d2f, _state),dtol*ae_maxreal(ae_fabs(nd2f, _state), (double)(1), _state)), __FILE__, __LINE__, "testminnlcunit.ap:2476");
        
        /*
         * Next point
         */
        v = v+h;
    }
    minnlcequalitypenaltyfunction(0.0, &f0, &df, &d2f, _state);
    ae_set_error_flag(wereerrors, ae_fp_neq(f0,(double)(0)), __FILE__, __LINE__, "testminnlcunit.ap:2484");
    ae_set_error_flag(wereerrors, ae_fp_neq(df,(double)(0)), __FILE__, __LINE__, "testminnlcunit.ap:2485");
    
    /*
     * Test inequality penalty function (correctly calculated and smooth)
     */
    h = 1.0E-4;
    v = 0.02;
    dtol = 1.0E-3;
    while(ae_fp_less_eq(v,2.00))
    {
        
        /*
         * Test numerical derivative; this test also checks continuity of the
         * function
         */
        minnlcinequalityshiftfunction(v-2*h, &fl2, &dfl2, &d2fl2, _state);
        minnlcinequalityshiftfunction(v-h, &fl, &dfl, &d2fl, _state);
        minnlcinequalityshiftfunction(v+h, &fr, &dfr, &d2fr, _state);
        minnlcinequalityshiftfunction(v+2*h, &fr2, &dfr2, &d2fr2, _state);
        minnlcinequalityshiftfunction(v, &f0, &df, &d2f, _state);
        ndf = (-fr2+8*fr-8*fl+fl2)/(12*h);
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(ndf-df, _state),dtol*ae_maxreal(ae_fabs(ndf, _state), (double)(1), _state)), __FILE__, __LINE__, "testminnlcunit.ap:2509");
        nd2f = (-dfr2+8*dfr-8*dfl+dfl2)/(12*h);
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(nd2f-d2f, _state),dtol*ae_maxreal(ae_fabs(nd2f, _state), (double)(1), _state)), __FILE__, __LINE__, "testminnlcunit.ap:2511");
        
        /*
         * Next point
         */
        v = v+h;
    }
    minnlcinequalityshiftfunction(1.0, &f0, &df, &d2f, _state);
    ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(f0, _state),1.0E-6), __FILE__, __LINE__, "testminnlcunit.ap:2519");
    ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(df+1, _state),1.0E-6), __FILE__, __LINE__, "testminnlcunit.ap:2520");
    
    /*
     * Test different properties shared by all solvers
     */
    for(solvertype=0; solvertype<=1; solvertype++)
    {
        
        /*
         * Test location reports
         */
        aulits = 50;
        rho = 200.0;
        n = 2;
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        x0.ptr.p_double[0] = 0.1;
        x0.ptr.p_double[1] = 0.2;
        xlast.ptr.p_double[0] = (double)(0);
        xlast.ptr.p_double[1] = (double)(0);
        minnlccreate(n, &x0, &state, _state);
        if( solvertype==0 )
        {
            minnlcsetalgoaul(&state, rho, aulits, _state);
        }
        else
        {
            if( solvertype==1 )
            {
                minnlcsetalgoslp(&state, _state);
            }
            else
            {
                ae_assert(ae_false, "Assertion failed", _state);
            }
        }
        minnlcsetcond(&state, 1.0E-9, 0, _state);
        minnlcsetnlc(&state, 0, 1, _state);
        minnlcsetxrep(&state, ae_true, _state);
        while(minnlciteration(&state, _state))
        {
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = ae_sqr(state.x.ptr.p_double[0]-1, _state)+ae_sqr(state.x.ptr.p_double[1]-1, _state);
                state.j.ptr.pp_double[0][0] = 2*(state.x.ptr.p_double[0]-1);
                state.j.ptr.pp_double[0][1] = 2*(state.x.ptr.p_double[1]-1);
                state.fi.ptr.p_double[1] = ae_sqr(state.x.ptr.p_double[0], _state)+ae_sqr(state.x.ptr.p_double[1], _state)-1;
                state.j.ptr.pp_double[1][0] = 2*state.x.ptr.p_double[0];
                state.j.ptr.pp_double[1][1] = 2*state.x.ptr.p_double[1];
                continue;
            }
            if( state.xupdated )
            {
                
                /*
                 * Save last point
                 */
                xlast.ptr.p_double[0] = state.x.ptr.p_double[0];
                xlast.ptr.p_double[1] = state.x.ptr.p_double[1];
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnlcresults(&state, &x1, &rep, _state);
        ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:2575");
        ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:2576");
        if( *wereerrors )
        {
            ae_frame_leave(_state);
            return;
        }
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[0]-xlast.ptr.p_double[0], _state),1.0E4*ae_machineepsilon), __FILE__, __LINE__, "testminnlcunit.ap:2579");
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[1]-xlast.ptr.p_double[1], _state),1.0E4*ae_machineepsilon), __FILE__, __LINE__, "testminnlcunit.ap:2580");
        
        /*
         * Test numerical differentiation
         */
        aulits = 50;
        rho = 200.0;
        tolx = 0.001;
        diffstep = 0.0001;
        n = 2;
        ae_vector_set_length(&x0, n, _state);
        x0.ptr.p_double[0] = 0.1;
        x0.ptr.p_double[1] = 0.2;
        minnlccreatef(n, &x0, diffstep, &state, _state);
        if( solvertype==0 )
        {
            minnlcsetalgoaul(&state, rho, aulits, _state);
        }
        else
        {
            if( solvertype==1 )
            {
                minnlcsetalgoslp(&state, _state);
            }
            else
            {
                ae_assert(ae_false, "Assertion failed", _state);
            }
        }
        minnlcsetcond(&state, 1.0E-9, 0, _state);
        minnlcsetnlc(&state, 0, 1, _state);
        while(minnlciteration(&state, _state))
        {
            if( state.needfi )
            {
                state.fi.ptr.p_double[0] = ae_sqr(state.x.ptr.p_double[0]-1, _state)+ae_sqr(state.x.ptr.p_double[1]-1, _state);
                state.fi.ptr.p_double[1] = ae_sqr(state.x.ptr.p_double[0], _state)+ae_sqr(state.x.ptr.p_double[1], _state)-1;
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnlcresults(&state, &x1, &rep, _state);
        ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:2614");
        ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:2615");
        if( *wereerrors )
        {
            ae_frame_leave(_state);
            return;
        }
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[0]-ae_sqrt((double)(2), _state)/2, _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:2618");
        ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[1]-ae_sqrt((double)(2), _state)/2, _state),tolx), __FILE__, __LINE__, "testminnlcunit.ap:2619");
        
        /*
         * Test gradient checking
         */
        aulits = 50;
        rho = 200.0;
        tolx = 0.0001;
        diffstep = 0.001;
        n = 2;
        ae_vector_set_length(&x0, n, _state);
        x0.ptr.p_double[0] = 0.1;
        x0.ptr.p_double[1] = 0.2;
        minnlccreate(n, &x0, &state, _state);
        if( solvertype==0 )
        {
            minnlcsetalgoaul(&state, rho, aulits, _state);
        }
        else
        {
            if( solvertype==1 )
            {
                minnlcsetalgoslp(&state, _state);
            }
            else
            {
                ae_assert(ae_false, "Assertion failed", _state);
            }
        }
        minnlcsetgradientcheck(&state, diffstep, _state);
        minnlcsetcond(&state, 1.0E-9, 0, _state);
        minnlcsetnlc(&state, 0, 1, _state);
        while(minnlciteration(&state, _state))
        {
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = ae_sqr(state.x.ptr.p_double[0]-1, _state)+ae_sqr(state.x.ptr.p_double[1]-1, _state);
                state.j.ptr.pp_double[0][0] = 2*(state.x.ptr.p_double[0]-1);
                state.j.ptr.pp_double[0][1] = 2*(state.x.ptr.p_double[1]-1);
                state.fi.ptr.p_double[1] = ae_sqr(state.x.ptr.p_double[0], _state)+ae_sqr(state.x.ptr.p_double[1], _state)-1;
                state.j.ptr.pp_double[1][0] = 2*state.x.ptr.p_double[0];
                state.j.ptr.pp_double[1][1] = (double)(0);
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnlcresults(&state, &x1, &rep, _state);
        ae_set_error_flag(wereerrors, rep.terminationtype!=-7, __FILE__, __LINE__, "testminnlcunit.ap:2658");
        ae_set_error_flag(wereerrors, rep.varidx!=1, __FILE__, __LINE__, "testminnlcunit.ap:2659");
        ae_set_error_flag(wereerrors, rep.funcidx!=1, __FILE__, __LINE__, "testminnlcunit.ap:2660");
        
        /*
         * Check handling of general linear constraints: solve linearly 
         * constrained twice, first time with constraints posed as linear
         * ones, second time with constraints posed as nonlinear ones.
         *
         * Linear constraints are normalized because we know that optimizer
         * normalizes them internally.
         *
         * We perform small amount of inner iterations - just 3 steps.
         * Only one outer iteration is performed. Such small number of
         * iterations allows to reduce influence of round-off errors
         * and compare results returned by different control paths within
         * optimizer (control path for linear constraints and one for
         * nonlinear constraints).
         *
         * We test following kinds of preconditioners:
         * * "none"
         * * "exact low rank", restart frequency is 1
         * * "exact robust", restart frequency is 1
         * Inexact LBFGS-based preconditioner is not tested because its
         * behavior greatly depends on order of equations.
         */
        n = 15;
        k = 5;
        maxits = 1;
        ae_vector_set_length(&x0, n, _state);
        ae_matrix_set_length(&c, k, n+1, _state);
        ae_vector_set_length(&ct, k, _state);
        for(prectype=0; prectype<=2; prectype++)
        {
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            for(i=0; i<=k-1; i++)
            {
                v = 0.0;
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                    v = v+ae_sqr(c.ptr.pp_double[i][j], _state);
                }
                v = 1/ae_sqrt(v, _state);
                ae_v_muld(&c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                ct.ptr.p_int[i] = 0;
            }
            minnlccreate(n, &x0, &state, _state);
            minnlcsetcond(&state, 0.0, maxits, _state);
            if( solvertype==0 )
            {
                aulits = 1;
                rho = 1.0E3;
                minnlcsetalgoaul(&state, rho, aulits, _state);
                if( prectype==0 )
                {
                    minnlcsetprecnone(&state, _state);
                }
                if( prectype==1 )
                {
                    minnlcsetprecexactlowrank(&state, 1, _state);
                }
                if( prectype==2 )
                {
                    minnlcsetprecexactrobust(&state, 1, _state);
                }
            }
            else
            {
                if( solvertype==1 )
                {
                    minnlcsetalgoslp(&state, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            minnlcsetlc(&state, &c, &ct, k, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+ae_sqr(state.x.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = 2*state.x.ptr.p_double[i];
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:2743");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:2744");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            minnlccreate(n, &x0, &state, _state);
            if( solvertype==0 )
            {
                aulits = 1;
                rho = 1.0E3;
                minnlcsetalgoaul(&state, rho, aulits, _state);
                if( prectype==0 )
                {
                    minnlcsetprecnone(&state, _state);
                }
                if( prectype==1 )
                {
                    minnlcsetprecexactlowrank(&state, 1, _state);
                }
                if( prectype==2 )
                {
                    minnlcsetprecexactrobust(&state, 1, _state);
                }
            }
            else
            {
                if( solvertype==1 )
                {
                    minnlcsetalgoslp(&state, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            minnlcsetcond(&state, 0.0, maxits, _state);
            minnlcsetnlc(&state, k, 0, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+ae_sqr(state.x.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = 2*state.x.ptr.p_double[i];
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        state.fi.ptr.p_double[1+i] = v-c.ptr.pp_double[i][n];
                        ae_v_move(&state.j.ptr.pp_double[1+i][0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x2, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x2, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:2787");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:2788");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(wereerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x2.ptr.p_double[i], _state),1.0E-4), __FILE__, __LINE__, "testminnlcunit.ap:2792");
            }
        }
        
        /*
         * Test integrity checks for NAN/INF:
         * * algorithm solves optimization problem, which is normal for some time (quadratic)
         * * after 5-th step we choose random component of gradient and consistently spoil
         *   it by NAN or INF.
         * * we check that correct termination code is returned (-8)
         */
        n = 100;
        for(pass=1; pass<=10; pass++)
        {
            spoiliteration = 5;
            stopiteration = 8;
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                
                /*
                 * Gradient can be spoiled by +INF, -INF, NAN
                 */
                spoilvar = hqrnduniformi(&rs, n, _state);
                i = hqrnduniformi(&rs, 3, _state);
                spoilval = _state->v_nan;
                if( i==0 )
                {
                    spoilval = _state->v_neginf;
                }
                if( i==1 )
                {
                    spoilval = _state->v_posinf;
                }
            }
            else
            {
                
                /*
                 * Function value can be spoiled only by NAN
                 * (+INF can be recognized as legitimate value during optimization)
                 */
                spoilvar = -1;
                spoilval = _state->v_nan;
            }
            spdmatrixrndcond(n, 1.0E5, &fulla, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            minnlccreate(n, &x0, &state, _state);
            if( solvertype==0 )
            {
                aulits = 5;
                rho = 1.0E3;
                minnlcsetalgoaul(&state, rho, aulits, _state);
            }
            else
            {
                if( solvertype==1 )
                {
                    minnlcsetalgoslp(&state, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            minnlcsetcond(&state, 0.0, stopiteration, _state);
            minnlcsetxrep(&state, ae_true, _state);
            k = -1;
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                        state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        }
                    }
                    if( k>=spoiliteration )
                    {
                        if( spoilvar<0 )
                        {
                            state.fi.ptr.p_double[0] = spoilval;
                        }
                        else
                        {
                            state.j.ptr.pp_double[0][spoilvar] = spoilval;
                        }
                    }
                    continue;
                }
                if( state.xupdated )
                {
                    inc(&k, _state);
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, rep.terminationtype!=-8, __FILE__, __LINE__, "testminnlcunit.ap:2884");
        }
        
        /*
         * Test that optimizer respects box constraints in all
         * intermediate points:
         * * test that analytic Jacobian respects them
         * * test that numerical Jacobian respects them
         *
         * NOTE: we skip SolverType=0 (AUL) because AUL optimizer
         *       does not provide such guarantee
         */
        if( solvertype!=0 )
        {
            n = 10;
            spdmatrixrndcond(n, 1.0E3, &fulla, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = hqrndnormal(&rs, _state);
                x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
                if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
                {
                    bndl.ptr.p_double[i] = (double)(0);
                    bndu.ptr.p_double[i] = _state->v_posinf;
                }
                else
                {
                    bndl.ptr.p_double[i] = _state->v_neginf;
                    bndu.ptr.p_double[i] = (double)(0);
                }
            }
            
            /*
             * Check analytic Jacobian
             */
            minnlccreate(n, &x0, &state, _state);
            if( solvertype==1 )
            {
                minnlcsetalgoslp(&state, _state);
            }
            else
            {
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcsetbc(&state, &bndl, &bndu, _state);
            minnlcsetcond(&state, 1.0E-8, 0, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(wereerrors, ae_fp_less(state.x.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminnlcunit.ap:2937");
                        ae_set_error_flag(wereerrors, ae_fp_greater(state.x.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminnlcunit.ap:2938");
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                        state.j.ptr.pp_double[0][i] = b.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                            state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:2952");
            
            /*
             * Check numerical Jacobian
             */
            minnlccreatef(n, &x0, 1.0E-4, &state, _state);
            if( solvertype==1 )
            {
                minnlcsetalgoslp(&state, _state);
            }
            else
            {
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcsetbc(&state, &bndl, &bndu, _state);
            minnlcsetcond(&state, 1.0E-8, 0, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfi )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(wereerrors, ae_fp_less(state.x.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminnlcunit.ap:2971");
                        ae_set_error_flag(wereerrors, ae_fp_greater(state.x.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminnlcunit.ap:2972");
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                        for(j=0; j<=n-1; j++)
                        {
                            state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*fulla.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:2982");
        }
    }
    
    /*
     * AUL-specific test.
     *
     * Test preconditioning:
     * * compare number of iterations required to solve problem with
     *   different preconditioners (LBFGS, exact, none)
     * * a set of trials is performed (100 trials)
     * * each trial is a solution of boundary/linearly constrained problem
     *   (linear constraints may be posed as nonlinear ones) with normalized
     *   constraint matrix. Normalization is essential for reproducibility
     *   of results .
     *
     * Outer loop checks handling of different types of constraints
     * (posed as linear or nonlinear ones)
     */
    n = 30;
    blocksize = 3;
    blockcnt = 3;
    rho = 1.0E3;
    aulits = 5;
    condv = 1.0E2;
    ae_vector_set_length(&x0, n, _state);
    ae_vector_set_length(&bndl, n, _state);
    ae_vector_set_length(&bndu, n, _state);
    ae_matrix_set_length(&c, blocksize*blockcnt, n+1, _state);
    ae_vector_set_length(&ct, blocksize*blockcnt, _state);
    for(ctype=0; ctype<=1; ctype++)
    {
        
        /*
         * First, initialize iteration counters
         */
        nlbfgs = 0;
        nexactlowrank = 0;
        nexactrobust = 0;
        nnone = 0;
        
        /*
         * Perform trials
         */
        for(trialidx=0; trialidx<=99; trialidx++)
        {
            
            /*
             * Generate:
             * * boundary constraints BndL/BndU and initial point X0
             * * block-diagonal matrix of linear constraints C such
             *   that X0 is feasible w.r.t. constraints given by C
             */
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
                {
                    bndl.ptr.p_double[i] = (double)(0);
                    bndu.ptr.p_double[i] = _state->v_posinf;
                    x0.ptr.p_double[i] = hqrnduniformr(&rs, _state);
                }
                else
                {
                    bndl.ptr.p_double[i] = (double)(0);
                    bndu.ptr.p_double[i] = (double)(0);
                    x0.ptr.p_double[i] = (double)(0);
                }
            }
            for(i=0; i<=blocksize*blockcnt-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[i][j] = 0.0;
                }
            }
            for(k=0; k<=blockcnt-1; k++)
            {
                rmatrixrndcond(blocksize, condv, &a, _state);
                for(i=0; i<=blocksize-1; i++)
                {
                    for(j=0; j<=blocksize-1; j++)
                    {
                        c.ptr.pp_double[k*blocksize+i][k*blocksize+j] = a.ptr.pp_double[i][j];
                    }
                }
            }
            for(i=0; i<=blocksize*blockcnt-1; i++)
            {
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                v = 1/ae_sqrt(v, _state);
                ae_v_muld(&c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
                v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x0.ptr.p_double[0], 1, ae_v_len(0,n-1));
                c.ptr.pp_double[i][n] = v;
                ct.ptr.p_int[i] = hqrnduniformi(&rs, 3, _state)-1;
            }
            
            /*
             * Test unpreconditioned iteration
             */
            minnlccreate(n, &x0, &state, _state);
            minnlcsetalgoaul(&state, rho, aulits, _state);
            minnlcsetcond(&state, 1.0E-9, 0, _state);
            if( ctype==0 )
            {
                minnlcsetlc(&state, &c, &ct, blocksize*blockcnt, _state);
            }
            else
            {
                minnlcsetnlc(&state, blocksize*blockcnt, 0, _state);
            }
            minnlcsetprecnone(&state, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+ae_sqr(state.x.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = 2*state.x.ptr.p_double[i];
                    }
                    if( ctype==1 )
                    {
                        for(i=0; i<=blocksize*blockcnt-1; i++)
                        {
                            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            state.fi.ptr.p_double[1+i] = v-c.ptr.pp_double[i][n];
                            ae_v_move(&state.j.ptr.pp_double[1+i][0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:3106");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:3107");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            nnone = nnone+rep.iterationscount;
            
            /*
             * Test LBFGS preconditioned iteration
             */
            minnlccreate(n, &x0, &state, _state);
            minnlcsetalgoaul(&state, rho, aulits, _state);
            minnlcsetcond(&state, 1.0E-9, 0, _state);
            if( ctype==0 )
            {
                minnlcsetlc(&state, &c, &ct, blocksize*blockcnt, _state);
            }
            else
            {
                minnlcsetnlc(&state, blocksize*blockcnt, 0, _state);
            }
            minnlcsetprecinexact(&state, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+ae_sqr(state.x.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = 2*state.x.ptr.p_double[i];
                    }
                    if( ctype==1 )
                    {
                        for(i=0; i<=blocksize*blockcnt-1; i++)
                        {
                            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            state.fi.ptr.p_double[1+i] = v-c.ptr.pp_double[i][n];
                            ae_v_move(&state.j.ptr.pp_double[1+i][0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:3147");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:3148");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            nlbfgs = nlbfgs+rep.iterationscount;
            
            /*
             * Test exact low rank preconditioner
             */
            minnlccreate(n, &x0, &state, _state);
            minnlcsetalgoaul(&state, rho, aulits, _state);
            minnlcsetcond(&state, 1.0E-9, 0, _state);
            if( ctype==0 )
            {
                minnlcsetlc(&state, &c, &ct, blocksize*blockcnt, _state);
            }
            else
            {
                minnlcsetnlc(&state, blocksize*blockcnt, 0, _state);
            }
            minnlcsetprecexactlowrank(&state, 3, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+ae_sqr(state.x.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = 2*state.x.ptr.p_double[i];
                    }
                    if( ctype==1 )
                    {
                        for(i=0; i<=blocksize*blockcnt-1; i++)
                        {
                            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            state.fi.ptr.p_double[1+i] = v-c.ptr.pp_double[i][n];
                            ae_v_move(&state.j.ptr.pp_double[1+i][0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:3188");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:3189");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            nexactlowrank = nexactlowrank+rep.iterationscount;
            
            /*
             * Test exact robust preconditioner
             */
            minnlccreate(n, &x0, &state, _state);
            minnlcsetalgoaul(&state, rho, aulits, _state);
            minnlcsetcond(&state, 1.0E-9, 0, _state);
            if( ctype==0 )
            {
                minnlcsetlc(&state, &c, &ct, blocksize*blockcnt, _state);
            }
            else
            {
                minnlcsetnlc(&state, blocksize*blockcnt, 0, _state);
            }
            minnlcsetprecexactrobust(&state, 3, _state);
            while(minnlciteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+ae_sqr(state.x.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = 2*state.x.ptr.p_double[i];
                    }
                    if( ctype==1 )
                    {
                        for(i=0; i<=blocksize*blockcnt-1; i++)
                        {
                            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            state.fi.ptr.p_double[1+i] = v-c.ptr.pp_double[i][n];
                            ae_v_move(&state.j.ptr.pp_double[1+i][0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                        }
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnlcresults(&state, &x1, &rep, _state);
            ae_set_error_flag(wereerrors, !isfinitevector(&x1, n, _state), __FILE__, __LINE__, "testminnlcunit.ap:3229");
            ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:3230");
            if( *wereerrors )
            {
                ae_frame_leave(_state);
                return;
            }
            nexactrobust = nexactrobust+rep.iterationscount;
        }
        
        /*
         * Compare.
         *
         * Preconditioners must be significantly different,
         * with exact being best one, inexact being second,
         * "none" being worst option.
         */
        ae_set_error_flag(wereerrors, !ae_fp_less((double)(nexactlowrank),0.9*nlbfgs), __FILE__, __LINE__, "testminnlcunit.ap:3243");
        ae_set_error_flag(wereerrors, !ae_fp_less((double)(nexactrobust),0.9*nlbfgs), __FILE__, __LINE__, "testminnlcunit.ap:3244");
        ae_set_error_flag(wereerrors, !ae_fp_less((double)(nlbfgs),0.9*nnone), __FILE__, __LINE__, "testminnlcunit.ap:3245");
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function performs tests for fixed bugs

On failure sets error flag.
*************************************************************************/
static void testminnlcunit_testbugs(ae_bool* wereerrors, ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    ae_int_t n;
    ae_int_t aulits;
    ae_int_t maxits;
    double rho;
    ae_int_t ckind;
    minnlcstate state;
    minnlcreport rep;
    ae_vector x0;
    ae_vector x1;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector ct;
    ae_matrix c;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&ct, 0, sizeof(ct));
    memset(&c, 0, sizeof(c));
    _hqrndstate_init(&rs, _state, ae_true);
    _minnlcstate_init(&state, _state, ae_true);
    _minnlcreport_init(&rep, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Bug description (fixed): sometimes on non-convex problems, when
     * Lagrange coefficient for inequality constraint becomes small,
     * algorithm performs VERY deep step into infeasible area (step is 1E50),
     * which de-stabilizes it and prevents from converging back to feasible area.
     *
     * Very rare situation, but must be fixed with additional "convexifying" term.
     * This test reproduces situation with convexified term turned off, then
     * checks that introduction of term solves issue.
     *
     * We perform three kinds of tests:
     * * with box inequality constraint
     * * with linear inequality constraint
     * * with nonlinear inequality constraint
     *
     * In all three cases we:
     * * first time solve non-convex problem with artificially moved stabilizing
     *   point and decreased initial value of Lagrange multiplier.
     * * second time we solve problem with good stabilizing point, but zero Lagrange multiplier
     * * last time solve same problem, but with default settings
     */
    aulits = 1;
    maxits = 1;
    rho = 100.0;
    n = 1;
    ae_vector_set_length(&x0, n, _state);
    x0.ptr.p_double[0] = 0.0;
    ae_vector_set_length(&bndl, n, _state);
    ae_vector_set_length(&bndu, n, _state);
    bndl.ptr.p_double[0] = 0.0;
    bndu.ptr.p_double[0] = _state->v_posinf;
    ae_matrix_set_length(&c, 1, 2, _state);
    ae_vector_set_length(&ct, 1, _state);
    c.ptr.pp_double[0][0] = 1.0;
    c.ptr.pp_double[0][1] = 0.0;
    ct.ptr.p_int[0] = 1;
    for(ckind=0; ckind<=2; ckind++)
    {
        minnlccreate(n, &x0, &state, _state);
        state.stabilizingpoint = -1.0E300;
        state.initialinequalitymultiplier = 1.0E-12;
        minnlcsetalgoaul(&state, rho, aulits, _state);
        minnlcsetcond(&state, 0.0, maxits, _state);
        if( ckind==0 )
        {
            minnlcsetbc(&state, &bndl, &bndu, _state);
        }
        if( ckind==1 )
        {
            minnlcsetlc(&state, &c, &ct, 1, _state);
        }
        if( ckind==2 )
        {
            minnlcsetnlc(&state, 0, 1, _state);
        }
        while(minnlciteration(&state, _state))
        {
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = state.x.ptr.p_double[0]-ae_sqr(state.x.ptr.p_double[0], _state);
                state.j.ptr.pp_double[0][0] = 1-2*state.x.ptr.p_double[0];
                if( ckind==2 )
                {
                    state.fi.ptr.p_double[1] = -state.x.ptr.p_double[0];
                    state.j.ptr.pp_double[1][0] = (double)(-1);
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnlcresults(&state, &x1, &rep, _state);
        ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:3341");
        if( *wereerrors )
        {
            ae_frame_leave(_state);
            return;
        }
        ae_set_error_flag(wereerrors, ae_fp_greater(x1.ptr.p_double[0],-1.0E6), __FILE__, __LINE__, "testminnlcunit.ap:3344");
        minnlccreate(n, &x0, &state, _state);
        state.stabilizingpoint = -1.0E2;
        state.initialinequalitymultiplier = 1.0E-12;
        minnlcsetalgoaul(&state, rho, aulits, _state);
        minnlcsetcond(&state, 0.0, maxits, _state);
        if( ckind==0 )
        {
            minnlcsetbc(&state, &bndl, &bndu, _state);
        }
        if( ckind==1 )
        {
            minnlcsetlc(&state, &c, &ct, 1, _state);
        }
        if( ckind==2 )
        {
            minnlcsetnlc(&state, 0, 1, _state);
        }
        while(minnlciteration(&state, _state))
        {
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = state.x.ptr.p_double[0]-ae_sqr(state.x.ptr.p_double[0], _state);
                state.j.ptr.pp_double[0][0] = 1-2*state.x.ptr.p_double[0];
                if( ckind==2 )
                {
                    state.fi.ptr.p_double[1] = -state.x.ptr.p_double[0];
                    state.j.ptr.pp_double[1][0] = (double)(-1);
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnlcresults(&state, &x1, &rep, _state);
        ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:3373");
        if( *wereerrors )
        {
            ae_frame_leave(_state);
            return;
        }
        ae_set_error_flag(wereerrors, ae_fp_less(x1.ptr.p_double[0],3*state.stabilizingpoint), __FILE__, __LINE__, "testminnlcunit.ap:3376");
        minnlccreate(n, &x0, &state, _state);
        minnlcsetalgoaul(&state, rho, aulits, _state);
        minnlcsetcond(&state, 0.0, maxits, _state);
        if( ckind==0 )
        {
            minnlcsetbc(&state, &bndl, &bndu, _state);
        }
        if( ckind==1 )
        {
            minnlcsetlc(&state, &c, &ct, 1, _state);
        }
        if( ckind==2 )
        {
            minnlcsetnlc(&state, 0, 1, _state);
        }
        while(minnlciteration(&state, _state))
        {
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = state.x.ptr.p_double[0]-ae_sqr(state.x.ptr.p_double[0], _state);
                state.j.ptr.pp_double[0][0] = 1-2*state.x.ptr.p_double[0];
                if( ckind==2 )
                {
                    state.fi.ptr.p_double[1] = -state.x.ptr.p_double[0];
                    state.j.ptr.pp_double[1][0] = (double)(-1);
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnlcresults(&state, &x1, &rep, _state);
        ae_set_error_flag(wereerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnlcunit.ap:3403");
        if( *wereerrors )
        {
            ae_frame_leave(_state);
            return;
        }
        ae_set_error_flag(wereerrors, ae_fp_less(x1.ptr.p_double[0],3*state.stabilizingpoint), __FILE__, __LINE__, "testminnlcunit.ap:3406");
    }
    ae_frame_leave(_state);
}



static double testminnsunit_scalingtesttol = 1.0E-6;
static ae_int_t testminnsunit_scalingtestcnt = 5;
static void testminnsunit_basictest0uc(ae_bool* errors, ae_state *_state);
static void testminnsunit_basictest1uc(ae_bool* errors, ae_state *_state);
static void testminnsunit_basictest0bc(ae_bool* errors, ae_state *_state);
static void testminnsunit_basictest1bc(ae_bool* errors, ae_state *_state);
static void testminnsunit_basictest0lc(ae_bool* errors, ae_state *_state);
static void testminnsunit_basictest1lc(ae_bool* errors, ae_state *_state);
static void testminnsunit_basictest0nlc(ae_bool* errors, ae_state *_state);
static void testminnsunit_testuc(ae_bool* primaryerrors,
     ae_bool* othererrors,
     ae_state *_state);
static void testminnsunit_testbc(ae_bool* primaryerrors,
     ae_bool* othererrors,
     ae_state *_state);
static void testminnsunit_testlc(ae_bool* primaryerrors,
     ae_bool* othererrors,
     ae_state *_state);
static void testminnsunit_testnlc(ae_bool* primaryerrors,
     ae_bool* othererrors,
     ae_state *_state);
static void testminnsunit_testother(ae_bool* othererrors,
     ae_state *_state);





ae_bool testminns(ae_bool silent, ae_state *_state)
{
    ae_bool wereerrors;
    ae_bool ucerrors;
    ae_bool bcerrors;
    ae_bool lcerrors;
    ae_bool nlcerrors;
    ae_bool othererrors;
    ae_bool result;


    wereerrors = ae_false;
    ucerrors = ae_false;
    bcerrors = ae_false;
    lcerrors = ae_false;
    nlcerrors = ae_false;
    othererrors = ae_false;
    
    /*
     * Basic tests
     */
    testminnsunit_basictest0nlc(&nlcerrors, _state);
    testminnsunit_basictest0uc(&ucerrors, _state);
    testminnsunit_basictest1uc(&ucerrors, _state);
    testminnsunit_basictest0bc(&bcerrors, _state);
    testminnsunit_basictest1bc(&bcerrors, _state);
    testminnsunit_basictest0lc(&lcerrors, _state);
    testminnsunit_basictest1lc(&lcerrors, _state);
    
    /*
     * Special tests
     */
    testminnsunit_testother(&othererrors, _state);
    
    /*
     * Full scale tests
     */
    testminnsunit_testuc(&ucerrors, &othererrors, _state);
    testminnsunit_testbc(&bcerrors, &othererrors, _state);
    testminnsunit_testlc(&lcerrors, &othererrors, _state);
    testminnsunit_testnlc(&nlcerrors, &othererrors, _state);
    
    /*
     * end
     */
    wereerrors = (((ucerrors||bcerrors)||lcerrors)||nlcerrors)||othererrors;
    if( !silent )
    {
        printf("TESTING MINNS OPTIMIZATION\n");
        printf("TESTS:\n");
        printf("* UNCONSTRAINED                           ");
        if( ucerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* BOUND CONSTRAINED                       ");
        if( bcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* LINEARLY CONSTRAINED                    ");
        if( lcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* NONLINEARLY CONSTRAINED                 ");
        if( nlcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* OTHER PROPERTIES                        ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( wereerrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !wereerrors;
    return result;
}


/*************************************************************************
Basic unconstrained test
*************************************************************************/
static void testminnsunit_basictest0uc(ae_bool* errors, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_vector x0;
    ae_vector x1;
    ae_vector d;
    minnsstate s;
    minnsreport rep;
    double sumits;
    double sumnfev;
    ae_int_t pass;
    ae_int_t passcount;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&d, 0, sizeof(d));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&s, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    n = 5;
    passcount = 10;
    sumits = (double)(0);
    sumnfev = (double)(0);
    ae_vector_set_length(&x0, n, _state);
    ae_vector_set_length(&d, n, _state);
    for(pass=1; pass<=10; pass++)
    {
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            d.ptr.p_double[i] = ae_pow((double)(10), 2*ae_randomreal(_state)-1, _state);
        }
        minnscreate(n, &x0, &s, _state);
        minnssetalgoags(&s, 0.1, 0.0, _state);
        while(minnsiteration(&s, _state))
        {
            if( s.needfij )
            {
                s.fi.ptr.p_double[0] = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    s.fi.ptr.p_double[0] = s.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(s.x.ptr.p_double[i], _state);
                    s.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(s.x.ptr.p_double[i], _state);
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnsresults(&s, &x1, &rep, _state);
        ae_set_error_flag(errors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:143");
        if( *errors )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i], _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:147");
        }
        sumits = sumits+(double)rep.iterationscount/(double)passcount;
        sumnfev = sumnfev+(double)rep.nfev/(double)passcount;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic unconstrained test: nonsmooth Rosenbrock posed as unconstrained problem.

             [                                                                       ]
    minimize [ 10*|x0^2-x1| + (1-x0)^2 + 100*max(sqrt(2)*x0-1,0) + 100*max(2*x1-1,0) ]
             [                                                                       ]

It's exact solution is x0=1/sqrt(2), x1=1/2
*************************************************************************/
static void testminnsunit_basictest1uc(ae_bool* errors, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    double v0;
    double v1;
    ae_vector x0;
    ae_vector x1;
    minnsstate s;
    minnsreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&s, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    n = 2;
    ae_vector_set_length(&x0, n, _state);
    x0.ptr.p_double[0] = (double)(0);
    x0.ptr.p_double[1] = (double)(0);
    minnscreate(n, &x0, &s, _state);
    minnssetalgoags(&s, 0.1, 0.0, _state);
    while(minnsiteration(&s, _state))
    {
        if( s.needfij )
        {
            v0 = s.x.ptr.p_double[0];
            v1 = s.x.ptr.p_double[1];
            s.fi.ptr.p_double[0] = 10*ae_fabs(ae_sqr(v0, _state)-v1, _state)+ae_sqr(v0-1, _state);
            s.j.ptr.pp_double[0][0] = 10*ae_sign(ae_sqr(v0, _state)-v1, _state)*2*v0+2*(v0-1);
            s.j.ptr.pp_double[0][1] = (double)(10*ae_sign(ae_sqr(v0, _state)-v1, _state)*(-1));
            if( ae_fp_greater(ae_sqrt((double)(2), _state)*v0-1,0.0) )
            {
                s.fi.ptr.p_double[0] = s.fi.ptr.p_double[0]+100*(ae_sqrt((double)(2), _state)*v0-1);
                s.j.ptr.pp_double[0][0] = s.j.ptr.pp_double[0][0]+100*ae_sqrt((double)(2), _state);
            }
            if( ae_fp_greater(2*v1-1,0.0) )
            {
                s.fi.ptr.p_double[0] = s.fi.ptr.p_double[0]+100*(2*v1-1);
                s.j.ptr.pp_double[0][1] = s.j.ptr.pp_double[0][1]+100*2;
            }
            continue;
        }
        ae_assert(ae_false, "Assertion failed", _state);
    }
    minnsresults(&s, &x1, &rep, _state);
    ae_set_error_flag(errors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:204");
    if( *errors )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[0], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[0]-1/ae_sqrt((double)(2), _state), _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:207");
    ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[1], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[1]-(double)1/(double)2, _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:208");
    ae_frame_leave(_state);
}


/*************************************************************************
Basic box constrained test
*************************************************************************/
static void testminnsunit_basictest0bc(ae_bool* errors, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_vector x0;
    ae_vector x1;
    ae_vector d;
    ae_vector bl;
    ae_vector bu;
    minnsstate s;
    minnsreport rep;
    double sumits;
    double sumnfev;
    ae_int_t pass;
    ae_int_t passcount;
    double v0;
    double v1;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&d, 0, sizeof(d));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&s, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    n = 5;
    passcount = 10;
    sumits = (double)(0);
    sumnfev = (double)(0);
    ae_vector_set_length(&x0, n, _state);
    ae_vector_set_length(&bl, n, _state);
    ae_vector_set_length(&bu, n, _state);
    ae_vector_set_length(&d, n, _state);
    for(pass=1; pass<=10; pass++)
    {
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            d.ptr.p_double[i] = ae_pow((double)(10), 2*ae_randomreal(_state)-1, _state);
            v0 = 2*ae_randomreal(_state)-1;
            v1 = 2*ae_randomreal(_state)-1;
            bl.ptr.p_double[i] = ae_minreal(v0, v1, _state);
            bu.ptr.p_double[i] = ae_maxreal(v0, v1, _state);
        }
        minnscreate(n, &x0, &s, _state);
        minnssetalgoags(&s, 0.1, 0.0, _state);
        minnssetbc(&s, &bl, &bu, _state);
        while(minnsiteration(&s, _state))
        {
            if( s.needfij )
            {
                s.fi.ptr.p_double[0] = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    s.fi.ptr.p_double[0] = s.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(s.x.ptr.p_double[i], _state);
                    s.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(s.x.ptr.p_double[i], _state);
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnsresults(&s, &x1, &rep, _state);
        ae_set_error_flag(errors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:263");
        if( *errors )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-boundval(0.0, bl.ptr.p_double[i], bu.ptr.p_double[i], _state), _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:267");
        }
        sumits = sumits+(double)rep.iterationscount/(double)passcount;
        sumnfev = sumnfev+(double)rep.nfev/(double)passcount;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic constrained test: nonsmooth Rosenbrock posed as box constrained problem.

             [                         ]
    minimize [ 10*|x0^2-x1| + (1-x0)^2 ]
             [                         ]
             
    s.t. x0<=1/sqrt(2), x1<=0.5

It's exact solution is x0=1/sqrt(2), x1=1/2
*************************************************************************/
static void testminnsunit_basictest1bc(ae_bool* errors, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    double v0;
    double v1;
    ae_vector x0;
    ae_vector x1;
    ae_vector bndl;
    ae_vector bndu;
    minnsstate s;
    minnsreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&s, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    n = 2;
    ae_vector_set_length(&x0, n, _state);
    ae_vector_set_length(&bndl, n, _state);
    ae_vector_set_length(&bndu, n, _state);
    x0.ptr.p_double[0] = (double)(0);
    x0.ptr.p_double[1] = (double)(0);
    bndl.ptr.p_double[0] = _state->v_neginf;
    bndl.ptr.p_double[1] = _state->v_neginf;
    bndu.ptr.p_double[0] = 1/ae_sqrt((double)(2), _state);
    bndu.ptr.p_double[1] = (double)1/(double)2;
    minnscreate(n, &x0, &s, _state);
    minnssetbc(&s, &bndl, &bndu, _state);
    minnssetalgoags(&s, 0.1, 0.0, _state);
    while(minnsiteration(&s, _state))
    {
        if( s.needfij )
        {
            v0 = s.x.ptr.p_double[0];
            v1 = s.x.ptr.p_double[1];
            s.fi.ptr.p_double[0] = 10*ae_fabs(ae_sqr(v0, _state)-v1, _state)+ae_sqr(v0-1, _state);
            s.j.ptr.pp_double[0][0] = 10*ae_sign(ae_sqr(v0, _state)-v1, _state)*2*v0+2*(v0-1);
            s.j.ptr.pp_double[0][1] = (double)(10*ae_sign(ae_sqr(v0, _state)-v1, _state)*(-1));
            continue;
        }
        ae_assert(ae_false, "Assertion failed", _state);
    }
    minnsresults(&s, &x1, &rep, _state);
    ae_set_error_flag(errors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:323");
    if( *errors )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[0], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[0]-1/ae_sqrt((double)(2), _state), _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:326");
    ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[1], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[1]-(double)1/(double)2, _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:327");
    ae_frame_leave(_state);
}


/*************************************************************************
Basic linearly constrained test
*************************************************************************/
static void testminnsunit_basictest0lc(ae_bool* errors, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_vector x0;
    ae_vector x1;
    ae_matrix c;
    ae_vector ct;
    double d;
    minnsstate s;
    minnsreport rep;
    double sumits;
    double sumnfev;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t nc;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minnsstate_init(&s, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    d = -10.0;
    n = 5;
    passcount = 10;
    sumits = (double)(0);
    sumnfev = (double)(0);
    ae_vector_set_length(&x0, n, _state);
    ae_matrix_set_length(&c, 2*n, n+1, _state);
    ae_vector_set_length(&ct, 2*n, _state);
    for(pass=1; pass<=10; pass++)
    {
        nc = 0;
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            if( ae_fp_less(ae_randomreal(_state),0.5) )
            {
                for(j=0; j<=n; j++)
                {
                    c.ptr.pp_double[nc][j] = 0.0;
                }
                c.ptr.pp_double[nc][i] = 1.0+ae_randomreal(_state);
                ct.ptr.p_int[nc] = 0;
                inc(&nc, _state);
            }
            else
            {
                for(j=0; j<=n; j++)
                {
                    c.ptr.pp_double[nc+0][j] = 0.0;
                    c.ptr.pp_double[nc+1][j] = 0.0;
                }
                c.ptr.pp_double[nc+0][i] = 1.0+ae_randomreal(_state);
                c.ptr.pp_double[nc+1][i] = 1.0+ae_randomreal(_state);
                ct.ptr.p_int[nc+0] = 1;
                ct.ptr.p_int[nc+1] = -1;
                nc = nc+2;
            }
        }
        minnscreate(n, &x0, &s, _state);
        minnssetalgoags(&s, 0.1, 0.0, _state);
        minnssetlc(&s, &c, &ct, nc, _state);
        while(minnsiteration(&s, _state))
        {
            if( s.needfij )
            {
                s.fi.ptr.p_double[0] = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    s.fi.ptr.p_double[0] = d*ae_sqr(s.x.ptr.p_double[i], _state);
                    s.j.ptr.pp_double[0][i] = d*2*s.x.ptr.p_double[i];
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnsresults(&s, &x1, &rep, _state);
        ae_set_error_flag(errors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:404");
        if( *errors )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i], _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:408");
        }
        sumits = sumits+(double)rep.iterationscount/(double)passcount;
        sumnfev = sumnfev+(double)rep.nfev/(double)passcount;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic constrained test: nonsmooth Rosenbrock posed as linearly constrained problem.

             [                         ]
    minimize [ 10*|x0^2-x1| + (1-x0)^2 ]
             [                         ]
             
    s.t. x0<=1/sqrt(2), x1<=0.5

It's exact solution is x0=1/sqrt(2), x1=1/2
*************************************************************************/
static void testminnsunit_basictest1lc(ae_bool* errors, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    double v0;
    double v1;
    ae_vector x0;
    ae_vector x1;
    ae_matrix c;
    ae_vector ct;
    minnsstate s;
    minnsreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&c, 0, sizeof(c));
    memset(&ct, 0, sizeof(ct));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minnsstate_init(&s, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    n = 2;
    ae_vector_set_length(&x0, n, _state);
    ae_matrix_set_length(&c, 2, n+1, _state);
    ae_vector_set_length(&ct, 2, _state);
    x0.ptr.p_double[0] = (double)(0);
    x0.ptr.p_double[1] = (double)(0);
    c.ptr.pp_double[0][0] = 1.0;
    c.ptr.pp_double[0][1] = 0.0;
    c.ptr.pp_double[0][2] = 1/ae_sqrt((double)(2), _state);
    c.ptr.pp_double[1][0] = 0.0;
    c.ptr.pp_double[1][1] = 1.0;
    c.ptr.pp_double[1][2] = (double)1/(double)2;
    ct.ptr.p_int[0] = -1;
    ct.ptr.p_int[1] = -1;
    minnscreate(n, &x0, &s, _state);
    minnssetlc(&s, &c, &ct, 2, _state);
    minnssetalgoags(&s, 0.1, 0.0, _state);
    while(minnsiteration(&s, _state))
    {
        if( s.needfij )
        {
            v0 = s.x.ptr.p_double[0];
            v1 = s.x.ptr.p_double[1];
            s.fi.ptr.p_double[0] = 10*ae_fabs(ae_sqr(v0, _state)-v1, _state)+ae_sqr(v0-1, _state);
            s.j.ptr.pp_double[0][0] = 10*ae_sign(ae_sqr(v0, _state)-v1, _state)*2*v0+2*(v0-1);
            s.j.ptr.pp_double[0][1] = (double)(10*ae_sign(ae_sqr(v0, _state)-v1, _state)*(-1));
            continue;
        }
        ae_assert(ae_false, "Assertion failed", _state);
    }
    minnsresults(&s, &x1, &rep, _state);
    ae_set_error_flag(errors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:470");
    if( *errors )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[0], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[0]-1/ae_sqrt((double)(2), _state), _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:473");
    ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[1], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[1]-(double)1/(double)2, _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:474");
    ae_frame_leave(_state);
}


/*************************************************************************
Basic nonlinearly constrained test
*************************************************************************/
static void testminnsunit_basictest0nlc(ae_bool* errors, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_vector x0;
    ae_vector x1;
    ae_matrix ec;
    ae_matrix ic;
    ae_int_t nec;
    ae_int_t nic;
    double d;
    minnsstate s;
    minnsreport rep;
    double sumits;
    double sumnfev;
    ae_int_t pass;
    ae_int_t passcount;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&ec, 0, sizeof(ec));
    memset(&ic, 0, sizeof(ic));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ec, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ic, 0, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&s, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    d = -10.0;
    n = 5;
    passcount = 10;
    sumits = (double)(0);
    sumnfev = (double)(0);
    ae_vector_set_length(&x0, n, _state);
    ae_matrix_set_length(&ec, 2*n, n+1, _state);
    ae_matrix_set_length(&ic, 2*n, n+1, _state);
    for(pass=1; pass<=10; pass++)
    {
        nec = 0;
        nic = 0;
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            if( ae_fp_less(ae_randomreal(_state),0.5) )
            {
                for(j=0; j<=n; j++)
                {
                    ec.ptr.pp_double[nec][j] = 0.0;
                }
                ec.ptr.pp_double[nec][i] = 1.0+ae_randomreal(_state);
                inc(&nec, _state);
            }
            else
            {
                for(j=0; j<=n; j++)
                {
                    ic.ptr.pp_double[nic+0][j] = 0.0;
                    ic.ptr.pp_double[nic+1][j] = 0.0;
                }
                ic.ptr.pp_double[nic+0][i] = 1.0+ae_randomreal(_state);
                ic.ptr.pp_double[nic+1][i] = -1.0-ae_randomreal(_state);
                nic = nic+2;
            }
        }
        minnscreate(n, &x0, &s, _state);
        minnssetalgoags(&s, 0.1, 100.0, _state);
        minnssetnlc(&s, nec, nic, _state);
        while(minnsiteration(&s, _state))
        {
            if( s.needfij )
            {
                s.fi.ptr.p_double[0] = 0.0;
                for(j=0; j<=n-1; j++)
                {
                    s.fi.ptr.p_double[0] = d*ae_sqr(s.x.ptr.p_double[j], _state);
                    s.j.ptr.pp_double[0][j] = d*2*s.x.ptr.p_double[j];
                }
                for(i=0; i<=nec-1; i++)
                {
                    s.fi.ptr.p_double[1+i] = -ec.ptr.pp_double[i][n];
                    for(j=0; j<=n-1; j++)
                    {
                        s.fi.ptr.p_double[1+i] = s.fi.ptr.p_double[1+i]+s.x.ptr.p_double[j]*ec.ptr.pp_double[i][j];
                        s.j.ptr.pp_double[1+i][j] = ec.ptr.pp_double[i][j];
                    }
                }
                for(i=0; i<=nic-1; i++)
                {
                    s.fi.ptr.p_double[1+nec+i] = -ic.ptr.pp_double[i][n];
                    for(j=0; j<=n-1; j++)
                    {
                        s.fi.ptr.p_double[1+nec+i] = s.fi.ptr.p_double[1+nec+i]+s.x.ptr.p_double[j]*ic.ptr.pp_double[i][j];
                        s.j.ptr.pp_double[1+nec+i][j] = ic.ptr.pp_double[i][j];
                    }
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnsresults(&s, &x1, &rep, _state);
        ae_set_error_flag(errors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:567");
        if( *errors )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errors, !ae_isfinite(x1.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i], _state),0.001), __FILE__, __LINE__, "testminnsunit.ap:571");
        }
        sumits = sumits+(double)rep.iterationscount/(double)passcount;
        sumnfev = sumnfev+(double)rep.nfev/(double)passcount;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Unconstrained test
*************************************************************************/
static void testminnsunit_testuc(ae_bool* primaryerrors,
     ae_bool* othererrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_vector x0;
    ae_vector x0s;
    ae_vector x1;
    ae_vector x1s;
    ae_vector d;
    ae_vector xc;
    ae_vector s;
    ae_vector xrfirst;
    ae_vector xrlast;
    minnsstate state;
    minnsreport rep;
    double v;
    ae_int_t pass;
    ae_bool requirexrep;
    double epsrad;
    ae_bool werexreports;
    double repferr;
    double xtol;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x0s, 0, sizeof(x0s));
    memset(&x1, 0, sizeof(x1));
    memset(&x1s, 0, sizeof(x1s));
    memset(&d, 0, sizeof(d));
    memset(&xc, 0, sizeof(xc));
    memset(&s, 0, sizeof(s));
    memset(&xrfirst, 0, sizeof(xrfirst));
    memset(&xrlast, 0, sizeof(xrlast));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xrfirst, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xrlast, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&state, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    for(pass=1; pass<=10; pass++)
    {
        for(n=1; n<=5; n++)
        {
            
            /*
             * First test:
             * * test that problem is successfully solved
             * * test that X-reports are performed correctly - present
             *   when requested, return first and last points correctly,
             *   not present by default, function value is reported
             *   correctly.
             * * we use non-unit scale, randomly chosen one, which results
             *   in badly conditioned problems (to check robustness)
             */
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&s, n, _state);
            ae_vector_set_length(&xrfirst, n, _state);
            ae_vector_set_length(&xrlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 10*(2*ae_randomreal(_state)-1);
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                d.ptr.p_double[i] = ae_pow((double)(10), 2*(2*ae_randomreal(_state)-1), _state);
                s.ptr.p_double[i] = ae_pow((double)(10), 2*(2*ae_randomreal(_state)-1), _state);
            }
            requirexrep = ae_fp_greater(ae_randomreal(_state),0.5);
            epsrad = 0.01*ae_pow((double)(10), -2*ae_randomreal(_state), _state);
            xtol = 15.0*epsrad;
            minnscreate(n, &x0, &state, _state);
            minnssetalgoags(&state, 0.1, 0.0, _state);
            minnssetcond(&state, epsrad, 0, _state);
            minnssetscale(&state, &s, _state);
            if( requirexrep )
            {
                minnssetxrep(&state, ae_true, _state);
            }
            werexreports = ae_false;
            repferr = 0.0;
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    continue;
                }
                if( state.xupdated )
                {
                    if( !werexreports )
                    {
                        ae_v_move(&xrfirst.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    }
                    ae_v_move(&xrlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    werexreports = ae_true;
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    repferr = ae_maxreal(repferr, ae_fabs(v-state.f, _state), _state);
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:668");
            ae_set_error_flag(othererrors, werexreports&&!requirexrep, __FILE__, __LINE__, "testminnsunit.ap:669");
            ae_set_error_flag(othererrors, requirexrep&&!werexreports, __FILE__, __LINE__, "testminnsunit.ap:670");
            ae_set_error_flag(othererrors, ae_fp_greater(repferr,10000*ae_machineepsilon), __FILE__, __LINE__, "testminnsunit.ap:671");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(primaryerrors, !ae_isfinite(x1.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-xc.ptr.p_double[i], _state)/s.ptr.p_double[i],xtol), __FILE__, __LINE__, "testminnsunit.ap:676");
                if( requirexrep )
                {
                    ae_set_error_flag(othererrors, !ae_isfinite(xrfirst.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x0.ptr.p_double[i]-xrfirst.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testminnsunit.ap:679");
                    ae_set_error_flag(othererrors, !ae_isfinite(xrlast.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-xrlast.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testminnsunit.ap:680");
                }
            }
            
            /*
             * Test numerical differentiation:
             * * test that problem is successfully solved
             * * test that correct function value is reported
             */
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&s, n, _state);
            ae_vector_set_length(&xrlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 10*(2*ae_randomreal(_state)-1);
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                d.ptr.p_double[i] = ae_pow((double)(10), 2*(2*ae_randomreal(_state)-1), _state);
                s.ptr.p_double[i] = ae_pow((double)(10), 2*(2*ae_randomreal(_state)-1), _state);
            }
            epsrad = 0.01*ae_pow((double)(10), -2*ae_randomreal(_state), _state);
            xtol = 15.0*epsrad;
            minnscreatef(n, &x0, epsrad/100, &state, _state);
            minnssetalgoags(&state, 0.1, 0.0, _state);
            minnssetcond(&state, epsrad, 0, _state);
            minnssetscale(&state, &s, _state);
            minnssetxrep(&state, ae_true, _state);
            repferr = 0.0;
            while(minnsiteration(&state, _state))
            {
                if( state.needfi )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    continue;
                }
                if( state.xupdated )
                {
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    repferr = ae_maxreal(repferr, ae_fabs(v-state.f, _state), _state);
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:731");
            ae_set_error_flag(othererrors, ae_fp_greater(repferr,10000*ae_machineepsilon), __FILE__, __LINE__, "testminnsunit.ap:732");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(primaryerrors, !ae_isfinite(x1.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-xc.ptr.p_double[i], _state)/s.ptr.p_double[i],xtol), __FILE__, __LINE__, "testminnsunit.ap:736");
            }
            
            /*
             * Test scaling: we perform several steps on unit-scale problem,
             * then we perform same amount of steps on re-scaled problem,
             * starting from same point (but scaled according to chosen scale).
             *
             * Correctly written optimizer should perform essentially same steps
             * (up to scale) on both problems. At least, it holds within first
             * several steps, before rounding errors start to accumulate.
             *
             * NOTE: we also check that correctly scaled points are reported.
             *       And, as side effect, we check MinNSRestartFrom().
             *
             * NOTE: we use moderate scale and diagonal coefficients in order
             *       to have well-conditioned system. We test correctness of 
             *       formulae here, not robustness of algorithm.
             */
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&x0s, n, _state);
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&s, n, _state);
            ae_vector_set_length(&xrlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = ae_pow((double)(10), 2*ae_randomreal(_state)-1, _state);
                d.ptr.p_double[i] = ae_pow((double)(10), 2*ae_randomreal(_state)-1, _state);
                x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x0s.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
            }
            minnscreate(n, &x0, &state, _state);
            minnssetalgoags(&state, 0.1, 0.0, _state);
            minnssetcond(&state, 0.0, testminnsunit_scalingtestcnt, _state);
            minnssetxrep(&state, ae_false, _state);
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:788");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            minnssetscale(&state, &s, _state);
            minnssetxrep(&state, ae_true, _state);
            minnsrestartfrom(&state, &x0s, _state);
            werexreports = ae_false;
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]/s.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]/s.ptr.p_double[i]-xc.ptr.p_double[i], _state)/s.ptr.p_double[i];
                    }
                    continue;
                }
                if( state.xupdated )
                {
                    ae_v_move(&xrlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    werexreports = ae_true;
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1s, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:818");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(primaryerrors, (!ae_isfinite(x1.ptr.p_double[i], _state)||!ae_isfinite(x1s.ptr.p_double[i], _state))||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x1s.ptr.p_double[i]/s.ptr.p_double[i], _state),1.0E-4), __FILE__, __LINE__, "testminnsunit.ap:823");
                ae_set_error_flag(othererrors, !ae_isfinite(xrlast.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1s.ptr.p_double[i]-xrlast.ptr.p_double[i], _state),testminnsunit_scalingtesttol), __FILE__, __LINE__, "testminnsunit.ap:827");
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Box constrained test
*************************************************************************/
static void testminnsunit_testbc(ae_bool* primaryerrors,
     ae_bool* othererrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_vector x0;
    ae_vector x0s;
    ae_vector x1;
    ae_vector x1s;
    ae_vector b;
    ae_vector d;
    ae_vector xc;
    ae_vector s;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector scaledbndl;
    ae_vector scaledbndu;
    ae_vector xrfirst;
    ae_vector xrlast;
    ae_matrix a;
    minnsstate state;
    minnsreport rep;
    double v;
    double v0;
    double v1;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool requirexrep;
    double epsrad;
    ae_bool werexreports;
    double repferr;
    double xtol;
    ae_int_t maxn;
    double conda;
    double gnorm;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x0s, 0, sizeof(x0s));
    memset(&x1, 0, sizeof(x1));
    memset(&x1s, 0, sizeof(x1s));
    memset(&b, 0, sizeof(b));
    memset(&d, 0, sizeof(d));
    memset(&xc, 0, sizeof(xc));
    memset(&s, 0, sizeof(s));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&scaledbndl, 0, sizeof(scaledbndl));
    memset(&scaledbndu, 0, sizeof(scaledbndu));
    memset(&xrfirst, 0, sizeof(xrfirst));
    memset(&xrlast, 0, sizeof(xrlast));
    memset(&a, 0, sizeof(a));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scaledbndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scaledbndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xrfirst, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xrlast, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&state, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    passcount = 10;
    maxn = 5;
    
    /*
     * First test:
     * * sparse function
     * * test that problem is successfully solved
     * * non-unit scale is used, which results in badly conditioned problem
     * * check that all iterates are feasible (box-constrained)
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&s, n, _state);
            ae_vector_set_length(&xrfirst, n, _state);
            ae_vector_set_length(&xrlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 10*(2*ae_randomreal(_state)-1);
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                d.ptr.p_double[i] = ae_pow((double)(10), 2*(2*ae_randomreal(_state)-1), _state);
                s.ptr.p_double[i] = ae_pow((double)(10), 2*(2*ae_randomreal(_state)-1), _state);
                bndl.ptr.p_double[i] = _state->v_neginf;
                bndu.ptr.p_double[i] = _state->v_posinf;
                k = ae_randominteger(5, _state);
                if( k==1 )
                {
                    bndl.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( k==2 )
                {
                    bndu.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( k==3 )
                {
                    v0 = 2*ae_randomreal(_state)-1;
                    v1 = 2*ae_randomreal(_state)-1;
                    bndl.ptr.p_double[i] = ae_minreal(v0, v1, _state);
                    bndu.ptr.p_double[i] = ae_maxreal(v0, v1, _state);
                }
                if( k==4 )
                {
                    bndl.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    bndu.ptr.p_double[i] = bndl.ptr.p_double[i];
                }
            }
            requirexrep = ae_fp_greater(ae_randomreal(_state),0.5);
            epsrad = 0.01*ae_pow((double)(10), -2*ae_randomreal(_state), _state);
            xtol = 15.0*epsrad;
            minnscreate(n, &x0, &state, _state);
            minnssetalgoags(&state, 0.1, 0.0, _state);
            minnssetcond(&state, epsrad, 0, _state);
            minnssetbc(&state, &bndl, &bndu, _state);
            minnssetscale(&state, &s, _state);
            if( requirexrep )
            {
                minnssetxrep(&state, ae_true, _state);
            }
            werexreports = ae_false;
            repferr = 0.0;
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    continue;
                }
                if( state.xupdated )
                {
                    if( !werexreports )
                    {
                        ae_v_move(&xrfirst.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    }
                    ae_v_move(&xrlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    werexreports = ae_true;
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    repferr = ae_maxreal(repferr, ae_fabs(v-state.f, _state), _state);
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(primaryerrors, ae_fp_less(state.x.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:944");
                        ae_set_error_flag(primaryerrors, ae_fp_greater(state.x.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:945");
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:953");
            ae_set_error_flag(othererrors, werexreports&&!requirexrep, __FILE__, __LINE__, "testminnsunit.ap:954");
            ae_set_error_flag(othererrors, requirexrep&&!werexreports, __FILE__, __LINE__, "testminnsunit.ap:955");
            ae_set_error_flag(othererrors, ae_fp_greater(repferr,10000*ae_machineepsilon), __FILE__, __LINE__, "testminnsunit.ap:956");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(primaryerrors, !ae_isfinite(x1.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-boundval(xc.ptr.p_double[i], bndl.ptr.p_double[i], bndu.ptr.p_double[i], _state), _state)/s.ptr.p_double[i],xtol), __FILE__, __LINE__, "testminnsunit.ap:961");
                ae_set_error_flag(primaryerrors, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:962");
                ae_set_error_flag(primaryerrors, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:963");
                if( requirexrep )
                {
                    ae_set_error_flag(othererrors, !ae_isfinite(xrfirst.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(boundval(x0.ptr.p_double[i], bndl.ptr.p_double[i], bndu.ptr.p_double[i], _state)-xrfirst.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testminnsunit.ap:966");
                    ae_set_error_flag(othererrors, !ae_isfinite(xrlast.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-xrlast.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testminnsunit.ap:967");
                }
            }
        }
    }
    
    /*
     * A bit harder test:
     * * dense quadratic function (smooth), may be prone to different
     *   rounding-related issues
     * * non-negativity box constraints
     * * unit scale is used
     * * extreme stopping criteria (EpsX=1.0E-12)
     * * single pass for each problem size
     * * check that constrained gradient at solution is small
     */
    conda = 1.0E3;
    epsrad = 1.0E-12;
    for(n=1; n<=10; n++)
    {
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&bndl, n, _state);
        ae_vector_set_length(&bndu, n, _state);
        ae_vector_set_length(&b, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 1.0;
            b.ptr.p_double[i] = ae_randomreal(_state)-0.5;
            bndl.ptr.p_double[i] = 0.0;
            bndu.ptr.p_double[i] = _state->v_posinf;
        }
        spdmatrixrndcond(n, conda, &a, _state);
        minnscreate(n, &x0, &state, _state);
        minnssetalgoags(&state, 0.1, 0.0, _state);
        minnssetcond(&state, epsrad, 0, _state);
        minnssetbc(&state, &bndl, &bndu, _state);
        while(minnsiteration(&state, _state))
        {
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    state.j.ptr.pp_double[0][i] = 0.0;
                }
                for(i=0; i<=n-1; i++)
                {
                    state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+b.ptr.p_double[i]*state.x.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+0.5*state.x.ptr.p_double[i]*a.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+b.ptr.p_double[i];
                    for(j=0; j<=n-1; j++)
                    {
                        state.j.ptr.pp_double[0][i] = state.j.ptr.pp_double[0][i]+a.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                    }
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnsresults(&state, &x1, &rep, _state);
        ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1030");
        if( *primaryerrors||(*othererrors) )
        {
            ae_frame_leave(_state);
            return;
        }
        gnorm = 0.0;
        for(i=0; i<=n-1; i++)
        {
            v = b.ptr.p_double[i];
            for(j=0; j<=n-1; j++)
            {
                v = v+a.ptr.pp_double[i][j]*x1.ptr.p_double[j];
            }
            if( ae_fp_eq(x1.ptr.p_double[i],bndl.ptr.p_double[i])&&ae_fp_greater(v,(double)(0)) )
            {
                v = (double)(0);
            }
            if( ae_fp_eq(x1.ptr.p_double[i],bndu.ptr.p_double[i])&&ae_fp_less(v,(double)(0)) )
            {
                v = (double)(0);
            }
            gnorm = gnorm+ae_sqr(v, _state);
            ae_set_error_flag(primaryerrors, ae_fp_less(x1.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:1045");
            ae_set_error_flag(primaryerrors, ae_fp_greater(x1.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:1046");
        }
        gnorm = ae_sqrt(gnorm, _state);
        ae_set_error_flag(primaryerrors, ae_fp_greater(gnorm,1.0E-5), __FILE__, __LINE__, "testminnsunit.ap:1049");
    }
    
    /*
     * Test on HIGHLY nonconvex bound constrained problem.
     * Algorithm should be able to stop.
     *
     * NOTE: because algorithm can be attracted to saddle points,
     *       x[i] may be -1, +1 or approximately zero.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = ae_randomreal(_state)-0.5;
                bndl.ptr.p_double[i] = -1.0;
                bndu.ptr.p_double[i] = 1.0;
            }
            epsrad = 0.0001;
            xtol = 15.0*epsrad;
            minnscreate(n, &x0, &state, _state);
            minnssetalgoags(&state, 0.1, 0.0, _state);
            minnssetcond(&state, epsrad, 0, _state);
            minnssetbc(&state, &bndl, &bndu, _state);
            v = -1000.0;
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v0 = ae_fabs(state.x.ptr.p_double[i], _state);
                        v1 = (double)(ae_sign(state.x.ptr.p_double[i], _state));
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+v*(v0+v0*v0);
                        state.j.ptr.pp_double[0][i] = v*(v1+2*v0*v1);
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1095");
            for(i=0; i<=n-1; i++)
            {
                v = ae_fabs(x1.ptr.p_double[i], _state);
                ae_set_error_flag(primaryerrors, !ae_isfinite(x1.ptr.p_double[i], _state), __FILE__, __LINE__, "testminnsunit.ap:1099");
                ae_set_error_flag(primaryerrors, ae_fp_neq(v,1.0)&&ae_fp_greater(v,xtol), __FILE__, __LINE__, "testminnsunit.ap:1100");
            }
        }
    }
    
    /*
     * Test numerical differentiation:
     * * test that problem is successfully solved
     * * test that correct function value is reported
     * * test that all iterates are within bound-constrained area
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&s, n, _state);
            ae_vector_set_length(&xrlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 10*(2*ae_randomreal(_state)-1);
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                d.ptr.p_double[i] = ae_pow((double)(10), 2*(2*ae_randomreal(_state)-1), _state);
                s.ptr.p_double[i] = ae_pow((double)(10), 2*(2*ae_randomreal(_state)-1), _state);
                bndl.ptr.p_double[i] = _state->v_neginf;
                bndu.ptr.p_double[i] = _state->v_posinf;
                k = ae_randominteger(5, _state);
                if( k==1 )
                {
                    bndl.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( k==2 )
                {
                    bndu.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( k==3 )
                {
                    v0 = 2*ae_randomreal(_state)-1;
                    v1 = 2*ae_randomreal(_state)-1;
                    bndl.ptr.p_double[i] = ae_minreal(v0, v1, _state);
                    bndu.ptr.p_double[i] = ae_maxreal(v0, v1, _state);
                }
                if( k==4 )
                {
                    bndl.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    bndu.ptr.p_double[i] = bndl.ptr.p_double[i];
                }
            }
            epsrad = 0.01*ae_pow((double)(10), -2*ae_randomreal(_state), _state);
            xtol = 15.0*epsrad;
            minnscreatef(n, &x0, epsrad/100, &state, _state);
            minnssetalgoags(&state, 0.1, 0.0, _state);
            minnssetcond(&state, epsrad, 0, _state);
            minnssetscale(&state, &s, _state);
            minnssetbc(&state, &bndl, &bndu, _state);
            minnssetxrep(&state, ae_true, _state);
            repferr = 0.0;
            while(minnsiteration(&state, _state))
            {
                if( state.needfi )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        ae_set_error_flag(primaryerrors, ae_fp_less(state.x.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:1164");
                        ae_set_error_flag(primaryerrors, ae_fp_greater(state.x.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:1165");
                    }
                    continue;
                }
                if( state.xupdated )
                {
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        ae_set_error_flag(primaryerrors, ae_fp_less(state.x.ptr.p_double[i],bndl.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:1176");
                        ae_set_error_flag(primaryerrors, ae_fp_greater(state.x.ptr.p_double[i],bndu.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:1177");
                    }
                    repferr = ae_maxreal(repferr, ae_fabs(v-state.f, _state), _state);
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1186");
            ae_set_error_flag(othererrors, ae_fp_greater(repferr,10000*ae_machineepsilon), __FILE__, __LINE__, "testminnsunit.ap:1187");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(primaryerrors, !ae_isfinite(x1.ptr.p_double[i], _state)||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-boundval(xc.ptr.p_double[i], bndl.ptr.p_double[i], bndu.ptr.p_double[i], _state), _state)/s.ptr.p_double[i],xtol), __FILE__, __LINE__, "testminnsunit.ap:1191");
            }
        }
    }
    
    /*
     * Test scaling: we perform several steps on unit-scale problem,
     * then we perform same amount of steps on re-scaled problem,
     * starting from same point (but scaled according to chosen scale).
     *
     * Correctly written optimizer should perform essentially same steps
     * (up to scale) on both problems. At least, it holds within first
     * several steps, before rounding errors start to accumulate.
     *
     * NOTE: we also check that correctly scaled points are reported.
     *       And, as side effect, we check MinNSRestartFrom().
     *
     * NOTE: we use very low scale and diagonal coefficients in order
     *       to have well-conditioned system. We test correctness of 
     *       formulae here, not robustness of algorithm.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&x0s, n, _state);
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&s, n, _state);
            ae_vector_set_length(&xrlast, n, _state);
            ae_vector_set_length(&bndl, n, _state);
            ae_vector_set_length(&bndu, n, _state);
            ae_vector_set_length(&scaledbndl, n, _state);
            ae_vector_set_length(&scaledbndu, n, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = ae_pow((double)(10), ae_randomreal(_state)-0.5, _state);
                d.ptr.p_double[i] = ae_pow((double)(10), ae_randomreal(_state)-0.5, _state);
                x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x0s.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                bndl.ptr.p_double[i] = _state->v_neginf;
                bndu.ptr.p_double[i] = _state->v_posinf;
                k = ae_randominteger(5, _state);
                if( k==1 )
                {
                    bndl.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( k==2 )
                {
                    bndu.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( k==3 )
                {
                    v0 = 2*ae_randomreal(_state)-1;
                    v1 = 2*ae_randomreal(_state)-1;
                    bndl.ptr.p_double[i] = ae_minreal(v0, v1, _state);
                    bndu.ptr.p_double[i] = ae_maxreal(v0, v1, _state);
                }
                if( k==4 )
                {
                    bndl.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    bndu.ptr.p_double[i] = bndl.ptr.p_double[i];
                }
                scaledbndl.ptr.p_double[i] = bndl.ptr.p_double[i]*s.ptr.p_double[i];
                scaledbndu.ptr.p_double[i] = bndu.ptr.p_double[i]*s.ptr.p_double[i];
            }
            minnscreate(n, &x0, &state, _state);
            minnssetalgoags(&state, 0.01, 0.0, _state);
            minnssetcond(&state, 0.0, testminnsunit_scalingtestcnt, _state);
            minnssetbc(&state, &bndl, &bndu, _state);
            minnssetxrep(&state, ae_false, _state);
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1272");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            minnssetscale(&state, &s, _state);
            minnssetbc(&state, &scaledbndl, &scaledbndu, _state);
            minnsrestartfrom(&state, &x0s, _state);
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]/s.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]/s.ptr.p_double[i]-xc.ptr.p_double[i], _state)/s.ptr.p_double[i];
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1s, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1293");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(primaryerrors, (!ae_isfinite(x1.ptr.p_double[i], _state)||!ae_isfinite(x1s.ptr.p_double[i], _state))||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x1s.ptr.p_double[i]/s.ptr.p_double[i], _state),testminnsunit_scalingtesttol), __FILE__, __LINE__, "testminnsunit.ap:1297");
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Linearly constrained test
*************************************************************************/
static void testminnsunit_testlc(ae_bool* primaryerrors,
     ae_bool* othererrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t nc;
    ae_vector x0;
    ae_vector x0s;
    ae_vector x1;
    ae_vector x2;
    ae_vector x1s;
    ae_vector d;
    ae_vector xc;
    ae_vector s;
    ae_vector bndl;
    ae_vector bndu;
    ae_matrix c;
    ae_matrix scaledc;
    ae_vector ct;
    ae_vector scaledbndl;
    ae_vector scaledbndu;
    ae_vector xrfirst;
    ae_vector xrlast;
    minnsstate state;
    minnsreport rep;
    double v;
    double v0;
    double v1;
    double vv;
    double flast0;
    double flast1;
    ae_int_t pass;
    double epsrad;
    double repferr;
    double xtol;
    double ftol;
    double rho;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x0s, 0, sizeof(x0s));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&x1s, 0, sizeof(x1s));
    memset(&d, 0, sizeof(d));
    memset(&xc, 0, sizeof(xc));
    memset(&s, 0, sizeof(s));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&c, 0, sizeof(c));
    memset(&scaledc, 0, sizeof(scaledc));
    memset(&ct, 0, sizeof(ct));
    memset(&scaledbndl, 0, sizeof(scaledbndl));
    memset(&scaledbndu, 0, sizeof(scaledbndu));
    memset(&xrfirst, 0, sizeof(xrfirst));
    memset(&xrlast, 0, sizeof(xrlast));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&scaledc, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&scaledbndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scaledbndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xrfirst, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xrlast, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&state, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    for(pass=1; pass<=10; pass++)
    {
        for(n=1; n<=5; n++)
        {
            
            /*
             * First test:
             * * smooth problem
             * * subject to random linear constraints
             * * with non-unit scale
             *
             * We:
             * * compare function value at constrained solution with function
             *   value for penalized unconstrained problem. We do not compare
             *   actual X-values returned, because they are highly unstable -
             *   function values at minimum show better stability.
             * * check that correct function values are reported
             */
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = 10*(2*ae_randomreal(_state)-1);
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                d.ptr.p_double[i] = 1+ae_randomreal(_state);
                s.ptr.p_double[i] = 1+ae_randomreal(_state);
            }
            nc = ae_randominteger((n+1)/2, _state);
            if( nc>0 )
            {
                ae_matrix_set_length(&c, nc, n+1, _state);
                ae_vector_set_length(&ct, nc, _state);
                for(i=0; i<=nc-1; i++)
                {
                    ct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
                    for(j=0; j<=n; j++)
                    {
                        c.ptr.pp_double[i][j] = ae_randomreal(_state)-0.5;
                    }
                }
            }
            epsrad = 0.00001;
            ftol = 0.01;
            minnscreate(n, &x0, &state, _state);
            minnssetalgoags(&state, 0.1, 0.0, _state);
            minnssetcond(&state, epsrad, 0, _state);
            minnssetscale(&state, &s, _state);
            minnssetxrep(&state, ae_true, _state);
            minnssetlc(&state, &c, &ct, nc, _state);
            repferr = 0.0;
            flast0 = _state->v_nan;
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_sqr(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*(2*(state.x.ptr.p_double[i]-xc.ptr.p_double[i]));
                    }
                    continue;
                }
                if( state.xupdated )
                {
                    flast0 = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        flast0 = flast0+d.ptr.p_double[i]*ae_sqr(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    repferr = ae_maxreal(repferr, ae_fabs(flast0-state.f, _state), _state);
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1402");
            ae_set_error_flag(primaryerrors, !ae_isfinite(flast0, _state), __FILE__, __LINE__, "testminnsunit.ap:1403");
            ae_set_error_flag(othererrors, ae_fp_greater(repferr,10000*ae_machineepsilon), __FILE__, __LINE__, "testminnsunit.ap:1404");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            minnssetlc(&state, &c, &ct, 0, _state);
            minnsrestartfrom(&state, &x0, _state);
            rho = 1000.0;
            repferr = 0.0;
            flast1 = _state->v_nan;
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_sqr(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*(2*(state.x.ptr.p_double[i]-xc.ptr.p_double[i]));
                    }
                    for(i=0; i<=nc-1; i++)
                    {
                        v = ae_v_dotproduct(&state.x.ptr.p_double[0], 1, &c.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                        v = v-c.ptr.pp_double[i][n];
                        vv = 0.0;
                        if( ct.ptr.p_int[i]<0 )
                        {
                            vv = (double)(ae_sign(ae_maxreal(v, 0.0, _state), _state));
                            v = ae_maxreal(v, 0.0, _state);
                        }
                        if( ct.ptr.p_int[i]==0 )
                        {
                            vv = (double)(ae_sign(v, _state));
                            v = ae_fabs(v, _state);
                        }
                        if( ct.ptr.p_int[i]>0 )
                        {
                            vv = (double)(-ae_sign(ae_maxreal(-v, 0.0, _state), _state));
                            v = ae_maxreal(-v, 0.0, _state);
                        }
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+rho*v;
                        for(j=0; j<=n-1; j++)
                        {
                            state.j.ptr.pp_double[0][j] = state.j.ptr.pp_double[0][j]+rho*vv*c.ptr.pp_double[i][j];
                        }
                    }
                    continue;
                }
                if( state.xupdated )
                {
                    flast1 = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        flast1 = flast1+d.ptr.p_double[i]*ae_sqr(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x2, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1462");
            ae_set_error_flag(primaryerrors, !ae_isfinite(flast1, _state), __FILE__, __LINE__, "testminnsunit.ap:1463");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            ae_set_error_flag(primaryerrors, ae_fp_greater(ae_fabs(flast0-flast1, _state),ftol), __FILE__, __LINE__, "testminnsunit.ap:1466");
            
            /*
             * Test on HIGHLY nonconvex linearly constrained problem.
             * Algorithm should be able to stop at the bounds.
             */
            ae_vector_set_length(&x0, n, _state);
            ae_matrix_set_length(&c, 2*n, n+1, _state);
            ae_vector_set_length(&ct, 2*n, _state);
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = ae_randomreal(_state)-0.5;
                for(j=0; j<=n-1; j++)
                {
                    c.ptr.pp_double[2*i+0][j] = 0.0;
                    c.ptr.pp_double[2*i+1][j] = 0.0;
                }
                c.ptr.pp_double[2*i+0][i] = 1.0;
                c.ptr.pp_double[2*i+0][n] = -1.0;
                ct.ptr.p_int[2*i+0] = 1;
                c.ptr.pp_double[2*i+1][i] = 1.0;
                c.ptr.pp_double[2*i+1][n] = 1.0;
                ct.ptr.p_int[2*i+1] = -1;
            }
            epsrad = 0.0001;
            xtol = 15.0*epsrad;
            minnscreate(n, &x0, &state, _state);
            minnssetalgoags(&state, 0.1, 0.0, _state);
            minnssetcond(&state, epsrad, 0, _state);
            minnssetlc(&state, &c, &ct, 2*n, _state);
            v = -1000.0;
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v0 = ae_fabs(state.x.ptr.p_double[i], _state);
                        v1 = (double)(ae_sign(state.x.ptr.p_double[i], _state));
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+v*(v0+v0*v0);
                        state.j.ptr.pp_double[0][i] = v*(v1+2*v0*v1);
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1516");
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(primaryerrors, !ae_isfinite(x1.ptr.p_double[i], _state), __FILE__, __LINE__, "testminnsunit.ap:1519");
                ae_set_error_flag(primaryerrors, (ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-1, _state),xtol)&&ae_fp_greater(ae_fabs(x1.ptr.p_double[i], _state),xtol))&&ae_fp_greater(ae_fabs(x1.ptr.p_double[i]+1, _state),xtol), __FILE__, __LINE__, "testminnsunit.ap:1520");
            }
            
            /*
             * Test scaling: we perform several steps on unit-scale problem,
             * then we perform same amount of steps on re-scaled problem,
             * starting from same point (but scaled according to chosen scale).
             *
             * Correctly written optimizer should perform essentially same steps
             * (up to scale) on both problems. At least, it holds within first
             * several steps, before rounding errors start to accumulate.
             *
             * NOTE: we also check that correctly scaled points are reported.
             *       And, as side effect, we check MinNSRestartFrom().
             *
             * NOTE: we use moderate scale and diagonal coefficients in order
             *       to have well-conditioned system. We test correctness of 
             *       formulae here, not robustness of algorithm.
             */
            ae_vector_set_length(&x0, n, _state);
            ae_vector_set_length(&xc, n, _state);
            ae_vector_set_length(&x0s, n, _state);
            ae_vector_set_length(&d, n, _state);
            ae_vector_set_length(&s, n, _state);
            ae_vector_set_length(&xrlast, n, _state);
            ae_matrix_set_length(&c, 2*n, n+1, _state);
            ae_matrix_set_length(&scaledc, 2*n, n+1, _state);
            ae_vector_set_length(&ct, 2*n, _state);
            for(i=0; i<=2*n-1; i++)
            {
                ct.ptr.p_int[i] = 0;
                for(j=0; j<=n; j++)
                {
                    c.ptr.pp_double[i][j] = (double)(0);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = ae_pow((double)(10), 2*ae_randomreal(_state)-1, _state);
                d.ptr.p_double[i] = ae_pow((double)(10), 2*ae_randomreal(_state)-1, _state);
                x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x0s.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                k = ae_randominteger(5, _state);
                if( k==1 )
                {
                    c.ptr.pp_double[2*i+0][i] = 1.0;
                    c.ptr.pp_double[2*i+0][n] = 2*ae_randomreal(_state)-1;
                    ct.ptr.p_int[2*i+0] = 1;
                }
                if( k==2 )
                {
                    c.ptr.pp_double[2*i+0][i] = 1.0;
                    c.ptr.pp_double[2*i+0][n] = 2*ae_randomreal(_state)-1;
                    ct.ptr.p_int[2*i+0] = -1;
                }
                if( k==3 )
                {
                    v0 = 2*ae_randomreal(_state)-1;
                    v1 = 2*ae_randomreal(_state)-1;
                    c.ptr.pp_double[2*i+0][i] = 1.0;
                    c.ptr.pp_double[2*i+0][n] = ae_minreal(v0, v1, _state);
                    c.ptr.pp_double[2*i+1][i] = 1.0;
                    c.ptr.pp_double[2*i+1][n] = ae_maxreal(v0, v1, _state);
                    ct.ptr.p_int[2*i+0] = 1;
                    ct.ptr.p_int[2*i+1] = -1;
                }
                if( k==4 )
                {
                    c.ptr.pp_double[2*i+0][i] = 1.0;
                    c.ptr.pp_double[2*i+0][n] = 2*ae_randomreal(_state)-1;
                    ct.ptr.p_int[2*i+0] = 0;
                }
            }
            for(i=0; i<=2*n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    scaledc.ptr.pp_double[i][j] = c.ptr.pp_double[i][j]/s.ptr.p_double[j];
                }
                scaledc.ptr.pp_double[i][n] = c.ptr.pp_double[i][n];
            }
            minnscreate(n, &x0, &state, _state);
            minnssetalgoags(&state, 0.1, 0.0, _state);
            minnssetcond(&state, 0.0, testminnsunit_scalingtestcnt, _state);
            minnssetlc(&state, &c, &ct, 2*n, _state);
            minnssetxrep(&state, ae_false, _state);
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1622");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            minnssetscale(&state, &s, _state);
            minnssetlc(&state, &scaledc, &ct, 2*n, _state);
            minnsrestartfrom(&state, &x0s, _state);
            while(minnsiteration(&state, _state))
            {
                if( state.needfij )
                {
                    state.fi.ptr.p_double[0] = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]/s.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                        state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]/s.ptr.p_double[i]-xc.ptr.p_double[i], _state)/s.ptr.p_double[i];
                    }
                    continue;
                }
                ae_assert(ae_false, "Assertion failed", _state);
            }
            minnsresults(&state, &x1s, &rep, _state);
            ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1643");
            if( *primaryerrors||(*othererrors) )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(primaryerrors, (!ae_isfinite(x1.ptr.p_double[i], _state)||!ae_isfinite(x1s.ptr.p_double[i], _state))||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x1s.ptr.p_double[i]/s.ptr.p_double[i], _state),testminnsunit_scalingtesttol), __FILE__, __LINE__, "testminnsunit.ap:1647");
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Nonlinearly constrained test
*************************************************************************/
static void testminnsunit_testnlc(ae_bool* primaryerrors,
     ae_bool* othererrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t nc;
    ae_int_t nec;
    ae_vector x0;
    ae_vector x0s;
    ae_vector x1;
    ae_vector x2;
    ae_vector x1s;
    ae_vector d;
    ae_vector xc;
    ae_vector s;
    ae_vector bndl;
    ae_vector bndu;
    ae_vector b;
    ae_vector r;
    ae_matrix c;
    ae_matrix scaledc;
    ae_vector ct;
    ae_vector scaledbndl;
    ae_vector scaledbndu;
    ae_vector xrfirst;
    ae_vector xrlast;
    minnsstate state;
    minnsreport rep;
    double v;
    ae_int_t pass;
    ae_int_t passcount;
    double epsrad;
    double xtol;
    double rho;
    ae_int_t maxn;
    double diffstep;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x0s, 0, sizeof(x0s));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&x1s, 0, sizeof(x1s));
    memset(&d, 0, sizeof(d));
    memset(&xc, 0, sizeof(xc));
    memset(&s, 0, sizeof(s));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&b, 0, sizeof(b));
    memset(&r, 0, sizeof(r));
    memset(&c, 0, sizeof(c));
    memset(&scaledc, 0, sizeof(scaledc));
    memset(&ct, 0, sizeof(ct));
    memset(&scaledbndl, 0, sizeof(scaledbndl));
    memset(&scaledbndu, 0, sizeof(scaledbndu));
    memset(&xrfirst, 0, sizeof(xrfirst));
    memset(&xrlast, 0, sizeof(xrlast));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&scaledc, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&scaledbndl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scaledbndu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xrfirst, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xrlast, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&state, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    passcount = 10;
    maxn = 5;
    rho = 100.0;
    
    /*
     * First test:
     * * simple problem
     * * subject to random nonlinear constraints of form r[i]*x[i] OPERATION 0.0,
     *   where OPERATION is <= or =
     * * with non-unit scale
     *
     * We:
     * * compare numerical solution with analytic one, which can be
     *   easily calculated
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(nc=1; nc<=n; nc++)
            {
                for(nec=0; nec<=nc; nec++)
                {
                    ae_vector_set_length(&x0, n, _state);
                    ae_vector_set_length(&xc, n, _state);
                    ae_vector_set_length(&d, n, _state);
                    ae_vector_set_length(&r, n, _state);
                    ae_vector_set_length(&s, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        d.ptr.p_double[i] = ae_pow((double)(10), ae_randomreal(_state)-0.5, _state);
                        s.ptr.p_double[i] = ae_pow((double)(10), ae_randomreal(_state)-0.5, _state);
                        r.ptr.p_double[i] = (2*ae_randominteger(2, _state)-1)*(0.1+ae_randomreal(_state));
                    }
                    epsrad = 0.001;
                    xtol = 0.01;
                    minnscreate(n, &x0, &state, _state);
                    minnssetalgoags(&state, 0.1, rho, _state);
                    minnssetcond(&state, epsrad, 0, _state);
                    minnssetscale(&state, &s, _state);
                    minnssetnlc(&state, nec, nc-nec, _state);
                    while(minnsiteration(&state, _state))
                    {
                        if( state.needfij )
                        {
                            state.fi.ptr.p_double[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                                state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                            }
                            for(i=1; i<=nc; i++)
                            {
                                state.fi.ptr.p_double[i] = state.x.ptr.p_double[i-1]*r.ptr.p_double[i-1];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.j.ptr.pp_double[i][j] = 0.0;
                                }
                                state.j.ptr.pp_double[i][i-1] = r.ptr.p_double[i-1];
                            }
                            continue;
                        }
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                    minnsresults(&state, &x1, &rep, _state);
                    ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1743");
                    if( *primaryerrors||(*othererrors) )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = xc.ptr.p_double[i];
                        if( i<nec )
                        {
                            v = 0.0;
                        }
                        if( i>=nec&&i<nc )
                        {
                            if( ae_fp_greater(r.ptr.p_double[i],0.0) )
                            {
                                v = ae_minreal(v, 0.0, _state);
                            }
                            if( ae_fp_less(r.ptr.p_double[i],0.0) )
                            {
                                v = ae_maxreal(v, 0.0, _state);
                            }
                        }
                        ae_set_error_flag(primaryerrors, !ae_isfinite(x1.ptr.p_double[i], _state), __FILE__, __LINE__, "testminnsunit.ap:1762");
                        ae_set_error_flag(primaryerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-v, _state),xtol*s.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:1763");
                    }
                }
            }
        }
    }
    
    /*
     * Numerical differentiation test.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(nc=1; nc<=n; nc++)
            {
                for(nec=0; nec<=nc; nec++)
                {
                    ae_vector_set_length(&x0, n, _state);
                    ae_vector_set_length(&xc, n, _state);
                    ae_vector_set_length(&d, n, _state);
                    ae_vector_set_length(&r, n, _state);
                    ae_vector_set_length(&s, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        d.ptr.p_double[i] = ae_pow((double)(10), ae_randomreal(_state)-0.5, _state);
                        s.ptr.p_double[i] = ae_pow((double)(10), ae_randomreal(_state)-0.5, _state);
                        r.ptr.p_double[i] = (2*ae_randominteger(2, _state)-1)*(0.1+ae_randomreal(_state));
                    }
                    epsrad = 0.001;
                    xtol = 0.01;
                    diffstep = 0.001;
                    minnscreatef(n, &x0, diffstep, &state, _state);
                    minnssetalgoags(&state, 0.1, rho, _state);
                    minnssetcond(&state, epsrad, 0, _state);
                    minnssetscale(&state, &s, _state);
                    minnssetnlc(&state, nec, nc-nec, _state);
                    while(minnsiteration(&state, _state))
                    {
                        if( state.needfi )
                        {
                            state.fi.ptr.p_double[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                            }
                            for(i=1; i<=nc; i++)
                            {
                                state.fi.ptr.p_double[i] = state.x.ptr.p_double[i-1]*r.ptr.p_double[i-1];
                            }
                            continue;
                        }
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                    minnsresults(&state, &x1, &rep, _state);
                    ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1813");
                    if( *primaryerrors||(*othererrors) )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = xc.ptr.p_double[i];
                        if( i<nec )
                        {
                            v = 0.0;
                        }
                        if( i>=nec&&i<nc )
                        {
                            if( ae_fp_greater(r.ptr.p_double[i],0.0) )
                            {
                                v = ae_minreal(v, 0.0, _state);
                            }
                            if( ae_fp_less(r.ptr.p_double[i],0.0) )
                            {
                                v = ae_maxreal(v, 0.0, _state);
                            }
                        }
                        ae_set_error_flag(primaryerrors, !ae_isfinite(x1.ptr.p_double[i], _state), __FILE__, __LINE__, "testminnsunit.ap:1832");
                        ae_set_error_flag(primaryerrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-v, _state),xtol*s.ptr.p_double[i]), __FILE__, __LINE__, "testminnsunit.ap:1833");
                    }
                }
            }
        }
    }
    
    /*
     * Test scaling: we perform several steps on unit-scale problem,
     * then we perform same amount of steps on re-scaled problem,
     * starting from same point (but scaled according to chosen scale).
     *
     * Correctly written optimizer should perform essentially same steps
     * (up to scale) on both problems. At least, it holds within first
     * several steps, before rounding errors start to accumulate.
     *
     * NOTE: we use moderate scale and diagonal coefficients in order
     *       to have well-conditioned system. We test correctness of 
     *       formulae here, not robustness of algorithm.
     *
     * NOTE: we have to use very relaxed thresholds for this test because
     *       each AGS iteration involves solution of several nested QP
     *       subproblems, so rounding errors accumulate too quickly.
     *       It does not mean that algorithm is inexact, just that two
     *       almost identical optimization sessions diverge too fast to
     *       compare them.
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(nc=1; nc<=n; nc++)
            {
                for(nec=0; nec<=nc; nec++)
                {
                    ae_vector_set_length(&x0, n, _state);
                    ae_vector_set_length(&x0s, n, _state);
                    ae_vector_set_length(&xc, n, _state);
                    ae_vector_set_length(&d, n, _state);
                    ae_vector_set_length(&r, n, _state);
                    ae_vector_set_length(&s, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        xc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        d.ptr.p_double[i] = ae_pow((double)(10), ae_randomreal(_state)-0.5, _state);
                        s.ptr.p_double[i] = ae_pow((double)(10), ae_randomreal(_state)-0.5, _state);
                        r.ptr.p_double[i] = (2*ae_randominteger(2, _state)-1)*(0.1+ae_randomreal(_state));
                        x0s.ptr.p_double[i] = x0.ptr.p_double[i]*s.ptr.p_double[i];
                    }
                    minnscreate(n, &x0, &state, _state);
                    minnssetalgoags(&state, 0.1, 1.0, _state);
                    minnssetcond(&state, 0.0, testminnsunit_scalingtestcnt, _state);
                    minnssetnlc(&state, nec, nc-nec, _state);
                    while(minnsiteration(&state, _state))
                    {
                        if( state.needfij )
                        {
                            state.fi.ptr.p_double[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                                state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                            }
                            for(i=1; i<=nc; i++)
                            {
                                state.fi.ptr.p_double[i] = state.x.ptr.p_double[i-1]*r.ptr.p_double[i-1];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.j.ptr.pp_double[i][j] = 0.0;
                                }
                                state.j.ptr.pp_double[i][i-1] = r.ptr.p_double[i-1];
                            }
                            continue;
                        }
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                    minnsresults(&state, &x1, &rep, _state);
                    ae_set_error_flag(primaryerrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:1906");
                    if( *primaryerrors||(*othererrors) )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    minnssetscale(&state, &s, _state);
                    minnsrestartfrom(&state, &x0s, _state);
                    while(minnsiteration(&state, _state))
                    {
                        if( state.needfij )
                        {
                            state.fi.ptr.p_double[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi.ptr.p_double[0] = state.fi.ptr.p_double[0]+d.ptr.p_double[i]*ae_fabs(state.x.ptr.p_double[i]/s.ptr.p_double[i]-xc.ptr.p_double[i], _state);
                                state.j.ptr.pp_double[0][i] = d.ptr.p_double[i]*ae_sign(state.x.ptr.p_double[i]/s.ptr.p_double[i]-xc.ptr.p_double[i], _state)/s.ptr.p_double[i];
                            }
                            for(i=1; i<=nc; i++)
                            {
                                state.fi.ptr.p_double[i] = state.x.ptr.p_double[i-1]/s.ptr.p_double[i-1]*r.ptr.p_double[i-1];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.j.ptr.pp_double[i][j] = 0.0;
                                }
                                state.j.ptr.pp_double[i][i-1] = r.ptr.p_double[i-1]/s.ptr.p_double[i-1];
                            }
                            continue;
                        }
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                    minnsresults(&state, &x1s, &rep, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(primaryerrors, (!ae_isfinite(x1.ptr.p_double[i], _state)||!ae_isfinite(x1s.ptr.p_double[i], _state))||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x1s.ptr.p_double[i]/s.ptr.p_double[i], _state),testminnsunit_scalingtesttol), __FILE__, __LINE__, "testminnsunit.ap:1937");
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Special tests
*************************************************************************/
static void testminnsunit_testother(ae_bool* othererrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t k;
    ae_vector x0;
    ae_vector x1;
    minnsstate state;
    minnsreport rep;
    double v0;
    double v1;
    double v;
    double xtol;
    double epsrad;
    double rho;

    ae_frame_make(_state, &_frame_block);
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    _minnsstate_init(&state, _state, ae_true);
    _minnsreport_init(&rep, _state, ae_true);

    
    /*
     * First test:
     * * 2D problem, minimization of F(x0,x1)=x1
     * * two constraints, with wildly different magnitudes
     *   * G0(x0,x1)=Rho*Abs(x0+x1)=0
     *   * H0(x0,x1)=Rho*(x0-1000)<=0
     *   where Rho is some large value
     *
     * Optimizer should be able to deal with situation when
     * magnitude of Jacobian components is so wildly different.
     */
    n = 2;
    ae_vector_set_length(&x0, n, _state);
    x0.ptr.p_double[0] = 0.1;
    x0.ptr.p_double[1] = 1.0;
    epsrad = 0.00001;
    xtol = 0.01;
    for(k=0; k<=6; k++)
    {
        rho = ae_pow((double)(10), (double)(k), _state);
        minnscreate(n, &x0, &state, _state);
        minnssetalgoags(&state, 0.1, 10.0, _state);
        minnssetcond(&state, epsrad, 0, _state);
        minnssetnlc(&state, 1, 1, _state);
        v = 1000.0;
        while(minnsiteration(&state, _state))
        {
            if( state.needfij )
            {
                v0 = state.x.ptr.p_double[0];
                v1 = state.x.ptr.p_double[1];
                state.fi.ptr.p_double[0] = v1;
                state.j.ptr.pp_double[0][0] = 0.0;
                state.j.ptr.pp_double[0][1] = 1.0;
                state.fi.ptr.p_double[1] = rho*ae_fabs(v0+v1, _state);
                state.j.ptr.pp_double[1][0] = rho*ae_sign(v0+v1, _state);
                state.j.ptr.pp_double[1][1] = rho*ae_sign(v0+v1, _state);
                state.fi.ptr.p_double[2] = rho*(v0-v);
                state.j.ptr.pp_double[2][0] = rho;
                state.j.ptr.pp_double[2][1] = 0.0;
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minnsresults(&state, &x1, &rep, _state);
        ae_set_error_flag(othererrors, rep.terminationtype<=0, __FILE__, __LINE__, "testminnsunit.ap:2003");
        ae_set_error_flag(othererrors, !ae_isfinite(x1.ptr.p_double[0], _state), __FILE__, __LINE__, "testminnsunit.ap:2004");
        ae_set_error_flag(othererrors, !ae_isfinite(x1.ptr.p_double[1], _state), __FILE__, __LINE__, "testminnsunit.ap:2005");
        ae_set_error_flag(othererrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[0]-v, _state),xtol), __FILE__, __LINE__, "testminnsunit.ap:2006");
        ae_set_error_flag(othererrors, ae_fp_greater(ae_fabs(x1.ptr.p_double[1]+v, _state),xtol), __FILE__, __LINE__, "testminnsunit.ap:2007");
    }
    ae_frame_leave(_state);
}



static void testminlmunit_testu(ae_bool* errorflag,
     ae_bool* statefieldsconsistencyflag,
     ae_state *_state);
static void testminlmunit_testbc(ae_bool* errorflag, ae_state *_state);
static void testminlmunit_testlc(ae_bool* errorflag, ae_state *_state);
static void testminlmunit_testother(ae_bool* errorflag,
     ae_bool* statefieldsconsistencyflag,
     ae_state *_state);
static ae_bool testminlmunit_rkindvsstatecheck(ae_int_t rkind,
     minlmstate* state,
     ae_state *_state);
static void testminlmunit_axmb(minlmstate* state,
     /* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     ae_state *_state);
static void testminlmunit_tryreproducefixedbugs(ae_bool* err,
     ae_state *_state);
static ae_bool testminlmunit_gradientchecktest(ae_state *_state);
static void testminlmunit_funcderiv(/* Real    */ ae_vector* a,
     /* Real    */ ae_vector* x0,
     /* Real    */ ae_vector* x,
     ae_int_t m,
     ae_int_t n,
     double anyconst,
     ae_int_t functype,
     /* Real    */ ae_vector* f,
     /* Real    */ ae_matrix* j,
     ae_state *_state);
static void testminlmunit_testfunc1(ae_int_t n,
     ae_int_t m,
     /* Real    */ ae_matrix* c,
     /* Real    */ ae_vector* x,
     double* f,
     ae_bool needf,
     /* Real    */ ae_vector* fi,
     ae_bool needfi,
     /* Real    */ ae_matrix* jac,
     ae_bool needjac,
     ae_state *_state);





ae_bool testminlm(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool uerrors;
    ae_bool bcerrors;
    ae_bool lcerrors;
    ae_bool scerror;
    ae_bool othererrors;
    ae_bool graderrors;
    ae_bool result;


    waserrors = ae_false;
    scerror = ae_false;
    othererrors = ae_false;
    uerrors = ae_false;
    bcerrors = ae_false;
    lcerrors = ae_false;
    
    /*
     * Various tests
     */
    testminlmunit_testother(&othererrors, &scerror, _state);
    
    /*
     * Tests sorted by constraint types
     */
    testminlmunit_testu(&uerrors, &scerror, _state);
    testminlmunit_testbc(&bcerrors, _state);
    testminlmunit_testlc(&lcerrors, _state);
    
    /*
     * Try to reproduce previously fixed bugs
     */
    testminlmunit_tryreproducefixedbugs(&othererrors, _state);
    
    /*
     *  Test for MinLMGradientCheck
     */
    graderrors = testminlmunit_gradientchecktest(_state);
    
    /*
     * end
     */
    waserrors = ((((uerrors||bcerrors)||lcerrors)||scerror)||othererrors)||graderrors;
    if( !silent )
    {
        printf("TESTING LEVENBERG-MARQUARDT OPTIMIZATION\n");
        printf("PROBLEM TYPES:\n");
        printf("* UNCONSTRAINED                           ");
        if( uerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* BOX CONSTRAINED                         ");
        if( bcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* LINEARLY CONSTRAINED                    ");
        if( lcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("STATE FIELDS CONSISTENCY:                 ");
        if( scerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TEST FOR VERIFICATION OF DERIVATIVES:     ");
        if( graderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Test for unconstrained problems.
On failure sets error flag, leaves it unchanged on success.
*************************************************************************/
static void testminlmunit_testu(ae_bool* errorflag,
     ae_bool* statefieldsconsistencyflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_vector x;
    ae_vector xe;
    ae_vector b;
    double h;
    ae_int_t rkind;
    ae_matrix a;
    double v;
    double s;
    double eps;
    double epsx;
    ae_int_t maxits;
    ae_int_t ckind;
    minlmstate state;
    minlmreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&xe, 0, sizeof(xe));
    memset(&b, 0, sizeof(b));
    memset(&a, 0, sizeof(a));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    _minlmstate_init(&state, _state, ae_true);
    _minlmreport_init(&rep, _state, ae_true);

    
    /*
     * Reference problem.
     * See comments for RKindVsStateCheck() for more info about RKind.
     *
     * NOTES: we also test negative RKind's corresponding to "inexact" schemes
     * which use approximate finite difference Jacobian.
     */
    ae_vector_set_length(&x, 3, _state);
    n = 3;
    m = 3;
    h = 0.0001;
    for(rkind=-2; rkind<=5; rkind++)
    {
        x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
        x.ptr.p_double[1] = 100*ae_randomreal(_state)-50;
        x.ptr.p_double[2] = 100*ae_randomreal(_state)-50;
        if( rkind==-2 )
        {
            minlmcreatev(n, m, &x, h, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==-1 )
        {
            minlmcreatev(n, m, &x, h, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==0 )
        {
            minlmcreatefj(n, m, &x, &state, _state);
        }
        if( rkind==1 )
        {
            minlmcreatefgj(n, m, &x, &state, _state);
        }
        if( rkind==2 )
        {
            minlmcreatefgh(n, &x, &state, _state);
        }
        if( rkind==3 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==4 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==5 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 2, _state);
        }
        while(minlmiteration(&state, _state))
        {
            
            /*
             * (x-2)^2 + y^2 + (z-x)^2
             */
            if( state.needfi )
            {
                state.fi.ptr.p_double[0] = state.x.ptr.p_double[0]-2;
                state.fi.ptr.p_double[1] = state.x.ptr.p_double[1];
                state.fi.ptr.p_double[2] = state.x.ptr.p_double[2]-state.x.ptr.p_double[0];
            }
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = state.x.ptr.p_double[0]-2;
                state.fi.ptr.p_double[1] = state.x.ptr.p_double[1];
                state.fi.ptr.p_double[2] = state.x.ptr.p_double[2]-state.x.ptr.p_double[0];
                state.j.ptr.pp_double[0][0] = (double)(1);
                state.j.ptr.pp_double[0][1] = (double)(0);
                state.j.ptr.pp_double[0][2] = (double)(0);
                state.j.ptr.pp_double[1][0] = (double)(0);
                state.j.ptr.pp_double[1][1] = (double)(1);
                state.j.ptr.pp_double[1][2] = (double)(0);
                state.j.ptr.pp_double[2][0] = (double)(-1);
                state.j.ptr.pp_double[2][1] = (double)(0);
                state.j.ptr.pp_double[2][2] = (double)(1);
            }
            if( (state.needf||state.needfg)||state.needfgh )
            {
                state.f = ae_sqr(state.x.ptr.p_double[0]-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
            }
            if( state.needfg||state.needfgh )
            {
                state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]-2)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
            }
            if( state.needfgh )
            {
                state.h.ptr.pp_double[0][0] = (double)(4);
                state.h.ptr.pp_double[0][1] = (double)(0);
                state.h.ptr.pp_double[0][2] = (double)(-2);
                state.h.ptr.pp_double[1][0] = (double)(0);
                state.h.ptr.pp_double[1][1] = (double)(2);
                state.h.ptr.pp_double[1][2] = (double)(0);
                state.h.ptr.pp_double[2][0] = (double)(-2);
                state.h.ptr.pp_double[2][1] = (double)(0);
                state.h.ptr.pp_double[2][2] = (double)(2);
            }
            ae_set_error_flag(statefieldsconsistencyflag, !testminlmunit_rkindvsstatecheck(rkind, &state, _state), __FILE__, __LINE__, "testminlmunit.ap:211");
        }
        minlmresults(&state, &x, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:214");
        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[0]-2, _state),0.001), __FILE__, __LINE__, "testminlmunit.ap:215");
        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.001), __FILE__, __LINE__, "testminlmunit.ap:216");
        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[2]-2, _state),0.001), __FILE__, __LINE__, "testminlmunit.ap:217");
    }
    
    /*
     * 1D problem #1
     *
     * NOTES: we also test negative RKind's corresponding to "inexact" schemes
     * which use approximate finite difference Jacobian.
     */
    for(rkind=-2; rkind<=5; rkind++)
    {
        ae_vector_set_length(&x, 1, _state);
        n = 1;
        m = 1;
        h = 0.00001;
        x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
        if( rkind==-2 )
        {
            minlmcreatev(n, m, &x, h, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==-1 )
        {
            minlmcreatev(n, m, &x, h, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==0 )
        {
            minlmcreatefj(n, m, &x, &state, _state);
        }
        if( rkind==1 )
        {
            minlmcreatefgj(n, m, &x, &state, _state);
        }
        if( rkind==2 )
        {
            minlmcreatefgh(n, &x, &state, _state);
        }
        if( rkind==3 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==4 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==5 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 2, _state);
        }
        while(minlmiteration(&state, _state))
        {
            if( state.needfi )
            {
                state.fi.ptr.p_double[0] = ae_sin(state.x.ptr.p_double[0], _state);
            }
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = ae_sin(state.x.ptr.p_double[0], _state);
                state.j.ptr.pp_double[0][0] = ae_cos(state.x.ptr.p_double[0], _state);
            }
            if( (state.needf||state.needfg)||state.needfgh )
            {
                state.f = ae_sqr(ae_sin(state.x.ptr.p_double[0], _state), _state);
            }
            if( state.needfg||state.needfgh )
            {
                state.g.ptr.p_double[0] = 2*ae_sin(state.x.ptr.p_double[0], _state)*ae_cos(state.x.ptr.p_double[0], _state);
            }
            if( state.needfgh )
            {
                state.h.ptr.pp_double[0][0] = 2*(ae_cos(state.x.ptr.p_double[0], _state)*ae_cos(state.x.ptr.p_double[0], _state)-ae_sin(state.x.ptr.p_double[0], _state)*ae_sin(state.x.ptr.p_double[0], _state));
            }
            ae_set_error_flag(statefieldsconsistencyflag, !testminlmunit_rkindvsstatecheck(rkind, &state, _state), __FILE__, __LINE__, "testminlmunit.ap:280");
        }
        minlmresults(&state, &x, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:283");
        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[0]/ae_pi-ae_round(x.ptr.p_double[0]/ae_pi, _state), _state),0.001), __FILE__, __LINE__, "testminlmunit.ap:284");
    }
    
    /*
     * Linear equations: test normal optimization and optimization with restarts
     */
    for(n=1; n<=10; n++)
    {
        
        /*
         * Prepare task
         */
        h = 0.00001;
        rmatrixrndcond(n, (double)(100), &a, _state);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xe, n, _state);
        ae_vector_set_length(&b, n, _state);
        for(i=0; i<=n-1; i++)
        {
            xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
            b.ptr.p_double[i] = v;
        }
        
        /*
         * Test different RKind
         *
         * NOTES: we also test negative RKind's corresponding to "inexact" schemes
         * which use approximate finite difference Jacobian.
         */
        for(rkind=-2; rkind<=5; rkind++)
        {
            
            /*
             * Solve task (first attempt)
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            if( rkind==-2 )
            {
                minlmcreatev(n, n, &x, h, &state, _state);
                minlmsetacctype(&state, 1, _state);
            }
            if( rkind==-1 )
            {
                minlmcreatev(n, n, &x, h, &state, _state);
                minlmsetacctype(&state, 0, _state);
            }
            if( rkind==0 )
            {
                minlmcreatefj(n, n, &x, &state, _state);
            }
            if( rkind==1 )
            {
                minlmcreatefgj(n, n, &x, &state, _state);
            }
            if( rkind==2 )
            {
                minlmcreatefgh(n, &x, &state, _state);
            }
            if( rkind==3 )
            {
                minlmcreatevj(n, n, &x, &state, _state);
                minlmsetacctype(&state, 0, _state);
            }
            if( rkind==4 )
            {
                minlmcreatevj(n, n, &x, &state, _state);
                minlmsetacctype(&state, 1, _state);
            }
            if( rkind==5 )
            {
                minlmcreatevj(n, n, &x, &state, _state);
                minlmsetacctype(&state, 2, _state);
            }
            while(minlmiteration(&state, _state))
            {
                testminlmunit_axmb(&state, &a, &b, n, _state);
                ae_set_error_flag(statefieldsconsistencyflag, !testminlmunit_rkindvsstatecheck(rkind, &state, _state), __FILE__, __LINE__, "testminlmunit.ap:356");
            }
            minlmresults(&state, &x, &rep, _state);
            ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:359");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[i]-xe.ptr.p_double[i], _state),0.001), __FILE__, __LINE__, "testminlmunit.ap:363");
            }
            
            /*
             * Now we try to restart algorithm from new point
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlmrestartfrom(&state, &x, _state);
            while(minlmiteration(&state, _state))
            {
                testminlmunit_axmb(&state, &a, &b, n, _state);
                ae_set_error_flag(statefieldsconsistencyflag, !testminlmunit_rkindvsstatecheck(rkind, &state, _state), __FILE__, __LINE__, "testminlmunit.ap:374");
            }
            minlmresults(&state, &x, &rep, _state);
            ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:377");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[i]-xe.ptr.p_double[i], _state),0.001), __FILE__, __LINE__, "testminlmunit.ap:381");
            }
        }
    }
    
    /*
     * Testing convergence properties using
     * different optimizer types and different conditions.
     *
     * Only limited subset of optimizers is tested because some
     * optimizers converge too quickly.
     */
    s = (double)(100);
    for(rkind=0; rkind<=5; rkind++)
    {
        
        /*
         * Skip FGH optimizer - it converges too quickly
         */
        if( rkind==2 )
        {
            continue;
        }
        
        /*
         * Test
         */
        for(ckind=0; ckind<=1; ckind++)
        {
            eps = (double)(0);
            epsx = (double)(0);
            maxits = 0;
            if( ckind==0 )
            {
                epsx = 1.0E-6;
                eps = 1.0E-4;
            }
            if( ckind==1 )
            {
                maxits = 2;
                eps = 0.05;
            }
            ae_vector_set_length(&x, 3, _state);
            n = 3;
            m = 3;
            for(i=0; i<=2; i++)
            {
                x.ptr.p_double[i] = (double)(6);
            }
            if( rkind==0 )
            {
                minlmcreatefj(n, m, &x, &state, _state);
            }
            if( rkind==1 )
            {
                minlmcreatefgj(n, m, &x, &state, _state);
            }
            ae_assert(rkind!=2, "Assertion failed", _state);
            if( rkind==3 )
            {
                minlmcreatevj(n, m, &x, &state, _state);
                minlmsetacctype(&state, 0, _state);
            }
            if( rkind==4 )
            {
                minlmcreatevj(n, m, &x, &state, _state);
                minlmsetacctype(&state, 1, _state);
            }
            if( rkind==5 )
            {
                minlmcreatevj(n, m, &x, &state, _state);
                minlmsetacctype(&state, 2, _state);
            }
            minlmsetcond(&state, epsx, maxits, _state);
            while(minlmiteration(&state, _state))
            {
                if( state.needfi||state.needfij )
                {
                    state.fi.ptr.p_double[0] = s*(ae_exp(state.x.ptr.p_double[0], _state)-2);
                    state.fi.ptr.p_double[1] = ae_sqr(state.x.ptr.p_double[1], _state)+1;
                    state.fi.ptr.p_double[2] = state.x.ptr.p_double[2]-state.x.ptr.p_double[0];
                }
                if( state.needfij )
                {
                    state.j.ptr.pp_double[0][0] = s*ae_exp(state.x.ptr.p_double[0], _state);
                    state.j.ptr.pp_double[0][1] = (double)(0);
                    state.j.ptr.pp_double[0][2] = (double)(0);
                    state.j.ptr.pp_double[1][0] = (double)(0);
                    state.j.ptr.pp_double[1][1] = 2*state.x.ptr.p_double[1];
                    state.j.ptr.pp_double[1][2] = (double)(0);
                    state.j.ptr.pp_double[2][0] = (double)(-1);
                    state.j.ptr.pp_double[2][1] = (double)(0);
                    state.j.ptr.pp_double[2][2] = (double)(1);
                }
                if( (state.needf||state.needfg)||state.needfgh )
                {
                    state.f = s*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state.x.ptr.p_double[1], _state)+1, _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
                }
                if( state.needfg||state.needfgh )
                {
                    state.g.ptr.p_double[0] = s*2*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                    state.g.ptr.p_double[1] = 2*(ae_sqr(state.x.ptr.p_double[1], _state)+1)*2*state.x.ptr.p_double[1];
                    state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
                }
                if( state.needfgh )
                {
                    state.h.ptr.pp_double[0][0] = s*(4*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state), _state)-4*ae_exp(state.x.ptr.p_double[0], _state))+2;
                    state.h.ptr.pp_double[0][1] = (double)(0);
                    state.h.ptr.pp_double[0][2] = (double)(-2);
                    state.h.ptr.pp_double[1][0] = (double)(0);
                    state.h.ptr.pp_double[1][1] = 12*ae_sqr(state.x.ptr.p_double[1], _state)+4;
                    state.h.ptr.pp_double[1][2] = (double)(0);
                    state.h.ptr.pp_double[2][0] = (double)(-2);
                    state.h.ptr.pp_double[2][1] = (double)(0);
                    state.h.ptr.pp_double[2][2] = (double)(2);
                }
                ae_set_error_flag(statefieldsconsistencyflag, !testminlmunit_rkindvsstatecheck(rkind, &state, _state), __FILE__, __LINE__, "testminlmunit.ap:485");
            }
            minlmresults(&state, &x, &rep, _state);
            ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:488");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            if( ckind==0 )
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log((double)(2), _state), _state),eps), __FILE__, __LINE__, "testminlmunit.ap:493");
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),eps), __FILE__, __LINE__, "testminlmunit.ap:494");
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log((double)(2), _state), _state),eps), __FILE__, __LINE__, "testminlmunit.ap:495");
                ae_set_error_flag(errorflag, rep.terminationtype!=2, __FILE__, __LINE__, "testminlmunit.ap:496");
            }
            if( ckind==1 )
            {
                ae_set_error_flag(errorflag, rep.terminationtype!=5, __FILE__, __LINE__, "testminlmunit.ap:500");
                ae_set_error_flag(errorflag, rep.iterationscount!=maxits, __FILE__, __LINE__, "testminlmunit.ap:501");
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test for box constrained problems.
On failure sets error flag, leaves it unchanged on success.
*************************************************************************/
static void testminlmunit_testbc(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    minlmstate state;
    minlmreport rep;
    ae_vector bl;
    ae_vector bu;
    ae_int_t n;
    ae_int_t m;
    ae_vector x0;
    ae_vector x;
    ae_vector xe;
    ae_vector x1;
    ae_vector d;
    ae_matrix c;
    ae_int_t i;
    ae_int_t j;
    double v;
    double h;
    ae_int_t tmpkind;
    hqrndstate rs;
    double epsx;
    double tolf;
    double f0;
    double f1;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x0, 0, sizeof(x0));
    memset(&x, 0, sizeof(x));
    memset(&xe, 0, sizeof(xe));
    memset(&x1, 0, sizeof(x1));
    memset(&d, 0, sizeof(d));
    memset(&c, 0, sizeof(c));
    memset(&rs, 0, sizeof(rs));
    _minlmstate_init(&state, _state, ae_true);
    _minlmreport_init(&rep, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Reference box constrained problem:
     *
     *     min sum((x[i]-xe[i])^4) subject to 0<=x[i]<=1
     *
     * NOTES:
     * 1. we test only two optimization modes - V and FGH,
     *    because from algorithm internals we can assume that actual
     *    mode being used doesn't matter for bound constrained optimization
     *    process.
     */
    for(tmpkind=0; tmpkind<=1; tmpkind++)
    {
        for(n=1; n<=5; n++)
        {
            ae_vector_set_length(&bl, n, _state);
            ae_vector_set_length(&bu, n, _state);
            ae_vector_set_length(&xe, n, _state);
            ae_vector_set_length(&x, n, _state);
            for(i=0; i<=n-1; i++)
            {
                bl.ptr.p_double[i] = (double)(0);
                bu.ptr.p_double[i] = (double)(1);
                xe.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                x.ptr.p_double[i] = ae_randomreal(_state);
            }
            if( tmpkind==0 )
            {
                minlmcreatefgh(n, &x, &state, _state);
            }
            if( tmpkind==1 )
            {
                minlmcreatev(n, n, &x, 1.0E-5, &state, _state);
            }
            minlmsetcond(&state, 1.0E-6, 0, _state);
            minlmsetbc(&state, &bl, &bu, _state);
            while(minlmiteration(&state, _state))
            {
                if( state.needfi )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[i] = ae_pow(state.x.ptr.p_double[i]-xe.ptr.p_double[i], (double)(2), _state);
                    }
                }
                if( (state.needf||state.needfg)||state.needfgh )
                {
                    state.f = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+ae_pow(state.x.ptr.p_double[i]-xe.ptr.p_double[i], (double)(4), _state);
                    }
                }
                if( state.needfg||state.needfgh )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        state.g.ptr.p_double[i] = 4*ae_pow(state.x.ptr.p_double[i]-xe.ptr.p_double[i], (double)(3), _state);
                    }
                }
                if( state.needfgh )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            state.h.ptr.pp_double[i][j] = (double)(0);
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        state.h.ptr.pp_double[i][i] = 12*ae_pow(state.x.ptr.p_double[i]-xe.ptr.p_double[i], (double)(2), _state);
                    }
                }
            }
            minlmresults(&state, &x, &rep, _state);
            if( rep.terminationtype>0 )
            {
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[i]-boundval(xe.ptr.p_double[i], bl.ptr.p_double[i], bu.ptr.p_double[i], _state), _state),5.0E-2), __FILE__, __LINE__, "testminlmunit.ap:593");
                }
            }
            else
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testminlmunit.ap:596");
            }
        }
    }
    
    /*
     * Minimize
     *
     *          [      [                   ]2  ]
     *     SUM_i[ SUM_j[ power(x_j,3)*c_ij ]   ]
     *          [      [                   ]   ]
     *
     * subject to non-negativity constraints on x_j
     */
    epsx = 1.0E-9;
    tolf = 1.0E-10;
    for(tmpkind=0; tmpkind<=1; tmpkind++)
    {
        for(n=1; n<=20; n++)
        {
            m = n+hqrnduniformi(&rs, n, _state);
            ae_vector_set_length(&bl, n, _state);
            ae_vector_set_length(&bu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                bl.ptr.p_double[i] = (double)(0);
                bu.ptr.p_double[i] = _state->v_posinf;
                x0.ptr.p_double[i] = 1+hqrnduniformr(&rs, _state);
            }
            ae_matrix_set_length(&c, m, n+1, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n; j++)
                {
                    c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
            }
            if( tmpkind==0 )
            {
                minlmcreatev(n, m, &x0, 10*epsx, &state, _state);
            }
            if( tmpkind==1 )
            {
                minlmcreatevj(n, m, &x0, &state, _state);
            }
            minlmsetcond(&state, epsx, 0, _state);
            minlmsetbc(&state, &bl, &bu, _state);
            while(minlmiteration(&state, _state))
            {
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:638");
                    ae_set_error_flag(errorflag, ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:639");
                }
                if( state.needfi )
                {
                    testminlmunit_testfunc1(n, m, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_false, _state);
                    continue;
                }
                if( state.needfij )
                {
                    testminlmunit_testfunc1(n, m, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_true, _state);
                    continue;
                }
                ae_assert(ae_false, "minlm test: integrity check failed", _state);
            }
            minlmresults(&state, &x, &rep, _state);
            ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:654");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            testminlmunit_testfunc1(n, m, &c, &x, &f0, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
            ae_vector_set_length(&x1, n, _state);
            h = 0.001;
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_less(x.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:663");
                ae_set_error_flag(errorflag, ae_fp_greater(x.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:664");
                if( ae_fp_greater_eq(x.ptr.p_double[i]+h,bl.ptr.p_double[i]) )
                {
                    for(j=0; j<=n-1; j++)
                    {
                        x1.ptr.p_double[j] = x.ptr.p_double[j];
                    }
                    x1.ptr.p_double[i] = x.ptr.p_double[i]+h;
                    testminlmunit_testfunc1(n, m, &c, &x1, &f1, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0*(1-tolf)), __FILE__, __LINE__, "testminlmunit.ap:673");
                }
                if( ae_fp_greater_eq(x.ptr.p_double[i]-h,bl.ptr.p_double[i]) )
                {
                    for(j=0; j<=n-1; j++)
                    {
                        x1.ptr.p_double[j] = x.ptr.p_double[j];
                    }
                    x1.ptr.p_double[i] = x.ptr.p_double[i]-h;
                    testminlmunit_testfunc1(n, m, &c, &x1, &f1, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0*(1-tolf)), __FILE__, __LINE__, "testminlmunit.ap:683");
                }
            }
        }
    }
    
    /*
     * Minimize
     *
     *          [      [                   ]2  ]
     *     SUM_i[ SUM_j[ power(x_j,3)*c_ij ]   ]
     *          [      [                   ]   ]
     *
     * subject to random box constraints on x_j
     */
    epsx = 1.0E-9;
    tolf = 1.0E-10;
    for(tmpkind=0; tmpkind<=1; tmpkind++)
    {
        for(n=1; n<=20; n++)
        {
            m = n+hqrnduniformi(&rs, n, _state);
            ae_vector_set_length(&bl, n, _state);
            ae_vector_set_length(&bu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                bl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bu.ptr.p_double[i] = bl.ptr.p_double[i]+hqrnduniformr(&rs, _state);
                x0.ptr.p_double[i] = 1+hqrnduniformr(&rs, _state);
            }
            ae_matrix_set_length(&c, m, n+1, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n; j++)
                {
                    c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
            }
            if( tmpkind==0 )
            {
                minlmcreatev(n, m, &x0, 10*epsx, &state, _state);
            }
            if( tmpkind==1 )
            {
                minlmcreatevj(n, m, &x0, &state, _state);
            }
            minlmsetcond(&state, epsx, 0, _state);
            minlmsetbc(&state, &bl, &bu, _state);
            while(minlmiteration(&state, _state))
            {
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:727");
                    ae_set_error_flag(errorflag, ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:728");
                }
                if( state.needfi )
                {
                    testminlmunit_testfunc1(n, m, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_false, _state);
                    continue;
                }
                if( state.needfij )
                {
                    testminlmunit_testfunc1(n, m, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_true, _state);
                    continue;
                }
                ae_assert(ae_false, "minlm test: integrity check failed", _state);
            }
            minlmresults(&state, &x, &rep, _state);
            ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:743");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            testminlmunit_testfunc1(n, m, &c, &x, &f0, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
            ae_vector_set_length(&x1, n, _state);
            h = 0.001;
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_less(x.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:752");
                ae_set_error_flag(errorflag, ae_fp_greater(x.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:753");
                if( ae_fp_greater_eq(x.ptr.p_double[i]+h,bl.ptr.p_double[i])&&ae_fp_less_eq(x.ptr.p_double[i]+h,bu.ptr.p_double[i]) )
                {
                    for(j=0; j<=n-1; j++)
                    {
                        x1.ptr.p_double[j] = x.ptr.p_double[j];
                    }
                    x1.ptr.p_double[i] = x.ptr.p_double[i]+h;
                    testminlmunit_testfunc1(n, m, &c, &x1, &f1, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0*(1-tolf)), __FILE__, __LINE__, "testminlmunit.ap:762");
                }
                if( ae_fp_greater_eq(x.ptr.p_double[i]-h,bl.ptr.p_double[i])&&ae_fp_less_eq(x.ptr.p_double[i]-h,bu.ptr.p_double[i]) )
                {
                    for(j=0; j<=n-1; j++)
                    {
                        x1.ptr.p_double[j] = x.ptr.p_double[j];
                    }
                    x1.ptr.p_double[i] = x.ptr.p_double[i]-h;
                    testminlmunit_testfunc1(n, m, &c, &x1, &f1, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0*(1-tolf)), __FILE__, __LINE__, "testminlmunit.ap:772");
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test for linearly constrained problems.
On failure sets error flag, leaves it unchanged on success.
*************************************************************************/
static void testminlmunit_testlc(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    minlmstate state;
    minlmreport rep;
    ae_vector bl;
    ae_vector bu;
    ae_int_t n;
    ae_int_t m;
    ae_int_t m1;
    ae_int_t m2;
    ae_vector x0;
    ae_vector x;
    ae_vector xe;
    ae_vector x1;
    ae_vector x12;
    ae_vector d;
    ae_matrix rawc;
    ae_vector rawct;
    ae_int_t rawccnt;
    ae_matrix c;
    ae_matrix c12;
    ae_matrix z;
    ae_int_t trialidx;
    ae_int_t i;
    ae_int_t j;
    double v;
    double h;
    ae_int_t optkind;
    hqrndstate rs;
    double epsx;
    double xtol;
    double tolf;
    double f0;
    double f1;
    ae_bool bflag;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x0, 0, sizeof(x0));
    memset(&x, 0, sizeof(x));
    memset(&xe, 0, sizeof(xe));
    memset(&x1, 0, sizeof(x1));
    memset(&x12, 0, sizeof(x12));
    memset(&d, 0, sizeof(d));
    memset(&rawc, 0, sizeof(rawc));
    memset(&rawct, 0, sizeof(rawct));
    memset(&c, 0, sizeof(c));
    memset(&c12, 0, sizeof(c12));
    memset(&z, 0, sizeof(z));
    memset(&rs, 0, sizeof(rs));
    _minlmstate_init(&state, _state, ae_true);
    _minlmreport_init(&rep, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x12, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rawc, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rawct, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c12, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Minimize
     *
     *          [      [                             ]2  ]
     *     SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ]   ]
     *          [      [                             ]   ]
     *
     * subject to mix of box and linear inequality constraints on x_j
     *
     * We check correctness of solution by sampling a few random points
     * around one returned by optimizer, and comparing function value
     * with target. Sampling is performed with respect to inequality
     * constraints.
     */
    epsx = 1.0E-12;
    xtol = 1.0E-8;
    tolf = 1.0E-10;
    for(optkind=0; optkind<=1; optkind++)
    {
        for(n=5; n<=20; n++)
        {
            
            /*
             * Generate problem
             */
            m = n+hqrnduniformi(&rs, n, _state);
            ae_vector_set_length(&bl, n, _state);
            ae_vector_set_length(&bu, n, _state);
            ae_vector_set_length(&x0, n, _state);
            for(i=0; i<=n-1; i++)
            {
                bl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bu.ptr.p_double[i] = bl.ptr.p_double[i]+hqrnduniformr(&rs, _state);
                x0.ptr.p_double[i] = bl.ptr.p_double[i]+(bu.ptr.p_double[i]-bl.ptr.p_double[i])*hqrnduniformr(&rs, _state);
            }
            ae_matrix_set_length(&c, m, n+1, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n; j++)
                {
                    c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
            }
            ae_assert(n>=5, "Assertion failed", _state);
            rawccnt = 3;
            ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
            ae_vector_set_length(&rawct, rawccnt, _state);
            for(i=0; i<=rawccnt-1; i++)
            {
                v = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    rawc.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                    v = v+x0.ptr.p_double[j]*rawc.ptr.pp_double[i][j];
                }
                rawc.ptr.pp_double[i][n] = v;
                rawct.ptr.p_int[i] = 2*hqrnduniformi(&rs, 2, _state)-1;
            }
            
            /*
             * Solve
             */
            if( optkind==0 )
            {
                minlmcreatev(n, m, &x0, 10*epsx, &state, _state);
            }
            if( optkind==1 )
            {
                minlmcreatevj(n, m, &x0, &state, _state);
            }
            minlmsetcond(&state, epsx, 0, _state);
            minlmsetbc(&state, &bl, &bu, _state);
            minlmsetlc(&state, &rawc, &rawct, rawccnt, _state);
            while(minlmiteration(&state, _state))
            {
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:877");
                    ae_set_error_flag(errorflag, ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:878");
                }
                if( state.needfi )
                {
                    testminlmunit_testfunc1(n, m, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_false, _state);
                    continue;
                }
                if( state.needfij )
                {
                    testminlmunit_testfunc1(n, m, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_true, _state);
                    continue;
                }
                ae_assert(ae_false, "minlm test: integrity check failed", _state);
            }
            minlmresults(&state, &x, &rep, _state);
            ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:893");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Test feasibility w.r.t. box and linear inequality constraints
             */
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_less(x.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:902");
                ae_set_error_flag(errorflag, ae_fp_greater(x.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:903");
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                v = v-rawc.ptr.pp_double[i][n];
                if( rawct.ptr.p_int[i]>0 )
                {
                    ae_set_error_flag(errorflag, ae_fp_less(v,-xtol), __FILE__, __LINE__, "testminlmunit.ap:910");
                }
                if( rawct.ptr.p_int[i]<0 )
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(v,xtol), __FILE__, __LINE__, "testminlmunit.ap:912");
                }
            }
            
            /*
             * Make several random trial steps and:
             * 0) generate small random trial step
             * 1) if step is infeasible, skip to next trial
             * 2) compare function value in the trial point against one in other points
             */
            testminlmunit_testfunc1(n, m, &c, &x, &f0, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
            ae_vector_set_length(&x1, n, _state);
            for(trialidx=0; trialidx<=10*n; trialidx++)
            {
                h = 0.001;
                for(i=0; i<=n-1; i++)
                {
                    do
                    {
                        x1.ptr.p_double[i] = x.ptr.p_double[i]+(hqrnduniformr(&rs, _state)*2-1)*h;
                    }
                    while(!(ae_fp_greater_eq(x1.ptr.p_double[i],bl.ptr.p_double[i])&&ae_fp_less_eq(x1.ptr.p_double[i],bu.ptr.p_double[i])));
                }
                bflag = ae_false;
                for(i=0; i<=rawccnt-1; i++)
                {
                    ae_assert(rawct.ptr.p_int[i]!=0, "Assertion failed", _state);
                    v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    v = v-rawc.ptr.pp_double[i][n];
                    bflag = bflag||(rawct.ptr.p_int[i]>0&&ae_fp_less(v,(double)(0)));
                    bflag = bflag||(rawct.ptr.p_int[i]<0&&ae_fp_greater(v,(double)(0)));
                }
                if( bflag )
                {
                    continue;
                }
                testminlmunit_testfunc1(n, m, &c, &x1, &f1, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
                ae_set_error_flag(errorflag, ae_fp_less(f1,f0*(1-tolf)), __FILE__, __LINE__, "testminlmunit.ap:949");
            }
        }
    }
    
    /*
     * Minimize
     *
     *          [      [                             ]2  ]
     *     SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ]   ]
     *          [      [                             ]   ]
     *
     * subject to linear EQUALITY constraints on x_j.
     *
     * We check correctness of solution by sampling a few random points
     * around one returned by optimizer, and comparing function value
     * with target. Sampling is performed with respect to equality
     * constraints. In order to simplify algorithm we use orthogonal
     * equality constraints.
     *
     * NOTE: we solve problem using VJ mode (analytic Jacobian) because
     *       roundoff errors from numerical differentiation sometimes
     *       prevent us from converging with good precision.
     */
    epsx = 1.0E-12;
    xtol = 1.0E-8;
    tolf = 1.0E-10;
    optkind = 1;
    for(n=10; n<=20; n++)
    {
        
        /*
         * Generate problem
         */
        m = n+hqrnduniformi(&rs, n, _state);
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        ae_matrix_set_length(&c, m, n+1, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n; j++)
            {
                c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
            }
        }
        ae_assert(n>=5, "Assertion failed", _state);
        rawccnt = 1+hqrnduniformi(&rs, 5, _state);
        rmatrixrndorthogonal(n, &z, _state);
        ae_matrix_set_length(&rawc, rawccnt, n+1, _state);
        ae_vector_set_length(&rawct, rawccnt, _state);
        for(i=0; i<=rawccnt-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                rawc.ptr.pp_double[i][j] = z.ptr.pp_double[i][j];
                v = v+x0.ptr.p_double[j]*rawc.ptr.pp_double[i][j];
            }
            rawc.ptr.pp_double[i][n] = v;
            rawct.ptr.p_int[i] = 0;
        }
        
        /*
         * Solve
         */
        if( optkind==0 )
        {
            minlmcreatev(n, m, &x0, 1.0E-12, &state, _state);
        }
        if( optkind==1 )
        {
            minlmcreatevj(n, m, &x0, &state, _state);
        }
        minlmsetcond(&state, epsx, 0, _state);
        minlmsetlc(&state, &rawc, &rawct, rawccnt, _state);
        while(minlmiteration(&state, _state))
        {
            if( state.needfi )
            {
                testminlmunit_testfunc1(n, m, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_false, _state);
                continue;
            }
            if( state.needfij )
            {
                testminlmunit_testfunc1(n, m, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_true, _state);
                continue;
            }
            ae_assert(ae_false, "minlm test: integrity check failed", _state);
        }
        minlmresults(&state, &x, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:1030");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Test feasibility w.r.t. linear equality constraints
         */
        for(i=0; i<=rawccnt-1; i++)
        {
            v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = v-rawc.ptr.pp_double[i][n];
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(v, _state),xtol), __FILE__, __LINE__, "testminlmunit.ap:1041");
        }
        
        /*
         * Make several random trial steps and:
         * 0) generate small random trial step
         * 1) project it onto equality constrained subspace
         * 2) compare function value in the trial point against one in other points
         */
        testminlmunit_testfunc1(n, m, &c, &x, &f0, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
        ae_vector_set_length(&x1, n, _state);
        for(trialidx=0; trialidx<=10*n; trialidx++)
        {
            h = 0.001;
            for(i=0; i<=n-1; i++)
            {
                x1.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                ae_v_subd(&x1.ptr.p_double[0], 1, &rawc.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
            }
            v = ae_v_dotproduct(&x1.ptr.p_double[0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,n-1));
            ae_assert(ae_fp_greater(v,(double)(0)), "Assertion failed", _state);
            v = h/ae_sqrt(v, _state);
            ae_v_muld(&x1.ptr.p_double[0], 1, ae_v_len(0,n-1), v);
            ae_v_add(&x1.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            testminlmunit_testfunc1(n, m, &c, &x1, &f1, ae_true, &state.fi, ae_false, &state.j, ae_false, _state);
            ae_set_error_flag(errorflag, ae_fp_less(f1,f0*(1-tolf)), __FILE__, __LINE__, "testminlmunit.ap:1071");
        }
    }
    
    /*
     * Minimize
     *
     *          [      [                               ]2  ]        [      [                               ]2  ]
     *     SUM_i[ SUM_j[ (0.1*x_j+power(x0_j,3))*c0_ij ]   ] + SUM_i[ SUM_j[ (0.1*x_j+power(x1_j,3))*c1_ij ]   ]
     *          [      [                               ]   ]        [      [                               ]   ]
     *
     * for two sets of unknowns (x0_j and x1_j) and two sets of
     * coefficients (c0_ij and c1_ij, M1*N and M2*N matrices) subject
     * to equality constraint
     *
     *     x0_j=x1_j for all j
     * 
     * Such optimization problem arises when we fit same model to
     * two distinct datasets and want to share SOME of coefficients
     * between fits. If we share ALL coefficients, it is equal to
     * fitting one model to combination of two datasets.
     *
     * Our test checks that such "combined" 2N-dimensional problem
     * solved with general linear constraints which "glue" two datasets
     * together returns same answer as N-dimensional problem on (M1+M2)-point
     * dataset.
     *
     * NOTE: we solve problem using VJ mode (analytic Jacobian) because
     *       roundoff errors from numerical differentiation prevent us
     *       from converging with good precision.
     */
    epsx = 1.0E-12;
    for(n=5; n<=20; n++)
    {
        
        /*
         * Generate problems
         */
        m1 = n+hqrnduniformi(&rs, n, _state);
        m2 = n+hqrnduniformi(&rs, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&x12, 2*n, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = hqrndnormal(&rs, _state);
            x12.ptr.p_double[2*i+0] = hqrndnormal(&rs, _state);
            x12.ptr.p_double[2*i+1] = hqrndnormal(&rs, _state);
        }
        ae_matrix_set_length(&c, m1+m2, n+1, _state);
        ae_matrix_set_length(&c12, m1+m2, 2*n+1, _state);
        for(i=0; i<=m1+m2-1; i++)
        {
            for(j=0; j<=2*n; j++)
            {
                c12.ptr.pp_double[i][j] = (double)(0);
            }
        }
        for(i=0; i<=m1+m2-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                if( i<m1 )
                {
                    c12.ptr.pp_double[i][j] = c.ptr.pp_double[i][j];
                }
                else
                {
                    c12.ptr.pp_double[i][n+j] = c.ptr.pp_double[i][j];
                }
            }
            c.ptr.pp_double[i][n] = hqrndnormal(&rs, _state);
            c12.ptr.pp_double[i][2*n] = c.ptr.pp_double[i][n];
        }
        rawccnt = n;
        ae_matrix_set_length(&rawc, rawccnt, 2*n+1, _state);
        ae_vector_set_length(&rawct, rawccnt, _state);
        for(i=0; i<=rawccnt-1; i++)
        {
            for(j=0; j<=2*n; j++)
            {
                rawc.ptr.pp_double[i][j] = (double)(0);
            }
            rawc.ptr.pp_double[i][i] = (double)(1);
            rawc.ptr.pp_double[i][n+i] = (double)(-1);
            rawct.ptr.p_int[i] = 0;
        }
        
        /*
         * Solve N-dimensional "combined" problem, store result to X1
         */
        minlmcreatevj(n, m1+m2, &x0, &state, _state);
        minlmsetcond(&state, epsx, 0, _state);
        while(minlmiteration(&state, _state))
        {
            if( state.needfi )
            {
                testminlmunit_testfunc1(n, m1+m2, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_false, _state);
                continue;
            }
            if( state.needfij )
            {
                testminlmunit_testfunc1(n, m1+m2, &c, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_true, _state);
                continue;
            }
            ae_assert(ae_false, "minlm test: integrity check failed", _state);
        }
        minlmresults(&state, &x, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:1169");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Solve N-dimensional "glued" problem, store result to X12
         */
        minlmcreatevj(2*n, m1+m2, &x12, &state, _state);
        minlmsetcond(&state, epsx, 0, _state);
        minlmsetlc(&state, &rawc, &rawct, rawccnt, _state);
        while(minlmiteration(&state, _state))
        {
            if( state.needfi )
            {
                testminlmunit_testfunc1(2*n, m1+m2, &c12, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_false, _state);
                continue;
            }
            if( state.needfij )
            {
                testminlmunit_testfunc1(2*n, m1+m2, &c12, &state.x, &v, ae_false, &state.fi, ae_true, &state.j, ae_true, _state);
                continue;
            }
            ae_assert(ae_false, "minlm test: integrity check failed", _state);
        }
        minlmresults(&state, &x12, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:1194");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Compare solutions
         */
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[i]-x12.ptr.p_double[i], _state),1.0E-3), __FILE__, __LINE__, "testminlmunit.ap:1203");
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x.ptr.p_double[i]-x12.ptr.p_double[n+i], _state),1.0E-3), __FILE__, __LINE__, "testminlmunit.ap:1204");
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x12.ptr.p_double[i]-x12.ptr.p_double[n+i], _state),1.0E-6), __FILE__, __LINE__, "testminlmunit.ap:1205");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test other properties
On failure sets error flag, leaves it unchanged on success.
*************************************************************************/
static void testminlmunit_testother(ae_bool* errorflag,
     ae_bool* statefieldsconsistencyflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    minlmstate state;
    minlmreport rep;
    ae_int_t n;
    ae_int_t m;
    double v;
    double s;
    ae_vector x;
    ae_vector xlast;
    ae_int_t i;
    ae_int_t j;
    ae_int_t rkind;
    double fprev;
    double xprev;
    double stpmax;
    ae_int_t stopcallidx;
    ae_int_t callidx;
    ae_int_t maxits;
    ae_bool terminationrequested;
    ae_int_t pass;
    ae_int_t spoilcnt;
    double mx;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&x, 0, sizeof(x));
    memset(&xlast, 0, sizeof(xlast));
    _minlmstate_init(&state, _state, ae_true);
    _minlmreport_init(&rep, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);

    
    /*
     * Other properties:
     * 1. test reports (F should form monotone sequence)
     * 2. test maximum step
     */
    for(rkind=0; rkind<=5; rkind++)
    {
        
        /*
         * reports:
         * * check that first report is initial point
         * * check that F is monotone decreasing
         * * check that last report is final result
         */
        n = 3;
        m = 3;
        s = (double)(100);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 6+ae_randomreal(_state);
        }
        if( rkind==0 )
        {
            minlmcreatefj(n, m, &x, &state, _state);
        }
        if( rkind==1 )
        {
            minlmcreatefgj(n, m, &x, &state, _state);
        }
        if( rkind==2 )
        {
            minlmcreatefgh(n, &x, &state, _state);
        }
        if( rkind==3 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==4 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==5 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 2, _state);
        }
        minlmsetcond(&state, (double)(0), 4, _state);
        minlmsetxrep(&state, ae_true, _state);
        fprev = ae_maxrealnumber;
        while(minlmiteration(&state, _state))
        {
            if( state.needfi||state.needfij )
            {
                state.fi.ptr.p_double[0] = ae_sqrt(s, _state)*(ae_exp(state.x.ptr.p_double[0], _state)-2);
                state.fi.ptr.p_double[1] = state.x.ptr.p_double[1];
                state.fi.ptr.p_double[2] = state.x.ptr.p_double[2]-state.x.ptr.p_double[0];
            }
            if( state.needfij )
            {
                state.j.ptr.pp_double[0][0] = ae_sqrt(s, _state)*ae_exp(state.x.ptr.p_double[0], _state);
                state.j.ptr.pp_double[0][1] = (double)(0);
                state.j.ptr.pp_double[0][2] = (double)(0);
                state.j.ptr.pp_double[1][0] = (double)(0);
                state.j.ptr.pp_double[1][1] = (double)(1);
                state.j.ptr.pp_double[1][2] = (double)(0);
                state.j.ptr.pp_double[2][0] = (double)(-1);
                state.j.ptr.pp_double[2][1] = (double)(0);
                state.j.ptr.pp_double[2][2] = (double)(1);
            }
            if( (state.needf||state.needfg)||state.needfgh )
            {
                state.f = s*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
            }
            if( state.needfg||state.needfgh )
            {
                state.g.ptr.p_double[0] = s*2*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
            }
            if( state.needfgh )
            {
                state.h.ptr.pp_double[0][0] = s*(4*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state), _state)-4*ae_exp(state.x.ptr.p_double[0], _state))+2;
                state.h.ptr.pp_double[0][1] = (double)(0);
                state.h.ptr.pp_double[0][2] = (double)(-2);
                state.h.ptr.pp_double[1][0] = (double)(0);
                state.h.ptr.pp_double[1][1] = (double)(2);
                state.h.ptr.pp_double[1][2] = (double)(0);
                state.h.ptr.pp_double[2][0] = (double)(-2);
                state.h.ptr.pp_double[2][1] = (double)(0);
                state.h.ptr.pp_double[2][2] = (double)(2);
            }
            ae_set_error_flag(statefieldsconsistencyflag, !testminlmunit_rkindvsstatecheck(rkind, &state, _state), __FILE__, __LINE__, "testminlmunit.ap:1319");
            if( state.xupdated )
            {
                ae_set_error_flag(errorflag, ae_fp_greater(state.f,fprev), __FILE__, __LINE__, "testminlmunit.ap:1322");
                if( ae_fp_eq(fprev,ae_maxrealnumber) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:1325");
                    }
                }
                fprev = state.f;
                ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            }
        }
        minlmresults(&state, &x, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testminlmunit.ap:1331");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:1335");
        }
    }
    n = 1;
    ae_vector_set_length(&x, n, _state);
    x.ptr.p_double[0] = (double)(100);
    stpmax = 0.05+0.05*ae_randomreal(_state);
    minlmcreatefgh(n, &x, &state, _state);
    minlmsetcond(&state, 1.0E-12, 0, _state);
    minlmsetstpmax(&state, stpmax, _state);
    minlmsetxrep(&state, ae_true, _state);
    xprev = x.ptr.p_double[0];
    while(minlmiteration(&state, _state))
    {
        if( (state.needf||state.needfg)||state.needfgh )
        {
            state.f = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
        }
        if( state.needfg||state.needfgh )
        {
            state.g.ptr.p_double[0] = ae_exp(state.x.ptr.p_double[0], _state)-ae_exp(-state.x.ptr.p_double[0], _state);
        }
        if( state.needfgh )
        {
            state.h.ptr.pp_double[0][0] = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
        }
        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax), __FILE__, __LINE__, "testminlmunit.ap:1354");
        if( state.xupdated )
        {
            xprev = state.x.ptr.p_double[0];
        }
    }
    
    /*
     * Check algorithm ability to handle request for termination:
     * * to terminate with correct return code = 8
     * * to return point which was "current" at the moment of termination
     */
    for(pass=1; pass<=50; pass++)
    {
        n = 3;
        m = 3;
        s = (double)(100);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 6+ae_randomreal(_state);
        }
        stopcallidx = ae_randominteger(20, _state);
        maxits = 25;
        minlmcreatevj(n, m, &x, &state, _state);
        minlmsetcond(&state, (double)(0), maxits, _state);
        minlmsetxrep(&state, ae_true, _state);
        callidx = 0;
        terminationrequested = ae_false;
        ae_v_move(&xlast.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        while(minlmiteration(&state, _state))
        {
            if( state.needfi||state.needfij )
            {
                state.fi.ptr.p_double[0] = ae_sqrt(s, _state)*(ae_exp(state.x.ptr.p_double[0], _state)-2);
                state.fi.ptr.p_double[1] = state.x.ptr.p_double[1];
                state.fi.ptr.p_double[2] = state.x.ptr.p_double[2]-state.x.ptr.p_double[0];
                if( state.needfij )
                {
                    state.j.ptr.pp_double[0][0] = ae_sqrt(s, _state)*ae_exp(state.x.ptr.p_double[0], _state);
                    state.j.ptr.pp_double[0][1] = (double)(0);
                    state.j.ptr.pp_double[0][2] = (double)(0);
                    state.j.ptr.pp_double[1][0] = (double)(0);
                    state.j.ptr.pp_double[1][1] = (double)(1);
                    state.j.ptr.pp_double[1][2] = (double)(0);
                    state.j.ptr.pp_double[2][0] = (double)(-1);
                    state.j.ptr.pp_double[2][1] = (double)(0);
                    state.j.ptr.pp_double[2][2] = (double)(1);
                }
                if( callidx==stopcallidx )
                {
                    minlmrequesttermination(&state, _state);
                    terminationrequested = ae_true;
                }
                inc(&callidx, _state);
                continue;
            }
            if( state.xupdated )
            {
                if( !terminationrequested )
                {
                    ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                }
                continue;
            }
            ae_assert(ae_false, "Assertion failed", _state);
        }
        minlmresults(&state, &x, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype!=8, __FILE__, __LINE__, "testminlmunit.ap:1418");
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]), __FILE__, __LINE__, "testminlmunit.ap:1420");
        }
    }
    
    /*
     * Test ability to detect NAN/INF values.
     *
     * We use Rosenbrock's function which is modified to return
     * NAN/INF (randomly) when near solution. Algorithm should set
     * appropriate error code (-8) on exit.
     */
    n = 10;
    m = 2*(n-1);
    s = (double)(10);
    ae_vector_set_length(&x, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = (double)(0);
    }
    minlmcreatevj(n, m, &x, &state, _state);
    minlmsetcond(&state, 1.0E-12, 0, _state);
    spoilcnt = 0;
    while(minlmiteration(&state, _state))
    {
        if( state.needfij )
        {
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    state.j.ptr.pp_double[i][j] = (double)(0);
                }
            }
            for(i=0; i<=n-2; i++)
            {
                state.fi.ptr.p_double[2*i+0] = s*(state.x.ptr.p_double[i+1]-ae_sqr(state.x.ptr.p_double[i], _state));
                state.j.ptr.pp_double[2*i+0][i] = -s*2*state.x.ptr.p_double[i];
                state.j.ptr.pp_double[2*i+0][i+1] = s;
                state.fi.ptr.p_double[2*i+1] = 1-state.x.ptr.p_double[i];
                state.j.ptr.pp_double[2*i+1][i] = (double)(-1);
            }
            mx = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                mx = ae_maxreal(mx, ae_fabs(state.x.ptr.p_double[i]-1, _state), _state);
            }
            if( ae_fp_less(mx,1.0E-2) )
            {
                i = ae_randominteger(3, _state);
                v = _state->v_nan;
                if( i==1 )
                {
                    v = _state->v_posinf;
                }
                if( i==2 )
                {
                    v = _state->v_neginf;
                }
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    state.fi.ptr.p_double[ae_randominteger(m, _state)] = v;
                }
                else
                {
                    state.j.ptr.pp_double[ae_randominteger(m, _state)][ae_randominteger(n, _state)] = v;
                }
                inc(&spoilcnt, _state);
            }
            continue;
        }
        if( state.needfi )
        {
            for(i=0; i<=n-2; i++)
            {
                state.fi.ptr.p_double[2*i+0] = s*(state.x.ptr.p_double[i+1]-ae_sqr(state.x.ptr.p_double[i], _state));
                state.fi.ptr.p_double[2*i+1] = 1-state.x.ptr.p_double[i];
            }
            mx = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                mx = ae_maxreal(mx, ae_fabs(state.x.ptr.p_double[i]-1, _state), _state);
            }
            if( ae_fp_less(mx,1.0E-2) )
            {
                i = ae_randominteger(3, _state);
                v = _state->v_nan;
                if( i==1 )
                {
                    v = _state->v_posinf;
                }
                if( i==2 )
                {
                    v = _state->v_neginf;
                }
                state.fi.ptr.p_double[ae_randominteger(m, _state)] = v;
                inc(&spoilcnt, _state);
            }
            continue;
        }
        ae_assert(ae_false, "Assertion failed", _state);
    }
    minlmresults(&state, &x, &rep, _state);
    ae_set_error_flag(errorflag, rep.terminationtype!=-8, __FILE__, __LINE__, "testminlmunit.ap:1512");
    ae_set_error_flag(errorflag, spoilcnt!=1, __FILE__, __LINE__, "testminlmunit.ap:1513");
    ae_frame_leave(_state);
}


/*************************************************************************
Asserts that State fields are consistent with RKind.
Returns False otherwise.

RKind is an algorithm selector:
* -2 = V, AccType=1
* -1 = V, AccType=0
*  0 = FJ
*  1 = FGJ
*  2 = FGH
*  3 = VJ, AccType=0
*  4 = VJ, AccType=1
*  5 = VJ, AccType=2

*************************************************************************/
static ae_bool testminlmunit_rkindvsstatecheck(ae_int_t rkind,
     minlmstate* state,
     ae_state *_state)
{
    ae_int_t nset;
    ae_bool result;


    nset = 0;
    if( state->needfi )
    {
        nset = nset+1;
    }
    if( state->needf )
    {
        nset = nset+1;
    }
    if( state->needfg )
    {
        nset = nset+1;
    }
    if( state->needfij )
    {
        nset = nset+1;
    }
    if( state->needfgh )
    {
        nset = nset+1;
    }
    if( state->xupdated )
    {
        nset = nset+1;
    }
    if( nset!=1 )
    {
        result = ae_false;
        return result;
    }
    if( rkind==-2 )
    {
        result = state->needfi||state->xupdated;
        return result;
    }
    if( rkind==-1 )
    {
        result = state->needfi||state->xupdated;
        return result;
    }
    if( rkind==0 )
    {
        result = (state->needf||state->needfij)||state->xupdated;
        return result;
    }
    if( rkind==1 )
    {
        result = ((state->needf||state->needfij)||state->needfg)||state->xupdated;
        return result;
    }
    if( rkind==2 )
    {
        result = ((state->needf||state->needfg)||state->needfgh)||state->xupdated;
        return result;
    }
    if( rkind==3 )
    {
        result = (state->needfi||state->needfij)||state->xupdated;
        return result;
    }
    if( rkind==4 )
    {
        result = (state->needfi||state->needfij)||state->xupdated;
        return result;
    }
    if( rkind==5 )
    {
        result = (state->needfi||state->needfij)||state->xupdated;
        return result;
    }
    result = ae_false;
    return result;
}


/*************************************************************************
Calculates FI/F/G/H for problem min(||Ax-b||)
*************************************************************************/
static void testminlmunit_axmb(minlmstate* state,
     /* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;


    if( (state->needf||state->needfg)||state->needfgh )
    {
        state->f = (double)(0);
    }
    if( state->needfg||state->needfgh )
    {
        for(i=0; i<=n-1; i++)
        {
            state->g.ptr.p_double[i] = (double)(0);
        }
    }
    if( state->needfgh )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                state->h.ptr.pp_double[i][j] = (double)(0);
            }
        }
    }
    for(i=0; i<=n-1; i++)
    {
        v = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        if( (state->needf||state->needfg)||state->needfgh )
        {
            state->f = state->f+ae_sqr(v-b->ptr.p_double[i], _state);
        }
        if( state->needfg||state->needfgh )
        {
            for(j=0; j<=n-1; j++)
            {
                state->g.ptr.p_double[j] = state->g.ptr.p_double[j]+2*(v-b->ptr.p_double[i])*a->ptr.pp_double[i][j];
            }
        }
        if( state->needfgh )
        {
            for(j=0; j<=n-1; j++)
            {
                for(k=0; k<=n-1; k++)
                {
                    state->h.ptr.pp_double[j][k] = state->h.ptr.pp_double[j][k]+2*a->ptr.pp_double[i][j]*a->ptr.pp_double[i][k];
                }
            }
        }
        if( state->needfi )
        {
            state->fi.ptr.p_double[i] = v-b->ptr.p_double[i];
        }
        if( state->needfij )
        {
            state->fi.ptr.p_double[i] = v-b->ptr.p_double[i];
            ae_v_move(&state->j.ptr.pp_double[i][0], 1, &a->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
        }
    }
}


/*************************************************************************
This function tries to reproduce previously fixed bugs; in case of bug
being present sets Err to True; leaves it unchanged otherwise.
*************************************************************************/
static void testminlmunit_tryreproducefixedbugs(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    minlmstate s;
    minlmreport rep;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    _minlmstate_init(&s, _state, ae_true);
    _minlmreport_init(&rep, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    
    /*
     * Reproduce bug reported by ISS:
     * when solving bound constrained problem with numerical differentiation
     * and starting from infeasible point, we won't stop at the feasible point
     */
    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&bl, 2, _state);
    ae_vector_set_length(&bu, 2, _state);
    x.ptr.p_double[0] = 2.0;
    bl.ptr.p_double[0] = -1.0;
    bu.ptr.p_double[0] = 1.0;
    x.ptr.p_double[1] = 2.0;
    bl.ptr.p_double[1] = -1.0;
    bu.ptr.p_double[1] = 1.0;
    minlmcreatev(2, 2, &x, 0.001, &s, _state);
    minlmsetbc(&s, &bl, &bu, _state);
    while(minlmiteration(&s, _state))
    {
        if( s.needfi )
        {
            s.fi.ptr.p_double[0] = ae_sqr(s.x.ptr.p_double[0], _state);
            s.fi.ptr.p_double[1] = ae_sqr(s.x.ptr.p_double[1], _state);
        }
    }
    minlmresults(&s, &x, &rep, _state);
    ae_set_error_flag(err, ((ae_fp_less(x.ptr.p_double[0],bl.ptr.p_double[0])||ae_fp_greater(x.ptr.p_double[0],bu.ptr.p_double[0]))||ae_fp_less(x.ptr.p_double[1],bl.ptr.p_double[1]))||ae_fp_greater(x.ptr.p_double[1],bu.ptr.p_double[1]), __FILE__, __LINE__, "testminlmunit.ap:1673");
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests, that gradient verified correctly.
*************************************************************************/
static ae_bool testminlmunit_gradientchecktest(ae_state *_state)
{
    ae_frame _frame_block;
    minlmstate state;
    minlmreport rep;
    ae_int_t n;
    ae_int_t m;
    ae_vector a;
    ae_vector x0;
    ae_vector x;
    ae_vector bl;
    ae_vector bu;
    ae_int_t infcomp;
    double teststep;
    double noise;
    double rndconst;
    ae_int_t nbrfunc;
    ae_int_t nbrcomp;
    ae_int_t func;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&x0, 0, sizeof(x0));
    memset(&x, 0, sizeof(x));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    _minlmstate_init(&state, _state, ae_true);
    _minlmreport_init(&rep, _state, ae_true);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);

    passcount = 35;
    teststep = 0.01;
    for(pass=1; pass<=passcount; pass++)
    {
        n = ae_randominteger(10, _state)+1;
        m = ae_randominteger(10, _state)+1;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&a, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        
        /*
         * Prepare test's parameters
         */
        func = ae_randominteger(3, _state)+1;
        nbrfunc = ae_randominteger(m, _state);
        nbrcomp = ae_randominteger(n, _state);
        noise = (double)(2*ae_randominteger(2, _state)-1);
        rndconst = 2*ae_randomreal(_state)-1;
        
        /*
         * Prepare function's parameters
         */
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
            a.ptr.p_double[i] = 5*ae_randomreal(_state)+1;
            x0.ptr.p_double[i] = 5*(2*ae_randomreal(_state)-1);
        }
        
        /*
         * Prepare boundary parameters
         */
        for(i=0; i<=n-1; i++)
        {
            bl.ptr.p_double[i] = -3*ae_randomreal(_state)-0.1;
            bu.ptr.p_double[i] = 3*ae_randomreal(_state)+0.1;
        }
        infcomp = ae_randominteger(n+1, _state);
        if( infcomp<n )
        {
            bl.ptr.p_double[infcomp] = _state->v_neginf;
        }
        infcomp = ae_randominteger(n+1, _state);
        if( infcomp<n )
        {
            bu.ptr.p_double[infcomp] = _state->v_posinf;
        }
        minlmcreatevj(n, m, &x, &state, _state);
        minlmsetcond(&state, (double)(0), 0, _state);
        minlmsetgradientcheck(&state, teststep, _state);
        minlmsetbc(&state, &bl, &bu, _state);
        
        /*
         * Check that the criterion passes a derivative if it is correct
         */
        while(minlmiteration(&state, _state))
        {
            if( state.needfij )
            {
                
                /*
                 * Check hat .X within the boundaries
                 */
                for(i=0; i<=n-1; i++)
                {
                    if( (ae_isfinite(bl.ptr.p_double[i], _state)&&ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]))||(ae_isfinite(bu.ptr.p_double[i], _state)&&ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i])) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
                testminlmunit_funcderiv(&a, &x0, &state.x, m, n, rndconst, func, &state.fi, &state.j, _state);
            }
        }
        minlmresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code does not equal to -7 and parameter .VarIdx
         * equal to -1.
         */
        if( (rep.terminationtype==-7||rep.funcidx!=-1)||rep.varidx!=-1 )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 5*randomnormal(_state);
        }
        minlmrestartfrom(&state, &x, _state);
        
        /*
         * Check that the criterion does not miss a derivative if
         * it is incorrect
         */
        while(minlmiteration(&state, _state))
        {
            if( state.needfij )
            {
                for(i=0; i<=n-1; i++)
                {
                    if( (ae_isfinite(bl.ptr.p_double[i], _state)&&ae_fp_less(state.x.ptr.p_double[i],bl.ptr.p_double[i]))||(ae_isfinite(bu.ptr.p_double[i], _state)&&ae_fp_greater(state.x.ptr.p_double[i],bu.ptr.p_double[i])) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
                testminlmunit_funcderiv(&a, &x0, &state.x, m, n, rndconst, func, &state.fi, &state.j, _state);
                state.j.ptr.pp_double[nbrfunc][nbrcomp] = state.j.ptr.pp_double[nbrfunc][nbrcomp]+noise;
            }
        }
        minlmresults(&state, &x, &rep, _state);
        
        /*
         * Check that error code equal to -7 and parameter .VarIdx
         * equal to number of incorrect component.
         */
        if( (rep.terminationtype!=-7||rep.funcidx!=nbrfunc)||rep.varidx!=nbrcomp )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function return function value and it derivatives. The number of
functions is M, dimension for each of functions is N.
F(XI)=SUM(fi(XI)); (XI={x,y,z}; i=0..M-1);
    Function's list:
        * funcType=1:
            fi(X)=(Aj*Xj-X0j)^2;
        * funcType=2:
            fi(X)=Aj*sin(Xj-X0j);
        * funcType=3:
            fi(X)=Aj*Xj-X0j;
            fM-1(X)=A(M-1)*((X(M-1)-X0(M-1))-(X0-X00)).
*************************************************************************/
static void testminlmunit_funcderiv(/* Real    */ ae_vector* a,
     /* Real    */ ae_vector* x0,
     /* Real    */ ae_vector* x,
     ae_int_t m,
     ae_int_t n,
     double anyconst,
     ae_int_t functype,
     /* Real    */ ae_vector* f,
     /* Real    */ ae_matrix* j,
     ae_state *_state)
{
    ae_int_t i0;
    ae_int_t j0;


    ae_assert(functype>=1&&functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).", _state);
    ae_assert(n>0, "FuncDeriv: N<=0", _state);
    ae_assert(m>0, "FuncDeriv: M<=0", _state);
    ae_assert(x->cnt>=n, "FuncDeriv: Length(X)<N", _state);
    ae_assert(isfinitevector(x, n, _state), "FuncDeriv: X contains NaN or Infinite.", _state);
    ae_assert(x0->cnt>=n, "FuncDeriv: Length(X0)<N", _state);
    ae_assert(isfinitevector(x0, n, _state), "FuncDeriv: X0 contains NaN or Infinite.", _state);
    ae_assert(a->cnt>=n, "FuncDeriv: Length(X)<N", _state);
    ae_assert(isfinitevector(a, n, _state), "FuncDeriv: A contains NaN or Infinite.", _state);
    if( functype==1 )
    {
        for(i0=0; i0<=m-1; i0++)
        {
            if( i0<n )
            {
                f->ptr.p_double[i0] = a->ptr.p_double[i0]*x->ptr.p_double[i0]-x0->ptr.p_double[i0];
            }
            else
            {
                f->ptr.p_double[i0] = anyconst;
            }
        }
        for(i0=0; i0<=m-1; i0++)
        {
            for(j0=0; j0<=n-1; j0++)
            {
                if( i0==j0 )
                {
                    j->ptr.pp_double[i0][j0] = a->ptr.p_double[j0];
                }
                else
                {
                    j->ptr.pp_double[i0][j0] = (double)(0);
                }
            }
        }
        return;
    }
    if( functype==2 )
    {
        for(i0=0; i0<=m-1; i0++)
        {
            if( i0<n )
            {
                f->ptr.p_double[i0] = a->ptr.p_double[i0]*ae_sin(x->ptr.p_double[i0]-x0->ptr.p_double[i0], _state);
            }
            else
            {
                f->ptr.p_double[i0] = anyconst;
            }
        }
        for(i0=0; i0<=m-1; i0++)
        {
            for(j0=0; j0<=n-1; j0++)
            {
                if( i0==j0 )
                {
                    j->ptr.pp_double[i0][j0] = a->ptr.p_double[j0]*ae_cos(x->ptr.p_double[j0]-x0->ptr.p_double[j0], _state);
                }
                else
                {
                    j->ptr.pp_double[i0][j0] = (double)(0);
                }
            }
        }
        return;
    }
    if( functype==3 )
    {
        for(i0=0; i0<=m-1; i0++)
        {
            if( i0<n )
            {
                f->ptr.p_double[i0] = a->ptr.p_double[i0]*x->ptr.p_double[i0]-x0->ptr.p_double[i0];
            }
            else
            {
                f->ptr.p_double[i0] = anyconst;
            }
        }
        for(i0=0; i0<=m-1; i0++)
        {
            for(j0=0; j0<=n-1; j0++)
            {
                if( i0==j0 )
                {
                    j->ptr.pp_double[i0][j0] = a->ptr.p_double[j0];
                }
                else
                {
                    j->ptr.pp_double[i0][j0] = (double)(0);
                }
            }
        }
        if( m>n&&n>1 )
        {
            f->ptr.p_double[n-1] = a->ptr.p_double[n-1]*(x->ptr.p_double[n-1]-x0->ptr.p_double[n-1]-(x->ptr.p_double[0]-x0->ptr.p_double[0]));
            j->ptr.pp_double[n-1][0] = -a->ptr.p_double[n-1];
            j->ptr.pp_double[n-1][n-1] = a->ptr.p_double[n-1];
            for(i0=1; i0<=n-2; i0++)
            {
                j->ptr.pp_double[n-1][i0] = (double)(0);
            }
        }
        return;
    }
}


/*************************************************************************
Test function 1:

    F(N, M, C, X) = SUM( f_i^2 )
    f_i = SUM( (power(x_j,3)+alpha*x_j)*c_ij )
*************************************************************************/
static void testminlmunit_testfunc1(ae_int_t n,
     ae_int_t m,
     /* Real    */ ae_matrix* c,
     /* Real    */ ae_vector* x,
     double* f,
     ae_bool needf,
     /* Real    */ ae_vector* fi,
     ae_bool needfi,
     /* Real    */ ae_matrix* jac,
     ae_bool needjac,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    double alpha;


    alpha = 0.01;
    if( needf )
    {
        *f = (double)(0);
    }
    for(i=0; i<=m-1; i++)
    {
        v = c->ptr.pp_double[i][n];
        for(j=0; j<=n-1; j++)
        {
            v = v+(alpha*x->ptr.p_double[j]+ae_pow(x->ptr.p_double[j], (double)(3), _state))*c->ptr.pp_double[i][j];
            if( needjac )
            {
                jac->ptr.pp_double[i][j] = (alpha+3*ae_pow(x->ptr.p_double[j], (double)(2), _state))*c->ptr.pp_double[i][j];
            }
        }
        if( needfi )
        {
            fi->ptr.p_double[i] = v;
        }
        if( needf )
        {
            *f = *f+v*v;
        }
    }
}



static void testevdunit_rmatrixfillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     double diagmag,
     ae_state *_state);
static void testevdunit_cmatrixfillsparsea(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     double diagmag,
     ae_state *_state);
static void testevdunit_rmatrixsymmetricsplit(/* Real    */ ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_state *_state);
static void testevdunit_cmatrixhermitiansplit(/* Complex */ ae_matrix* a,
     ae_int_t n,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_state *_state);
static void testevdunit_unset2d(/* Real    */ ae_matrix* a,
     ae_state *_state);
static void testevdunit_cunset2d(/* Complex */ ae_matrix* a,
     ae_state *_state);
static void testevdunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state);
static double testevdunit_tdtestproduct(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     /* Real    */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state);
static double testevdunit_testproduct(/* Real    */ ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state);
static double testevdunit_testort(/* Real    */ ae_matrix* z,
     ae_int_t n,
     ae_state *_state);
static double testevdunit_testcproduct(/* Complex */ ae_matrix* a,
     ae_int_t n,
     /* Complex */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state);
static double testevdunit_testcort(/* Complex */ ae_matrix* z,
     ae_int_t n,
     ae_state *_state);
static void testevdunit_testsevdproblem(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_int_t n,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testhevdproblem(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_int_t n,
     double threshold,
     ae_bool* herrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testsevdbiproblem(/* Real    */ ae_matrix* afull,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testhevdbiproblem(/* Complex */ ae_matrix* afull,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* herrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testtdevdproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state);
static void testevdunit_testtdevdbiproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testnsevdproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* nserrors,
     ae_state *_state);
static void testevdunit_testevdset(ae_int_t n,
     double threshold,
     double bithreshold,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_bool* nserrors,
     ae_bool* serrors,
     ae_bool* herrors,
     ae_bool* tderrors,
     ae_bool* sbierrors,
     ae_bool* hbierrors,
     ae_bool* tdbierrors,
     ae_state *_state);
static void testevdunit_testsisymm(ae_bool* errorflag, ae_state *_state);





/*************************************************************************
Testing symmetric EVD subroutine
*************************************************************************/
ae_bool testevd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ra;
    ae_int_t n;
    ae_int_t j;
    ae_int_t failc;
    ae_int_t runs;
    double failthreshold;
    double threshold;
    double bithreshold;
    ae_bool waserrors;
    ae_bool nserrors;
    ae_bool serrors;
    ae_bool herrors;
    ae_bool tderrors;
    ae_bool sbierrors;
    ae_bool hbierrors;
    ae_bool tdbierrors;
    ae_bool sisymmerrors;
    ae_bool wfailed;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ra, 0, sizeof(ra));
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);

    failthreshold = 0.005;
    threshold = 1.0E-8;
    bithreshold = 1.0E-6;
    nserrors = ae_false;
    serrors = ae_false;
    herrors = ae_false;
    tderrors = ae_false;
    sbierrors = ae_false;
    hbierrors = ae_false;
    tdbierrors = ae_false;
    sisymmerrors = ae_false;
    failc = 0;
    runs = 0;
    
    /*
     * Test subspace iteration solver
     */
    testevdunit_testsisymm(&sisymmerrors, _state);
    
    /*
     * Test dense solvers
     */
    for(n=1; n<=ablasblocksize(&ra, _state); n++)
    {
        testevdunit_testevdset(n, threshold, bithreshold, &failc, &runs, &nserrors, &serrors, &herrors, &tderrors, &sbierrors, &hbierrors, &tdbierrors, _state);
    }
    for(j=2; j<=3; j++)
    {
        for(n=j*ablasblocksize(&ra, _state)-1; n<=j*ablasblocksize(&ra, _state)+1; n++)
        {
            testevdunit_testevdset(n, threshold, bithreshold, &failc, &runs, &nserrors, &serrors, &herrors, &tderrors, &sbierrors, &hbierrors, &tdbierrors, _state);
        }
    }
    
    /*
     * report
     */
    wfailed = ae_fp_greater((double)failc/(double)runs,failthreshold);
    waserrors = (((((((nserrors||serrors)||herrors)||tderrors)||sbierrors)||hbierrors)||tdbierrors)||wfailed)||sisymmerrors;
    if( !silent )
    {
        printf("TESTING EVD UNIT\n");
        printf("DENSE DIRECT SOLVERS:\n");
        printf("* NS                                     ");
        if( !nserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* S                                      ");
        if( !serrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* H                                      ");
        if( !herrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* TD                                     ");
        if( !tderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* SBI                                    ");
        if( !sbierrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* HBI                                    ");
        if( !hbierrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* TDBI                                   ");
        if( !tdbierrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* FAILURE THRESHOLD                      ");
        if( !wfailed )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("ITERATIVE SOLVERS:\n");
        printf("* SUBSPACE ITERATION (S)                 ");
        if( !sisymmerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Sparse fill

Sparcity - sparcity level, in [0,1] (0=dense matrix).
DiagMAg - magnitude of dense diagonal entries; zero value means that diagonal
is sparse too, non-zero value means that diagonal is dense
*************************************************************************/
static void testevdunit_rmatrixfillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     double diagmag,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_double[i][j] = (double)(0);
            }
        }
    }
    if( ae_fp_greater(diagmag,(double)(0)) )
    {
        for(i=0; i<=ae_minint(m, n, _state)-1; i++)
        {
            a->ptr.pp_double[i][i] = diagmag*(2*ae_randomreal(_state)-1);
        }
    }
}


/*************************************************************************
Sparse fill

Sparcity - sparcity level, in [0,1] (0=dense matrix).
DiagMAg - magnitude of dense diagonal entries; zero value means that diagonal
is sparse too, non-zero value means that diagonal is dense
*************************************************************************/
static void testevdunit_cmatrixfillsparsea(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     double diagmag,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a->ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_i(0);
            }
        }
    }
    if( ae_fp_greater(diagmag,(double)(0)) )
    {
        for(i=0; i<=ae_minint(m, n, _state)-1; i++)
        {
            a->ptr.pp_complex[i][i].x = diagmag*(2*ae_randomreal(_state)-1);
            a->ptr.pp_complex[i][i].y = diagmag*(2*ae_randomreal(_state)-1);
        }
    }
}


/*************************************************************************
Copies A to AL (lower half) and AU (upper half), filling unused parts by
random garbage.
*************************************************************************/
static void testevdunit_rmatrixsymmetricsplit(/* Real    */ ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            al->ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            al->ptr.pp_double[j][i] = a->ptr.pp_double[i][j];
            au->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
            au->ptr.pp_double[j][i] = 2*ae_randomreal(_state)-1;
        }
        al->ptr.pp_double[i][i] = a->ptr.pp_double[i][i];
        au->ptr.pp_double[i][i] = a->ptr.pp_double[i][i];
    }
}


/*************************************************************************
Copies A to AL (lower half) and AU (upper half), filling unused parts by
random garbage.
*************************************************************************/
static void testevdunit_cmatrixhermitiansplit(/* Complex */ ae_matrix* a,
     ae_int_t n,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            al->ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
            al->ptr.pp_complex[j][i] = ae_c_conj(a->ptr.pp_complex[i][j], _state);
            au->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
            au->ptr.pp_complex[j][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
        }
        al->ptr.pp_complex[i][i] = a->ptr.pp_complex[i][i];
        au->ptr.pp_complex[i][i] = a->ptr.pp_complex[i][i];
    }
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testevdunit_unset2d(/* Real    */ ae_matrix* a,
     ae_state *_state)
{

    ae_matrix_clear(a);

    if( a->rows*a->cols>0 )
    {
        ae_matrix_set_length(a, 1, 1, _state);
    }
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testevdunit_cunset2d(/* Complex */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testevdunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state)
{

    ae_vector_clear(a);

    if( a->cnt>0 )
    {
        ae_vector_set_length(a, 1, _state);
    }
}


/*************************************************************************
Tests Z*Lambda*Z' against tridiag(D,E).
Returns relative error.
*************************************************************************/
static double testevdunit_tdtestproduct(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     /* Real    */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double mx;
    double result;


    result = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Calculate V = A[i,j], A = Z*Lambda*Z'
             */
            v = (double)(0);
            for(k=0; k<=n-1; k++)
            {
                v = v+z->ptr.pp_double[i][k]*lambdav->ptr.p_double[k]*z->ptr.pp_double[j][k];
            }
            
            /*
             * Compare
             */
            if( ae_iabs(i-j, _state)==0 )
            {
                result = ae_maxreal(result, ae_fabs(v-d->ptr.p_double[i], _state), _state);
            }
            if( ae_iabs(i-j, _state)==1 )
            {
                result = ae_maxreal(result, ae_fabs(v-e->ptr.p_double[ae_minint(i, j, _state)], _state), _state);
            }
            if( ae_iabs(i-j, _state)>1 )
            {
                result = ae_maxreal(result, ae_fabs(v, _state), _state);
            }
        }
    }
    mx = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(d->ptr.p_double[i], _state), _state);
    }
    for(i=0; i<=n-2; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(e->ptr.p_double[i], _state), _state);
    }
    if( ae_fp_eq(mx,(double)(0)) )
    {
        mx = (double)(1);
    }
    result = result/mx;
    return result;
}


/*************************************************************************
Tests Z*Lambda*Z' against A
Returns relative error.
*************************************************************************/
static double testevdunit_testproduct(/* Real    */ ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double mx;
    double result;


    result = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Calculate V = A[i,j], A = Z*Lambda*Z'
             */
            v = (double)(0);
            for(k=0; k<=n-1; k++)
            {
                v = v+z->ptr.pp_double[i][k]*lambdav->ptr.p_double[k]*z->ptr.pp_double[j][k];
            }
            
            /*
             * Compare
             */
            result = ae_maxreal(result, ae_fabs(v-a->ptr.pp_double[i][j], _state), _state);
        }
    }
    mx = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            mx = ae_maxreal(mx, ae_fabs(a->ptr.pp_double[i][j], _state), _state);
        }
    }
    if( ae_fp_eq(mx,(double)(0)) )
    {
        mx = (double)(1);
    }
    result = result/mx;
    return result;
}


/*************************************************************************
Tests Z*Z' against diag(1...1)
Returns absolute error.
*************************************************************************/
static double testevdunit_testort(/* Real    */ ae_matrix* z,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    double result;


    result = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&z->ptr.pp_double[0][i], z->stride, &z->ptr.pp_double[0][j], z->stride, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            result = ae_maxreal(result, ae_fabs(v, _state), _state);
        }
    }
    return result;
}


/*************************************************************************
Tests Z*Lambda*Z' against A
Returns relative error.
*************************************************************************/
static double testevdunit_testcproduct(/* Complex */ ae_matrix* a,
     ae_int_t n,
     /* Complex */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_complex v;
    double mx;
    double result;


    result = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Calculate V = A[i,j], A = Z*Lambda*Z'
             */
            v = ae_complex_from_i(0);
            for(k=0; k<=n-1; k++)
            {
                v = ae_c_add(v,ae_c_mul(ae_c_mul_d(z->ptr.pp_complex[i][k],lambdav->ptr.p_double[k]),ae_c_conj(z->ptr.pp_complex[j][k], _state)));
            }
            
            /*
             * Compare
             */
            result = ae_maxreal(result, ae_c_abs(ae_c_sub(v,a->ptr.pp_complex[i][j]), _state), _state);
        }
    }
    mx = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            mx = ae_maxreal(mx, ae_c_abs(a->ptr.pp_complex[i][j], _state), _state);
        }
    }
    if( ae_fp_eq(mx,(double)(0)) )
    {
        mx = (double)(1);
    }
    result = result/mx;
    return result;
}


/*************************************************************************
Tests Z*Z' against diag(1...1)
Returns absolute error.
*************************************************************************/
static double testevdunit_testcort(/* Complex */ ae_matrix* z,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    double result;


    result = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&z->ptr.pp_complex[0][i], z->stride, "N", &z->ptr.pp_complex[0][j], z->stride, "Conj", ae_v_len(0,n-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            result = ae_maxreal(result, ae_c_abs(v, _state), _state);
        }
    }
    return result;
}


/*************************************************************************
Tests SEVD problem
*************************************************************************/
static void testevdunit_testsevdproblem(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_int_t n,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&lambdav, 0, sizeof(lambdav));
    memset(&lambdaref, 0, sizeof(lambdaref));
    memset(&z, 0, sizeof(z));
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test simple EVD: values and full vectors, lower A
     */
    testevdunit_unset1d(&lambdaref, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevd(al, n, 1, ae_false, &lambdaref, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    *serrors = *serrors||ae_fp_greater(testevdunit_testproduct(a, n, &z, &lambdaref, _state),threshold);
    *serrors = *serrors||ae_fp_greater(testevdunit_testort(&z, n, _state),threshold);
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdaref.ptr.p_double[i+1],lambdaref.ptr.p_double[i]) )
        {
            *serrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Test simple EVD: values and full vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevd(au, n, 1, ae_true, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    *serrors = *serrors||ae_fp_greater(testevdunit_testproduct(a, n, &z, &lambdav, _state),threshold);
    *serrors = *serrors||ae_fp_greater(testevdunit_testort(&z, n, _state),threshold);
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdav.ptr.p_double[i+1],lambdav.ptr.p_double[i]) )
        {
            *serrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Test simple EVD: values only, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevd(al, n, 0, ae_false, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[i]-lambdaref.ptr.p_double[i], _state),threshold);
    }
    
    /*
     * Test simple EVD: values only, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevd(au, n, 0, ae_true, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[i]-lambdaref.ptr.p_double[i], _state),threshold);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests SEVD problem
*************************************************************************/
static void testevdunit_testhevdproblem(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_int_t n,
     double threshold,
     ae_bool* herrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&lambdav, 0, sizeof(lambdav));
    memset(&lambdaref, 0, sizeof(lambdaref));
    memset(&z, 0, sizeof(z));
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * Test simple EVD: values and full vectors, lower A
     */
    testevdunit_unset1d(&lambdaref, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevd(al, n, 1, ae_false, &lambdaref, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    *herrors = *herrors||ae_fp_greater(testevdunit_testcproduct(a, n, &z, &lambdaref, _state),threshold);
    *herrors = *herrors||ae_fp_greater(testevdunit_testcort(&z, n, _state),threshold);
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdaref.ptr.p_double[i+1],lambdaref.ptr.p_double[i]) )
        {
            *herrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Test simple EVD: values and full vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevd(au, n, 1, ae_true, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    *herrors = *herrors||ae_fp_greater(testevdunit_testcproduct(a, n, &z, &lambdav, _state),threshold);
    *herrors = *herrors||ae_fp_greater(testevdunit_testcort(&z, n, _state),threshold);
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdav.ptr.p_double[i+1],lambdav.ptr.p_double[i]) )
        {
            *herrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Test simple EVD: values only, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevd(al, n, 0, ae_false, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[i]-lambdaref.ptr.p_double[i], _state),threshold);
    }
    
    /*
     * Test simple EVD: values only, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevd(au, n, 0, ae_true, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[i]-lambdaref.ptr.p_double[i], _state),threshold);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests EVD problem

DistVals    -   is True, when eigenvalues are distinct. Is False, when we
                are solving sparse task with  lots  of  zero  eigenvalues.
                In such cases some tests related to the  eigenvectors  are
                not performed.
*************************************************************************/
static void testevdunit_testsevdbiproblem(/* Real    */ ae_matrix* afull,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_matrix zref;
    ae_matrix a1;
    ae_matrix a2;
    ae_matrix ar;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t m;
    ae_int_t i1;
    ae_int_t i2;
    double v;
    double a;
    double b;

    ae_frame_make(_state, &_frame_block);
    memset(&lambdav, 0, sizeof(lambdav));
    memset(&lambdaref, 0, sizeof(lambdaref));
    memset(&z, 0, sizeof(z));
    memset(&zref, 0, sizeof(zref));
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    memset(&ar, 0, sizeof(ar));
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&zref, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ar, 0, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&lambdaref, n-1+1, _state);
    ae_matrix_set_length(&zref, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    
    /*
     * Reference EVD
     */
    *runs = *runs+1;
    if( !smatrixevd(afull, n, 1, ae_true, &lambdaref, &zref, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Select random interval boundaries.
     * If there are non-distinct eigenvalues at the boundaries,
     * we move indexes further until values splits. It is done to
     * avoid situations where we can't get definite answer.
     */
    i1 = ae_randominteger(n, _state);
    i2 = i1+ae_randominteger(n-i1, _state);
    while(i1>0)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i1-1]-lambdaref.ptr.p_double[i1], _state),10*threshold) )
        {
            break;
        }
        i1 = i1-1;
    }
    while(i2<n-1)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i2+1]-lambdaref.ptr.p_double[i2], _state),10*threshold) )
        {
            break;
        }
        i2 = i2+1;
    }
    
    /*
     * Select A, B
     */
    if( i1>0 )
    {
        a = 0.5*(lambdaref.ptr.p_double[i1]+lambdaref.ptr.p_double[i1-1]);
    }
    else
    {
        a = lambdaref.ptr.p_double[0]-1;
    }
    if( i2<n-1 )
    {
        b = 0.5*(lambdaref.ptr.p_double[i2]+lambdaref.ptr.p_double[i2+1]);
    }
    else
    {
        b = lambdaref.ptr.p_double[n-1]+1;
    }
    
    /*
     * Test interval, no vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdr(al, n, 0, ae_false, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, no vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdr(au, n, 0, ae_true, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdi(al, n, 0, ae_false, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdi(au, n, 0, ae_true, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdr(al, n, 1, ae_false, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,(double)(0)) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    
    /*
     * Test interval, vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdr(au, n, 1, ae_true, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,(double)(0)) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdi(al, n, 1, ae_false, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,(double)(0)) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdi(au, n, 1, ae_true, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,(double)(0)) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests EVD problem

DistVals    -   is True, when eigenvalues are distinct. Is False, when we
                are solving sparse task with  lots  of  zero  eigenvalues.
                In such cases some tests related to the  eigenvectors  are
                not performed.
*************************************************************************/
static void testevdunit_testhevdbiproblem(/* Complex */ ae_matrix* afull,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* herrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_matrix zref;
    ae_matrix a1;
    ae_matrix a2;
    ae_matrix ar;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t m;
    ae_int_t i1;
    ae_int_t i2;
    ae_complex v;
    double a;
    double b;

    ae_frame_make(_state, &_frame_block);
    memset(&lambdav, 0, sizeof(lambdav));
    memset(&lambdaref, 0, sizeof(lambdaref));
    memset(&z, 0, sizeof(z));
    memset(&zref, 0, sizeof(zref));
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    memset(&ar, 0, sizeof(ar));
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&zref, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ar, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_vector_set_length(&lambdaref, n-1+1, _state);
    ae_matrix_set_length(&zref, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    
    /*
     * Reference EVD
     */
    *runs = *runs+1;
    if( !hmatrixevd(afull, n, 1, ae_true, &lambdaref, &zref, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Select random interval boundaries.
     * If there are non-distinct eigenvalues at the boundaries,
     * we move indexes further until values splits. It is done to
     * avoid situations where we can't get definite answer.
     */
    i1 = ae_randominteger(n, _state);
    i2 = i1+ae_randominteger(n-i1, _state);
    while(i1>0)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i1-1]-lambdaref.ptr.p_double[i1], _state),10*threshold) )
        {
            break;
        }
        i1 = i1-1;
    }
    while(i2<n-1)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i2+1]-lambdaref.ptr.p_double[i2], _state),10*threshold) )
        {
            break;
        }
        i2 = i2+1;
    }
    
    /*
     * Select A, B
     */
    if( i1>0 )
    {
        a = 0.5*(lambdaref.ptr.p_double[i1]+lambdaref.ptr.p_double[i1-1]);
    }
    else
    {
        a = lambdaref.ptr.p_double[0]-1;
    }
    if( i2<n-1 )
    {
        b = 0.5*(lambdaref.ptr.p_double[i2]+lambdaref.ptr.p_double[i2+1]);
    }
    else
    {
        b = lambdaref.ptr.p_double[n-1]+1;
    }
    
    /*
     * Test interval, no vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdr(al, n, 0, ae_false, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, no vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdr(au, n, 0, ae_true, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdi(al, n, 0, ae_false, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdi(au, n, 0, ae_true, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdr(al, n, 1, ae_false, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&z.ptr.pp_complex[0][j], z.stride, "N", &zref.ptr.pp_complex[0][i1+j], zref.stride, "Conj", ae_v_len(0,n-1));
            v = ae_c_conj(ae_c_div_d(v,ae_c_abs(v, _state)), _state);
            ae_v_cmulc(&z.ptr.pp_complex[0][j], z.stride, ae_v_len(0,n-1), v);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *herrors = *herrors||ae_fp_greater(ae_c_abs(ae_c_sub(z.ptr.pp_complex[i][j],zref.ptr.pp_complex[i][i1+j]), _state),threshold);
            }
        }
    }
    
    /*
     * Test interval, vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdr(au, n, 1, ae_true, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&z.ptr.pp_complex[0][j], z.stride, "N", &zref.ptr.pp_complex[0][i1+j], zref.stride, "Conj", ae_v_len(0,n-1));
            v = ae_c_conj(ae_c_div_d(v,ae_c_abs(v, _state)), _state);
            ae_v_cmulc(&z.ptr.pp_complex[0][j], z.stride, ae_v_len(0,n-1), v);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *herrors = *herrors||ae_fp_greater(ae_c_abs(ae_c_sub(z.ptr.pp_complex[i][j],zref.ptr.pp_complex[i][i1+j]), _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdi(al, n, 1, ae_false, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&z.ptr.pp_complex[0][j], z.stride, "N", &zref.ptr.pp_complex[0][i1+j], zref.stride, "Conj", ae_v_len(0,n-1));
            v = ae_c_conj(ae_c_div_d(v,ae_c_abs(v, _state)), _state);
            ae_v_cmulc(&z.ptr.pp_complex[0][j], z.stride, ae_v_len(0,n-1), v);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *herrors = *herrors||ae_fp_greater(ae_c_abs(ae_c_sub(z.ptr.pp_complex[i][j],zref.ptr.pp_complex[i][i1+j]), _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdi(au, n, 1, ae_true, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&z.ptr.pp_complex[0][j], z.stride, "N", &zref.ptr.pp_complex[0][i1+j], zref.stride, "Conj", ae_v_len(0,n-1));
            v = ae_c_conj(ae_c_div_d(v,ae_c_abs(v, _state)), _state);
            ae_v_cmulc(&z.ptr.pp_complex[0][j], z.stride, ae_v_len(0,n-1), v);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *herrors = *herrors||ae_fp_greater(ae_c_abs(ae_c_sub(z.ptr.pp_complex[i][j],zref.ptr.pp_complex[i][i1+j]), _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests EVD problem
*************************************************************************/
static void testevdunit_testtdevdproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector ee;
    ae_vector lambda2;
    ae_matrix z;
    ae_matrix zref;
    ae_matrix a1;
    ae_matrix a2;
    ae_bool wsucc;
    ae_int_t i;
    ae_int_t j;
    double v;
    double worstseparation;
    double requiredseparation;
    double specialthreshold;

    ae_frame_make(_state, &_frame_block);
    memset(&lambdav, 0, sizeof(lambdav));
    memset(&ee, 0, sizeof(ee));
    memset(&lambda2, 0, sizeof(lambda2));
    memset(&z, 0, sizeof(z));
    memset(&zref, 0, sizeof(zref));
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ee, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambda2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&zref, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&lambdav, n-1+1, _state);
    ae_vector_set_length(&lambda2, n-1+1, _state);
    ae_matrix_set_length(&zref, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    if( n>1 )
    {
        ae_vector_set_length(&ee, n-2+1, _state);
    }
    
    /*
     * Test simple EVD: values and full vectors
     */
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        ee.ptr.p_double[i] = e->ptr.p_double[i];
    }
    testevdunit_unset2d(&z, _state);
    wsucc = smatrixtdevd(&lambdav, &ee, n, 2, &z, _state);
    if( !wsucc )
    {
        ae_set_error_flag(tderrors, ae_true, __FILE__, __LINE__, "testevdunit.ap:1113");
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(tderrors, ae_fp_greater(testevdunit_tdtestproduct(d, e, n, &z, &lambdav, _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1116");
    ae_set_error_flag(tderrors, ae_fp_greater(testevdunit_testort(&z, n, _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1117");
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdav.ptr.p_double[i+1],lambdav.ptr.p_double[i]) )
        {
            ae_set_error_flag(tderrors, ae_true, __FILE__, __LINE__, "testevdunit.ap:1121");
            ae_frame_leave(_state);
            return;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            zref.ptr.pp_double[i][j] = z.ptr.pp_double[i][j];
        }
    }
    
    /*
     * Test values only variant
     */
    for(i=0; i<=n-1; i++)
    {
        lambda2.ptr.p_double[i] = d->ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        ee.ptr.p_double[i] = e->ptr.p_double[i];
    }
    testevdunit_unset2d(&z, _state);
    wsucc = smatrixtdevd(&lambda2, &ee, n, 0, &z, _state);
    if( !wsucc )
    {
        ae_set_error_flag(tderrors, ae_true, __FILE__, __LINE__, "testevdunit.ap:1139");
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        ae_set_error_flag(tderrors, ae_fp_greater(ae_fabs(lambda2.ptr.p_double[i]-lambdav.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1143");
    }
    
    /*
     * Test multiplication variant
     */
    for(i=0; i<=n-1; i++)
    {
        lambda2.ptr.p_double[i] = d->ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        ee.ptr.p_double[i] = e->ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
        }
    }
    wsucc = smatrixtdevd(&lambda2, &ee, n, 1, &a1, _state);
    if( !wsucc )
    {
        ae_set_error_flag(tderrors, ae_true, __FILE__, __LINE__, "testevdunit.ap:1161");
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        ae_set_error_flag(tderrors, ae_fp_greater(ae_fabs(lambda2.ptr.p_double[i]-lambdav.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1165");
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&a2.ptr.pp_double[i][0], 1, &zref.ptr.pp_double[0][j], zref.stride, ae_v_len(0,n-1));
            
            /*
             * next line is a bit complicated because
             * depending on algorithm used we can get either
             * z or -z as eigenvector. so we compare result
             * with both A*ZRef and -A*ZRef
             */
            ae_set_error_flag(tderrors, ae_fp_greater(ae_fabs(v-a1.ptr.pp_double[i][j], _state),threshold)&&ae_fp_greater(ae_fabs(v+a1.ptr.pp_double[i][j], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1177");
        }
    }
    
    /*
     * Test first row variant.
     *
     * NOTE: this test is special because ZNeeded=3 is ALGLIB-specific feature
     *       which is NOT supported by Intel MKL. Thus, MKL-capable version of
     *       ALGLIB will use different algorithms for ZNeeded=3 and for ZNeeded<3.
     *
     *       In most cases it is OK, but when problem happened to be degenerate
     *       (two close eigenvalues), Z computed by ALGLIB may be different from
     *       Z computed by MKL (up to arbitrary rotation), which will lead to
     *       failure of the test, because ZNeeded=2 is used as reference value
     *       for ZNeeded=3.
     *
     *       That's why this test is performed only for well-separated matrices,
     *       and with custom threshold.
     */
    requiredseparation = 1.0E-6;
    specialthreshold = 1.0E-6;
    worstseparation = ae_maxrealnumber;
    for(i=0; i<=n-2; i++)
    {
        worstseparation = ae_minreal(worstseparation, ae_fabs(lambdav.ptr.p_double[i+1]-lambdav.ptr.p_double[i], _state), _state);
    }
    if( ae_fp_greater(worstseparation,requiredseparation) )
    {
        for(i=0; i<=n-1; i++)
        {
            lambda2.ptr.p_double[i] = d->ptr.p_double[i];
        }
        for(i=0; i<=n-2; i++)
        {
            ee.ptr.p_double[i] = e->ptr.p_double[i];
        }
        testevdunit_unset2d(&z, _state);
        wsucc = smatrixtdevd(&lambda2, &ee, n, 3, &z, _state);
        if( !wsucc )
        {
            ae_set_error_flag(tderrors, ae_true, __FILE__, __LINE__, "testevdunit.ap:1211");
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(tderrors, ae_fp_greater(ae_fabs(lambda2.ptr.p_double[i]-lambdav.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1216");
            
            /*
             * next line is a bit complicated because
             * depending on algorithm used we can get either
             * z or -z as eigenvector. so we compare result
             * with both z and -z
             */
            ae_set_error_flag(tderrors, ae_fp_greater(ae_fabs(z.ptr.pp_double[0][i]-zref.ptr.pp_double[0][i], _state),specialthreshold)&&ae_fp_greater(ae_fabs(z.ptr.pp_double[0][i]+zref.ptr.pp_double[0][i], _state),specialthreshold), __FILE__, __LINE__, "testevdunit.ap:1224");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests EVD problem

DistVals    -   is True, when eigenvalues are distinct. Is False, when we
                are solving sparse task with  lots  of  zero  eigenvalues.
                In such cases some tests related to the  eigenvectors  are
                not performed.
*************************************************************************/
static void testevdunit_testtdevdbiproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_matrix zref;
    ae_matrix a1;
    ae_matrix a2;
    ae_matrix ar;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t m;
    ae_int_t i1;
    ae_int_t i2;
    double v;
    double a;
    double b;

    ae_frame_make(_state, &_frame_block);
    memset(&lambdav, 0, sizeof(lambdav));
    memset(&lambdaref, 0, sizeof(lambdaref));
    memset(&z, 0, sizeof(z));
    memset(&zref, 0, sizeof(zref));
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    memset(&ar, 0, sizeof(ar));
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&zref, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ar, 0, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&lambdaref, n-1+1, _state);
    ae_matrix_set_length(&zref, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    
    /*
     * Reference EVD
     */
    ae_vector_set_length(&lambdaref, n, _state);
    ae_v_move(&lambdaref.ptr.p_double[0], 1, &d->ptr.p_double[0], 1, ae_v_len(0,n-1));
    *runs = *runs+1;
    if( !smatrixtdevd(&lambdaref, e, n, 2, &zref, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Select random interval boundaries.
     * If there are non-distinct eigenvalues at the boundaries,
     * we move indexes further until values splits. It is done to
     * avoid situations where we can't get definite answer.
     */
    i1 = ae_randominteger(n, _state);
    i2 = i1+ae_randominteger(n-i1, _state);
    while(i1>0)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i1-1]-lambdaref.ptr.p_double[i1], _state),10*threshold) )
        {
            break;
        }
        i1 = i1-1;
    }
    while(i2<n-1)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i2+1]-lambdaref.ptr.p_double[i2], _state),10*threshold) )
        {
            break;
        }
        i2 = i2+1;
    }
    
    /*
     * Test different combinations
     */
    
    /*
     * Select A, B
     */
    if( i1>0 )
    {
        a = 0.5*(lambdaref.ptr.p_double[i1]+lambdaref.ptr.p_double[i1-1]);
    }
    else
    {
        a = lambdaref.ptr.p_double[0]-1;
    }
    if( i2<n-1 )
    {
        b = 0.5*(lambdaref.ptr.p_double[i2]+lambdaref.ptr.p_double[i2+1]);
    }
    else
    {
        b = lambdaref.ptr.p_double[n-1]+1;
    }
    
    /*
     * Test interval, no vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    *runs = *runs+1;
    if( !smatrixtdevdr(&lambdav, e, n, 0, a, b, &m, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    *runs = *runs+1;
    if( !smatrixtdevdi(&lambdav, e, n, 0, i1, i2, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, transform vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
        }
    }
    *runs = *runs+1;
    if( !smatrixtdevdr(&lambdav, e, n, 1, a, b, &m, &a1, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        ae_matrix_set_length(&ar, n-1+1, m-1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                v = ae_v_dotproduct(&a2.ptr.pp_double[i][0], 1, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
                ar.ptr.pp_double[i][j] = v;
            }
        }
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&a1.ptr.pp_double[0][j], a1.stride, &ar.ptr.pp_double[0][j], ar.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,(double)(0)) )
            {
                ae_v_muld(&ar.ptr.pp_double[0][j], ar.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(a1.ptr.pp_double[i][j]-ar.ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, transform vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
        }
    }
    *runs = *runs+1;
    if( !smatrixtdevdi(&lambdav, e, n, 1, i1, i2, &a1, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        ae_matrix_set_length(&ar, n-1+1, m-1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                v = ae_v_dotproduct(&a2.ptr.pp_double[i][0], 1, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
                ar.ptr.pp_double[i][j] = v;
            }
        }
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&a1.ptr.pp_double[0][j], a1.stride, &ar.ptr.pp_double[0][j], ar.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,(double)(0)) )
            {
                ae_v_muld(&ar.ptr.pp_double[0][j], ar.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(a1.ptr.pp_double[i][j]-ar.ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    
    /*
     * Test interval, do not transform vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    ae_matrix_set_length(&z, 0+1, 0+1, _state);
    *runs = *runs+1;
    if( !smatrixtdevdr(&lambdav, e, n, 2, a, b, &m, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,(double)(0)) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, do not transform vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    ae_matrix_set_length(&z, 0+1, 0+1, _state);
    *runs = *runs+1;
    if( !smatrixtdevdi(&lambdav, e, n, 2, i1, i2, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,(double)(0)) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Non-symmetric problem
*************************************************************************/
static void testevdunit_testnsevdproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* nserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t vjob;
    ae_bool needl;
    ae_bool needr;
    ae_vector wr0;
    ae_vector wi0;
    ae_vector wr1;
    ae_vector wi1;
    ae_vector wr0s;
    ae_vector wi0s;
    ae_vector wr1s;
    ae_vector wi1s;
    ae_matrix vl;
    ae_matrix vr;
    ae_vector vec1r;
    ae_vector vec1i;
    ae_vector vec2r;
    ae_vector vec2i;
    ae_vector vec3r;
    ae_vector vec3i;
    double curwr;
    double curwi;
    double vt;
    double tmp;
    double vnorm;

    ae_frame_make(_state, &_frame_block);
    memset(&wr0, 0, sizeof(wr0));
    memset(&wi0, 0, sizeof(wi0));
    memset(&wr1, 0, sizeof(wr1));
    memset(&wi1, 0, sizeof(wi1));
    memset(&wr0s, 0, sizeof(wr0s));
    memset(&wi0s, 0, sizeof(wi0s));
    memset(&wr1s, 0, sizeof(wr1s));
    memset(&wi1s, 0, sizeof(wi1s));
    memset(&vl, 0, sizeof(vl));
    memset(&vr, 0, sizeof(vr));
    memset(&vec1r, 0, sizeof(vec1r));
    memset(&vec1i, 0, sizeof(vec1i));
    memset(&vec2r, 0, sizeof(vec2r));
    memset(&vec2i, 0, sizeof(vec2i));
    memset(&vec3r, 0, sizeof(vec3r));
    memset(&vec3i, 0, sizeof(vec3i));
    ae_vector_init(&wr0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wi0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wr1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wi1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wr0s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wi0s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wr1s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wi1s, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vl, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vr, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec1r, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec1i, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec2r, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec2i, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec3r, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec3i, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&vec1r, n-1+1, _state);
    ae_vector_set_length(&vec2r, n-1+1, _state);
    ae_vector_set_length(&vec3r, n-1+1, _state);
    ae_vector_set_length(&vec1i, n-1+1, _state);
    ae_vector_set_length(&vec2i, n-1+1, _state);
    ae_vector_set_length(&vec3i, n-1+1, _state);
    ae_vector_set_length(&wr0s, n-1+1, _state);
    ae_vector_set_length(&wr1s, n-1+1, _state);
    ae_vector_set_length(&wi0s, n-1+1, _state);
    ae_vector_set_length(&wi1s, n-1+1, _state);
    mx = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater(ae_fabs(a->ptr.pp_double[i][j], _state),mx) )
            {
                mx = ae_fabs(a->ptr.pp_double[i][j], _state);
            }
        }
    }
    if( ae_fp_eq(mx,(double)(0)) )
    {
        mx = (double)(1);
    }
    
    /*
     * Load values-only
     */
    if( !rmatrixevd(a, n, 0, &wr0, &wi0, &vl, &vr, _state) )
    {
        ae_set_error_flag(nserrors, ae_true, __FILE__, __LINE__, "testevdunit.ap:1554");
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Test different jobs
     */
    for(vjob=1; vjob<=3; vjob++)
    {
        needr = vjob==1||vjob==3;
        needl = vjob==2||vjob==3;
        if( !rmatrixevd(a, n, vjob, &wr1, &wi1, &vl, &vr, _state) )
        {
            ae_set_error_flag(nserrors, ae_true, __FILE__, __LINE__, "testevdunit.ap:1567");
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Test values:
         * 1. sort by real part
         * 2. test
         */
        ae_v_move(&wr0s.ptr.p_double[0], 1, &wr0.ptr.p_double[0], 1, ae_v_len(0,n-1));
        ae_v_move(&wi0s.ptr.p_double[0], 1, &wi0.ptr.p_double[0], 1, ae_v_len(0,n-1));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-2-i; j++)
            {
                if( ae_fp_greater(wr0s.ptr.p_double[j],wr0s.ptr.p_double[j+1]) )
                {
                    tmp = wr0s.ptr.p_double[j];
                    wr0s.ptr.p_double[j] = wr0s.ptr.p_double[j+1];
                    wr0s.ptr.p_double[j+1] = tmp;
                    tmp = wi0s.ptr.p_double[j];
                    wi0s.ptr.p_double[j] = wi0s.ptr.p_double[j+1];
                    wi0s.ptr.p_double[j+1] = tmp;
                }
            }
        }
        ae_v_move(&wr1s.ptr.p_double[0], 1, &wr1.ptr.p_double[0], 1, ae_v_len(0,n-1));
        ae_v_move(&wi1s.ptr.p_double[0], 1, &wi1.ptr.p_double[0], 1, ae_v_len(0,n-1));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-2-i; j++)
            {
                if( ae_fp_greater(wr1s.ptr.p_double[j],wr1s.ptr.p_double[j+1]) )
                {
                    tmp = wr1s.ptr.p_double[j];
                    wr1s.ptr.p_double[j] = wr1s.ptr.p_double[j+1];
                    wr1s.ptr.p_double[j+1] = tmp;
                    tmp = wi1s.ptr.p_double[j];
                    wi1s.ptr.p_double[j] = wi1s.ptr.p_double[j+1];
                    wi1s.ptr.p_double[j+1] = tmp;
                }
            }
        }
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(nserrors, ae_fp_greater(ae_fabs(wr0s.ptr.p_double[i]-wr1s.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1604");
            ae_set_error_flag(nserrors, ae_fp_greater(ae_fabs(wi0s.ptr.p_double[i]-wi1s.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1605");
        }
        
        /*
         * Test right vectors
         */
        if( needr )
        {
            k = 0;
            while(k<=n-1)
            {
                curwr = (double)(0);
                curwi = (double)(0);
                if( ae_fp_eq(wi1.ptr.p_double[k],(double)(0)) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k], vr.stride, ae_v_len(0,n-1));
                    for(i=0; i<=n-1; i++)
                    {
                        vec1i.ptr.p_double[i] = (double)(0);
                    }
                    curwr = wr1.ptr.p_double[k];
                    curwi = (double)(0);
                }
                if( ae_fp_greater(wi1.ptr.p_double[k],(double)(0)) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k], vr.stride, ae_v_len(0,n-1));
                    ae_v_move(&vec1i.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k+1], vr.stride, ae_v_len(0,n-1));
                    curwr = wr1.ptr.p_double[k];
                    curwi = wi1.ptr.p_double[k];
                }
                if( ae_fp_less(wi1.ptr.p_double[k],(double)(0)) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k-1], vr.stride, ae_v_len(0,n-1));
                    ae_v_moveneg(&vec1i.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k], vr.stride, ae_v_len(0,n-1));
                    curwr = wr1.ptr.p_double[k];
                    curwi = wi1.ptr.p_double[k];
                }
                vnorm = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    vt = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    vec2r.ptr.p_double[i] = vt;
                    vt = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    vec2i.ptr.p_double[i] = vt;
                    vnorm = vnorm+ae_sqr(vec1r.ptr.p_double[i], _state)+ae_sqr(vec1i.ptr.p_double[i], _state);
                }
                vnorm = ae_sqrt(vnorm, _state);
                ae_v_moved(&vec3r.ptr.p_double[0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1), curwr);
                ae_v_subd(&vec3r.ptr.p_double[0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1), curwi);
                ae_v_moved(&vec3i.ptr.p_double[0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1), curwi);
                ae_v_addd(&vec3i.ptr.p_double[0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1), curwr);
                ae_set_error_flag(nserrors, ae_fp_less(vnorm,1.0E-3)||!ae_isfinite(vnorm, _state), __FILE__, __LINE__, "testevdunit.ap:1654");
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(nserrors, ae_fp_greater(ae_fabs(vec2r.ptr.p_double[i]-vec3r.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1657");
                    ae_set_error_flag(nserrors, ae_fp_greater(ae_fabs(vec2i.ptr.p_double[i]-vec3i.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1658");
                }
                k = k+1;
            }
        }
        
        /*
         * Test left vectors
         */
        curwr = (double)(0);
        curwi = (double)(0);
        if( needl )
        {
            k = 0;
            while(k<=n-1)
            {
                if( ae_fp_eq(wi1.ptr.p_double[k],(double)(0)) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k], vl.stride, ae_v_len(0,n-1));
                    for(i=0; i<=n-1; i++)
                    {
                        vec1i.ptr.p_double[i] = (double)(0);
                    }
                    curwr = wr1.ptr.p_double[k];
                    curwi = (double)(0);
                }
                if( ae_fp_greater(wi1.ptr.p_double[k],(double)(0)) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k], vl.stride, ae_v_len(0,n-1));
                    ae_v_move(&vec1i.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k+1], vl.stride, ae_v_len(0,n-1));
                    curwr = wr1.ptr.p_double[k];
                    curwi = wi1.ptr.p_double[k];
                }
                if( ae_fp_less(wi1.ptr.p_double[k],(double)(0)) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k-1], vl.stride, ae_v_len(0,n-1));
                    ae_v_moveneg(&vec1i.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k], vl.stride, ae_v_len(0,n-1));
                    curwr = wr1.ptr.p_double[k];
                    curwi = wi1.ptr.p_double[k];
                }
                vnorm = 0.0;
                for(j=0; j<=n-1; j++)
                {
                    vt = ae_v_dotproduct(&vec1r.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
                    vec2r.ptr.p_double[j] = vt;
                    vt = ae_v_dotproduct(&vec1i.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
                    vec2i.ptr.p_double[j] = -vt;
                    vnorm = vnorm+ae_sqr(vec1r.ptr.p_double[j], _state)+ae_sqr(vec1i.ptr.p_double[j], _state);
                }
                vnorm = ae_sqrt(vnorm, _state);
                ae_v_moved(&vec3r.ptr.p_double[0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1), curwr);
                ae_v_addd(&vec3r.ptr.p_double[0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1), curwi);
                ae_v_moved(&vec3i.ptr.p_double[0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1), curwi);
                ae_v_addd(&vec3i.ptr.p_double[0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1), -curwr);
                ae_set_error_flag(nserrors, ae_fp_less(vnorm,1.0E-3)||!ae_isfinite(vnorm, _state), __FILE__, __LINE__, "testevdunit.ap:1710");
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(nserrors, ae_fp_greater(ae_fabs(vec2r.ptr.p_double[i]-vec3r.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1713");
                    ae_set_error_flag(nserrors, ae_fp_greater(ae_fabs(vec2i.ptr.p_double[i]-vec3i.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testevdunit.ap:1714");
                }
                k = k+1;
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Testing EVD subroutines for one N

NOTES:
* BIThreshold is a threshold for bisection-and-inverse-iteration subroutines.
  special threshold is needed because these subroutines may have much more
  larger error than QR-based algorithms.
*************************************************************************/
static void testevdunit_testevdset(ae_int_t n,
     double threshold,
     double bithreshold,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_bool* nserrors,
     ae_bool* serrors,
     ae_bool* herrors,
     ae_bool* tderrors,
     ae_bool* sbierrors,
     ae_bool* hbierrors,
     ae_bool* tdbierrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ra;
    ae_matrix ral;
    ae_matrix rau;
    ae_matrix ca;
    ae_matrix cal;
    ae_matrix cau;
    ae_vector d;
    ae_vector e;
    ae_int_t i;
    ae_int_t j;
    ae_int_t mkind;

    ae_frame_make(_state, &_frame_block);
    memset(&ra, 0, sizeof(ra));
    memset(&ral, 0, sizeof(ral));
    memset(&rau, 0, sizeof(rau));
    memset(&ca, 0, sizeof(ca));
    memset(&cal, 0, sizeof(cal));
    memset(&cau, 0, sizeof(cau));
    memset(&d, 0, sizeof(d));
    memset(&e, 0, sizeof(e));
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ral, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rau, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cal, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cau, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test symmetric problems
     */
    ae_matrix_set_length(&ra, n, n, _state);
    ae_matrix_set_length(&ral, n, n, _state);
    ae_matrix_set_length(&rau, n, n, _state);
    ae_matrix_set_length(&ca, n, n, _state);
    ae_matrix_set_length(&cal, n, n, _state);
    ae_matrix_set_length(&cau, n, n, _state);
    
    /*
     * Zero matrices
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = (double)(0);
            ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
    testevdunit_rmatrixsymmetricsplit(&ra, n, &ral, &rau, _state);
    testevdunit_cmatrixhermitiansplit(&ca, n, &cal, &cau, _state);
    testevdunit_testsevdproblem(&ra, &ral, &rau, n, threshold, serrors, failc, runs, _state);
    testevdunit_testhevdproblem(&ca, &cal, &cau, n, threshold, herrors, failc, runs, _state);
    testevdunit_testsevdbiproblem(&ra, &ral, &rau, n, ae_false, bithreshold, sbierrors, failc, runs, _state);
    testevdunit_testhevdbiproblem(&ca, &cal, &cau, n, ae_false, bithreshold, hbierrors, failc, runs, _state);
    
    /*
     * Random matrix
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
            ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
            ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
        }
        ra.ptr.pp_double[i][i] = 2*ae_randomreal(_state)-1;
        ca.ptr.pp_complex[i][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
    }
    testevdunit_rmatrixsymmetricsplit(&ra, n, &ral, &rau, _state);
    testevdunit_cmatrixhermitiansplit(&ca, n, &cal, &cau, _state);
    testevdunit_testsevdproblem(&ra, &ral, &rau, n, threshold, serrors, failc, runs, _state);
    testevdunit_testhevdproblem(&ca, &cal, &cau, n, threshold, herrors, failc, runs, _state);
    
    /*
     * Random diagonally dominant matrix with distinct eigenvalues
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = 0.1*(2*ae_randomreal(_state)-1)/n;
            ca.ptr.pp_complex[i][j].x = 0.1*(2*ae_randomreal(_state)-1)/n;
            ca.ptr.pp_complex[i][j].y = 0.1*(2*ae_randomreal(_state)-1)/n;
            ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
            ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
        }
        ra.ptr.pp_double[i][i] = 0.1*(2*ae_randomreal(_state)-1)+i;
        ca.ptr.pp_complex[i][i] = ae_complex_from_d(0.1*(2*ae_randomreal(_state)-1)+i);
    }
    testevdunit_rmatrixsymmetricsplit(&ra, n, &ral, &rau, _state);
    testevdunit_cmatrixhermitiansplit(&ca, n, &cal, &cau, _state);
    testevdunit_testsevdproblem(&ra, &ral, &rau, n, threshold, serrors, failc, runs, _state);
    testevdunit_testhevdproblem(&ca, &cal, &cau, n, threshold, herrors, failc, runs, _state);
    testevdunit_testsevdbiproblem(&ra, &ral, &rau, n, ae_true, bithreshold, sbierrors, failc, runs, _state);
    testevdunit_testhevdbiproblem(&ca, &cal, &cau, n, ae_true, bithreshold, hbierrors, failc, runs, _state);
    
    /*
     * Sparse matrices
     */
    testevdunit_rmatrixfillsparsea(&ra, n, n, 0.995, (double)(0), _state);
    testevdunit_cmatrixfillsparsea(&ca, n, n, 0.995, (double)(0), _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
            ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
        }
        ca.ptr.pp_complex[i][i].y = (double)(0);
    }
    testevdunit_rmatrixsymmetricsplit(&ra, n, &ral, &rau, _state);
    testevdunit_cmatrixhermitiansplit(&ca, n, &cal, &cau, _state);
    testevdunit_testsevdproblem(&ra, &ral, &rau, n, threshold, serrors, failc, runs, _state);
    testevdunit_testhevdproblem(&ca, &cal, &cau, n, threshold, herrors, failc, runs, _state);
    testevdunit_testsevdbiproblem(&ra, &ral, &rau, n, ae_false, bithreshold, sbierrors, failc, runs, _state);
    testevdunit_testhevdbiproblem(&ca, &cal, &cau, n, ae_false, bithreshold, hbierrors, failc, runs, _state);
    
    /*
     * testing tridiagonal problems
     */
    for(mkind=0; mkind<=7; mkind++)
    {
        ae_vector_set_length(&d, n, _state);
        if( n>1 )
        {
            ae_vector_set_length(&e, n-1, _state);
        }
        if( mkind==0 )
        {
            
            /*
             * Zero matrix
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = (double)(0);
            }
        }
        if( mkind==1 )
        {
            
            /*
             * Diagonal matrix
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = (double)(0);
            }
        }
        if( mkind==2 )
        {
            
            /*
             * Off-diagonal matrix
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        if( mkind==3 )
        {
            
            /*
             * Dense matrix with blocks
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            j = 1;
            i = 2;
            while(j<=n-2)
            {
                e.ptr.p_double[j] = (double)(0);
                j = j+i;
                i = i+1;
            }
        }
        if( mkind==4 )
        {
            
            /*
             * dense matrix
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        if( mkind==5 )
        {
            
            /*
             * Diagonal matrix with distinct eigenvalues
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1)+i;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = (double)(0);
            }
        }
        if( mkind==6 )
        {
            
            /*
             * Off-diagonal matrix with distinct eigenvalues
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1)+i+1;
            }
        }
        if( mkind==7 )
        {
            
            /*
             * dense matrix with distinct eigenvalues
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1)+i+1;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
            }
        }
        testevdunit_testtdevdproblem(&d, &e, n, threshold, tderrors, _state);
        testevdunit_testtdevdbiproblem(&d, &e, n, (mkind==5||mkind==6)||mkind==7, bithreshold, tdbierrors, failc, runs, _state);
    }
    
    /*
     * Test non-symmetric problems
     */
    
    /*
     * Test non-symmetric problems: zero, random, sparse matrices.
     */
    ae_matrix_set_length(&ra, n, n, _state);
    ae_matrix_set_length(&ca, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = (double)(0);
            ca.ptr.pp_complex[i][j] = ae_complex_from_i(0);
        }
    }
    testevdunit_testnsevdproblem(&ra, n, threshold, nserrors, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
            ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
        }
    }
    testevdunit_testnsevdproblem(&ra, n, threshold, nserrors, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Testing symmetric mode of subspace iteration solver.

On failure sets error flag, on success flag is not changed.
*************************************************************************/
static void testevdunit_testsisymm(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_int_t smode;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    hqrndstate rs;
    double sgn;
    ae_vector diaga;
    double decaya;
    ae_bool isupper;
    ae_matrix qa;
    ae_matrix densea;
    ae_matrix halfa;
    ae_matrix ra;
    ae_matrix rb;
    ae_matrix tmp2;
    sparsematrix halfsa;
    ae_int_t sformat;
    double v;
    double mx;
    eigsubspacestate s;
    eigsubspacereport rep;
    ae_vector sw;
    ae_matrix sz;
    ae_vector u;
    double tollambda;
    double tolvector;
    ae_int_t itscount;
    ae_int_t callcount;
    ae_int_t requestsize;
    ae_int_t requesttype;
    ae_int_t pass;
    ae_int_t cnta;
    ae_int_t cntb;
    ae_int_t cntc;
    double nu;
    double eps;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&diaga, 0, sizeof(diaga));
    memset(&qa, 0, sizeof(qa));
    memset(&densea, 0, sizeof(densea));
    memset(&halfa, 0, sizeof(halfa));
    memset(&ra, 0, sizeof(ra));
    memset(&rb, 0, sizeof(rb));
    memset(&tmp2, 0, sizeof(tmp2));
    memset(&halfsa, 0, sizeof(halfsa));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&sw, 0, sizeof(sw));
    memset(&sz, 0, sizeof(sz));
    memset(&u, 0, sizeof(u));
    _hqrndstate_init(&rs, _state, ae_true);
    ae_vector_init(&diaga, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&qa, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&densea, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&halfa, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rb, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&tmp2, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&halfsa, _state, ae_true);
    _eigsubspacestate_init(&s, _state, ae_true);
    _eigsubspacereport_init(&rep, _state, ae_true);
    ae_vector_init(&sw, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&sz, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&u, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Problem with weakly separated eigenvalues (but not too weak),
     * either with alternating sign - or all positive. EPS-based
     * stopping condition is used because we can not tell how many
     * iterations is required to solve it.
     */
    tollambda = 1.0E-9;
    tolvector = 1.0E-4;
    for(n=1; n<=25; n++)
    {
        for(m=1; m<=n; m++)
        {
            for(smode=0; smode<=2; smode++)
            {
                
                /*
                 * Generate eigenproblem
                 */
                sgn = (double)(2*hqrnduniformi(&rs, 2, _state)-1);
                decaya = 1.05;
                ae_vector_set_length(&diaga, n, _state);
                diaga.ptr.p_double[0] = ae_pow((double)(10), 2*hqrnduniformr(&rs, _state)-1, _state);
                for(i=1; i<=n-1; i++)
                {
                    diaga.ptr.p_double[i] = diaga.ptr.p_double[i-1]*sgn/(decaya*(1+0.01*hqrnduniformr(&rs, _state)));
                }
                rmatrixrndorthogonal(n, &qa, _state);
                ae_matrix_set_length(&densea, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = (double)(0);
                        for(k=0; k<=n-1; k++)
                        {
                            v = v+qa.ptr.pp_double[k][i]*diaga.ptr.p_double[k]*qa.ptr.pp_double[k][j];
                        }
                        densea.ptr.pp_double[i][j] = v;
                    }
                }
                isupper = ae_fp_greater(hqrnduniformr(&rs, _state),0.5);
                ae_matrix_set_length(&halfa, n, n, _state);
                sparsecreate(n, n, 0, &halfsa, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        halfa.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state);
                        if( j>=i&&isupper )
                        {
                            halfa.ptr.pp_double[i][j] = densea.ptr.pp_double[i][j];
                            sparseset(&halfsa, i, j, densea.ptr.pp_double[i][j], _state);
                        }
                        if( j<=i&&!isupper )
                        {
                            halfa.ptr.pp_double[i][j] = densea.ptr.pp_double[i][j];
                            sparseset(&halfsa, i, j, densea.ptr.pp_double[i][j], _state);
                        }
                    }
                }
                sformat = hqrnduniformi(&rs, 2, _state);
                if( sformat==0 )
                {
                    sparseconverttocrs(&halfsa, _state);
                }
                if( sformat==1 )
                {
                    sparseconverttosks(&halfsa, _state);
                }
                
                /*
                 * Solve with eigensolver operating in dense mode
                 */
                eigsubspacecreate(n, m, &s, _state);
                eigsubspacesetcond(&s, tollambda/100, 0, _state);
                if( smode==0 )
                {
                    
                    /*
                     * Dense mode
                     */
                    eigsubspacesolvedenses(&s, &halfa, isupper, &sw, &sz, &rep, _state);
                }
                else
                {
                    if( smode==1 )
                    {
                        
                        /*
                         * Sparse mode
                         */
                        eigsubspacesolvesparses(&s, &halfsa, isupper, &sw, &sz, &rep, _state);
                    }
                    else
                    {
                        if( smode==2 )
                        {
                            
                            /*
                             * Out-of-core mode, symmetric version
                             */
                            eigsubspaceoocstart(&s, 0, _state);
                            while(eigsubspaceooccontinue(&s, _state))
                            {
                                eigsubspaceoocgetrequestinfo(&s, &requesttype, &requestsize, _state);
                                ae_assert(requesttype==0, "EVDI: integrity check failed in unit test", _state);
                                ae_assert(requestsize>0, "EVDI: integrity check failed in unit test", _state);
                                eigsubspaceoocgetrequestdata(&s, &ra, _state);
                                rmatrixsetlengthatleast(&rb, n, requestsize, _state);
                                rmatrixgemm(n, requestsize, n, 1.0, &densea, 0, 0, 0, &ra, 0, 0, 0, 0.0, &rb, 0, 0, _state);
                                eigsubspaceoocsendresult(&s, &rb, _state);
                            }
                            eigsubspaceoocstop(&s, &sw, &sz, &rep, _state);
                        }
                        else
                        {
                            ae_assert(ae_false, "unittest: integrity check failed", _state);
                        }
                    }
                }
                
                /*
                 * Compare against reference values
                 */
                for(i=0; i<=m-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(sw.ptr.p_double[i]-diaga.ptr.p_double[i], _state),tollambda), __FILE__, __LINE__, "testevdunit.ap:2126");
                    v = ae_v_dotproduct(&qa.ptr.pp_double[i][0], 1, &sz.ptr.pp_double[0][i], sz.stride, ae_v_len(0,n-1));
                    mx = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        mx = ae_maxreal(mx, ae_fabs(sz.ptr.pp_double[j][i]*ae_sign(v, _state)-qa.ptr.pp_double[i][j], _state), _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_greater(mx,tolvector), __FILE__, __LINE__, "testevdunit.ap:2131");
                }
            }
        }
    }
    
    /*
     * Problem with highly separated eigenvalues (either with alternating
     * sign - or all positive). Only a few iterations is performed, we
     * want to check convergence properties on such problems.
     */
    tollambda = 1.0E-9;
    tolvector = 1.0E-4;
    itscount = 5;
    for(n=1; n<=25; n++)
    {
        for(m=1; m<=n; m++)
        {
            for(smode=0; smode<=2; smode++)
            {
                
                /*
                 * Generate eigenproblem
                 */
                sgn = (double)(2*hqrnduniformi(&rs, 2, _state)-1);
                decaya = 1.05;
                ae_vector_set_length(&diaga, n, _state);
                diaga.ptr.p_double[0] = ae_pow((double)(10), 2*hqrnduniformr(&rs, _state)-1, _state);
                for(i=1; i<=n-1; i++)
                {
                    diaga.ptr.p_double[i] = diaga.ptr.p_double[i-1]*sgn/(decaya*(1+0.01*hqrnduniformr(&rs, _state)));
                }
                for(i=m; i<=n-1; i++)
                {
                    diaga.ptr.p_double[i] = diaga.ptr.p_double[i]/(100+10*hqrnduniformr(&rs, _state));
                }
                rmatrixrndorthogonal(n, &qa, _state);
                ae_matrix_set_length(&densea, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = (double)(0);
                        for(k=0; k<=n-1; k++)
                        {
                            v = v+qa.ptr.pp_double[k][i]*diaga.ptr.p_double[k]*qa.ptr.pp_double[k][j];
                        }
                        densea.ptr.pp_double[i][j] = v;
                    }
                }
                isupper = ae_fp_greater(hqrnduniformr(&rs, _state),0.5);
                ae_matrix_set_length(&halfa, n, n, _state);
                sparsecreate(n, n, 0, &halfsa, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        halfa.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state);
                        if( j>=i&&isupper )
                        {
                            halfa.ptr.pp_double[i][j] = densea.ptr.pp_double[i][j];
                            sparseset(&halfsa, i, j, densea.ptr.pp_double[i][j], _state);
                        }
                        if( j<=i&&!isupper )
                        {
                            halfa.ptr.pp_double[i][j] = densea.ptr.pp_double[i][j];
                            sparseset(&halfsa, i, j, densea.ptr.pp_double[i][j], _state);
                        }
                    }
                }
                sformat = hqrnduniformi(&rs, 2, _state);
                if( sformat==0 )
                {
                    sparseconverttocrs(&halfsa, _state);
                }
                if( sformat==1 )
                {
                    sparseconverttosks(&halfsa, _state);
                }
                
                /*
                 * Solve with eigensolver operating in dense mode
                 */
                eigsubspacecreate(n, m, &s, _state);
                eigsubspacesetcond(&s, (double)(0), itscount, _state);
                if( smode==0 )
                {
                    
                    /*
                     * Dense mode
                     */
                    eigsubspacesolvedenses(&s, &halfa, isupper, &sw, &sz, &rep, _state);
                }
                else
                {
                    if( smode==1 )
                    {
                        
                        /*
                         * Sparse mode
                         */
                        eigsubspacesolvesparses(&s, &halfsa, isupper, &sw, &sz, &rep, _state);
                    }
                    else
                    {
                        if( smode==2 )
                        {
                            
                            /*
                             * Out-of-core mode, symmetric version
                             *
                             * NOTE: we check that solver performs no more than ItsCount+2 calls
                             */
                            callcount = 0;
                            eigsubspaceoocstart(&s, 0, _state);
                            while(eigsubspaceooccontinue(&s, _state))
                            {
                                eigsubspaceoocgetrequestinfo(&s, &requesttype, &requestsize, _state);
                                ae_assert(requesttype==0, "EVDI: integrity check failed in unit test", _state);
                                ae_assert(requestsize>0, "EVDI: integrity check failed in unit test", _state);
                                eigsubspaceoocgetrequestdata(&s, &ra, _state);
                                rmatrixsetlengthatleast(&rb, n, requestsize, _state);
                                rmatrixgemm(n, requestsize, n, 1.0, &densea, 0, 0, 0, &ra, 0, 0, 0, 0.0, &rb, 0, 0, _state);
                                eigsubspaceoocsendresult(&s, &rb, _state);
                                callcount = callcount+1;
                            }
                            eigsubspaceoocstop(&s, &sw, &sz, &rep, _state);
                            ae_set_error_flag(errorflag, callcount>itscount+2, __FILE__, __LINE__, "testevdunit.ap:2238");
                        }
                        else
                        {
                            ae_assert(ae_false, "unittest: integrity check failed", _state);
                        }
                    }
                }
                
                /*
                 * Compare against reference values
                 */
                ae_set_error_flag(errorflag, rep.iterationscount>itscount, __FILE__, __LINE__, "testevdunit.ap:2246");
                for(i=0; i<=m-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(sw.ptr.p_double[i]-diaga.ptr.p_double[i], _state),tollambda), __FILE__, __LINE__, "testevdunit.ap:2249");
                    v = ae_v_dotproduct(&qa.ptr.pp_double[i][0], 1, &sz.ptr.pp_double[0][i], sz.stride, ae_v_len(0,n-1));
                    mx = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        mx = ae_maxreal(mx, ae_fabs(sz.ptr.pp_double[j][i]*ae_sign(v, _state)-qa.ptr.pp_double[i][j], _state), _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_greater(mx,tolvector), __FILE__, __LINE__, "testevdunit.ap:2254");
                }
            }
        }
    }
    
    /*
     * Problem with numerically zero matrix. We check ability to stop
     * and to return orthogonal vectors.
     */
    tollambda = 1.0E-9;
    itscount = 5;
    for(n=1; n<=25; n++)
    {
        for(m=1; m<=n; m++)
        {
            
            /*
             * Generate eigenproblem
             */
            ae_matrix_set_length(&densea, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    densea.ptr.pp_double[i][j] = (double)(0);
                }
            }
            
            /*
             * Solve with eigensolver operating in dense mode,
             * iteration count based stopping condition.
             */
            eigsubspacecreate(n, m, &s, _state);
            eigsubspacesetcond(&s, (double)(0), itscount, _state);
            eigsubspacesolvedenses(&s, &densea, ae_true, &sw, &sz, &rep, _state);
            ae_set_error_flag(errorflag, rep.iterationscount>itscount, __FILE__, __LINE__, "testevdunit.ap:2283");
            for(i=0; i<=m-1; i++)
            {
                for(j=i; j<=m-1; j++)
                {
                    v = ae_v_dotproduct(&sz.ptr.pp_double[0][i], sz.stride, &sz.ptr.pp_double[0][j], sz.stride, ae_v_len(0,n-1));
                    if( j==i )
                    {
                        v = v-1;
                    }
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(v, _state),1.0E3*ae_machineepsilon), __FILE__, __LINE__, "testevdunit.ap:2291");
                }
                ae_set_error_flag(errorflag, ae_fp_neq(sw.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testevdunit.ap:2293");
            }
            
            /*
             * Solve with eigensolver operating in dense mode,
             * eps-based stopping condition.
             */
            eigsubspacecreate(n, m, &s, _state);
            eigsubspacesetcond(&s, tollambda, 0, _state);
            eigsubspacesolvedenses(&s, &densea, ae_true, &sw, &sz, &rep, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=i; j<=m-1; j++)
                {
                    v = ae_v_dotproduct(&sz.ptr.pp_double[0][i], sz.stride, &sz.ptr.pp_double[0][j], sz.stride, ae_v_len(0,n-1));
                    if( j==i )
                    {
                        v = v-1;
                    }
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(v, _state),1.0E3*ae_machineepsilon), __FILE__, __LINE__, "testevdunit.ap:2310");
                }
                ae_set_error_flag(errorflag, ae_fp_neq(sw.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testevdunit.ap:2312");
            }
        }
    }
    
    /*
     * Test warm start functionality:
     * * for many randomly generated problems...
     * * ...solve original problem with EPS-based criterion
     * * ...then apply small rank-1 correction to the matrix
     * * ...and solve again, with warm start
     * * ...and solve again, with explicit cold start
     * * do so many times and calculate total iteration counts
     *
     * Iteration counts for warm starts should be significantly lower,
     * whilst solution found should be within tolerances.
     */
    cnta = 0;
    cntb = 0;
    cntc = 0;
    eps = 1.0E-3;
    nu = 0.001;
    for(pass=1; pass<=50; pass++)
    {
        
        /*
         * Generate eigenproblem and rank-1 update
         */
        n = 50+hqrnduniformi(&rs, 20, _state);
        m = 1+hqrnduniformi(&rs, 5, _state);
        ae_matrix_set_length(&ra, n, n, _state);
        ae_matrix_set_length(&tmp2, n, n, _state);
        ae_vector_set_length(&u, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                tmp2.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
            }
        }
        rmatrixgemm(n, n, n, 1.0, &tmp2, 0, 0, 0, &tmp2, 0, 0, 1, 0.0, &ra, 0, 0, _state);
        
        /*
         * Solve with eigensolver operating in cold-start mode,
         * iteration count based stopping condition.
         */
        eigsubspacecreate(n, m, &s, _state);
        eigsubspacesetcond(&s, eps, 0, _state);
        eigsubspacesolvedenses(&s, &ra, ae_true, &sw, &sz, &rep, _state);
        cnta = cnta+rep.iterationscount;
        
        /*
         * Solve with eigensolver operating in warm-start mode,
         * iteration count based stopping condition.
         */
        for(i=0; i<=n-1; i++)
        {
            u.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        rmatrixger(n, n, &ra, 0, 0, nu, &u, 0, &u, 0, _state);
        eigsubspacesetwarmstart(&s, ae_true, _state);
        eigsubspacesolvedenses(&s, &ra, ae_true, &sw, &sz, &rep, _state);
        cntb = cntb+rep.iterationscount;
        
        /*
         * Solve with eigensolver operating in explicit cold-start mode,
         * iteration count based stopping condition.
         */
        for(i=0; i<=n-1; i++)
        {
            u.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        rmatrixger(n, n, &ra, 0, 0, nu, &u, 0, &u, 0, _state);
        eigsubspacesetwarmstart(&s, ae_false, _state);
        eigsubspacesolvedenses(&s, &ra, ae_true, &sw, &sz, &rep, _state);
        cntc = cntc+rep.iterationscount;
    }
    ae_set_error_flag(errorflag, ae_fp_greater((double)(cntb),0.66*cnta), __FILE__, __LINE__, "testevdunit.ap:2387");
    ae_set_error_flag(errorflag, ae_fp_less((double)(cntc),0.66*cnta), __FILE__, __LINE__, "testevdunit.ap:2388");
    ae_frame_leave(_state);
}



static void testbasestatunit_testranking(ae_bool* err, ae_state *_state);





ae_bool testbasestat(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool s1errors;
    ae_bool covcorrerrors;
    ae_bool rankerrors;
    double threshold;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t kx;
    ae_int_t ky;
    ae_int_t ctype;
    ae_int_t cidxx;
    ae_int_t cidxy;
    ae_vector x;
    ae_vector y;
    ae_matrix mx;
    ae_matrix my;
    ae_matrix cc;
    ae_matrix cp;
    ae_matrix cs;
    double mean;
    double variance;
    double skewness;
    double kurtosis;
    double adev;
    double median;
    double pv;
    double v;
    double tmean;
    double tvariance;
    double tskewness;
    double tkurtosis;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&mx, 0, sizeof(mx));
    memset(&my, 0, sizeof(my));
    memset(&cc, 0, sizeof(cc));
    memset(&cp, 0, sizeof(cp));
    memset(&cs, 0, sizeof(cs));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mx, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&my, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cc, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cp, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cs, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Primary settings
     */
    waserrors = ae_false;
    s1errors = ae_false;
    covcorrerrors = ae_false;
    rankerrors = ae_false;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * Ranking
     */
    testbasestatunit_testranking(&rankerrors, _state);
    
    /*
     * * prepare X and Y - two test samples
     * * test 1-sample coefficients
     * * test for SampleMean, SampleVariance,
     *   SampleSkewness, SampleKurtosis.
     */
    n = 10;
    ae_vector_set_length(&x, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = ae_sqr((double)(i), _state);
    }
    samplemoments(&x, n, &mean, &variance, &skewness, &kurtosis, _state);
    s1errors = s1errors||ae_fp_greater(ae_fabs(mean-28.5, _state),0.001);
    s1errors = s1errors||ae_fp_greater(ae_fabs(variance-801.1667, _state),0.001);
    s1errors = s1errors||ae_fp_greater(ae_fabs(skewness-0.5751, _state),0.001);
    s1errors = s1errors||ae_fp_greater(ae_fabs(kurtosis+1.2666, _state),0.001);
    tmean = samplemean(&x, n, _state);
    tvariance = samplevariance(&x, n, _state);
    tskewness = sampleskewness(&x, n, _state);
    tkurtosis = samplekurtosis(&x, n, _state);
    s1errors = s1errors||ae_fp_greater(ae_fabs(mean-tmean, _state),1.0E-15);
    s1errors = s1errors||ae_fp_greater(ae_fabs(variance-tvariance, _state),1.0E-15);
    s1errors = s1errors||ae_fp_greater(ae_fabs(skewness-tskewness, _state),1.0E-15);
    s1errors = s1errors||ae_fp_greater(ae_fabs(kurtosis-tkurtosis, _state),1.0E-15);
    sampleadev(&x, n, &adev, _state);
    s1errors = s1errors||ae_fp_greater(ae_fabs(adev-23.2000, _state),0.001);
    samplemedian(&x, n, &median, _state);
    s1errors = s1errors||ae_fp_greater(ae_fabs(median-0.5*(16+25), _state),0.001);
    for(i=0; i<=n-1; i++)
    {
        samplepercentile(&x, n, (double)i/(double)(n-1), &pv, _state);
        s1errors = s1errors||ae_fp_greater(ae_fabs(pv-x.ptr.p_double[i], _state),0.001);
    }
    samplepercentile(&x, n, 0.5, &pv, _state);
    s1errors = s1errors||ae_fp_greater(ae_fabs(pv-0.5*(16+25), _state),0.001);
    
    /*
     * test covariance/correlation:
     * * 2-sample coefficients
     *
     * We generate random matrices MX and MY
     */
    n = 10;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&y, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = ae_sqr((double)(i), _state);
        y.ptr.p_double[i] = (double)(i);
    }
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(pearsoncorr2(&x, &y, n, _state)-0.9627, _state),0.0001);
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(spearmancorr2(&x, &y, n, _state)-1.0000, _state),0.0001);
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(cov2(&x, &y, n, _state)-82.5000, _state),0.0001);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = ae_sqr(i-0.5*n, _state);
        y.ptr.p_double[i] = (double)(i);
    }
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(pearsoncorr2(&x, &y, n, _state)+0.3676, _state),0.0001);
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(spearmancorr2(&x, &y, n, _state)+0.2761, _state),0.0001);
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(cov2(&x, &y, n, _state)+9.1667, _state),0.0001);
    
    /*
     * test covariance/correlation:
     * * matrix covariance/correlation
     * * matrix cross-covariance/cross-correlation
     *
     * We generate random matrices MX and MY which contain KX (KY)
     * columns, all except one are random, one of them is constant.
     * We test that function (a) do not crash on constant column,
     * and (b) return variances/correlations that are exactly zero
     * for this column.
     *
     * CType control variable controls type of constant: 0 - no constant
     * column, 1 - zero column, 2 - nonzero column with value whose
     * binary representation contains many non-zero bits. Using such
     * type of constant column we are able to ensure than even in the
     * presense of roundoff error functions correctly detect constant
     * columns.
     */
    for(n=0; n<=10; n++)
    {
        if( n>0 )
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
        }
        for(ctype=0; ctype<=2; ctype++)
        {
            for(kx=1; kx<=10; kx++)
            {
                for(ky=1; ky<=10; ky++)
                {
                    
                    /*
                     * Fill matrices, add constant column (when CType=1 or =2)
                     */
                    cidxx = -1;
                    cidxy = -1;
                    if( n>0 )
                    {
                        ae_matrix_set_length(&mx, n, kx, _state);
                        ae_matrix_set_length(&my, n, ky, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=kx-1; j++)
                            {
                                mx.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                            for(j=0; j<=ky-1; j++)
                            {
                                my.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        if( ctype==1 )
                        {
                            cidxx = ae_randominteger(kx, _state);
                            cidxy = ae_randominteger(ky, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                mx.ptr.pp_double[i][cidxx] = 0.0;
                                my.ptr.pp_double[i][cidxy] = 0.0;
                            }
                        }
                        if( ctype==2 )
                        {
                            cidxx = ae_randominteger(kx, _state);
                            cidxy = ae_randominteger(ky, _state);
                            v = ae_sqrt((double)(ae_randominteger(kx, _state)+1)/(double)kx, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                mx.ptr.pp_double[i][cidxx] = v;
                                my.ptr.pp_double[i][cidxy] = v;
                            }
                        }
                    }
                    
                    /*
                     * test covariance/correlation matrix using
                     * 2-sample functions as reference point.
                     *
                     * We also test that coefficients for constant variables
                     * are exactly zero.
                     */
                    covm(&mx, n, kx, &cc, _state);
                    pearsoncorrm(&mx, n, kx, &cp, _state);
                    spearmancorrm(&mx, n, kx, &cs, _state);
                    for(i=0; i<=kx-1; i++)
                    {
                        for(j=0; j<=kx-1; j++)
                        {
                            if( n>0 )
                            {
                                ae_v_move(&x.ptr.p_double[0], 1, &mx.ptr.pp_double[0][i], mx.stride, ae_v_len(0,n-1));
                                ae_v_move(&y.ptr.p_double[0], 1, &mx.ptr.pp_double[0][j], mx.stride, ae_v_len(0,n-1));
                            }
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(cov2(&x, &y, n, _state)-cc.ptr.pp_double[i][j], _state),threshold);
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(pearsoncorr2(&x, &y, n, _state)-cp.ptr.pp_double[i][j], _state),threshold);
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(spearmancorr2(&x, &y, n, _state)-cs.ptr.pp_double[i][j], _state),threshold);
                        }
                    }
                    if( ctype!=0&&n>0 )
                    {
                        for(i=0; i<=kx-1; i++)
                        {
                            covcorrerrors = covcorrerrors||ae_fp_neq(cc.ptr.pp_double[i][cidxx],(double)(0));
                            covcorrerrors = covcorrerrors||ae_fp_neq(cc.ptr.pp_double[cidxx][i],(double)(0));
                            covcorrerrors = covcorrerrors||ae_fp_neq(cp.ptr.pp_double[i][cidxx],(double)(0));
                            covcorrerrors = covcorrerrors||ae_fp_neq(cp.ptr.pp_double[cidxx][i],(double)(0));
                            covcorrerrors = covcorrerrors||ae_fp_neq(cs.ptr.pp_double[i][cidxx],(double)(0));
                            covcorrerrors = covcorrerrors||ae_fp_neq(cs.ptr.pp_double[cidxx][i],(double)(0));
                        }
                    }
                    
                    /*
                     * test cross-covariance/cross-correlation matrix using
                     * 2-sample functions as reference point.
                     *
                     * We also test that coefficients for constant variables
                     * are exactly zero.
                     */
                    covm2(&mx, &my, n, kx, ky, &cc, _state);
                    pearsoncorrm2(&mx, &my, n, kx, ky, &cp, _state);
                    spearmancorrm2(&mx, &my, n, kx, ky, &cs, _state);
                    for(i=0; i<=kx-1; i++)
                    {
                        for(j=0; j<=ky-1; j++)
                        {
                            if( n>0 )
                            {
                                ae_v_move(&x.ptr.p_double[0], 1, &mx.ptr.pp_double[0][i], mx.stride, ae_v_len(0,n-1));
                                ae_v_move(&y.ptr.p_double[0], 1, &my.ptr.pp_double[0][j], my.stride, ae_v_len(0,n-1));
                            }
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(cov2(&x, &y, n, _state)-cc.ptr.pp_double[i][j], _state),threshold);
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(pearsoncorr2(&x, &y, n, _state)-cp.ptr.pp_double[i][j], _state),threshold);
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(spearmancorr2(&x, &y, n, _state)-cs.ptr.pp_double[i][j], _state),threshold);
                        }
                    }
                    if( ctype!=0&&n>0 )
                    {
                        for(i=0; i<=kx-1; i++)
                        {
                            covcorrerrors = covcorrerrors||ae_fp_neq(cc.ptr.pp_double[i][cidxy],(double)(0));
                            covcorrerrors = covcorrerrors||ae_fp_neq(cp.ptr.pp_double[i][cidxy],(double)(0));
                            covcorrerrors = covcorrerrors||ae_fp_neq(cs.ptr.pp_double[i][cidxy],(double)(0));
                        }
                        for(j=0; j<=ky-1; j++)
                        {
                            covcorrerrors = covcorrerrors||ae_fp_neq(cc.ptr.pp_double[cidxx][j],(double)(0));
                            covcorrerrors = covcorrerrors||ae_fp_neq(cp.ptr.pp_double[cidxx][j],(double)(0));
                            covcorrerrors = covcorrerrors||ae_fp_neq(cs.ptr.pp_double[cidxx][j],(double)(0));
                        }
                    }
                }
            }
        }
    }
    
    /*
     * Final report
     */
    waserrors = (s1errors||covcorrerrors)||rankerrors;
    if( !silent )
    {
        printf("DESC.STAT TEST\n");
        printf("TOTAL RESULTS:                           ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* 1-SAMPLE FUNCTIONALITY:                ");
        if( !s1errors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* CORRELATION/COVARIATION:               ");
        if( !covcorrerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* RANKING:                               ");
        if( !rankerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests ranking functionality. In case  of  failure  it  sets
Err parameter to True; this parameter is left unchanged otherwise.
*************************************************************************/
static void testbasestatunit_testranking(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t testk;
    ae_int_t npoints;
    ae_int_t nfeatures;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix xy0;
    ae_matrix xy1;
    ae_matrix xy2;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&xy0, 0, sizeof(xy0));
    memset(&xy1, 0, sizeof(xy1));
    memset(&xy2, 0, sizeof(xy2));
    ae_matrix_init(&xy0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test 1 - large array, unique ranks, each row is obtained as follows:
     * * we generate X[i=0..N-1] = I
     * * we add random noise: X[i] := X[i] + 0.2*randomreal()-0.1
     * * we perform random permutation
     *
     * Such dataset has following properties:
     * * all data are unique within their rows
     * * rank(X[i]) = round(X[i])
     *
     * We perform several tests with different NPoints/NFeatures.
     */
    for(testk=0; testk<=2; testk++)
    {
        
        /*
         * Select problem size
         */
        if( testk==0 )
        {
            npoints = 200;
            nfeatures = 1000;
        }
        else
        {
            if( testk==1 )
            {
                npoints = 1000;
                nfeatures = 200;
            }
            else
            {
                npoints = ae_round(ae_sqrt(smpactivationlevel(_state), _state), _state);
                nfeatures = ae_round(ae_sqrt(smpactivationlevel(_state), _state), _state);
            }
        }
        
        /*
         * Generate XY0, XY1, XY2
         */
        ae_matrix_set_length(&xy0, npoints, nfeatures, _state);
        ae_matrix_set_length(&xy1, npoints, nfeatures, _state);
        ae_matrix_set_length(&xy2, npoints, nfeatures, _state);
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nfeatures-1; j++)
            {
                xy0.ptr.pp_double[i][j] = j+0.2*ae_randomreal(_state)-0.1;
            }
            for(j=0; j<=nfeatures-2; j++)
            {
                k = ae_randominteger(nfeatures-j, _state);
                if( k!=0 )
                {
                    v = xy0.ptr.pp_double[i][j];
                    xy0.ptr.pp_double[i][j] = xy0.ptr.pp_double[i][j+k];
                    xy0.ptr.pp_double[i][j+k] = v;
                }
            }
            for(j=0; j<=nfeatures-1; j++)
            {
                xy1.ptr.pp_double[i][j] = xy0.ptr.pp_double[i][j];
                xy2.ptr.pp_double[i][j] = xy0.ptr.pp_double[i][j];
            }
        }
        
        /*
         * Test uncentered ranks
         */
        rankdata(&xy0, npoints, nfeatures, _state);
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nfeatures-1; j++)
            {
                if( ae_fp_neq(xy0.ptr.pp_double[i][j],(double)(ae_round(xy2.ptr.pp_double[i][j], _state))) )
                {
                    *err = ae_true;
                }
            }
        }
        
        /*
         * Test centered ranks:
         * they must be equal to uncentered ranks minus (NFeatures-1)/2
         */
        rankdatacentered(&xy1, npoints, nfeatures, _state);
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nfeatures-1; j++)
            {
                if( ae_fp_neq(xy1.ptr.pp_double[i][j],ae_round(xy2.ptr.pp_double[i][j], _state)-(double)(nfeatures-1)/(double)2) )
                {
                    *err = ae_true;
                }
            }
        }
    }
    
    /*
     * Test correct handling of tied ranks
     */
    npoints = 3;
    nfeatures = 4;
    ae_matrix_set_length(&xy0, npoints, nfeatures, _state);
    ae_matrix_set_length(&xy1, npoints, nfeatures, _state);
    xy0.ptr.pp_double[0][0] = 2.25;
    xy0.ptr.pp_double[0][1] = 3.75;
    xy0.ptr.pp_double[0][2] = 3.25;
    xy0.ptr.pp_double[0][3] = 2.25;
    xy0.ptr.pp_double[1][0] = (double)(2);
    xy0.ptr.pp_double[1][1] = (double)(2);
    xy0.ptr.pp_double[1][2] = (double)(2);
    xy0.ptr.pp_double[1][3] = (double)(7);
    xy0.ptr.pp_double[2][0] = (double)(9);
    xy0.ptr.pp_double[2][1] = (double)(9);
    xy0.ptr.pp_double[2][2] = (double)(9);
    xy0.ptr.pp_double[2][3] = (double)(9);
    for(i=0; i<=npoints-1; i++)
    {
        for(j=0; j<=nfeatures-1; j++)
        {
            xy1.ptr.pp_double[i][j] = xy0.ptr.pp_double[i][j];
        }
    }
    rankdata(&xy0, npoints, nfeatures, _state);
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[0][0]-0.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[0][1]-3.0, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[0][2]-2.0, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[0][3]-0.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[1][0]-1.0, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[1][1]-1.0, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[1][2]-1.0, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[1][3]-3.0, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[2][0]-1.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[2][1]-1.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[2][2]-1.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[2][3]-1.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    rankdatacentered(&xy1, npoints, nfeatures, _state);
    if( ae_fp_greater(ae_fabs(xy1.ptr.pp_double[0][0]+1.0, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy1.ptr.pp_double[0][1]-1.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy1.ptr.pp_double[0][2]-0.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy1.ptr.pp_double[0][3]+1.0, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy1.ptr.pp_double[1][0]+0.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy1.ptr.pp_double[1][1]+0.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy1.ptr.pp_double[1][2]+0.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_greater(ae_fabs(xy1.ptr.pp_double[1][3]-1.5, _state),10*ae_machineepsilon) )
    {
        *err = ae_true;
    }
    if( ae_fp_neq(xy1.ptr.pp_double[2][0],(double)(0)) )
    {
        *err = ae_true;
    }
    if( ae_fp_neq(xy1.ptr.pp_double[2][1],(double)(0)) )
    {
        *err = ae_true;
    }
    if( ae_fp_neq(xy1.ptr.pp_double[2][2],(double)(0)) )
    {
        *err = ae_true;
    }
    if( ae_fp_neq(xy1.ptr.pp_double[2][3],(double)(0)) )
    {
        *err = ae_true;
    }
    ae_frame_leave(_state);
}



static void testpcaunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state);





ae_bool testpca(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxn;
    ae_int_t maxm;
    double threshold;
    ae_int_t m;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t info;
    ae_vector means;
    ae_vector s;
    ae_vector s2;
    ae_vector t2;
    ae_vector t3;
    ae_matrix v;
    ae_matrix v2;
    ae_matrix x;
    double t;
    double h;
    sparsematrix xsparse;
    double tmean;
    double tmeans;
    double tstddev;
    double tstddevs;
    double tmean2;
    double tmeans2;
    double tstddev2;
    double tstddevs2;
    hqrndstate rs;
    ae_int_t requested;
    double tolreduced;
    double varcomplete;
    double varreduced;
    ae_int_t pass;
    ae_bool pcaconverrors;
    ae_bool pcaorterrors;
    ae_bool pcavarerrors;
    ae_bool pcaopterrors;
    ae_bool pcadensesubspaceerrors;
    ae_bool pcasparsesubspaceerrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&means, 0, sizeof(means));
    memset(&s, 0, sizeof(s));
    memset(&s2, 0, sizeof(s2));
    memset(&t2, 0, sizeof(t2));
    memset(&t3, 0, sizeof(t3));
    memset(&v, 0, sizeof(v));
    memset(&v2, 0, sizeof(v2));
    memset(&x, 0, sizeof(x));
    memset(&xsparse, 0, sizeof(xsparse));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&means, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t3, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&xsparse, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Primary settings
     */
    maxm = 10;
    maxn = 100;
    threshold = 1.0E5*ae_machineepsilon;
    waserrors = ae_false;
    pcaconverrors = ae_false;
    pcaorterrors = ae_false;
    pcavarerrors = ae_false;
    pcaopterrors = ae_false;
    pcadensesubspaceerrors = ae_false;
    pcasparsesubspaceerrors = ae_false;
    
    /*
     * Test 1: N random points in M-dimensional space, full PCA.
     */
    for(m=1; m<=maxm; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * Generate task
             */
            ae_matrix_set_length(&x, n-1+1, m-1+1, _state);
            ae_vector_set_length(&means, m-1+1, _state);
            for(j=0; j<=m-1; j++)
            {
                means.ptr.p_double[j] = 1.5*ae_randomreal(_state)-0.75;
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    x.ptr.pp_double[i][j] = means.ptr.p_double[j]+(2*ae_randomreal(_state)-1);
                }
            }
            
            /*
             * Solve
             */
            pcabuildbasis(&x, n, m, &info, &s, &v, _state);
            if( info!=1 )
            {
                pcaconverrors = ae_true;
                continue;
            }
            
            /*
             * Orthogonality test
             */
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    t = ae_v_dotproduct(&v.ptr.pp_double[0][i], v.stride, &v.ptr.pp_double[0][j], v.stride, ae_v_len(0,m-1));
                    if( i==j )
                    {
                        t = t-1;
                    }
                    pcaorterrors = pcaorterrors||ae_fp_greater(ae_fabs(t, _state),threshold);
                }
            }
            
            /*
             * Variance test
             */
            ae_vector_set_length(&t2, n-1+1, _state);
            for(k=0; k<=m-1; k++)
            {
                for(i=0; i<=n-1; i++)
                {
                    t = ae_v_dotproduct(&x.ptr.pp_double[i][0], 1, &v.ptr.pp_double[0][k], v.stride, ae_v_len(0,m-1));
                    t2.ptr.p_double[i] = t;
                }
                testpcaunit_calculatemv(&t2, n, &tmean, &tmeans, &tstddev, &tstddevs, _state);
                if( n!=1 )
                {
                    t = ae_sqr(tstddev, _state)*n/(n-1);
                }
                else
                {
                    t = (double)(0);
                }
                pcavarerrors = pcavarerrors||ae_fp_greater(ae_fabs(t-s.ptr.p_double[k], _state),threshold);
            }
            for(k=0; k<=m-2; k++)
            {
                pcavarerrors = pcavarerrors||ae_fp_less(s.ptr.p_double[k],s.ptr.p_double[k+1]);
            }
            
            /*
             * Optimality: different perturbations in V[..,0] can't
             * increase variance of projection - can only decrease.
             */
            ae_vector_set_length(&t2, n-1+1, _state);
            ae_vector_set_length(&t3, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                t = ae_v_dotproduct(&x.ptr.pp_double[i][0], 1, &v.ptr.pp_double[0][0], v.stride, ae_v_len(0,m-1));
                t2.ptr.p_double[i] = t;
            }
            testpcaunit_calculatemv(&t2, n, &tmean, &tmeans, &tstddev, &tstddevs, _state);
            for(k=0; k<=2*m-1; k++)
            {
                h = 0.001;
                if( k%2!=0 )
                {
                    h = -h;
                }
                ae_v_move(&t3.ptr.p_double[0], 1, &t2.ptr.p_double[0], 1, ae_v_len(0,n-1));
                ae_v_addd(&t3.ptr.p_double[0], 1, &x.ptr.pp_double[0][k/2], x.stride, ae_v_len(0,n-1), h);
                t = (double)(0);
                for(j=0; j<=m-1; j++)
                {
                    if( j!=k/2 )
                    {
                        t = t+ae_sqr(v.ptr.pp_double[j][0], _state);
                    }
                    else
                    {
                        t = t+ae_sqr(v.ptr.pp_double[j][0]+h, _state);
                    }
                }
                t = 1/ae_sqrt(t, _state);
                ae_v_muld(&t3.ptr.p_double[0], 1, ae_v_len(0,n-1), t);
                testpcaunit_calculatemv(&t3, n, &tmean2, &tmeans2, &tstddev2, &tstddevs2, _state);
                pcaopterrors = pcaopterrors||ae_fp_greater(tstddev2,tstddev+threshold);
            }
        }
    }
    
    /*
     * Special test for N=0
     */
    for(m=1; m<=maxm; m++)
    {
        
        /*
         * Solve
         */
        pcabuildbasis(&x, 0, m, &info, &s, &v, _state);
        if( info!=1 )
        {
            pcaconverrors = ae_true;
            continue;
        }
        
        /*
         * Orthogonality test
         */
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                t = ae_v_dotproduct(&v.ptr.pp_double[0][i], v.stride, &v.ptr.pp_double[0][j], v.stride, ae_v_len(0,m-1));
                if( i==j )
                {
                    t = t-1;
                }
                pcaorterrors = pcaorterrors||ae_fp_greater(ae_fabs(t, _state),threshold);
            }
        }
    }
    
    /*
     * Test dense reduced subspace PCA on randomly generated matrix. Because
     * matrix is random, it may have bad spectral properties, so large
     * number of iterations is performed.
     *
     * Following properties are tested:
     * * principal vectors are orthogonal
     * * variance values are computed correctly
     * * variance values are decreasing
     * * variance by inexact reduced PCA deviates from variance explained
     *   by top REQUESTED vectors of complete PCA by at most TolReduced.
     */
    tolreduced = 1.0E-3;
    for(m=1; m<=maxm; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * Generate task
             */
            requested = 1+hqrnduniformi(&rs, m, _state);
            ae_vector_set_length(&means, m, _state);
            for(j=0; j<=m-1; j++)
            {
                means.ptr.p_double[j] = 100*hqrndnormal(&rs, _state);
            }
            ae_matrix_set_length(&x, n, m, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    x.ptr.pp_double[i][j] = hqrndnormal(&rs, _state)+means.ptr.p_double[j];
                }
            }
            
            /*
             * Solve
             */
            pcatruncatedsubspace(&x, n, m, requested, 1.0E-9, 0, &s, &v, _state);
            
            /*
             * Orthogonality test
             */
            for(i=0; i<=requested-1; i++)
            {
                for(j=0; j<=requested-1; j++)
                {
                    t = ae_v_dotproduct(&v.ptr.pp_double[0][i], v.stride, &v.ptr.pp_double[0][j], v.stride, ae_v_len(0,m-1));
                    if( i==j )
                    {
                        t = t-1;
                    }
                    ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_greater(ae_fabs(t, _state),threshold), __FILE__, __LINE__, "testpcaunit.ap:217");
                }
            }
            
            /*
             * Variance test
             */
            ae_vector_set_length(&t2, n, _state);
            for(k=0; k<=requested-1; k++)
            {
                for(i=0; i<=n-1; i++)
                {
                    t = ae_v_dotproduct(&x.ptr.pp_double[i][0], 1, &v.ptr.pp_double[0][k], v.stride, ae_v_len(0,m-1));
                    t2.ptr.p_double[i] = t;
                }
                testpcaunit_calculatemv(&t2, n, &tmean, &tmeans, &tstddev, &tstddevs, _state);
                if( n!=1 )
                {
                    t = ae_sqr(tstddev, _state)*n/(n-1);
                }
                else
                {
                    t = (double)(0);
                }
                ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_greater(ae_fabs(t-s.ptr.p_double[k], _state),threshold), __FILE__, __LINE__, "testpcaunit.ap:236");
            }
            for(k=0; k<=requested-2; k++)
            {
                ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_less(ae_fabs(s.ptr.p_double[k], _state),ae_fabs(s.ptr.p_double[k+1], _state)), __FILE__, __LINE__, "testpcaunit.ap:239");
            }
            for(k=0; k<=requested-1; k++)
            {
                ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_less_eq(s.ptr.p_double[k],(double)(0))&&ae_fp_greater(ae_fabs(s.ptr.p_double[k], _state),1000*ae_machineepsilon*ae_fabs(s.ptr.p_double[0], _state)), __FILE__, __LINE__, "testpcaunit.ap:241");
            }
            
            /*
             * Compare variance explained by top REQUESTED vectors from
             * full PCA and variance explained by reduced PCA.
             */
            pcabuildbasis(&x, n, m, &info, &s2, &v2, _state);
            ae_assert(info>0, "PCA error: solver nonconvergence", _state);
            varcomplete = 0.0;
            varreduced = 0.0;
            for(k=0; k<=requested-1; k++)
            {
                varreduced = varreduced+s.ptr.p_double[k];
                varcomplete = varcomplete+s2.ptr.p_double[k];
            }
            ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_less((varreduced-varcomplete)/varcomplete,-tolreduced), __FILE__, __LINE__, "testpcaunit.ap:256");
            ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_greater((varreduced-varcomplete)/varcomplete,1.0E5*ae_machineepsilon), __FILE__, __LINE__, "testpcaunit.ap:257");
        }
    }
    
    /*
     * Test dense subspace reduced PCA on specially designed problem with good
     * spectral properties. Only limited number of iterations is performed,
     * and we expect fast convergence.
     */
    for(pass=0; pass<=3; pass++)
    {
        
        /*
         * Generate task
         */
        m = -1;
        requested = -1;
        if( pass==0 )
        {
            m = 50+hqrnduniformi(&rs, 50, _state);
            requested = 1+hqrnduniformi(&rs, 10, _state);
        }
        if( pass==1 )
        {
            m = 100+hqrnduniformi(&rs, 50, _state);
            requested = 1+hqrnduniformi(&rs, 10, _state);
        }
        if( pass==2 )
        {
            m = 100+hqrnduniformi(&rs, 50, _state);
            requested = 25+hqrnduniformi(&rs, 10, _state);
        }
        if( pass==3 )
        {
            m = 200+hqrnduniformi(&rs, 200, _state);
            requested = 1+hqrnduniformi(&rs, 10, _state);
        }
        ae_assert(m>0&&requested>0, "PCA test: integrity failure", _state);
        n = 2*m;
        tolreduced = 1.0E-3;
        ae_matrix_set_length(&x, n, m, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                x.ptr.pp_double[i][j] = (double)(0);
            }
        }
        x.ptr.pp_double[0][0] = 1.0;
        for(i=1; i<=m-1; i++)
        {
            x.ptr.pp_double[i][i] = x.ptr.pp_double[i-1][i-1]*(1/1.05);
        }
        rmatrixrndorthogonalfromtheleft(&x, n, m, _state);
        rmatrixrndorthogonalfromtheright(&x, n, m, _state);
        
        /*
         * Solve
         */
        if( ae_fp_greater(hqrnduniformr(&rs, _state),0.5) )
        {
            pcatruncatedsubspace(&x, n, m, requested, 0.0, 20, &s, &v, _state);
        }
        else
        {
            pcatruncatedsubspace(&x, n, m, requested, 0.0, 0, &s, &v, _state);
        }
        
        /*
         * Orthogonality test
         */
        for(i=0; i<=requested-1; i++)
        {
            for(j=0; j<=requested-1; j++)
            {
                t = ae_v_dotproduct(&v.ptr.pp_double[0][i], v.stride, &v.ptr.pp_double[0][j], v.stride, ae_v_len(0,m-1));
                if( i==j )
                {
                    t = t-1;
                }
                ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_greater(ae_fabs(t, _state),threshold), __FILE__, __LINE__, "testpcaunit.ap:323");
            }
        }
        
        /*
         * Variance test
         */
        ae_vector_set_length(&t2, n, _state);
        for(k=0; k<=requested-1; k++)
        {
            for(i=0; i<=n-1; i++)
            {
                t = ae_v_dotproduct(&x.ptr.pp_double[i][0], 1, &v.ptr.pp_double[0][k], v.stride, ae_v_len(0,m-1));
                t2.ptr.p_double[i] = t;
            }
            testpcaunit_calculatemv(&t2, n, &tmean, &tmeans, &tstddev, &tstddevs, _state);
            if( n!=1 )
            {
                t = ae_sqr(tstddev, _state)*n/(n-1);
            }
            else
            {
                t = (double)(0);
            }
            ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_greater(ae_fabs(t-s.ptr.p_double[k], _state),threshold), __FILE__, __LINE__, "testpcaunit.ap:342");
        }
        for(k=0; k<=requested-2; k++)
        {
            ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_less(s.ptr.p_double[k],s.ptr.p_double[k+1]), __FILE__, __LINE__, "testpcaunit.ap:345");
        }
        
        /*
         * Compare variance explained by top REQUESTED vectors from
         * full PCA and variance explained by reduced PCA.
         */
        pcabuildbasis(&x, n, m, &info, &s2, &v2, _state);
        ae_assert(info>0, "PCA error: solver nonconvergence", _state);
        varcomplete = 0.0;
        varreduced = 0.0;
        for(k=0; k<=requested-1; k++)
        {
            varreduced = varreduced+s.ptr.p_double[k];
            varcomplete = varcomplete+s2.ptr.p_double[k];
        }
        ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_less((varreduced-varcomplete)/varcomplete,-tolreduced), __FILE__, __LINE__, "testpcaunit.ap:360");
        ae_set_error_flag(&pcadensesubspaceerrors, ae_fp_greater((varreduced-varcomplete)/varcomplete,1.0E5*ae_machineepsilon), __FILE__, __LINE__, "testpcaunit.ap:361");
    }
    
    /*
     * Test sparse reduced subspace PCA on randomly generated matrix with
     * two non-zero elements at each row. Because matrix is random, it may
     * have bad spectral properties, so large number of iterations is performed.
     *
     * Following properties are tested:
     * * principal vectors are orthogonal
     * * variance values are computed correctly
     * * variance values are decreasing
     * * variance by inexact reduced PCA deviates from variance explained
     *   by top REQUESTED vectors of complete PCA by at most TolReduced.
     */
    tolreduced = 1.0E-3;
    for(m=1; m<=maxm; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * Generate task
             */
            requested = 1+hqrnduniformi(&rs, m, _state);
            ae_vector_set_length(&means, m, _state);
            for(j=0; j<=m-1; j++)
            {
                means.ptr.p_double[j] = 100*hqrndnormal(&rs, _state);
            }
            sparsecreate(n, m, 0, &xsparse, _state);
            for(i=0; i<=n-1; i++)
            {
                j = hqrnduniformi(&rs, m, _state);
                sparseset(&xsparse, i, j, hqrndnormal(&rs, _state)+means.ptr.p_double[j], _state);
                j = hqrnduniformi(&rs, m, _state);
                sparseset(&xsparse, i, j, hqrndnormal(&rs, _state)+means.ptr.p_double[j], _state);
            }
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                sparseconverttocrs(&xsparse, _state);
            }
            ae_matrix_set_length(&x, n, m, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    x.ptr.pp_double[i][j] = sparseget(&xsparse, i, j, _state);
                }
            }
            
            /*
             * Solve
             */
            pcatruncatedsubspacesparse(&xsparse, n, m, requested, 1.0E-9, 0, &s, &v, _state);
            
            /*
             * Orthogonality test
             */
            for(i=0; i<=requested-1; i++)
            {
                for(j=0; j<=requested-1; j++)
                {
                    t = ae_v_dotproduct(&v.ptr.pp_double[0][i], v.stride, &v.ptr.pp_double[0][j], v.stride, ae_v_len(0,m-1));
                    if( i==j )
                    {
                        t = t-1;
                    }
                    ae_set_error_flag(&pcasparsesubspaceerrors, ae_fp_greater(ae_fabs(t, _state),threshold), __FILE__, __LINE__, "testpcaunit.ap:417");
                }
            }
            
            /*
             * Variance test
             */
            ae_vector_set_length(&t2, n, _state);
            for(k=0; k<=requested-1; k++)
            {
                for(i=0; i<=n-1; i++)
                {
                    t = ae_v_dotproduct(&x.ptr.pp_double[i][0], 1, &v.ptr.pp_double[0][k], v.stride, ae_v_len(0,m-1));
                    t2.ptr.p_double[i] = t;
                }
                testpcaunit_calculatemv(&t2, n, &tmean, &tmeans, &tstddev, &tstddevs, _state);
                if( n!=1 )
                {
                    t = ae_sqr(tstddev, _state)*n/(n-1);
                }
                else
                {
                    t = (double)(0);
                }
                ae_set_error_flag(&pcasparsesubspaceerrors, ae_fp_greater(ae_fabs(t-s.ptr.p_double[k], _state),threshold*rmaxabs3(t, s.ptr.p_double[k], 1.0, _state)), __FILE__, __LINE__, "testpcaunit.ap:436");
            }
            for(k=0; k<=requested-2; k++)
            {
                ae_set_error_flag(&pcasparsesubspaceerrors, ae_fp_less(ae_fabs(s.ptr.p_double[k], _state),ae_fabs(s.ptr.p_double[k+1], _state)), __FILE__, __LINE__, "testpcaunit.ap:439");
            }
            for(k=0; k<=requested-1; k++)
            {
                ae_set_error_flag(&pcasparsesubspaceerrors, ae_fp_less_eq(s.ptr.p_double[k],(double)(0))&&ae_fp_greater(ae_fabs(s.ptr.p_double[k], _state),1000*ae_machineepsilon*ae_fabs(s.ptr.p_double[0], _state)), __FILE__, __LINE__, "testpcaunit.ap:441");
            }
            
            /*
             * Compare variance explained by top REQUESTED vectors from
             * full PCA and variance explained by reduced PCA.
             */
            pcabuildbasis(&x, n, m, &info, &s2, &v2, _state);
            ae_assert(info>0, "PCA error: solver nonconvergence", _state);
            varcomplete = 0.0;
            varreduced = 0.0;
            for(k=0; k<=requested-1; k++)
            {
                varreduced = varreduced+s.ptr.p_double[k];
                varcomplete = varcomplete+s2.ptr.p_double[k];
            }
            ae_set_error_flag(&pcasparsesubspaceerrors, ae_fp_less((varreduced-varcomplete)/varcomplete,-tolreduced), __FILE__, __LINE__, "testpcaunit.ap:456");
            ae_set_error_flag(&pcasparsesubspaceerrors, ae_fp_greater((varreduced-varcomplete)/varcomplete,1.0E5*ae_machineepsilon), __FILE__, __LINE__, "testpcaunit.ap:457");
        }
    }
    
    /*
     * Final report
     */
    waserrors = ((((pcaconverrors||pcaorterrors)||pcavarerrors)||pcaopterrors)||pcadensesubspaceerrors)||pcasparsesubspaceerrors;
    if( !silent )
    {
        printf("PCA TEST\n");
        printf("COMPLETE PCA:\n");
        printf("* CONVERGENCE                            ");
        if( !pcaconverrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* ORTOGONALITY                           ");
        if( !pcaorterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* VARIANCE REPORT                        ");
        if( !pcavarerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OPTIMALITY                             ");
        if( !pcaopterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SUBSPACE PCA:\n");
        printf("* DENSE SUBSPACE PCA                     ");
        if( !pcadensesubspaceerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* SPARSE SUBSPACE PCA                    ");
        if( !pcasparsesubspaceerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Moments estimates and their errors
*************************************************************************/
static void testpcaunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state)
{
    ae_int_t i;
    double v1;
    double v2;
    double variance;

    *mean = 0;
    *means = 0;
    *stddev = 0;
    *stddevs = 0;

    *mean = (double)(0);
    *means = (double)(1);
    *stddev = (double)(0);
    *stddevs = (double)(1);
    variance = (double)(0);
    if( n<=1 )
    {
        return;
    }
    
    /*
     * Mean
     */
    for(i=0; i<=n-1; i++)
    {
        *mean = *mean+x->ptr.p_double[i];
    }
    *mean = *mean/n;
    
    /*
     * Variance (using corrected two-pass algorithm)
     */
    if( n!=1 )
    {
        v1 = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v1 = v1+ae_sqr(x->ptr.p_double[i]-(*mean), _state);
        }
        v2 = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v2 = v2+(x->ptr.p_double[i]-(*mean));
        }
        v2 = ae_sqr(v2, _state)/n;
        variance = (v1-v2)/n;
        if( ae_fp_less(variance,(double)(0)) )
        {
            variance = (double)(0);
        }
        *stddev = ae_sqrt(variance, _state);
    }
    
    /*
     * Errors
     */
    *means = *stddev/ae_sqrt((double)(n), _state);
    *stddevs = *stddev*ae_sqrt((double)(2), _state)/ae_sqrt((double)(n-1), _state);
}



static void testbdssunit_unset1di(/* Integer */ ae_vector* a,
     ae_state *_state);





/*************************************************************************
Testing BDSS operations
*************************************************************************/
ae_bool testbdss(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_int_t maxnq;
    ae_vector a;
    ae_vector a0;
    ae_vector at;
    ae_matrix p;
    ae_vector thresholds;
    ae_int_t ni;
    ae_vector c;
    ae_vector p1;
    ae_vector p2;
    ae_vector ties;
    ae_vector pt1;
    ae_vector pt2;
    ae_int_t tiecount;
    ae_int_t c1;
    ae_int_t c0;
    ae_int_t nc;
    ae_vector tmp;
    ae_vector sortrbuf;
    ae_vector sortrbuf2;
    ae_vector sortibuf;
    double pal;
    double pbl;
    double par;
    double pbr;
    double cve;
    double cvr;
    ae_int_t info;
    double threshold;
    ae_vector tiebuf;
    ae_vector cntbuf;
    double rms;
    double cvrms;
    ae_bool waserrors;
    ae_bool tieserrors;
    ae_bool split2errors;
    ae_bool optimalsplitkerrors;
    ae_bool splitkerrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&a0, 0, sizeof(a0));
    memset(&at, 0, sizeof(at));
    memset(&p, 0, sizeof(p));
    memset(&thresholds, 0, sizeof(thresholds));
    memset(&c, 0, sizeof(c));
    memset(&p1, 0, sizeof(p1));
    memset(&p2, 0, sizeof(p2));
    memset(&ties, 0, sizeof(ties));
    memset(&pt1, 0, sizeof(pt1));
    memset(&pt2, 0, sizeof(pt2));
    memset(&tmp, 0, sizeof(tmp));
    memset(&sortrbuf, 0, sizeof(sortrbuf));
    memset(&sortrbuf2, 0, sizeof(sortrbuf2));
    memset(&sortibuf, 0, sizeof(sortibuf));
    memset(&tiebuf, 0, sizeof(tiebuf));
    memset(&cntbuf, 0, sizeof(cntbuf));
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&at, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&thresholds, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&ties, 0, DT_INT, _state, ae_true);
    ae_vector_init(&pt1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&pt2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sortrbuf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sortrbuf2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sortibuf, 0, DT_INT, _state, ae_true);
    ae_vector_init(&tiebuf, 0, DT_INT, _state, ae_true);
    ae_vector_init(&cntbuf, 0, DT_INT, _state, ae_true);

    waserrors = ae_false;
    tieserrors = ae_false;
    split2errors = ae_false;
    splitkerrors = ae_false;
    optimalsplitkerrors = ae_false;
    maxn = 100;
    maxnq = 49;
    passcount = 10;
    
    /*
     * Test ties
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * untied data, test DSTie
             */
            testbdssunit_unset1di(&p1, _state);
            testbdssunit_unset1di(&p2, _state);
            testbdssunit_unset1di(&pt1, _state);
            testbdssunit_unset1di(&pt2, _state);
            ae_vector_set_length(&a, n-1+1, _state);
            ae_vector_set_length(&a0, n-1+1, _state);
            ae_vector_set_length(&at, n-1+1, _state);
            ae_vector_set_length(&tmp, n-1+1, _state);
            a.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
            tmp.ptr.p_double[0] = ae_randomreal(_state);
            for(i=1; i<=n-1; i++)
            {
                
                /*
                 * A is randomly permuted
                 */
                a.ptr.p_double[i] = a.ptr.p_double[i-1]+0.1*ae_randomreal(_state)+0.1;
                tmp.ptr.p_double[i] = ae_randomreal(_state);
            }
            tagsortfastr(&tmp, &a, &sortrbuf, &sortrbuf2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                at.ptr.p_double[i] = a.ptr.p_double[i];
            }
            dstie(&a0, n, &ties, &tiecount, &p1, &p2, _state);
            tagsort(&at, n, &pt1, &pt2, _state);
            for(i=0; i<=n-1; i++)
            {
                tieserrors = tieserrors||p1.ptr.p_int[i]!=pt1.ptr.p_int[i];
                tieserrors = tieserrors||p2.ptr.p_int[i]!=pt2.ptr.p_int[i];
            }
            tieserrors = tieserrors||tiecount!=n;
            if( tiecount==n )
            {
                for(i=0; i<=n; i++)
                {
                    tieserrors = tieserrors||ties.ptr.p_int[i]!=i;
                }
            }
            
            /*
             * tied data, test DSTie
             */
            testbdssunit_unset1di(&p1, _state);
            testbdssunit_unset1di(&p2, _state);
            testbdssunit_unset1di(&pt1, _state);
            testbdssunit_unset1di(&pt2, _state);
            ae_vector_set_length(&a, n-1+1, _state);
            ae_vector_set_length(&a0, n-1+1, _state);
            ae_vector_set_length(&at, n-1+1, _state);
            c1 = 0;
            c0 = 0;
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(ae_randominteger(2, _state));
                if( ae_fp_eq(a.ptr.p_double[i],(double)(0)) )
                {
                    c0 = c0+1;
                }
                else
                {
                    c1 = c1+1;
                }
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                at.ptr.p_double[i] = a.ptr.p_double[i];
            }
            dstie(&a0, n, &ties, &tiecount, &p1, &p2, _state);
            tagsort(&at, n, &pt1, &pt2, _state);
            for(i=0; i<=n-1; i++)
            {
                tieserrors = tieserrors||p1.ptr.p_int[i]!=pt1.ptr.p_int[i];
                tieserrors = tieserrors||p2.ptr.p_int[i]!=pt2.ptr.p_int[i];
            }
            if( c0==0||c1==0 )
            {
                tieserrors = tieserrors||tiecount!=1;
                if( tiecount==1 )
                {
                    tieserrors = tieserrors||ties.ptr.p_int[0]!=0;
                    tieserrors = tieserrors||ties.ptr.p_int[1]!=n;
                }
            }
            else
            {
                tieserrors = tieserrors||tiecount!=2;
                if( tiecount==2 )
                {
                    tieserrors = tieserrors||ties.ptr.p_int[0]!=0;
                    tieserrors = tieserrors||ties.ptr.p_int[1]!=c0;
                    tieserrors = tieserrors||ties.ptr.p_int[2]!=n;
                }
            }
        }
    }
    
    /*
     * split-2
     */
    
    /*
     * General tests for different N's
     */
    for(n=1; n<=maxn; n++)
    {
        ae_vector_set_length(&a, n-1+1, _state);
        ae_vector_set_length(&c, n-1+1, _state);
        
        /*
         * one-tie test
         */
        if( n%2==0 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(n);
                c.ptr.p_int[i] = i%2;
            }
            dsoptimalsplit2(&a, &c, n, &info, &threshold, &pal, &pbl, &par, &pbr, &cve, _state);
            if( info!=-3 )
            {
                split2errors = ae_true;
                continue;
            }
        }
        
        /*
         * two-tie test
         */
        
        /*
         * test #1
         */
        if( n>1 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(i/((n+1)/2));
                c.ptr.p_int[i] = i/((n+1)/2);
            }
            dsoptimalsplit2(&a, &c, n, &info, &threshold, &pal, &pbl, &par, &pbr, &cve, _state);
            if( info!=1 )
            {
                split2errors = ae_true;
                continue;
            }
            split2errors = split2errors||ae_fp_greater(ae_fabs(threshold-0.5, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(pal-1, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(pbl-0, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(par-0, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(pbr-1, _state),100*ae_machineepsilon);
        }
    }
    
    /*
     * Special "CREDIT"-test (transparency coefficient)
     */
    n = 110;
    ae_vector_set_length(&a, n-1+1, _state);
    ae_vector_set_length(&c, n-1+1, _state);
    a.ptr.p_double[0] = 0.000;
    c.ptr.p_int[0] = 0;
    a.ptr.p_double[1] = 0.000;
    c.ptr.p_int[1] = 0;
    a.ptr.p_double[2] = 0.000;
    c.ptr.p_int[2] = 0;
    a.ptr.p_double[3] = 0.000;
    c.ptr.p_int[3] = 0;
    a.ptr.p_double[4] = 0.000;
    c.ptr.p_int[4] = 0;
    a.ptr.p_double[5] = 0.000;
    c.ptr.p_int[5] = 0;
    a.ptr.p_double[6] = 0.000;
    c.ptr.p_int[6] = 0;
    a.ptr.p_double[7] = 0.000;
    c.ptr.p_int[7] = 1;
    a.ptr.p_double[8] = 0.000;
    c.ptr.p_int[8] = 0;
    a.ptr.p_double[9] = 0.000;
    c.ptr.p_int[9] = 1;
    a.ptr.p_double[10] = 0.000;
    c.ptr.p_int[10] = 0;
    a.ptr.p_double[11] = 0.000;
    c.ptr.p_int[11] = 0;
    a.ptr.p_double[12] = 0.000;
    c.ptr.p_int[12] = 0;
    a.ptr.p_double[13] = 0.000;
    c.ptr.p_int[13] = 0;
    a.ptr.p_double[14] = 0.000;
    c.ptr.p_int[14] = 0;
    a.ptr.p_double[15] = 0.000;
    c.ptr.p_int[15] = 0;
    a.ptr.p_double[16] = 0.000;
    c.ptr.p_int[16] = 0;
    a.ptr.p_double[17] = 0.000;
    c.ptr.p_int[17] = 0;
    a.ptr.p_double[18] = 0.000;
    c.ptr.p_int[18] = 0;
    a.ptr.p_double[19] = 0.000;
    c.ptr.p_int[19] = 0;
    a.ptr.p_double[20] = 0.000;
    c.ptr.p_int[20] = 0;
    a.ptr.p_double[21] = 0.000;
    c.ptr.p_int[21] = 0;
    a.ptr.p_double[22] = 0.000;
    c.ptr.p_int[22] = 1;
    a.ptr.p_double[23] = 0.000;
    c.ptr.p_int[23] = 0;
    a.ptr.p_double[24] = 0.000;
    c.ptr.p_int[24] = 0;
    a.ptr.p_double[25] = 0.000;
    c.ptr.p_int[25] = 0;
    a.ptr.p_double[26] = 0.000;
    c.ptr.p_int[26] = 0;
    a.ptr.p_double[27] = 0.000;
    c.ptr.p_int[27] = 1;
    a.ptr.p_double[28] = 0.000;
    c.ptr.p_int[28] = 0;
    a.ptr.p_double[29] = 0.000;
    c.ptr.p_int[29] = 1;
    a.ptr.p_double[30] = 0.000;
    c.ptr.p_int[30] = 0;
    a.ptr.p_double[31] = 0.000;
    c.ptr.p_int[31] = 1;
    a.ptr.p_double[32] = 0.000;
    c.ptr.p_int[32] = 0;
    a.ptr.p_double[33] = 0.000;
    c.ptr.p_int[33] = 1;
    a.ptr.p_double[34] = 0.000;
    c.ptr.p_int[34] = 0;
    a.ptr.p_double[35] = 0.030;
    c.ptr.p_int[35] = 0;
    a.ptr.p_double[36] = 0.030;
    c.ptr.p_int[36] = 0;
    a.ptr.p_double[37] = 0.050;
    c.ptr.p_int[37] = 0;
    a.ptr.p_double[38] = 0.070;
    c.ptr.p_int[38] = 1;
    a.ptr.p_double[39] = 0.110;
    c.ptr.p_int[39] = 0;
    a.ptr.p_double[40] = 0.110;
    c.ptr.p_int[40] = 1;
    a.ptr.p_double[41] = 0.120;
    c.ptr.p_int[41] = 0;
    a.ptr.p_double[42] = 0.130;
    c.ptr.p_int[42] = 0;
    a.ptr.p_double[43] = 0.140;
    c.ptr.p_int[43] = 0;
    a.ptr.p_double[44] = 0.140;
    c.ptr.p_int[44] = 0;
    a.ptr.p_double[45] = 0.140;
    c.ptr.p_int[45] = 0;
    a.ptr.p_double[46] = 0.150;
    c.ptr.p_int[46] = 0;
    a.ptr.p_double[47] = 0.150;
    c.ptr.p_int[47] = 0;
    a.ptr.p_double[48] = 0.170;
    c.ptr.p_int[48] = 0;
    a.ptr.p_double[49] = 0.190;
    c.ptr.p_int[49] = 1;
    a.ptr.p_double[50] = 0.200;
    c.ptr.p_int[50] = 0;
    a.ptr.p_double[51] = 0.200;
    c.ptr.p_int[51] = 0;
    a.ptr.p_double[52] = 0.250;
    c.ptr.p_int[52] = 0;
    a.ptr.p_double[53] = 0.250;
    c.ptr.p_int[53] = 0;
    a.ptr.p_double[54] = 0.260;
    c.ptr.p_int[54] = 0;
    a.ptr.p_double[55] = 0.270;
    c.ptr.p_int[55] = 0;
    a.ptr.p_double[56] = 0.280;
    c.ptr.p_int[56] = 0;
    a.ptr.p_double[57] = 0.310;
    c.ptr.p_int[57] = 0;
    a.ptr.p_double[58] = 0.310;
    c.ptr.p_int[58] = 0;
    a.ptr.p_double[59] = 0.330;
    c.ptr.p_int[59] = 0;
    a.ptr.p_double[60] = 0.330;
    c.ptr.p_int[60] = 0;
    a.ptr.p_double[61] = 0.340;
    c.ptr.p_int[61] = 0;
    a.ptr.p_double[62] = 0.340;
    c.ptr.p_int[62] = 0;
    a.ptr.p_double[63] = 0.370;
    c.ptr.p_int[63] = 0;
    a.ptr.p_double[64] = 0.380;
    c.ptr.p_int[64] = 1;
    a.ptr.p_double[65] = 0.380;
    c.ptr.p_int[65] = 0;
    a.ptr.p_double[66] = 0.410;
    c.ptr.p_int[66] = 0;
    a.ptr.p_double[67] = 0.460;
    c.ptr.p_int[67] = 0;
    a.ptr.p_double[68] = 0.520;
    c.ptr.p_int[68] = 0;
    a.ptr.p_double[69] = 0.530;
    c.ptr.p_int[69] = 0;
    a.ptr.p_double[70] = 0.540;
    c.ptr.p_int[70] = 0;
    a.ptr.p_double[71] = 0.560;
    c.ptr.p_int[71] = 0;
    a.ptr.p_double[72] = 0.560;
    c.ptr.p_int[72] = 0;
    a.ptr.p_double[73] = 0.570;
    c.ptr.p_int[73] = 0;
    a.ptr.p_double[74] = 0.600;
    c.ptr.p_int[74] = 0;
    a.ptr.p_double[75] = 0.600;
    c.ptr.p_int[75] = 0;
    a.ptr.p_double[76] = 0.620;
    c.ptr.p_int[76] = 0;
    a.ptr.p_double[77] = 0.650;
    c.ptr.p_int[77] = 0;
    a.ptr.p_double[78] = 0.660;
    c.ptr.p_int[78] = 0;
    a.ptr.p_double[79] = 0.680;
    c.ptr.p_int[79] = 0;
    a.ptr.p_double[80] = 0.700;
    c.ptr.p_int[80] = 0;
    a.ptr.p_double[81] = 0.750;
    c.ptr.p_int[81] = 0;
    a.ptr.p_double[82] = 0.770;
    c.ptr.p_int[82] = 0;
    a.ptr.p_double[83] = 0.770;
    c.ptr.p_int[83] = 0;
    a.ptr.p_double[84] = 0.770;
    c.ptr.p_int[84] = 0;
    a.ptr.p_double[85] = 0.790;
    c.ptr.p_int[85] = 0;
    a.ptr.p_double[86] = 0.810;
    c.ptr.p_int[86] = 0;
    a.ptr.p_double[87] = 0.840;
    c.ptr.p_int[87] = 0;
    a.ptr.p_double[88] = 0.860;
    c.ptr.p_int[88] = 0;
    a.ptr.p_double[89] = 0.870;
    c.ptr.p_int[89] = 0;
    a.ptr.p_double[90] = 0.890;
    c.ptr.p_int[90] = 0;
    a.ptr.p_double[91] = 0.900;
    c.ptr.p_int[91] = 1;
    a.ptr.p_double[92] = 0.900;
    c.ptr.p_int[92] = 0;
    a.ptr.p_double[93] = 0.910;
    c.ptr.p_int[93] = 0;
    a.ptr.p_double[94] = 0.940;
    c.ptr.p_int[94] = 0;
    a.ptr.p_double[95] = 0.950;
    c.ptr.p_int[95] = 0;
    a.ptr.p_double[96] = 0.952;
    c.ptr.p_int[96] = 0;
    a.ptr.p_double[97] = 0.970;
    c.ptr.p_int[97] = 0;
    a.ptr.p_double[98] = 0.970;
    c.ptr.p_int[98] = 0;
    a.ptr.p_double[99] = 0.980;
    c.ptr.p_int[99] = 0;
    a.ptr.p_double[100] = 1.000;
    c.ptr.p_int[100] = 0;
    a.ptr.p_double[101] = 1.000;
    c.ptr.p_int[101] = 0;
    a.ptr.p_double[102] = 1.000;
    c.ptr.p_int[102] = 0;
    a.ptr.p_double[103] = 1.000;
    c.ptr.p_int[103] = 0;
    a.ptr.p_double[104] = 1.000;
    c.ptr.p_int[104] = 0;
    a.ptr.p_double[105] = 1.020;
    c.ptr.p_int[105] = 0;
    a.ptr.p_double[106] = 1.090;
    c.ptr.p_int[106] = 0;
    a.ptr.p_double[107] = 1.130;
    c.ptr.p_int[107] = 0;
    a.ptr.p_double[108] = 1.840;
    c.ptr.p_int[108] = 0;
    a.ptr.p_double[109] = 2.470;
    c.ptr.p_int[109] = 0;
    dsoptimalsplit2(&a, &c, n, &info, &threshold, &pal, &pbl, &par, &pbr, &cve, _state);
    if( info!=1 )
    {
        split2errors = ae_true;
    }
    else
    {
        split2errors = split2errors||ae_fp_greater(ae_fabs(threshold-0.195, _state),100*ae_machineepsilon);
        split2errors = split2errors||ae_fp_greater(ae_fabs(pal-0.80, _state),0.02);
        split2errors = split2errors||ae_fp_greater(ae_fabs(pbl-0.20, _state),0.02);
        split2errors = split2errors||ae_fp_greater(ae_fabs(par-0.97, _state),0.02);
        split2errors = split2errors||ae_fp_greater(ae_fabs(pbr-0.03, _state),0.02);
    }
    
    /*
     * split-2 fast
     */
    
    /*
     * General tests for different N's
     */
    for(n=1; n<=maxn; n++)
    {
        ae_vector_set_length(&a, n-1+1, _state);
        ae_vector_set_length(&c, n-1+1, _state);
        ae_vector_set_length(&tiebuf, n+1, _state);
        ae_vector_set_length(&cntbuf, 3+1, _state);
        
        /*
         * one-tie test
         */
        if( n%2==0 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(n);
                c.ptr.p_int[i] = i%2;
            }
            dsoptimalsplit2fast(&a, &c, &tiebuf, &cntbuf, &sortrbuf, &sortibuf, n, 2, 0.00, &info, &threshold, &rms, &cvrms, _state);
            if( info!=-3 )
            {
                split2errors = ae_true;
                continue;
            }
        }
        
        /*
         * two-tie test
         */
        
        /*
         * test #1
         */
        if( n>1 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(i/((n+1)/2));
                c.ptr.p_int[i] = i/((n+1)/2);
            }
            dsoptimalsplit2fast(&a, &c, &tiebuf, &cntbuf, &sortrbuf, &sortibuf, n, 2, 0.00, &info, &threshold, &rms, &cvrms, _state);
            if( info!=1 )
            {
                split2errors = ae_true;
                continue;
            }
            split2errors = split2errors||ae_fp_greater(ae_fabs(threshold-0.5, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(rms-0, _state),100*ae_machineepsilon);
            if( n==2 )
            {
                split2errors = split2errors||ae_fp_greater(ae_fabs(cvrms-0.5, _state),100*ae_machineepsilon);
            }
            else
            {
                if( n==3 )
                {
                    split2errors = split2errors||ae_fp_greater(ae_fabs(cvrms-ae_sqrt((2*0+2*0+2*0.25)/6, _state), _state),100*ae_machineepsilon);
                }
                else
                {
                    split2errors = split2errors||ae_fp_greater(ae_fabs(cvrms, _state),100*ae_machineepsilon);
                }
            }
        }
    }
    
    /*
     * special tests
     */
    n = 10;
    ae_vector_set_length(&a, n-1+1, _state);
    ae_vector_set_length(&c, n-1+1, _state);
    ae_vector_set_length(&tiebuf, n+1, _state);
    ae_vector_set_length(&cntbuf, 2*3-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        a.ptr.p_double[i] = (double)(i);
        if( i<=n-3 )
        {
            c.ptr.p_int[i] = 0;
        }
        else
        {
            c.ptr.p_int[i] = i-(n-3);
        }
    }
    dsoptimalsplit2fast(&a, &c, &tiebuf, &cntbuf, &sortrbuf, &sortibuf, n, 3, 0.00, &info, &threshold, &rms, &cvrms, _state);
    if( info!=1 )
    {
        split2errors = ae_true;
    }
    else
    {
        split2errors = split2errors||ae_fp_greater(ae_fabs(threshold-(n-2.5), _state),100*ae_machineepsilon);
        split2errors = split2errors||ae_fp_greater(ae_fabs(rms-ae_sqrt((0.25+0.25+0.25+0.25)/(3*n), _state), _state),100*ae_machineepsilon);
        split2errors = split2errors||ae_fp_greater(ae_fabs(cvrms-ae_sqrt((double)(1+1+1+1)/(double)(3*n), _state), _state),100*ae_machineepsilon);
    }
    
    /*
     * Optimal split-K
     */
    
    /*
     * General tests for different N's
     */
    for(n=1; n<=maxnq; n++)
    {
        ae_vector_set_length(&a, n-1+1, _state);
        ae_vector_set_length(&c, n-1+1, _state);
        
        /*
         * one-tie test
         */
        if( n%2==0 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(n);
                c.ptr.p_int[i] = i%2;
            }
            dsoptimalsplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=-3 )
            {
                optimalsplitkerrors = ae_true;
                continue;
            }
        }
        
        /*
         * two-tie test
         */
        
        /*
         * test #1
         */
        if( n>1 )
        {
            c0 = 0;
            c1 = 0;
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(i/((n+1)/2));
                c.ptr.p_int[i] = i/((n+1)/2);
                if( c.ptr.p_int[i]==0 )
                {
                    c0 = c0+1;
                }
                if( c.ptr.p_int[i]==1 )
                {
                    c1 = c1+1;
                }
            }
            dsoptimalsplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                optimalsplitkerrors = ae_true;
                continue;
            }
            optimalsplitkerrors = optimalsplitkerrors||ni!=2;
            optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[0]-0.5, _state),100*ae_machineepsilon);
            optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(cve-(-c0*ae_log((double)c0/(double)(c0+1), _state)-c1*ae_log((double)c1/(double)(c1+1), _state)), _state),100*ae_machineepsilon);
        }
        
        /*
         * test #2
         */
        if( n>2 )
        {
            c0 = 1+ae_randominteger(n-1, _state);
            c1 = n-c0;
            for(i=0; i<=n-1; i++)
            {
                if( i<c0 )
                {
                    a.ptr.p_double[i] = (double)(0);
                    c.ptr.p_int[i] = 0;
                }
                else
                {
                    a.ptr.p_double[i] = (double)(1);
                    c.ptr.p_int[i] = 1;
                }
            }
            dsoptimalsplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                optimalsplitkerrors = ae_true;
                continue;
            }
            optimalsplitkerrors = optimalsplitkerrors||ni!=2;
            optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[0]-0.5, _state),100*ae_machineepsilon);
            optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(cve-(-c0*ae_log((double)c0/(double)(c0+1), _state)-c1*ae_log((double)c1/(double)(c1+1), _state)), _state),100*ae_machineepsilon);
        }
        
        /*
         * multi-tie test
         */
        if( n>=16 )
        {
            
            /*
             * Multi-tie test.
             *
             * First NC-1 ties have C0 entries, remaining NC-th tie
             * have C1 entries.
             */
            nc = ae_round(ae_sqrt((double)(n), _state), _state);
            c0 = n/nc;
            c1 = n-c0*(nc-1);
            for(i=0; i<=nc-2; i++)
            {
                for(j=c0*i; j<=c0*(i+1)-1; j++)
                {
                    a.ptr.p_double[j] = (double)(j);
                    c.ptr.p_int[j] = i;
                }
            }
            for(j=c0*(nc-1); j<=n-1; j++)
            {
                a.ptr.p_double[j] = (double)(j);
                c.ptr.p_int[j] = nc-1;
            }
            dsoptimalsplitk(&a, &c, n, nc, nc+ae_randominteger(nc, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                optimalsplitkerrors = ae_true;
                continue;
            }
            optimalsplitkerrors = optimalsplitkerrors||ni!=nc;
            if( ni==nc )
            {
                for(i=0; i<=nc-2; i++)
                {
                    optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[i]-(c0*(i+1)-1+0.5), _state),100*ae_machineepsilon);
                }
                cvr = -((nc-1)*c0*ae_log((double)c0/(double)(c0+nc-1), _state)+c1*ae_log((double)c1/(double)(c1+nc-1), _state));
                optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(cve-cvr, _state),100*ae_machineepsilon);
            }
        }
    }
    
    /*
     * Non-optimal split-K
     */
    
    /*
     * General tests for different N's
     */
    for(n=1; n<=maxnq; n++)
    {
        ae_vector_set_length(&a, n-1+1, _state);
        ae_vector_set_length(&c, n-1+1, _state);
        
        /*
         * one-tie test
         */
        if( n%2==0 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(99);
                c.ptr.p_int[i] = i%2;
            }
            dssplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=-3 )
            {
                splitkerrors = ae_true;
                continue;
            }
        }
        
        /*
         * two-tie test
         */
        
        /*
         * test #1
         */
        if( n>1 )
        {
            c0 = 0;
            c1 = 0;
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = (double)(i/((n+1)/2));
                c.ptr.p_int[i] = i/((n+1)/2);
                if( c.ptr.p_int[i]==0 )
                {
                    c0 = c0+1;
                }
                if( c.ptr.p_int[i]==1 )
                {
                    c1 = c1+1;
                }
            }
            dssplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                splitkerrors = ae_true;
                continue;
            }
            splitkerrors = splitkerrors||ni!=2;
            if( ni==2 )
            {
                splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[0]-0.5, _state),100*ae_machineepsilon);
                splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(cve-(-c0*ae_log((double)c0/(double)(c0+1), _state)-c1*ae_log((double)c1/(double)(c1+1), _state)), _state),100*ae_machineepsilon);
            }
        }
        
        /*
         * test #2
         */
        if( n>2 )
        {
            c0 = 1+ae_randominteger(n-1, _state);
            c1 = n-c0;
            for(i=0; i<=n-1; i++)
            {
                if( i<c0 )
                {
                    a.ptr.p_double[i] = (double)(0);
                    c.ptr.p_int[i] = 0;
                }
                else
                {
                    a.ptr.p_double[i] = (double)(1);
                    c.ptr.p_int[i] = 1;
                }
            }
            dssplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                splitkerrors = ae_true;
                continue;
            }
            splitkerrors = splitkerrors||ni!=2;
            if( ni==2 )
            {
                splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[0]-0.5, _state),100*ae_machineepsilon);
                splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(cve-(-c0*ae_log((double)c0/(double)(c0+1), _state)-c1*ae_log((double)c1/(double)(c1+1), _state)), _state),100*ae_machineepsilon);
            }
        }
        
        /*
         * multi-tie test
         */
        for(c0=4; c0<=n; c0++)
        {
            if( (n%c0==0&&n/c0<=c0)&&n/c0>1 )
            {
                nc = n/c0;
                for(i=0; i<=nc-1; i++)
                {
                    for(j=c0*i; j<=c0*(i+1)-1; j++)
                    {
                        a.ptr.p_double[j] = (double)(j);
                        c.ptr.p_int[j] = i;
                    }
                }
                dssplitk(&a, &c, n, nc, nc+ae_randominteger(nc, _state), &info, &thresholds, &ni, &cve, _state);
                if( info!=1 )
                {
                    splitkerrors = ae_true;
                    continue;
                }
                splitkerrors = splitkerrors||ni!=nc;
                if( ni==nc )
                {
                    for(i=0; i<=nc-2; i++)
                    {
                        splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[i]-(c0*(i+1)-1+0.5), _state),100*ae_machineepsilon);
                    }
                    cvr = -nc*c0*ae_log((double)c0/(double)(c0+nc-1), _state);
                    splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(cve-cvr, _state),100*ae_machineepsilon);
                }
            }
        }
    }
    
    /*
     * report
     */
    waserrors = ((tieserrors||split2errors)||optimalsplitkerrors)||splitkerrors;
    if( !silent )
    {
        printf("TESTING BASIC DATASET SUBROUTINES\n");
        printf("TIES:                               ");
        if( !tieserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SPLIT-2:                            ");
        if( !split2errors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("OPTIMAL SPLIT-K:                    ");
        if( !optimalsplitkerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SPLIT-K:                            ");
        if( !splitkerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testbdssunit_unset1di(/* Integer */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_int[0] = ae_randominteger(3, _state)-1;
}



static double testmlpbaseunit_vectordiff(/* Real    */ ae_vector* g0,
     /* Real    */ ae_vector* g1,
     ae_int_t n,
     double s,
     ae_state *_state);
static void testmlpbaseunit_createnetwork(multilayerperceptron* network,
     ae_int_t nkind,
     double a1,
     double a2,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_state *_state);
static void testmlpbaseunit_unsetnetwork(multilayerperceptron* network,
     ae_state *_state);
static void testmlpbaseunit_testinformational(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testmlpbaseunit_testprocessing(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testmlpbaseunit_testgradient(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_int_t sizemin,
     ae_int_t sizemax,
     ae_bool* err,
     ae_state *_state);
static void testmlpbaseunit_testhessian(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testmlpbaseunit_testerr(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_int_t sizemin,
     ae_int_t sizemax,
     ae_bool* err,
     ae_state *_state);
static void testmlpbaseunit_spectests(ae_bool* inferrors,
     ae_bool* procerrors,
     ae_bool* graderrors,
     ae_bool* hesserrors,
     ae_bool* errerrors,
     ae_state *_state);
static ae_bool testmlpbaseunit_testmlpgbsubset(ae_state *_state);





ae_bool testmlpbase(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_int_t maxhid;
    ae_int_t sizemin;
    ae_int_t sizemax;
    ae_int_t nf;
    ae_int_t nl;
    ae_int_t nhid1;
    ae_int_t nhid2;
    ae_int_t nkind;
    multilayerperceptron network;
    multilayerperceptron network2;
    ae_matrix xy;
    ae_matrix valxy;
    ae_bool inferrors;
    ae_bool procerrors;
    ae_bool graderrors;
    ae_bool hesserrors;
    ae_bool errerrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&network, 0, sizeof(network));
    memset(&network2, 0, sizeof(network2));
    memset(&xy, 0, sizeof(xy));
    memset(&valxy, 0, sizeof(valxy));
    _multilayerperceptron_init(&network, _state, ae_true);
    _multilayerperceptron_init(&network2, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&valxy, 0, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    inferrors = ae_false;
    procerrors = ae_false;
    graderrors = ae_false;
    hesserrors = ae_false;
    errerrors = ae_false;
    passcount = 5;
    maxn = 3;
    maxhid = 3;
    
    /*
     * Special tests
     */
    testmlpbaseunit_spectests(&inferrors, &procerrors, &graderrors, &hesserrors, &errerrors, _state);
    
    /*
     * General multilayer network tests.
     * These tests are performed with small dataset, whose size is in [0,10].
     * We test correctness of functions on small sets, but do not test code
     * which splits large dataset into smaller chunks.
     */
    sizemin = 0;
    sizemax = 10;
    for(nf=1; nf<=maxn; nf++)
    {
        for(nl=1; nl<=maxn; nl++)
        {
            for(nhid1=0; nhid1<=maxhid; nhid1++)
            {
                for(nhid2=0; nhid2<=maxhid; nhid2++)
                {
                    for(nkind=0; nkind<=3; nkind++)
                    {
                        
                        /*
                         *  Skip meaningless parameters combinations
                         */
                        if( nkind==1&&nl<2 )
                        {
                            continue;
                        }
                        if( nhid1==0&&nhid2!=0 )
                        {
                            continue;
                        }
                        
                        /*
                         * Tests
                         */
                        testmlpbaseunit_testinformational(nkind, nf, nhid1, nhid2, nl, passcount, &inferrors, _state);
                        testmlpbaseunit_testprocessing(nkind, nf, nhid1, nhid2, nl, passcount, &procerrors, _state);
                        testmlpbaseunit_testgradient(nkind, nf, nhid1, nhid2, nl, passcount, sizemin, sizemax, &graderrors, _state);
                        testmlpbaseunit_testhessian(nkind, nf, nhid1, nhid2, nl, passcount, &hesserrors, _state);
                        testmlpbaseunit_testerr(nkind, nf, nhid1, nhid2, nl, passcount, sizemin, sizemax, &errerrors, _state);
                    }
                }
            }
        }
    }
    
    /*
     * Special tests on large datasets: test ability to correctly split
     * work into smaller chunks.
     */
    nf = 2;
    nhid1 = 20;
    nhid2 = 20;
    nl = 2;
    sizemin = 50000;
    sizemax = 50000;
    testmlpbaseunit_testerr(0, nf, nhid1, nhid2, nl, 1, sizemin, sizemax, &errerrors, _state);
    testmlpbaseunit_testgradient(0, nf, nhid1, nhid2, nl, 1, sizemin, sizemax, &graderrors, _state);
    
    /*
     * Test for MLPGradBatch____Subset()
     */
    graderrors = graderrors||testmlpbaseunit_testmlpgbsubset(_state);
    
    /*
     * Final report
     */
    waserrors = (((inferrors||procerrors)||graderrors)||hesserrors)||errerrors;
    if( !silent )
    {
        printf("MLP TEST\n");
        printf("INFORMATIONAL FUNCTIONS:                 ");
        if( !inferrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("BASIC PROCESSING:                        ");
        if( !procerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("GRADIENT CALCULATION:                    ");
        if( !graderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("HESSIAN CALCULATION:                     ");
        if( !hesserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("ERROR FUNCTIONS:                         ");
        if( !errerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function compares vectors G0 and G1 and returns
    ||G0-G1||/max(||G0||,||G1||,S)

For zero G0, G1 and S (all three quantities are zero) it returns zero.
*************************************************************************/
static double testmlpbaseunit_vectordiff(/* Real    */ ae_vector* g0,
     /* Real    */ ae_vector* g1,
     ae_int_t n,
     double s,
     ae_state *_state)
{
    ae_int_t i;
    double norm0;
    double norm1;
    double diff;
    double result;


    norm0 = (double)(0);
    norm1 = (double)(0);
    diff = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        norm0 = norm0+ae_sqr(g0->ptr.p_double[i], _state);
        norm1 = norm1+ae_sqr(g1->ptr.p_double[i], _state);
        diff = diff+ae_sqr(g0->ptr.p_double[i]-g1->ptr.p_double[i], _state);
    }
    norm0 = ae_sqrt(norm0, _state);
    norm1 = ae_sqrt(norm1, _state);
    diff = ae_sqrt(diff, _state);
    if( (ae_fp_neq(norm0,(double)(0))||ae_fp_neq(norm1,(double)(0)))||ae_fp_neq(s,(double)(0)) )
    {
        diff = diff/ae_maxreal(ae_maxreal(norm0, norm1, _state), s, _state);
    }
    else
    {
        diff = (double)(0);
    }
    result = diff;
    return result;
}


/*************************************************************************
Network creation

This function creates network with desired structure.

Network is created using one of the three methods:
a) straightforward creation using MLPCreate???()
b) MLPCreate???() for proxy object, which is copied with PassThroughSerializer()
c) MLPCreate???() for proxy object, which is copied with MLPCopy()

One of these methods is chosen at random.
*************************************************************************/
static void testmlpbaseunit_createnetwork(multilayerperceptron* network,
     ae_int_t nkind,
     double a1,
     double a2,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t mkind;
    multilayerperceptron tmp;

    ae_frame_make(_state, &_frame_block);
    memset(&tmp, 0, sizeof(tmp));
    _multilayerperceptron_init(&tmp, _state, ae_true);

    ae_assert(((nin>0&&nhid1>=0)&&nhid2>=0)&&nout>0, "CreateNetwork error", _state);
    ae_assert(nhid1!=0||nhid2==0, "CreateNetwork error", _state);
    ae_assert(nkind!=1||nout>=2, "CreateNetwork error", _state);
    mkind = ae_randominteger(3, _state);
    if( nhid1==0 )
    {
        
        /*
         * No hidden layers
         */
        if( nkind==0 )
        {
            if( mkind==0 )
            {
                mlpcreate0(nin, nout, network, _state);
            }
            if( mkind==1 )
            {
                mlpcreate0(nin, nout, &tmp, _state);
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    mlpalloc(&_local_serializer, &tmp, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                    ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpserialize(&_local_serializer, &tmp, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpunserialize(&_local_serializer, network, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
            }
            if( mkind==2 )
            {
                mlpcreate0(nin, nout, &tmp, _state);
                mlpcopy(&tmp, network, _state);
            }
        }
        else
        {
            if( nkind==1 )
            {
                if( mkind==0 )
                {
                    mlpcreatec0(nin, nout, network, _state);
                }
                if( mkind==1 )
                {
                    mlpcreatec0(nin, nout, &tmp, _state);
                    {
                        /*
                         * This code passes data structure through serializers
                         * (serializes it to string and loads back)
                         */
                        ae_serializer _local_serializer;
                        ae_int_t _local_ssize;
                        ae_frame _local_frame_block;
                        ae_dyn_block _local_dynamic_block;
                        
                        ae_frame_make(_state, &_local_frame_block);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_alloc_start(&_local_serializer);
                        mlpalloc(&_local_serializer, &tmp, _state);
                        _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                        memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                        ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                        ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpserialize(&_local_serializer, &tmp, _state);
                        ae_serializer_stop(&_local_serializer, _state);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpunserialize(&_local_serializer, network, _state);
                        ae_serializer_stop(&_local_serializer, _state);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_frame_leave(_state);
                    }
                }
                if( mkind==2 )
                {
                    mlpcreatec0(nin, nout, &tmp, _state);
                    mlpcopy(&tmp, network, _state);
                }
            }
            else
            {
                if( nkind==2 )
                {
                    if( mkind==0 )
                    {
                        mlpcreateb0(nin, nout, a1, a2, network, _state);
                    }
                    if( mkind==1 )
                    {
                        mlpcreateb0(nin, nout, a1, a2, &tmp, _state);
                        {
                            /*
                             * This code passes data structure through serializers
                             * (serializes it to string and loads back)
                             */
                            ae_serializer _local_serializer;
                            ae_int_t _local_ssize;
                            ae_frame _local_frame_block;
                            ae_dyn_block _local_dynamic_block;
                            
                            ae_frame_make(_state, &_local_frame_block);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_alloc_start(&_local_serializer);
                            mlpalloc(&_local_serializer, &tmp, _state);
                            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                            memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                            ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpserialize(&_local_serializer, &tmp, _state);
                            ae_serializer_stop(&_local_serializer, _state);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpunserialize(&_local_serializer, network, _state);
                            ae_serializer_stop(&_local_serializer, _state);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_frame_leave(_state);
                        }
                    }
                    if( mkind==2 )
                    {
                        mlpcreateb0(nin, nout, a1, a2, &tmp, _state);
                        mlpcopy(&tmp, network, _state);
                    }
                }
                else
                {
                    if( nkind==3 )
                    {
                        if( mkind==0 )
                        {
                            mlpcreater0(nin, nout, a1, a2, network, _state);
                        }
                        if( mkind==1 )
                        {
                            mlpcreater0(nin, nout, a1, a2, &tmp, _state);
                            {
                                /*
                                 * This code passes data structure through serializers
                                 * (serializes it to string and loads back)
                                 */
                                ae_serializer _local_serializer;
                                ae_int_t _local_ssize;
                                ae_frame _local_frame_block;
                                ae_dyn_block _local_dynamic_block;
                                
                                ae_frame_make(_state, &_local_frame_block);
                                
                                ae_serializer_init(&_local_serializer);
                                ae_serializer_alloc_start(&_local_serializer);
                                mlpalloc(&_local_serializer, &tmp, _state);
                                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                                memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                                ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                                mlpserialize(&_local_serializer, &tmp, _state);
                                ae_serializer_stop(&_local_serializer, _state);
                                ae_serializer_clear(&_local_serializer);
                                
                                ae_serializer_init(&_local_serializer);
                                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                                mlpunserialize(&_local_serializer, network, _state);
                                ae_serializer_stop(&_local_serializer, _state);
                                ae_serializer_clear(&_local_serializer);
                                
                                ae_frame_leave(_state);
                            }
                        }
                        if( mkind==2 )
                        {
                            mlpcreater0(nin, nout, a1, a2, &tmp, _state);
                            mlpcopy(&tmp, network, _state);
                        }
                    }
                }
            }
        }
        mlprandomizefull(network, _state);
        ae_frame_leave(_state);
        return;
    }
    if( nhid2==0 )
    {
        
        /*
         * One hidden layer
         */
        if( nkind==0 )
        {
            if( mkind==0 )
            {
                mlpcreate1(nin, nhid1, nout, network, _state);
            }
            if( mkind==1 )
            {
                mlpcreate1(nin, nhid1, nout, &tmp, _state);
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    mlpalloc(&_local_serializer, &tmp, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                    ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpserialize(&_local_serializer, &tmp, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpunserialize(&_local_serializer, network, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
            }
            if( mkind==2 )
            {
                mlpcreate1(nin, nhid1, nout, &tmp, _state);
                mlpcopy(&tmp, network, _state);
            }
        }
        else
        {
            if( nkind==1 )
            {
                if( mkind==0 )
                {
                    mlpcreatec1(nin, nhid1, nout, network, _state);
                }
                if( mkind==1 )
                {
                    mlpcreatec1(nin, nhid1, nout, &tmp, _state);
                    {
                        /*
                         * This code passes data structure through serializers
                         * (serializes it to string and loads back)
                         */
                        ae_serializer _local_serializer;
                        ae_int_t _local_ssize;
                        ae_frame _local_frame_block;
                        ae_dyn_block _local_dynamic_block;
                        
                        ae_frame_make(_state, &_local_frame_block);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_alloc_start(&_local_serializer);
                        mlpalloc(&_local_serializer, &tmp, _state);
                        _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                        memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                        ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                        ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpserialize(&_local_serializer, &tmp, _state);
                        ae_serializer_stop(&_local_serializer, _state);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpunserialize(&_local_serializer, network, _state);
                        ae_serializer_stop(&_local_serializer, _state);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_frame_leave(_state);
                    }
                }
                if( mkind==2 )
                {
                    mlpcreatec1(nin, nhid1, nout, &tmp, _state);
                    mlpcopy(&tmp, network, _state);
                }
            }
            else
            {
                if( nkind==2 )
                {
                    if( mkind==0 )
                    {
                        mlpcreateb1(nin, nhid1, nout, a1, a2, network, _state);
                    }
                    if( mkind==1 )
                    {
                        mlpcreateb1(nin, nhid1, nout, a1, a2, &tmp, _state);
                        {
                            /*
                             * This code passes data structure through serializers
                             * (serializes it to string and loads back)
                             */
                            ae_serializer _local_serializer;
                            ae_int_t _local_ssize;
                            ae_frame _local_frame_block;
                            ae_dyn_block _local_dynamic_block;
                            
                            ae_frame_make(_state, &_local_frame_block);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_alloc_start(&_local_serializer);
                            mlpalloc(&_local_serializer, &tmp, _state);
                            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                            memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                            ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpserialize(&_local_serializer, &tmp, _state);
                            ae_serializer_stop(&_local_serializer, _state);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpunserialize(&_local_serializer, network, _state);
                            ae_serializer_stop(&_local_serializer, _state);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_frame_leave(_state);
                        }
                    }
                    if( mkind==2 )
                    {
                        mlpcreateb1(nin, nhid1, nout, a1, a2, &tmp, _state);
                        mlpcopy(&tmp, network, _state);
                    }
                }
                else
                {
                    if( nkind==3 )
                    {
                        if( mkind==0 )
                        {
                            mlpcreater1(nin, nhid1, nout, a1, a2, network, _state);
                        }
                        if( mkind==1 )
                        {
                            mlpcreater1(nin, nhid1, nout, a1, a2, &tmp, _state);
                            {
                                /*
                                 * This code passes data structure through serializers
                                 * (serializes it to string and loads back)
                                 */
                                ae_serializer _local_serializer;
                                ae_int_t _local_ssize;
                                ae_frame _local_frame_block;
                                ae_dyn_block _local_dynamic_block;
                                
                                ae_frame_make(_state, &_local_frame_block);
                                
                                ae_serializer_init(&_local_serializer);
                                ae_serializer_alloc_start(&_local_serializer);
                                mlpalloc(&_local_serializer, &tmp, _state);
                                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                                memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                                ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                                mlpserialize(&_local_serializer, &tmp, _state);
                                ae_serializer_stop(&_local_serializer, _state);
                                ae_serializer_clear(&_local_serializer);
                                
                                ae_serializer_init(&_local_serializer);
                                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                                mlpunserialize(&_local_serializer, network, _state);
                                ae_serializer_stop(&_local_serializer, _state);
                                ae_serializer_clear(&_local_serializer);
                                
                                ae_frame_leave(_state);
                            }
                        }
                        if( mkind==2 )
                        {
                            mlpcreater1(nin, nhid1, nout, a1, a2, &tmp, _state);
                            mlpcopy(&tmp, network, _state);
                        }
                    }
                }
            }
        }
        mlprandomizefull(network, _state);
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Two hidden layers
     */
    if( nkind==0 )
    {
        if( mkind==0 )
        {
            mlpcreate2(nin, nhid1, nhid2, nout, network, _state);
        }
        if( mkind==1 )
        {
            mlpcreate2(nin, nhid1, nhid2, nout, &tmp, _state);
            {
                /*
                 * This code passes data structure through serializers
                 * (serializes it to string and loads back)
                 */
                ae_serializer _local_serializer;
                ae_int_t _local_ssize;
                ae_frame _local_frame_block;
                ae_dyn_block _local_dynamic_block;
                
                ae_frame_make(_state, &_local_frame_block);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_alloc_start(&_local_serializer);
                mlpalloc(&_local_serializer, &tmp, _state);
                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                mlpserialize(&_local_serializer, &tmp, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                mlpunserialize(&_local_serializer, network, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_frame_leave(_state);
            }
        }
        if( mkind==2 )
        {
            mlpcreate2(nin, nhid1, nhid2, nout, &tmp, _state);
            mlpcopy(&tmp, network, _state);
        }
    }
    else
    {
        if( nkind==1 )
        {
            if( mkind==0 )
            {
                mlpcreatec2(nin, nhid1, nhid2, nout, network, _state);
            }
            if( mkind==1 )
            {
                mlpcreatec2(nin, nhid1, nhid2, nout, &tmp, _state);
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    mlpalloc(&_local_serializer, &tmp, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                    ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpserialize(&_local_serializer, &tmp, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpunserialize(&_local_serializer, network, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
            }
            if( mkind==2 )
            {
                mlpcreatec2(nin, nhid1, nhid2, nout, &tmp, _state);
                mlpcopy(&tmp, network, _state);
            }
        }
        else
        {
            if( nkind==2 )
            {
                if( mkind==0 )
                {
                    mlpcreateb2(nin, nhid1, nhid2, nout, a1, a2, network, _state);
                }
                if( mkind==1 )
                {
                    mlpcreateb2(nin, nhid1, nhid2, nout, a1, a2, &tmp, _state);
                    {
                        /*
                         * This code passes data structure through serializers
                         * (serializes it to string and loads back)
                         */
                        ae_serializer _local_serializer;
                        ae_int_t _local_ssize;
                        ae_frame _local_frame_block;
                        ae_dyn_block _local_dynamic_block;
                        
                        ae_frame_make(_state, &_local_frame_block);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_alloc_start(&_local_serializer);
                        mlpalloc(&_local_serializer, &tmp, _state);
                        _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                        memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                        ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                        ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpserialize(&_local_serializer, &tmp, _state);
                        ae_serializer_stop(&_local_serializer, _state);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpunserialize(&_local_serializer, network, _state);
                        ae_serializer_stop(&_local_serializer, _state);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_frame_leave(_state);
                    }
                }
                if( mkind==2 )
                {
                    mlpcreateb2(nin, nhid1, nhid2, nout, a1, a2, &tmp, _state);
                    mlpcopy(&tmp, network, _state);
                }
            }
            else
            {
                if( nkind==3 )
                {
                    if( mkind==0 )
                    {
                        mlpcreater2(nin, nhid1, nhid2, nout, a1, a2, network, _state);
                    }
                    if( mkind==1 )
                    {
                        mlpcreater2(nin, nhid1, nhid2, nout, a1, a2, &tmp, _state);
                        {
                            /*
                             * This code passes data structure through serializers
                             * (serializes it to string and loads back)
                             */
                            ae_serializer _local_serializer;
                            ae_int_t _local_ssize;
                            ae_frame _local_frame_block;
                            ae_dyn_block _local_dynamic_block;
                            
                            ae_frame_make(_state, &_local_frame_block);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_alloc_start(&_local_serializer);
                            mlpalloc(&_local_serializer, &tmp, _state);
                            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                            memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                            ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpserialize(&_local_serializer, &tmp, _state);
                            ae_serializer_stop(&_local_serializer, _state);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpunserialize(&_local_serializer, network, _state);
                            ae_serializer_stop(&_local_serializer, _state);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_frame_leave(_state);
                        }
                    }
                    if( mkind==2 )
                    {
                        mlpcreater2(nin, nhid1, nhid2, nout, a1, a2, &tmp, _state);
                        mlpcopy(&tmp, network, _state);
                    }
                }
            }
        }
    }
    mlprandomizefull(network, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Unsets network (initialize it to smallest network possible
*************************************************************************/
static void testmlpbaseunit_unsetnetwork(multilayerperceptron* network,
     ae_state *_state)
{


    mlpcreate0(1, 1, network, _state);
}


/*************************************************************************
Informational functions test
*************************************************************************/
static void testmlpbaseunit_testinformational(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron network;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double threshold;
    ae_int_t nlayers;
    ae_int_t nmax;
    ae_matrix neurons;
    ae_vector x;
    ae_vector y;
    double mean;
    double sigma;
    ae_int_t fkind;
    double c;
    double f;
    double df;
    double d2f;
    double s;

    ae_frame_make(_state, &_frame_block);
    memset(&network, 0, sizeof(network));
    memset(&neurons, 0, sizeof(neurons));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    _multilayerperceptron_init(&network, _state, ae_true);
    ae_matrix_init(&neurons, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);

    threshold = 100000*ae_machineepsilon;
    testmlpbaseunit_createnetwork(&network, nkind, 0.0, 0.0, nin, nhid1, nhid2, nout, _state);
    
    /*
     * test MLPProperties()
     */
    mlpproperties(&network, &n1, &n2, &wcount, _state);
    *err = ((*err||n1!=nin)||n2!=nout)||wcount<=0;
    *err = ((*err||mlpgetinputscount(&network, _state)!=nin)||mlpgetoutputscount(&network, _state)!=nout)||mlpgetweightscount(&network, _state)!=wcount;
    
    /*
     * Test network geometry functions
     *
     * In order to do this we calculate neural network output using
     * informational functions only, and compare results with ones
     * obtained with MLPProcess():
     * 1. we allocate 2-dimensional array of neurons and fill it by zeros
     * 2. we full first layer of neurons by input values
     * 3. we move through array, calculating values of subsequent layers
     * 4. if we have classification network, we SOFTMAX-normalize output layer
     * 5. we apply scaling to the outputs
     * 6. we compare results with ones obtained by MLPProcess()
     *
     * NOTE: it is important to do (4) before (5), because on SOFTMAX network
     *       MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order
     *       to test it implicitly, we apply it to the classifier results
     *       (already normalized). If one of the coefficients deviates from
     *       expected values, we will get error during (6).
     */
    nlayers = 2;
    nmax = ae_maxint(nin, nout, _state);
    if( nhid1!=0 )
    {
        nlayers = 3;
        nmax = ae_maxint(nmax, nhid1, _state);
    }
    if( nhid2!=0 )
    {
        nlayers = 4;
        nmax = ae_maxint(nmax, nhid2, _state);
    }
    ae_matrix_set_length(&neurons, nlayers, nmax, _state);
    for(i=0; i<=nlayers-1; i++)
    {
        for(j=0; j<=nmax-1; j++)
        {
            neurons.ptr.pp_double[i][j] = (double)(0);
        }
    }
    ae_vector_set_length(&x, nin, _state);
    for(i=0; i<=nin-1; i++)
    {
        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
    }
    ae_vector_set_length(&y, nout, _state);
    for(i=0; i<=nout-1; i++)
    {
        y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
    }
    for(j=0; j<=nin-1; j++)
    {
        mlpgetinputscaling(&network, j, &mean, &sigma, _state);
        neurons.ptr.pp_double[0][j] = (x.ptr.p_double[j]-mean)/sigma;
    }
    for(i=1; i<=nlayers-1; i++)
    {
        for(j=0; j<=mlpgetlayersize(&network, i, _state)-1; j++)
        {
            for(k=0; k<=mlpgetlayersize(&network, i-1, _state)-1; k++)
            {
                neurons.ptr.pp_double[i][j] = neurons.ptr.pp_double[i][j]+mlpgetweight(&network, i-1, k, i, j, _state)*neurons.ptr.pp_double[i-1][k];
            }
            mlpgetneuroninfo(&network, i, j, &fkind, &c, _state);
            mlpactivationfunction(neurons.ptr.pp_double[i][j]-c, fkind, &f, &df, &d2f, _state);
            neurons.ptr.pp_double[i][j] = f;
        }
    }
    if( nkind==1 )
    {
        s = (double)(0);
        for(j=0; j<=nout-1; j++)
        {
            s = s+ae_exp(neurons.ptr.pp_double[nlayers-1][j], _state);
        }
        for(j=0; j<=nout-1; j++)
        {
            neurons.ptr.pp_double[nlayers-1][j] = ae_exp(neurons.ptr.pp_double[nlayers-1][j], _state)/s;
        }
    }
    for(j=0; j<=nout-1; j++)
    {
        mlpgetoutputscaling(&network, j, &mean, &sigma, _state);
        neurons.ptr.pp_double[nlayers-1][j] = neurons.ptr.pp_double[nlayers-1][j]*sigma+mean;
    }
    mlpprocess(&network, &x, &y, _state);
    for(j=0; j<=nout-1; j++)
    {
        *err = *err||ae_fp_greater(ae_fabs(neurons.ptr.pp_double[nlayers-1][j]-y.ptr.p_double[j], _state),threshold);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Processing functions test
*************************************************************************/
static void testmlpbaseunit_testprocessing(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron network;
    multilayerperceptron network2;
    sparsematrix sparsexy;
    ae_matrix densexy;
    ae_int_t npoints;
    ae_int_t subnp;
    ae_bool iscls;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    ae_bool zeronet;
    double a1;
    double a2;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_bool allsame;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    ae_vector p0;
    ae_vector p1;
    ae_int_t pcount;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&network, 0, sizeof(network));
    memset(&network2, 0, sizeof(network2));
    memset(&sparsexy, 0, sizeof(sparsexy));
    memset(&densexy, 0, sizeof(densexy));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&y1, 0, sizeof(y1));
    memset(&y2, 0, sizeof(y2));
    memset(&p0, 0, sizeof(p0));
    memset(&p1, 0, sizeof(p1));
    _multilayerperceptron_init(&network, _state, ae_true);
    _multilayerperceptron_init(&network2, _state, ae_true);
    _sparsematrix_init(&sparsexy, _state, ae_true);
    ae_matrix_init(&densexy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p1, 0, DT_REAL, _state, ae_true);

    ae_assert(passcount>=2, "PassCount<2!", _state);
    
    /*
     * Prepare network
     */
    a1 = (double)(0);
    a2 = (double)(0);
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    testmlpbaseunit_createnetwork(&network, nkind, a1, a2, nin, nhid1, nhid2, nout, _state);
    mlpproperties(&network, &n1, &n2, &wcount, _state);
    iscls = mlpissoftmax(&network, _state);
    
    /*
     * Initialize arrays
     */
    ae_vector_set_length(&x1, nin, _state);
    ae_vector_set_length(&x2, nin, _state);
    ae_vector_set_length(&y1, nout, _state);
    ae_vector_set_length(&y2, nout, _state);
    
    /*
     * Initialize sets
     */
    npoints = ae_randominteger(11, _state)+10;
    if( iscls )
    {
        ae_matrix_set_length(&densexy, npoints, nin+1, _state);
        sparsecreate(npoints, nin+1, npoints, &sparsexy, _state);
    }
    else
    {
        ae_matrix_set_length(&densexy, npoints, nin+nout, _state);
        sparsecreate(npoints, nin+nout, npoints, &sparsexy, _state);
    }
    sparseconverttocrs(&sparsexy, _state);
    
    /*
     * Main cycle
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Last run is made on zero network
         */
        mlprandomizefull(&network, _state);
        zeronet = ae_false;
        if( pass==passcount )
        {
            ae_v_muld(&network.weights.ptr.p_double[0], 1, ae_v_len(0,wcount-1), 0);
            zeronet = ae_true;
        }
        
        /*
         * Same inputs leads to same outputs
         */
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        mlpprocess(&network, &x1, &y1, _state);
        mlpprocess(&network, &x2, &y2, _state);
        ae_set_error_flag(err, ae_fp_neq(testmlpbaseunit_vectordiff(&y1, &y2, nout, 1.0, _state),0.0), __FILE__, __LINE__, "testmlpbaseunit.ap:513");
        
        /*
         * Same inputs on original network leads to same outputs
         * on copy created:
         * * using MLPCopy
         * * using MLPCopyShared
         */
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=nout-1; i++)
        {
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        testmlpbaseunit_unsetnetwork(&network2, _state);
        mlpcopy(&network, &network2, _state);
        mlpprocess(&network, &x1, &y1, _state);
        mlpprocess(&network2, &x2, &y2, _state);
        ae_set_error_flag(err, ae_fp_neq(testmlpbaseunit_vectordiff(&y1, &y2, nout, 1.0, _state),0.0), __FILE__, __LINE__, "testmlpbaseunit.ap:534");
        for(i=0; i<=nout-1; i++)
        {
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        testmlpbaseunit_unsetnetwork(&network2, _state);
        mlpcopyshared(&network, &network2, _state);
        mlpprocess(&network, &x1, &y1, _state);
        mlpprocess(&network2, &x2, &y2, _state);
        ae_set_error_flag(err, ae_fp_neq(testmlpbaseunit_vectordiff(&y1, &y2, nout, 1.0, _state),0.0), __FILE__, __LINE__, "testmlpbaseunit.ap:541");
        
        /*
         * Additionally we tests functions for copying of tunable
         * parameters by:
         * * copying network using MLPCopy
         * * randomizing tunable parameters with MLPRandomizeFull()
         * * copying tunable parameters with:
         *   a) MLPCopyTunableParameters
         *   b) combination of MLPExportTunableParameters and
         *      MLPImportTunableParameters - we export parameters
         *      to P1, copy PCount elements to P2, then test import.
         */
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=nout-1; i++)
        {
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        testmlpbaseunit_unsetnetwork(&network2, _state);
        mlpcopy(&network, &network2, _state);
        mlprandomizefull(&network2, _state);
        mlpcopytunableparameters(&network, &network2, _state);
        mlpprocess(&network, &x1, &y1, _state);
        mlpprocess(&network2, &x2, &y2, _state);
        ae_set_error_flag(err, ae_fp_neq(testmlpbaseunit_vectordiff(&y1, &y2, nout, 1.0, _state),0.0), __FILE__, __LINE__, "testmlpbaseunit.ap:571");
        for(i=0; i<=nout-1; i++)
        {
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        testmlpbaseunit_unsetnetwork(&network2, _state);
        mlpcopy(&network, &network2, _state);
        mlprandomizefull(&network2, _state);
        mlpexporttunableparameters(&network, &p0, &pcount, _state);
        ae_vector_set_length(&p1, pcount, _state);
        for(i=0; i<=pcount-1; i++)
        {
            p1.ptr.p_double[i] = p0.ptr.p_double[i];
        }
        mlpimporttunableparameters(&network2, &p1, _state);
        mlpprocess(&network, &x1, &y1, _state);
        mlpprocess(&network2, &x2, &y2, _state);
        ae_set_error_flag(err, ae_fp_neq(testmlpbaseunit_vectordiff(&y1, &y2, nout, 1.0, _state),0.0), __FILE__, __LINE__, "testmlpbaseunit.ap:585");
        
        /*
         * Same inputs on original network leads to same outputs
         * on copy created using MLPSerialize
         */
        testmlpbaseunit_unsetnetwork(&network2, _state);
        {
            /*
             * This code passes data structure through serializers
             * (serializes it to string and loads back)
             */
            ae_serializer _local_serializer;
            ae_int_t _local_ssize;
            ae_frame _local_frame_block;
            ae_dyn_block _local_dynamic_block;
            
            ae_frame_make(_state, &_local_frame_block);
            
            ae_serializer_init(&_local_serializer);
            ae_serializer_alloc_start(&_local_serializer);
            mlpalloc(&_local_serializer, &network, _state);
            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
            memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
            ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
            mlpserialize(&_local_serializer, &network, _state);
            ae_serializer_stop(&_local_serializer, _state);
            ae_serializer_clear(&_local_serializer);
            
            ae_serializer_init(&_local_serializer);
            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
            mlpunserialize(&_local_serializer, &network2, _state);
            ae_serializer_stop(&_local_serializer, _state);
            ae_serializer_clear(&_local_serializer);
            
            ae_frame_leave(_state);
        }
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        mlpprocess(&network, &x1, &y1, _state);
        mlpprocess(&network2, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nout-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Different inputs leads to different outputs (non-zero network)
         */
        if( !zeronet )
        {
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = y1.ptr.p_double[i];
            }
            mlpprocess(&network, &x1, &y1, _state);
            mlpprocess(&network, &x2, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||allsame;
        }
        
        /*
         * Randomization changes outputs (when inputs are unchanged, non-zero network)
         */
        if( !zeronet )
        {
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = y1.ptr.p_double[i];
            }
            mlpcopy(&network, &network2, _state);
            mlprandomize(&network2, _state);
            mlpprocess(&network, &x1, &y1, _state);
            mlpprocess(&network2, &x1, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||allsame;
        }
        
        /*
         * Full randomization changes outputs (when inputs are unchanged, non-zero network)
         */
        if( !zeronet )
        {
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = y1.ptr.p_double[i];
            }
            mlpcopy(&network, &network2, _state);
            mlprandomizefull(&network2, _state);
            mlpprocess(&network, &x1, &y1, _state);
            mlpprocess(&network2, &x1, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||allsame;
        }
        
        /*
         * Normalization properties
         */
        if( nkind==1 )
        {
            
            /*
             * Classifier network outputs are normalized
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpprocess(&network, &x1, &y1, _state);
            v = (double)(0);
            for(i=0; i<=nout-1; i++)
            {
                v = v+y1.ptr.p_double[i];
                *err = *err||ae_fp_less(y1.ptr.p_double[i],(double)(0));
            }
            *err = *err||ae_fp_greater(ae_fabs(v-1, _state),1000*ae_machineepsilon);
        }
        if( nkind==2 )
        {
            
            /*
             * B-type network outputs are bounded from above/below
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpprocess(&network, &x1, &y1, _state);
            for(i=0; i<=nout-1; i++)
            {
                if( ae_fp_greater_eq(a2,(double)(0)) )
                {
                    *err = *err||ae_fp_less(y1.ptr.p_double[i],a1);
                }
                else
                {
                    *err = *err||ae_fp_greater(y1.ptr.p_double[i],a1);
                }
            }
        }
        if( nkind==3 )
        {
            
            /*
             * R-type network outputs are within [A1,A2] (or [A2,A1])
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpprocess(&network, &x1, &y1, _state);
            for(i=0; i<=nout-1; i++)
            {
                *err = (*err||ae_fp_less(y1.ptr.p_double[i],ae_minreal(a1, a2, _state)))||ae_fp_greater(y1.ptr.p_double[i],ae_maxreal(a1, a2, _state));
            }
        }
        
        /*
         * Comperison MLPInitPreprocessor results with
         * MLPInitPreprocessorSparse results
         */
        sparseconverttohash(&sparsexy, _state);
        if( iscls )
        {
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=nin-1; j++)
                {
                    densexy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    sparseset(&sparsexy, i, j, densexy.ptr.pp_double[i][j], _state);
                }
                densexy.ptr.pp_double[i][nin] = (double)(ae_randominteger(nout, _state));
                sparseset(&sparsexy, i, j, densexy.ptr.pp_double[i][nin], _state);
            }
        }
        else
        {
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=nin+nout-1; j++)
                {
                    densexy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    sparseset(&sparsexy, i, j, densexy.ptr.pp_double[i][j], _state);
                }
            }
        }
        sparseconverttocrs(&sparsexy, _state);
        mlpcopy(&network, &network2, _state);
        mlpinitpreprocessor(&network, &densexy, npoints, _state);
        mlpinitpreprocessorsparse(&network2, &sparsexy, npoints, _state);
        subnp = ae_randominteger(npoints, _state);
        for(i=0; i<=subnp-1; i++)
        {
            for(j=0; j<=nin-1; j++)
            {
                x1.ptr.p_double[j] = 2*ae_randomreal(_state)-1;
            }
            mlpprocess(&network, &x1, &y1, _state);
            mlpprocess(&network2, &x1, &y2, _state);
            for(j=0; j<=nout-1; j++)
            {
                *err = *err||ae_fp_greater(ae_fabs(y1.ptr.p_double[j]-y2.ptr.p_double[j], _state),1.0E-6);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Gradient functions test
*************************************************************************/
static void testmlpbaseunit_testgradient(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_int_t sizemin,
     ae_int_t sizemax,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron network;
    sparsematrix sparsexy;
    sparsematrix sparsexy2;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    double h;
    double etol;
    double escale;
    double gscale;
    double nonstricttolerance;
    double a1;
    double a2;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t ssize;
    ae_int_t subsetsize;
    ae_int_t rowsize;
    ae_matrix xy;
    ae_matrix xy2;
    ae_vector grad1;
    ae_vector grad2;
    ae_vector gradsp;
    ae_vector x;
    ae_vector y;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    ae_vector idx;
    double v;
    double e;
    double e1;
    double e2;
    double esp;
    double v1;
    double v2;
    double v3;
    double v4;
    double wprev;
    double referencee;
    ae_vector referenceg;

    ae_frame_make(_state, &_frame_block);
    memset(&network, 0, sizeof(network));
    memset(&sparsexy, 0, sizeof(sparsexy));
    memset(&sparsexy2, 0, sizeof(sparsexy2));
    memset(&xy, 0, sizeof(xy));
    memset(&xy2, 0, sizeof(xy2));
    memset(&grad1, 0, sizeof(grad1));
    memset(&grad2, 0, sizeof(grad2));
    memset(&gradsp, 0, sizeof(gradsp));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&y1, 0, sizeof(y1));
    memset(&y2, 0, sizeof(y2));
    memset(&idx, 0, sizeof(idx));
    memset(&referenceg, 0, sizeof(referenceg));
    _multilayerperceptron_init(&network, _state, ae_true);
    _sparsematrix_init(&sparsexy, _state, ae_true);
    _sparsematrix_init(&sparsexy2, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gradsp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&idx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&referenceg, 0, DT_REAL, _state, ae_true);

    a1 = (double)(0);
    a2 = (double)(0);
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    testmlpbaseunit_createnetwork(&network, nkind, a1, a2, nin, nhid1, nhid2, nout, _state);
    mlpproperties(&network, &n1, &n2, &wcount, _state);
    h = 0.0001;
    etol = 1.0E-2;
    escale = 1.0E-2;
    gscale = 1.0E-2;
    nonstricttolerance = 0.01;
    
    /*
     * Initialize
     */
    ae_vector_set_length(&x, nin, _state);
    ae_vector_set_length(&x1, nin, _state);
    ae_vector_set_length(&x2, nin, _state);
    ae_vector_set_length(&y, nout, _state);
    ae_vector_set_length(&y1, nout, _state);
    ae_vector_set_length(&y2, nout, _state);
    ae_vector_set_length(&referenceg, wcount, _state);
    ae_vector_set_length(&grad1, wcount, _state);
    ae_vector_set_length(&grad2, wcount, _state);
    
    /*
     * Process
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Randomize network, then re-randomaze weights manually.
         *
         * NOTE: weights magnitude is chosen to be small, about 0.1,
         *       which allows us to avoid oversaturated network.
         *       In 10% of cases we use zero weights.
         */
        mlprandomizefull(&network, _state);
        if( ae_fp_less_eq(ae_randomreal(_state),0.1) )
        {
            for(i=0; i<=wcount-1; i++)
            {
                network.weights.ptr.p_double[i] = 0.0;
            }
        }
        else
        {
            for(i=0; i<=wcount-1; i++)
            {
                network.weights.ptr.p_double[i] = 0.2*ae_randomreal(_state)-0.1;
            }
        }
        
        /*
         * Test MLPError(), MLPErrorSparse(), MLPGrad() for single-element dataset:
         * * generate input X, output Y, combine them in dataset XY
         * * calculate "reference" error on dataset manually (call MLPProcess and evaluate sum-of-squared errors)
         * * calculate "reference" gradient by performing numerical differentiation of "reference" error
         *   using 4-point differentiation formula
         * * test error/gradient returned by MLPGrad(), MLPError(), MLPErrorSparse()
         */
        ae_matrix_set_length(&xy, 1, nin+nout, _state);
        sparsecreate(1, nin+nout, nin+nout, &sparsexy, _state);
        for(i=0; i<=nin-1; i++)
        {
            x.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
        }
        ae_v_move(&xy.ptr.pp_double[0][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,nin-1));
        for(i=0; i<=nin-1; i++)
        {
            sparseset(&sparsexy, 0, i, x.ptr.p_double[i], _state);
        }
        if( mlpissoftmax(&network, _state) )
        {
            for(i=0; i<=nout-1; i++)
            {
                y.ptr.p_double[i] = (double)(0);
            }
            xy.ptr.pp_double[0][nin] = (double)(ae_randominteger(nout, _state));
            sparseset(&sparsexy, 0, nin, xy.ptr.pp_double[0][nin], _state);
            y.ptr.p_double[ae_round(xy.ptr.pp_double[0][nin], _state)] = (double)(1);
        }
        else
        {
            for(i=0; i<=nout-1; i++)
            {
                y.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
                sparseset(&sparsexy, 0, nin+i, y.ptr.p_double[i], _state);
            }
            ae_v_move(&xy.ptr.pp_double[0][nin], 1, &y.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
        }
        sparseconverttocrs(&sparsexy, _state);
        mlpprocess(&network, &x, &y2, _state);
        ae_v_sub(&y2.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
        referencee = ae_v_dotproduct(&y2.ptr.p_double[0], 1, &y2.ptr.p_double[0], 1, ae_v_len(0,nout-1));
        referencee = referencee/2;
        for(i=0; i<=wcount-1; i++)
        {
            wprev = network.weights.ptr.p_double[i];
            network.weights.ptr.p_double[i] = wprev-2*h;
            mlpprocess(&network, &x, &y1, _state);
            ae_v_sub(&y1.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v1 = ae_v_dotproduct(&y1.ptr.p_double[0], 1, &y1.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v1 = v1/2;
            network.weights.ptr.p_double[i] = wprev-h;
            mlpprocess(&network, &x, &y1, _state);
            ae_v_sub(&y1.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v2 = ae_v_dotproduct(&y1.ptr.p_double[0], 1, &y1.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v2 = v2/2;
            network.weights.ptr.p_double[i] = wprev+h;
            mlpprocess(&network, &x, &y1, _state);
            ae_v_sub(&y1.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v3 = ae_v_dotproduct(&y1.ptr.p_double[0], 1, &y1.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v3 = v3/2;
            network.weights.ptr.p_double[i] = wprev+2*h;
            mlpprocess(&network, &x, &y1, _state);
            ae_v_sub(&y1.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v4 = ae_v_dotproduct(&y1.ptr.p_double[0], 1, &y1.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v4 = v4/2;
            network.weights.ptr.p_double[i] = wprev;
            referenceg.ptr.p_double[i] = (v1-8*v2+8*v3-v4)/(12*h);
        }
        mlpgrad(&network, &x, &y, &e, &grad2, _state);
        seterrorflagdiff(err, e, referencee, etol, escale, _state);
        seterrorflagdiff(err, mlperror(&network, &xy, 1, _state), referencee, etol, escale, _state);
        seterrorflagdiff(err, mlperrorsparse(&network, &sparsexy, 1, _state), referencee, etol, escale, _state);
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &grad2, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:948");
        
        /*
         * Test MLPErrorN(), MLPGradN() for single-element dataset:
         * * generate input X, output Y, combine them in dataset XY
         * * calculate "reference" error on dataset manually (call MLPProcess and evaluate sum-of-squared errors)
         * * calculate "reference" gradient by performing numerical differentiation of "reference" error
         * * test error/gradient returned by MLPGradN(), MLPErrorN()
         *
         * NOTE: because we use inexact 2-point formula, we perform gradient test with NonStrictTolerance
         */
        ae_matrix_set_length(&xy, 1, nin+nout, _state);
        for(i=0; i<=nin-1; i++)
        {
            x.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
        }
        ae_v_move(&xy.ptr.pp_double[0][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,nin-1));
        if( mlpissoftmax(&network, _state) )
        {
            for(i=0; i<=nout-1; i++)
            {
                y.ptr.p_double[i] = (double)(0);
            }
            xy.ptr.pp_double[0][nin] = (double)(ae_randominteger(nout, _state));
            y.ptr.p_double[ae_round(xy.ptr.pp_double[0][nin], _state)] = (double)(1);
        }
        else
        {
            for(i=0; i<=nout-1; i++)
            {
                y.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
            }
            ae_v_move(&xy.ptr.pp_double[0][nin], 1, &y.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
        }
        mlpprocess(&network, &x, &y2, _state);
        referencee = (double)(0);
        if( nkind!=1 )
        {
            for(i=0; i<=nout-1; i++)
            {
                referencee = referencee+0.5*ae_sqr(y2.ptr.p_double[i]-y.ptr.p_double[i], _state);
            }
        }
        else
        {
            for(i=0; i<=nout-1; i++)
            {
                if( ae_fp_neq(y.ptr.p_double[i],(double)(0)) )
                {
                    if( ae_fp_eq(y2.ptr.p_double[i],(double)(0)) )
                    {
                        referencee = referencee+y.ptr.p_double[i]*ae_log(ae_maxrealnumber, _state);
                    }
                    else
                    {
                        referencee = referencee+y.ptr.p_double[i]*ae_log(y.ptr.p_double[i]/y2.ptr.p_double[i], _state);
                    }
                }
            }
        }
        for(i=0; i<=wcount-1; i++)
        {
            wprev = network.weights.ptr.p_double[i];
            network.weights.ptr.p_double[i] = wprev+h;
            mlpprocess(&network, &x, &y2, _state);
            network.weights.ptr.p_double[i] = wprev-h;
            mlpprocess(&network, &x, &y1, _state);
            network.weights.ptr.p_double[i] = wprev;
            v = (double)(0);
            if( nkind!=1 )
            {
                for(j=0; j<=nout-1; j++)
                {
                    v = v+0.5*(ae_sqr(y2.ptr.p_double[j]-y.ptr.p_double[j], _state)-ae_sqr(y1.ptr.p_double[j]-y.ptr.p_double[j], _state))/(2*h);
                }
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    if( ae_fp_neq(y.ptr.p_double[j],(double)(0)) )
                    {
                        if( ae_fp_eq(y2.ptr.p_double[j],(double)(0)) )
                        {
                            v = v+y.ptr.p_double[j]*ae_log(ae_maxrealnumber, _state);
                        }
                        else
                        {
                            v = v+y.ptr.p_double[j]*ae_log(y.ptr.p_double[j]/y2.ptr.p_double[j], _state);
                        }
                        if( ae_fp_eq(y1.ptr.p_double[j],(double)(0)) )
                        {
                            v = v-y.ptr.p_double[j]*ae_log(ae_maxrealnumber, _state);
                        }
                        else
                        {
                            v = v-y.ptr.p_double[j]*ae_log(y.ptr.p_double[j]/y1.ptr.p_double[j], _state);
                        }
                    }
                }
                v = v/(2*h);
            }
            referenceg.ptr.p_double[i] = v;
        }
        mlpgradn(&network, &x, &y, &e, &grad2, _state);
        seterrorflagdiff(err, e, referencee, etol, escale, _state);
        seterrorflagdiff(err, mlperrorn(&network, &xy, 1, _state), referencee, etol, escale, _state);
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &grad2, wcount, gscale, _state),nonstricttolerance), __FILE__, __LINE__, "testmlpbaseunit.ap:1029");
        
        /*
         * Test that gradient calculation functions automatically allocate
         * space for gradient, if needed.
         *
         * NOTE: we perform test with empty dataset.
         */
        sparsecreate(1, nin+nout, 0, &sparsexy, _state);
        sparseconverttocrs(&sparsexy, _state);
        ae_vector_set_length(&grad1, 1, _state);
        mlpgradbatch(&network, &xy, 0, &e1, &grad1, _state);
        ae_set_error_flag(err, grad1.cnt!=wcount, __FILE__, __LINE__, "testmlpbaseunit.ap:1042");
        ae_vector_set_length(&grad1, 1, _state);
        mlpgradbatchsparse(&network, &sparsexy, 0, &e1, &grad1, _state);
        ae_set_error_flag(err, grad1.cnt!=wcount, __FILE__, __LINE__, "testmlpbaseunit.ap:1045");
        ae_vector_set_length(&grad1, 1, _state);
        mlpgradbatchsubset(&network, &xy, 0, &idx, 0, &e1, &grad1, _state);
        ae_set_error_flag(err, grad1.cnt!=wcount, __FILE__, __LINE__, "testmlpbaseunit.ap:1048");
        ae_vector_set_length(&grad1, 1, _state);
        mlpgradbatchsparsesubset(&network, &sparsexy, 0, &idx, 0, &e1, &grad1, _state);
        ae_set_error_flag(err, grad1.cnt!=wcount, __FILE__, __LINE__, "testmlpbaseunit.ap:1051");
        
        /*
         * Test MLPError(), MLPErrorSparse(), MLPGradBatch(), MLPGradBatchSparse() for many-element dataset:
         * * generate random dataset XY
         * * calculate "reference" error/gradient using MLPGrad(), which was tested in previous
         *   section and is assumed to work correctly
         * * test results returned by MLPGradBatch/MLPGradBatchSparse against reference ones
         *
         * NOTE: about 10% of tests are performed with zero SSize
         */
        ssize = sizemin+ae_randominteger(sizemax-sizemin+1, _state);
        ae_matrix_set_length(&xy, ae_maxint(ssize, 1, _state), nin+nout, _state);
        sparsecreate(ae_maxint(ssize, 1, _state), nin+nout, ssize*(nin+nout), &sparsexy, _state);
        for(i=0; i<=wcount-1; i++)
        {
            referenceg.ptr.p_double[i] = (double)(0);
        }
        referencee = (double)(0);
        for(i=0; i<=ssize-1; i++)
        {
            for(j=0; j<=nin-1; j++)
            {
                x1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                sparseset(&sparsexy, i, j, x1.ptr.p_double[j], _state);
            }
            ae_v_move(&xy.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,nin-1));
            if( mlpissoftmax(&network, _state) )
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = (double)(0);
                }
                xy.ptr.pp_double[i][nin] = (double)(ae_randominteger(nout, _state));
                sparseset(&sparsexy, i, nin, xy.ptr.pp_double[i][nin], _state);
                y1.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)] = (double)(1);
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                    sparseset(&sparsexy, i, nin+j, y1.ptr.p_double[j], _state);
                }
                ae_v_move(&xy.ptr.pp_double[i][nin], 1, &y1.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
            }
            mlpgrad(&network, &x1, &y1, &v, &grad2, _state);
            referencee = referencee+v;
            ae_v_add(&referenceg.ptr.p_double[0], 1, &grad2.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
        }
        sparseconverttocrs(&sparsexy, _state);
        e2 = mlperror(&network, &xy, ssize, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        e2 = mlperrorsparse(&network, &sparsexy, ssize, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        mlpgradbatch(&network, &xy, ssize, &e2, &grad2, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &grad2, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:1104");
        mlpgradbatchsparse(&network, &sparsexy, ssize, &esp, &gradsp, _state);
        seterrorflagdiff(err, esp, referencee, etol, escale, _state);
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &gradsp, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:1107");
        
        /*
         * Test MLPErrorSubset(), MLPGradBatchSubset(), MLPErrorSparseSubset(), MLPGradBatchSparseSubset()
         * for many-element dataset with different types of subsets:
         * * generate random dataset XY
         * * "reference" error/gradient are calculated with MLPGradBatch(),
         *   which was tested in previous section and is assumed to work correctly
         * * we perform tests for different subsets:
         *   * SubsetSize<0 - subset is a full dataset
         *   * SubsetSize=0 - subset is empty
         *   * SubsetSize>0 - random subset
         */
        ssize = sizemin+ae_randominteger(sizemax-sizemin+1, _state);
        ae_matrix_set_length(&xy, ae_maxint(ssize, 1, _state), nin+nout, _state);
        sparsecreate(ae_maxint(ssize, 1, _state), nin+nout, ssize*(nin+nout), &sparsexy, _state);
        for(i=0; i<=ssize-1; i++)
        {
            for(j=0; j<=nin-1; j++)
            {
                x1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                sparseset(&sparsexy, i, j, x1.ptr.p_double[j], _state);
            }
            ae_v_move(&xy.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,nin-1));
            if( mlpissoftmax(&network, _state) )
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = (double)(0);
                }
                xy.ptr.pp_double[i][nin] = (double)(ae_randominteger(nout, _state));
                sparseset(&sparsexy, i, nin, xy.ptr.pp_double[i][nin], _state);
                y1.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)] = (double)(1);
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                    sparseset(&sparsexy, i, nin+j, y1.ptr.p_double[j], _state);
                }
                ae_v_move(&xy.ptr.pp_double[i][nin], 1, &y1.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
            }
        }
        sparseconverttocrs(&sparsexy, _state);
        if( ssize>0 )
        {
            subsetsize = 1+ae_randominteger(10, _state);
            ae_matrix_set_length(&xy2, subsetsize, nin+nout, _state);
            ae_vector_set_length(&idx, subsetsize, _state);
            sparsecreate(subsetsize, nin+nout, subsetsize*(nin+nout), &sparsexy2, _state);
            if( mlpissoftmax(&network, _state) )
            {
                rowsize = nin+1;
            }
            else
            {
                rowsize = nin+nout;
            }
            for(i=0; i<=subsetsize-1; i++)
            {
                k = ae_randominteger(ssize, _state);
                idx.ptr.p_int[i] = k;
                for(j=0; j<=rowsize-1; j++)
                {
                    xy2.ptr.pp_double[i][j] = xy.ptr.pp_double[k][j];
                    sparseset(&sparsexy2, i, j, sparseget(&sparsexy, k, j, _state), _state);
                }
            }
            sparseconverttocrs(&sparsexy2, _state);
        }
        else
        {
            subsetsize = 0;
            ae_matrix_set_length(&xy2, 0, 0, _state);
            ae_vector_set_length(&idx, 0, _state);
            sparsecreate(1, nin+nout, 0, &sparsexy2, _state);
            sparseconverttocrs(&sparsexy2, _state);
        }
        mlpgradbatch(&network, &xy, ssize, &referencee, &referenceg, _state);
        e2 = mlperrorsubset(&network, &xy, ssize, &idx, -1, _state);
        esp = mlperrorsparsesubset(&network, &sparsexy, ssize, &idx, -1, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        seterrorflagdiff(err, esp, referencee, etol, escale, _state);
        mlpgradbatchsubset(&network, &xy, ssize, &idx, -1, &e2, &grad2, _state);
        mlpgradbatchsparsesubset(&network, &sparsexy, ssize, &idx, -1, &esp, &gradsp, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        seterrorflagdiff(err, esp, referencee, etol, escale, _state);
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &grad2, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:1192");
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &gradsp, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:1193");
        mlpgradbatch(&network, &xy, 0, &referencee, &referenceg, _state);
        e2 = mlperrorsubset(&network, &xy, ssize, &idx, 0, _state);
        esp = mlperrorsparsesubset(&network, &sparsexy, ssize, &idx, 0, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        seterrorflagdiff(err, esp, referencee, etol, escale, _state);
        mlpgradbatchsubset(&network, &xy, ssize, &idx, 0, &e2, &grad2, _state);
        mlpgradbatchsparsesubset(&network, &sparsexy, ssize, &idx, 0, &esp, &gradsp, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        seterrorflagdiff(err, esp, referencee, etol, escale, _state);
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &grad2, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:1205");
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &gradsp, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:1206");
        mlpgradbatch(&network, &xy2, subsetsize, &referencee, &referenceg, _state);
        e2 = mlperrorsubset(&network, &xy, ssize, &idx, subsetsize, _state);
        esp = mlperrorsparsesubset(&network, &sparsexy, ssize, &idx, subsetsize, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        seterrorflagdiff(err, esp, referencee, etol, escale, _state);
        mlpgradbatchsubset(&network, &xy, ssize, &idx, subsetsize, &e2, &grad2, _state);
        mlpgradbatchsparsesubset(&network, &sparsexy, ssize, &idx, subsetsize, &esp, &gradsp, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        seterrorflagdiff(err, esp, referencee, etol, escale, _state);
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &grad2, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:1218");
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &gradsp, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:1219");
        
        /*
         * Test MLPGradNBatch() for many-element dataset:
         * * generate random dataset XY
         * * calculate "reference" error/gradient using MLPGrad(), which was tested in previous
         *   section and is assumed to work correctly
         * * test results returned by MLPGradNBatch against reference ones
         */
        ssize = sizemin+ae_randominteger(sizemax-sizemin+1, _state);
        ae_matrix_set_length(&xy, ssize, nin+nout, _state);
        for(i=0; i<=wcount-1; i++)
        {
            referenceg.ptr.p_double[i] = (double)(0);
        }
        referencee = (double)(0);
        for(i=0; i<=ssize-1; i++)
        {
            for(j=0; j<=nin-1; j++)
            {
                x1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
            }
            ae_v_move(&xy.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,nin-1));
            if( mlpissoftmax(&network, _state) )
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = (double)(0);
                }
                xy.ptr.pp_double[i][nin] = (double)(ae_randominteger(nout, _state));
                y1.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)] = (double)(1);
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                }
                ae_v_move(&xy.ptr.pp_double[i][nin], 1, &y1.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
            }
            mlpgradn(&network, &x1, &y1, &v, &grad2, _state);
            referencee = referencee+v;
            ae_v_add(&referenceg.ptr.p_double[0], 1, &grad2.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
        }
        mlpgradnbatch(&network, &xy, ssize, &e2, &grad2, _state);
        seterrorflagdiff(err, e2, referencee, etol, escale, _state);
        ae_set_error_flag(err, ae_fp_greater(testmlpbaseunit_vectordiff(&referenceg, &grad2, wcount, gscale, _state),etol), __FILE__, __LINE__, "testmlpbaseunit.ap:1258");
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Hessian functions test
*************************************************************************/
static void testmlpbaseunit_testhessian(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron network;
    ae_int_t hkind;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    double h;
    double etol;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ssize;
    double a1;
    double a2;
    ae_matrix xy;
    ae_matrix h1;
    ae_matrix h2;
    ae_vector grad1;
    ae_vector grad2;
    ae_vector grad3;
    ae_vector x;
    ae_vector y;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    double v;
    double e1;
    double e2;
    double wprev;

    ae_frame_make(_state, &_frame_block);
    memset(&network, 0, sizeof(network));
    memset(&xy, 0, sizeof(xy));
    memset(&h1, 0, sizeof(h1));
    memset(&h2, 0, sizeof(h2));
    memset(&grad1, 0, sizeof(grad1));
    memset(&grad2, 0, sizeof(grad2));
    memset(&grad3, 0, sizeof(grad3));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&y1, 0, sizeof(y1));
    memset(&y2, 0, sizeof(y2));
    _multilayerperceptron_init(&network, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&h1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&h2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad3, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);

    ae_assert(passcount>=2, "PassCount<2!", _state);
    a1 = (double)(0);
    a2 = (double)(0);
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    testmlpbaseunit_createnetwork(&network, nkind, a1, a2, nin, nhid1, nhid2, nout, _state);
    mlpproperties(&network, &n1, &n2, &wcount, _state);
    h = 0.0001;
    etol = 0.05;
    
    /*
     * Initialize
     */
    ae_vector_set_length(&x, nin-1+1, _state);
    ae_vector_set_length(&x1, nin-1+1, _state);
    ae_vector_set_length(&x2, nin-1+1, _state);
    ae_vector_set_length(&y, nout-1+1, _state);
    ae_vector_set_length(&y1, nout-1+1, _state);
    ae_vector_set_length(&y2, nout-1+1, _state);
    ae_vector_set_length(&grad1, wcount-1+1, _state);
    ae_vector_set_length(&grad2, wcount-1+1, _state);
    ae_vector_set_length(&grad3, wcount-1+1, _state);
    ae_matrix_set_length(&h1, wcount-1+1, wcount-1+1, _state);
    ae_matrix_set_length(&h2, wcount-1+1, wcount-1+1, _state);
    
    /*
     * Process
     */
    for(pass=1; pass<=passcount; pass++)
    {
        mlprandomizefull(&network, _state);
        
        /*
         * Test hessian calculation .
         * E1 contains total error (calculated using MLPGrad/MLPGradN)
         * Grad1 contains total gradient (calculated using MLPGrad/MLPGradN)
         * H1 contains Hessian calculated using differences of gradients
         *
         * E2, Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch
         */
        for(hkind=0; hkind<=1; hkind++)
        {
            ssize = 1+ae_randominteger(10, _state);
            ae_matrix_set_length(&xy, ssize-1+1, nin+nout-1+1, _state);
            for(i=0; i<=wcount-1; i++)
            {
                grad1.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=wcount-1; i++)
            {
                for(j=0; j<=wcount-1; j++)
                {
                    h1.ptr.pp_double[i][j] = (double)(0);
                }
            }
            e1 = (double)(0);
            for(i=0; i<=ssize-1; i++)
            {
                
                /*
                 * X, Y
                 */
                for(j=0; j<=nin-1; j++)
                {
                    x1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                }
                ae_v_move(&xy.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,nin-1));
                if( mlpissoftmax(&network, _state) )
                {
                    for(j=0; j<=nout-1; j++)
                    {
                        y1.ptr.p_double[j] = (double)(0);
                    }
                    xy.ptr.pp_double[i][nin] = (double)(ae_randominteger(nout, _state));
                    y1.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)] = (double)(1);
                }
                else
                {
                    for(j=0; j<=nout-1; j++)
                    {
                        y1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                    }
                    ae_v_move(&xy.ptr.pp_double[i][nin], 1, &y1.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
                }
                
                /*
                 * E1, Grad1
                 */
                if( hkind==0 )
                {
                    mlpgrad(&network, &x1, &y1, &v, &grad2, _state);
                }
                else
                {
                    mlpgradn(&network, &x1, &y1, &v, &grad2, _state);
                }
                e1 = e1+v;
                ae_v_add(&grad1.ptr.p_double[0], 1, &grad2.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
                
                /*
                 * H1
                 */
                for(j=0; j<=wcount-1; j++)
                {
                    wprev = network.weights.ptr.p_double[j];
                    network.weights.ptr.p_double[j] = wprev-2*h;
                    if( hkind==0 )
                    {
                        mlpgrad(&network, &x1, &y1, &v, &grad2, _state);
                    }
                    else
                    {
                        mlpgradn(&network, &x1, &y1, &v, &grad2, _state);
                    }
                    network.weights.ptr.p_double[j] = wprev-h;
                    if( hkind==0 )
                    {
                        mlpgrad(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    else
                    {
                        mlpgradn(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    ae_v_subd(&grad2.ptr.p_double[0], 1, &grad3.ptr.p_double[0], 1, ae_v_len(0,wcount-1), 8);
                    network.weights.ptr.p_double[j] = wprev+h;
                    if( hkind==0 )
                    {
                        mlpgrad(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    else
                    {
                        mlpgradn(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    ae_v_addd(&grad2.ptr.p_double[0], 1, &grad3.ptr.p_double[0], 1, ae_v_len(0,wcount-1), 8);
                    network.weights.ptr.p_double[j] = wprev+2*h;
                    if( hkind==0 )
                    {
                        mlpgrad(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    else
                    {
                        mlpgradn(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    ae_v_sub(&grad2.ptr.p_double[0], 1, &grad3.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
                    v = 1/(12*h);
                    ae_v_addd(&h1.ptr.pp_double[j][0], 1, &grad2.ptr.p_double[0], 1, ae_v_len(0,wcount-1), v);
                    network.weights.ptr.p_double[j] = wprev;
                }
            }
            if( hkind==0 )
            {
                mlphessianbatch(&network, &xy, ssize, &e2, &grad2, &h2, _state);
            }
            else
            {
                mlphessiannbatch(&network, &xy, ssize, &e2, &grad2, &h2, _state);
            }
            *err = *err||ae_fp_greater(ae_fabs(e1-e2, _state)/e1,etol);
            for(i=0; i<=wcount-1; i++)
            {
                if( ae_fp_greater(ae_fabs(grad1.ptr.p_double[i], _state),1.0E-2) )
                {
                    *err = *err||ae_fp_greater(ae_fabs((grad2.ptr.p_double[i]-grad1.ptr.p_double[i])/grad1.ptr.p_double[i], _state),etol);
                }
                else
                {
                    *err = *err||ae_fp_greater(ae_fabs(grad2.ptr.p_double[i]-grad1.ptr.p_double[i], _state),etol);
                }
            }
            for(i=0; i<=wcount-1; i++)
            {
                for(j=0; j<=wcount-1; j++)
                {
                    if( ae_fp_greater(ae_fabs(h1.ptr.pp_double[i][j], _state),5.0E-2) )
                    {
                        *err = *err||ae_fp_greater(ae_fabs((h1.ptr.pp_double[i][j]-h2.ptr.pp_double[i][j])/h1.ptr.pp_double[i][j], _state),etol);
                    }
                    else
                    {
                        *err = *err||ae_fp_greater(ae_fabs(h2.ptr.pp_double[i][j]-h1.ptr.pp_double[i][j], _state),etol);
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Error functions (other than MLPError and MLPErrorN) test.

Network of type NKind is created, with  NIn  inputs,  NHid1*NHid2   hidden
layers (one layer if NHid2=0), NOut outputs. PassCount  random  passes  is
performed. Dataset has random size in [SizeMin,SizeMax].
*************************************************************************/
static void testmlpbaseunit_testerr(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_int_t sizemin,
     ae_int_t sizemax,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron network;
    sparsematrix sparsexy;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    double etol;
    double escale;
    double a1;
    double a2;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t ssize;
    ae_int_t subsetsize;
    ae_matrix xy;
    ae_vector y;
    ae_vector x1;
    ae_vector y1;
    ae_vector idx;
    ae_vector dummy;
    double refrmserror;
    double refclserror;
    double refrelclserror;
    double refavgce;
    double refavgerror;
    double refavgrelerror;
    ae_int_t avgrelcnt;
    modelerrors allerrors;
    ae_int_t nnmax;
    ae_int_t dsmax;
    double relclstolerance;

    ae_frame_make(_state, &_frame_block);
    memset(&network, 0, sizeof(network));
    memset(&sparsexy, 0, sizeof(sparsexy));
    memset(&xy, 0, sizeof(xy));
    memset(&y, 0, sizeof(y));
    memset(&x1, 0, sizeof(x1));
    memset(&y1, 0, sizeof(y1));
    memset(&idx, 0, sizeof(idx));
    memset(&dummy, 0, sizeof(dummy));
    memset(&allerrors, 0, sizeof(allerrors));
    _multilayerperceptron_init(&network, _state, ae_true);
    _sparsematrix_init(&sparsexy, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&idx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&dummy, 0, DT_INT, _state, ae_true);
    _modelerrors_init(&allerrors, _state, ae_true);

    a1 = (double)(0);
    a2 = (double)(0);
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    testmlpbaseunit_createnetwork(&network, nkind, a1, a2, nin, nhid1, nhid2, nout, _state);
    mlpproperties(&network, &n1, &n2, &wcount, _state);
    etol = 1.0E-4;
    escale = 1.0E-2;
    
    /*
     * Initialize
     */
    ae_vector_set_length(&x1, nin, _state);
    ae_vector_set_length(&y, nout, _state);
    ae_vector_set_length(&y1, nout, _state);
    
    /*
     * Process
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Randomize network, then re-randomaze weights manually.
         *
         * NOTE: weights magnitude is chosen to be small, about 0.1,
         *       which allows us to avoid oversaturated network.
         *       In 10% of cases we use zero weights.
         */
        mlprandomizefull(&network, _state);
        if( ae_fp_less_eq(ae_randomreal(_state),0.1) )
        {
            for(i=0; i<=wcount-1; i++)
            {
                network.weights.ptr.p_double[i] = 0.0;
            }
        }
        else
        {
            for(i=0; i<=wcount-1; i++)
            {
                network.weights.ptr.p_double[i] = 0.2*ae_randomreal(_state)-0.1;
            }
        }
        
        /*
         * Generate random dataset.
         * Calculate reference errors.
         *
         * NOTE: about 10% of tests are performed with zero SSize
         */
        ssize = sizemin+ae_randominteger(sizemax-sizemin+1, _state);
        if( mlpissoftmax(&network, _state) )
        {
            ae_matrix_set_length(&xy, ae_maxint(ssize, 1, _state), nin+1, _state);
            sparsecreate(ae_maxint(ssize, 1, _state), nin+1, 0, &sparsexy, _state);
        }
        else
        {
            ae_matrix_set_length(&xy, ae_maxint(ssize, 1, _state), nin+nout, _state);
            sparsecreate(ae_maxint(ssize, 1, _state), nin+nout, 0, &sparsexy, _state);
        }
        refrmserror = 0.0;
        refclserror = 0.0;
        refavgce = 0.0;
        refavgerror = 0.0;
        refavgrelerror = 0.0;
        avgrelcnt = 0;
        for(i=0; i<=ssize-1; i++)
        {
            
            /*
             * Fill I-th row
             */
            for(j=0; j<=nin-1; j++)
            {
                x1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                sparseset(&sparsexy, i, j, x1.ptr.p_double[j], _state);
            }
            ae_v_move(&xy.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,nin-1));
            if( mlpissoftmax(&network, _state) )
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = (double)(0);
                }
                xy.ptr.pp_double[i][nin] = (double)(ae_randominteger(nout, _state));
                sparseset(&sparsexy, i, nin, xy.ptr.pp_double[i][nin], _state);
                y1.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)] = (double)(1);
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                    sparseset(&sparsexy, i, nin+j, y1.ptr.p_double[j], _state);
                }
                ae_v_move(&xy.ptr.pp_double[i][nin], 1, &y1.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
            }
            
            /*
             * Process
             */
            mlpprocess(&network, &x1, &y, _state);
            
            /*
             * Update reference errors
             */
            nnmax = 0;
            if( mlpissoftmax(&network, _state) )
            {
                if( ae_fp_greater(y.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)],(double)(0)) )
                {
                    refavgce = refavgce+ae_log(1/y.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)], _state);
                }
                else
                {
                    refavgce = refavgce+ae_log(ae_maxrealnumber, _state);
                }
            }
            if( mlpissoftmax(&network, _state) )
            {
                dsmax = ae_round(xy.ptr.pp_double[i][nin], _state);
            }
            else
            {
                dsmax = 0;
            }
            for(j=0; j<=nout-1; j++)
            {
                refrmserror = refrmserror+ae_sqr(y.ptr.p_double[j]-y1.ptr.p_double[j], _state);
                refavgerror = refavgerror+ae_fabs(y.ptr.p_double[j]-y1.ptr.p_double[j], _state);
                if( ae_fp_neq(y1.ptr.p_double[j],(double)(0)) )
                {
                    refavgrelerror = refavgrelerror+ae_fabs(y.ptr.p_double[j]-y1.ptr.p_double[j], _state)/ae_fabs(y1.ptr.p_double[j], _state);
                    avgrelcnt = avgrelcnt+1;
                }
                if( ae_fp_greater(y.ptr.p_double[j],y.ptr.p_double[nnmax]) )
                {
                    nnmax = j;
                }
                if( !mlpissoftmax(&network, _state)&&ae_fp_greater(y1.ptr.p_double[j],y1.ptr.p_double[dsmax]) )
                {
                    dsmax = j;
                }
            }
            if( nnmax!=dsmax )
            {
                refclserror = refclserror+1;
            }
        }
        sparseconverttocrs(&sparsexy, _state);
        if( ssize>0 )
        {
            refrmserror = ae_sqrt(refrmserror/(ssize*nout), _state);
            refavgerror = refavgerror/(ssize*nout);
            refrelclserror = refclserror/ssize;
            refavgce = refavgce/(ssize*ae_log((double)(2), _state));
        }
        else
        {
            refrelclserror = 0.0;
        }
        if( avgrelcnt>0 )
        {
            refavgrelerror = refavgrelerror/avgrelcnt;
        }
        
        /*
         * Test "continuous" errors on full dataset
         */
        seterrorflagdiff(err, mlprmserror(&network, &xy, ssize, _state), refrmserror, etol, escale, _state);
        seterrorflagdiff(err, mlpavgce(&network, &xy, ssize, _state), refavgce, etol, escale, _state);
        seterrorflagdiff(err, mlpavgerror(&network, &xy, ssize, _state), refavgerror, etol, escale, _state);
        seterrorflagdiff(err, mlpavgrelerror(&network, &xy, ssize, _state), refavgrelerror, etol, escale, _state);
        seterrorflagdiff(err, mlprmserrorsparse(&network, &sparsexy, ssize, _state), refrmserror, etol, escale, _state);
        seterrorflagdiff(err, mlpavgcesparse(&network, &sparsexy, ssize, _state), refavgce, etol, escale, _state);
        seterrorflagdiff(err, mlpavgerrorsparse(&network, &sparsexy, ssize, _state), refavgerror, etol, escale, _state);
        seterrorflagdiff(err, mlpavgrelerrorsparse(&network, &sparsexy, ssize, _state), refavgrelerror, etol, escale, _state);
        mlpallerrorssubset(&network, &xy, ssize, &dummy, -1, &allerrors, _state);
        seterrorflagdiff(err, allerrors.avgce, refavgce, etol, escale, _state);
        seterrorflagdiff(err, allerrors.rmserror, refrmserror, etol, escale, _state);
        seterrorflagdiff(err, allerrors.avgerror, refavgerror, etol, escale, _state);
        seterrorflagdiff(err, allerrors.avgrelerror, refavgrelerror, etol, escale, _state);
        mlpallerrorssparsesubset(&network, &sparsexy, ssize, &dummy, -1, &allerrors, _state);
        seterrorflagdiff(err, allerrors.avgce, refavgce, etol, escale, _state);
        seterrorflagdiff(err, allerrors.rmserror, refrmserror, etol, escale, _state);
        seterrorflagdiff(err, allerrors.avgerror, refavgerror, etol, escale, _state);
        seterrorflagdiff(err, allerrors.avgrelerror, refavgrelerror, etol, escale, _state);
        
        /*
         * Test errors on dataset given by subset.
         * We perform only limited test for RMS error, assuming that either all errors
         * are calculated correctly (subject to subset given by Idx) - or none of them.
         */
        if( ssize>0 )
        {
            subsetsize = ae_randominteger(10, _state);
        }
        else
        {
            subsetsize = 0;
        }
        ae_vector_set_length(&idx, subsetsize, _state);
        refrmserror = 0.0;
        for(i=0; i<=subsetsize-1; i++)
        {
            k = ae_randominteger(ssize, _state);
            idx.ptr.p_int[i] = k;
            ae_v_move(&x1.ptr.p_double[0], 1, &xy.ptr.pp_double[k][0], 1, ae_v_len(0,nin-1));
            if( mlpissoftmax(&network, _state) )
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = (double)(0);
                }
                y1.ptr.p_double[ae_round(xy.ptr.pp_double[k][nin], _state)] = (double)(1);
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = xy.ptr.pp_double[k][nin+j];
                }
            }
            mlpprocess(&network, &x1, &y, _state);
            for(j=0; j<=nout-1; j++)
            {
                refrmserror = refrmserror+ae_sqr(y.ptr.p_double[j]-y1.ptr.p_double[j], _state);
            }
        }
        if( subsetsize>0 )
        {
            refrmserror = ae_sqrt(refrmserror/(subsetsize*nout), _state);
        }
        mlpallerrorssubset(&network, &xy, ssize, &idx, subsetsize, &allerrors, _state);
        seterrorflagdiff(err, allerrors.rmserror, refrmserror, etol, escale, _state);
        mlpallerrorssparsesubset(&network, &sparsexy, ssize, &idx, subsetsize, &allerrors, _state);
        seterrorflagdiff(err, allerrors.rmserror, refrmserror, etol, escale, _state);
        
        /*
         * Test "discontinuous" error function.
         * Even slight changes in the network output may force these functions
         * to change by 1. So, we test them with relaxed criteria, corresponding to
         * difference in classification of two samples.
         */
        if( ssize>0 )
        {
            relclstolerance = 2.5/ssize;
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(mlpclserror(&network, &xy, ssize, _state)-refclserror, _state),ssize*relclstolerance), __FILE__, __LINE__, "testmlpbaseunit.ap:1728");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(mlprelclserror(&network, &xy, ssize, _state)-refrelclserror, _state),relclstolerance), __FILE__, __LINE__, "testmlpbaseunit.ap:1729");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(mlprelclserrorsparse(&network, &sparsexy, ssize, _state)-refrelclserror, _state),relclstolerance), __FILE__, __LINE__, "testmlpbaseunit.ap:1730");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Special tests
*************************************************************************/
static void testmlpbaseunit_spectests(ae_bool* inferrors,
     ae_bool* procerrors,
     ae_bool* graderrors,
     ae_bool* hesserrors,
     ae_bool* errerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron net;
    ae_matrix xy;
    double f;
    ae_vector g;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&net, 0, sizeof(net));
    memset(&xy, 0, sizeof(xy));
    memset(&g, 0, sizeof(g));
    _multilayerperceptron_init(&net, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);

    
    /*
     * Special test for overflow in TanH:
     * * create 1x1x1 linear network
     * * create dataset with 1 item: [x, y] = [0, 1]
     * * set network weights to [10000000, 10000000, 10000000, 10000000]
     * * check that error function is finite
     * * check that gradient is finite
     */
    mlpcreate1(1, 1, 1, &net, _state);
    ae_matrix_set_length(&xy, 1, 2, _state);
    xy.ptr.pp_double[0][0] = (double)(0);
    xy.ptr.pp_double[0][1] = 1.0;
    for(i=0; i<=mlpgetweightscount(&net, _state)-1; i++)
    {
        net.weights.ptr.p_double[i] = 10000000.0;
    }
    mlpgradbatch(&net, &xy, 1, &f, &g, _state);
    ae_set_error_flag(graderrors, !ae_isfinite(f, _state), __FILE__, __LINE__, "testmlpbaseunit.ap:1903");
    ae_set_error_flag(graderrors, !ae_isfinite(mlperror(&net, &xy, 1, _state), _state), __FILE__, __LINE__, "testmlpbaseunit.ap:1904");
    for(i=0; i<=mlpgetweightscount(&net, _state)-1; i++)
    {
        ae_set_error_flag(graderrors, !ae_isfinite(g.ptr.p_double[i], _state), __FILE__, __LINE__, "testmlpbaseunit.ap:1906");
    }
    
    /*
     * Special test for overflow in SOFTMAX layer:
     * * create 1x1x2 classifier network
     * * create dataset with 1 item: [x, y] = [0, 1]
     * * set network weights to [10000000, 10000000, 10000000, 10000000]
     * * check that error function is finite
     * * check that gradient is finite
     */
    mlpcreatec1(1, 1, 2, &net, _state);
    ae_matrix_set_length(&xy, 1, 2, _state);
    xy.ptr.pp_double[0][0] = (double)(0);
    xy.ptr.pp_double[0][1] = (double)(1);
    for(i=0; i<=mlpgetweightscount(&net, _state)-1; i++)
    {
        net.weights.ptr.p_double[i] = 10000000.0;
    }
    mlpgradbatch(&net, &xy, 1, &f, &g, _state);
    ae_set_error_flag(graderrors, !ae_isfinite(f, _state), __FILE__, __LINE__, "testmlpbaseunit.ap:1924");
    ae_set_error_flag(graderrors, !ae_isfinite(mlperror(&net, &xy, 1, _state), _state), __FILE__, __LINE__, "testmlpbaseunit.ap:1925");
    for(i=0; i<=mlpgetweightscount(&net, _state)-1; i++)
    {
        ae_set_error_flag(graderrors, !ae_isfinite(g.ptr.p_double[i], _state), __FILE__, __LINE__, "testmlpbaseunit.ap:1927");
    }
    ae_frame_leave(_state);
}


/*************************************************************************
The function test functions MLPGradBatchMasked and MLPGradBatchSparseMasked.
*************************************************************************/
static ae_bool testmlpbaseunit_testmlpgbsubset(ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron net;
    ae_matrix a;
    ae_matrix parta;
    sparsematrix sa;
    sparsematrix partsa;
    ae_vector idx;
    double e1;
    double e2;
    ae_vector grad1;
    ae_vector grad2;
    ae_int_t nin;
    ae_int_t nout;
    ae_int_t w;
    ae_int_t wcount;
    ae_int_t nhid1;
    ae_int_t nhid2;
    ae_int_t nkind;
    double a1;
    double a2;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t ssize;
    ae_int_t maxssize;
    ae_int_t sbsize;
    ae_int_t nvar;
    ae_int_t variant;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&net, 0, sizeof(net));
    memset(&a, 0, sizeof(a));
    memset(&parta, 0, sizeof(parta));
    memset(&sa, 0, sizeof(sa));
    memset(&partsa, 0, sizeof(partsa));
    memset(&idx, 0, sizeof(idx));
    memset(&grad1, 0, sizeof(grad1));
    memset(&grad2, 0, sizeof(grad2));
    _multilayerperceptron_init(&net, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&parta, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    _sparsematrix_init(&partsa, _state, ae_true);
    ae_vector_init(&idx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&grad1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad2, 0, DT_REAL, _state, ae_true);

    
    /*
     * Variant:
     *  * 1 - there are all rows;
     *  * 2 - there are no one rows;
     *  * 3 - there are some random rows.
     */
    nvar = 3;
    maxssize = 96;
    for(ssize=0; ssize<=maxssize; ssize++)
    {
        ae_vector_set_length(&idx, ssize, _state);
        nkind = ae_randominteger(4, _state);
        a1 = (double)(0);
        a2 = (double)(0);
        if( nkind==2 )
        {
            a1 = 1000*ae_randomreal(_state)-500;
            a2 = 2*ae_randomreal(_state)-1;
        }
        if( nkind==3 )
        {
            a1 = 1000*ae_randomreal(_state)-500;
            a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
        }
        nin = ae_randominteger(20, _state)+1;
        nhid1 = ae_randominteger(5, _state);
        if( nhid1==0 )
        {
            nhid2 = 0;
        }
        else
        {
            nhid2 = ae_randominteger(5, _state);
        }
        nout = ae_randominteger(20, _state)+2;
        testmlpbaseunit_createnetwork(&net, nkind, a1, a2, nin, nhid1, nhid2, nout, _state);
        mlpproperties(&net, &n1, &n2, &wcount, _state);
        if( mlpissoftmax(&net, _state) )
        {
            w = nin+1;
            if( ssize>0 )
            {
                ae_matrix_set_length(&a, ssize, w, _state);
                sparsecreate(ssize, w, ssize*w, &sa, _state);
            }
            else
            {
                ae_matrix_set_length(&a, 0, 0, _state);
                sparsecreate(1, 1, 0, &sa, _state);
            }
            for(i=0; i<=ssize-1; i++)
            {
                for(j=0; j<=w-1; j++)
                {
                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    sparseset(&sa, i, j, a.ptr.pp_double[i][j], _state);
                }
            }
            for(i=0; i<=ssize-1; i++)
            {
                a.ptr.pp_double[i][nin] = (double)(ae_randominteger(nout, _state));
                sparseset(&sa, i, nin, a.ptr.pp_double[i][nin], _state);
            }
        }
        else
        {
            w = nin+nout;
            if( ssize>0 )
            {
                ae_matrix_set_length(&a, ssize, w, _state);
                sparsecreate(ssize, w, ssize*w, &sa, _state);
            }
            else
            {
                ae_matrix_set_length(&a, 0, 0, _state);
                sparsecreate(1, 1, 0, &sa, _state);
            }
            for(i=0; i<=ssize-1; i++)
            {
                for(j=0; j<=w-1; j++)
                {
                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    sparseset(&sa, i, j, a.ptr.pp_double[i][j], _state);
                }
            }
        }
        sparseconverttocrs(&sa, _state);
        for(variant=1; variant<=nvar; variant++)
        {
            sbsize = -1;
            if( variant==1 )
            {
                sbsize = ssize;
                for(i=0; i<=sbsize-1; i++)
                {
                    idx.ptr.p_int[i] = i;
                }
            }
            if( variant==2 )
            {
                sbsize = 0;
            }
            if( variant==3 )
            {
                if( ssize==0 )
                {
                    sbsize = 0;
                }
                else
                {
                    sbsize = ae_randominteger(ssize, _state);
                }
                for(i=0; i<=sbsize-1; i++)
                {
                    idx.ptr.p_int[i] = ae_randominteger(ssize, _state);
                }
            }
            ae_assert(sbsize>=0, "mlpbase test: integrity check failed", _state);
            if( sbsize!=0 )
            {
                ae_matrix_set_length(&parta, sbsize, w, _state);
                sparsecreate(sbsize, w, sbsize*w, &partsa, _state);
            }
            else
            {
                ae_matrix_set_length(&parta, 0, 0, _state);
                sparsecreate(1, 1, 0, &partsa, _state);
            }
            for(i=0; i<=sbsize-1; i++)
            {
                ae_v_move(&parta.ptr.pp_double[i][0], 1, &a.ptr.pp_double[idx.ptr.p_int[i]][0], 1, ae_v_len(0,w-1));
                for(j=0; j<=w-1; j++)
                {
                    sparseset(&partsa, i, j, parta.ptr.pp_double[i][j], _state);
                }
            }
            sparseconverttocrs(&partsa, _state);
            mlpgradbatch(&net, &parta, sbsize, &e1, &grad1, _state);
            mlpgradbatchsubset(&net, &a, ssize, &idx, sbsize, &e2, &grad2, _state);
            
            /*
             * Test for dense matrix
             */
            if( ae_fp_greater(ae_fabs(e1-e2, _state),1.0E-6) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(i=0; i<=wcount-1; i++)
            {
                if( ae_fp_greater(ae_fabs(grad1.ptr.p_double[i]-grad2.ptr.p_double[i], _state),1.0E-6) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            
            /*
             * Test for sparse matrix
             */
            mlpgradbatchsparse(&net, &partsa, sbsize, &e1, &grad1, _state);
            mlpgradbatchsparsesubset(&net, &sa, ssize, &idx, sbsize, &e2, &grad2, _state);
            if( ae_fp_greater(ae_fabs(e1-e2, _state),1.0E-6) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(i=0; i<=wcount-1; i++)
            {
                if( ae_fp_greater(ae_fabs(grad1.ptr.p_double[i]-grad2.ptr.p_double[i], _state),1.0E-6) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}



static void testldaunit_gensimpleset(ae_int_t nfeatures,
     ae_int_t nclasses,
     ae_int_t nsamples,
     ae_int_t axis,
     /* Real    */ ae_matrix* xy,
     ae_state *_state);
static void testldaunit_gendeg1set(ae_int_t nfeatures,
     ae_int_t nclasses,
     ae_int_t nsamples,
     ae_int_t axis,
     /* Real    */ ae_matrix* xy,
     ae_state *_state);
static double testldaunit_generatenormal(double mean,
     double sigma,
     ae_state *_state);
static ae_bool testldaunit_testwn(/* Real    */ ae_matrix* xy,
     /* Real    */ ae_matrix* wn,
     ae_int_t ns,
     ae_int_t nf,
     ae_int_t nc,
     ae_int_t ndeg,
     ae_state *_state);
static double testldaunit_calcj(ae_int_t nf,
     /* Real    */ ae_matrix* st,
     /* Real    */ ae_matrix* sw,
     /* Real    */ ae_vector* w,
     double* p,
     double* q,
     ae_state *_state);
static void testldaunit_fishers(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nfeatures,
     ae_int_t nclasses,
     /* Real    */ ae_matrix* st,
     /* Real    */ ae_matrix* sw,
     ae_state *_state);





ae_bool testlda(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxnf;
    ae_int_t maxns;
    ae_int_t maxnc;
    ae_int_t passcount;
    ae_bool ldanerrors;
    ae_bool lda1errors;
    ae_bool waserrors;
    ae_int_t nf;
    ae_int_t nc;
    ae_int_t ns;
    ae_int_t i;
    ae_int_t info;
    ae_int_t pass;
    ae_int_t axis;
    ae_matrix xy;
    ae_matrix wn;
    ae_vector w1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&wn, 0, sizeof(wn));
    memset(&w1, 0, sizeof(w1));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&wn, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w1, 0, DT_REAL, _state, ae_true);

    
    /*
     * Primary settings
     */
    maxnf = 10;
    maxns = 1000;
    maxnc = 5;
    passcount = 1;
    waserrors = ae_false;
    ldanerrors = ae_false;
    lda1errors = ae_false;
    
    /*
     * General tests
     */
    for(nf=1; nf<=maxnf; nf++)
    {
        for(nc=2; nc<=maxnc; nc++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Simple test for LDA-N/LDA-1
                 */
                axis = ae_randominteger(nf, _state);
                ns = maxns/2+ae_randominteger(maxns/2, _state);
                testldaunit_gensimpleset(nf, nc, ns, axis, &xy, _state);
                fisherldan(&xy, ns, nf, nc, &info, &wn, _state);
                if( info!=1 )
                {
                    ldanerrors = ae_true;
                    continue;
                }
                ldanerrors = ldanerrors||!testldaunit_testwn(&xy, &wn, ns, nf, nc, 0, _state);
                ldanerrors = ldanerrors||ae_fp_less_eq(ae_fabs(wn.ptr.pp_double[axis][0], _state),0.75);
                fisherlda(&xy, ns, nf, nc, &info, &w1, _state);
                for(i=0; i<=nf-1; i++)
                {
                    lda1errors = lda1errors||ae_fp_neq(w1.ptr.p_double[i],wn.ptr.pp_double[i][0]);
                }
                
                /*
                 * Degenerate test for LDA-N
                 */
                if( nf>=3 )
                {
                    ns = maxns/2+ae_randominteger(maxns/2, _state);
                    
                    /*
                     * there are two duplicate features,
                     * axis is oriented along non-duplicate feature
                     */
                    axis = ae_randominteger(nf-2, _state);
                    testldaunit_gendeg1set(nf, nc, ns, axis, &xy, _state);
                    fisherldan(&xy, ns, nf, nc, &info, &wn, _state);
                    if( info!=2 )
                    {
                        ldanerrors = ae_true;
                        continue;
                    }
                    ldanerrors = ldanerrors||ae_fp_less_eq(wn.ptr.pp_double[axis][0],0.75);
                    fisherlda(&xy, ns, nf, nc, &info, &w1, _state);
                    for(i=0; i<=nf-1; i++)
                    {
                        lda1errors = lda1errors||ae_fp_neq(w1.ptr.p_double[i],wn.ptr.pp_double[i][0]);
                    }
                }
            }
        }
    }
    
    /*
     * Final report
     */
    waserrors = ldanerrors||lda1errors;
    if( !silent )
    {
        printf("LDA TEST\n");
        printf("FISHER LDA-N:                            ");
        if( !ldanerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("FISHER LDA-1:                            ");
        if( !lda1errors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Generates 'simple' set - a sequence of unit 'balls' at (0,0), (1,0), (2,0)
and so on.
*************************************************************************/
static void testldaunit_gensimpleset(ae_int_t nfeatures,
     ae_int_t nclasses,
     ae_int_t nsamples,
     ae_int_t axis,
     /* Real    */ ae_matrix* xy,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t c;

    ae_matrix_clear(xy);

    ae_assert(axis>=0&&axis<nfeatures, "GenSimpleSet: wrong Axis!", _state);
    ae_matrix_set_length(xy, nsamples-1+1, nfeatures+1, _state);
    for(i=0; i<=nsamples-1; i++)
    {
        for(j=0; j<=nfeatures-1; j++)
        {
            xy->ptr.pp_double[i][j] = testldaunit_generatenormal(0.0, 1.0, _state);
        }
        c = i%nclasses;
        xy->ptr.pp_double[i][axis] = xy->ptr.pp_double[i][axis]+c;
        xy->ptr.pp_double[i][nfeatures] = (double)(c);
    }
}


/*************************************************************************
Generates 'degenerate' set #1.
NFeatures>=3.
*************************************************************************/
static void testldaunit_gendeg1set(ae_int_t nfeatures,
     ae_int_t nclasses,
     ae_int_t nsamples,
     ae_int_t axis,
     /* Real    */ ae_matrix* xy,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t c;

    ae_matrix_clear(xy);

    ae_assert(axis>=0&&axis<nfeatures, "GenDeg1Set: wrong Axis!", _state);
    ae_assert(nfeatures>=3, "GenDeg1Set: wrong NFeatures!", _state);
    ae_matrix_set_length(xy, nsamples-1+1, nfeatures+1, _state);
    if( axis>=nfeatures-2 )
    {
        axis = nfeatures-3;
    }
    for(i=0; i<=nsamples-1; i++)
    {
        for(j=0; j<=nfeatures-2; j++)
        {
            xy->ptr.pp_double[i][j] = testldaunit_generatenormal(0.0, 1.0, _state);
        }
        xy->ptr.pp_double[i][nfeatures-1] = xy->ptr.pp_double[i][nfeatures-2];
        c = i%nclasses;
        xy->ptr.pp_double[i][axis] = xy->ptr.pp_double[i][axis]+c;
        xy->ptr.pp_double[i][nfeatures] = (double)(c);
    }
}


/*************************************************************************
Normal random number
*************************************************************************/
static double testldaunit_generatenormal(double mean,
     double sigma,
     ae_state *_state)
{
    double u;
    double v;
    double sum;
    double result;


    result = mean;
    for(;;)
    {
        u = (2*ae_randominteger(2, _state)-1)*ae_randomreal(_state);
        v = (2*ae_randominteger(2, _state)-1)*ae_randomreal(_state);
        sum = u*u+v*v;
        if( ae_fp_less(sum,(double)(1))&&ae_fp_greater(sum,(double)(0)) )
        {
            sum = ae_sqrt(-2*ae_log(sum, _state)/sum, _state);
            result = sigma*u*sum+mean;
            break;
        }
    }
    return result;
}


/*************************************************************************
Tests WN for correctness
*************************************************************************/
static ae_bool testldaunit_testwn(/* Real    */ ae_matrix* xy,
     /* Real    */ ae_matrix* wn,
     ae_int_t ns,
     ae_int_t nf,
     ae_int_t nc,
     ae_int_t ndeg,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix st;
    ae_matrix sw;
    ae_matrix a;
    ae_matrix z;
    ae_vector tx;
    ae_vector jp;
    ae_vector jq;
    ae_vector work;
    ae_int_t i;
    ae_int_t j;
    double v;
    double wprev;
    double tol;
    double p;
    double q;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&st, 0, sizeof(st));
    memset(&sw, 0, sizeof(sw));
    memset(&a, 0, sizeof(a));
    memset(&z, 0, sizeof(z));
    memset(&tx, 0, sizeof(tx));
    memset(&jp, 0, sizeof(jp));
    memset(&jq, 0, sizeof(jq));
    memset(&work, 0, sizeof(work));
    ae_matrix_init(&st, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&sw, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&jp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&jq, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    tol = (double)(10000);
    result = ae_true;
    testldaunit_fishers(xy, ns, nf, nc, &st, &sw, _state);
    
    /*
     * Test for decreasing of J
     */
    ae_vector_set_length(&tx, nf-1+1, _state);
    ae_vector_set_length(&jp, nf-1+1, _state);
    ae_vector_set_length(&jq, nf-1+1, _state);
    for(j=0; j<=nf-1; j++)
    {
        ae_v_move(&tx.ptr.p_double[0], 1, &wn->ptr.pp_double[0][j], wn->stride, ae_v_len(0,nf-1));
        v = testldaunit_calcj(nf, &st, &sw, &tx, &p, &q, _state);
        jp.ptr.p_double[j] = p;
        jq.ptr.p_double[j] = q;
    }
    for(i=1; i<=nf-1-ndeg; i++)
    {
        result = result&&ae_fp_greater_eq(jp.ptr.p_double[i-1]/jq.ptr.p_double[i-1],(1-tol*ae_machineepsilon)*jp.ptr.p_double[i]/jq.ptr.p_double[i]);
    }
    for(i=nf-1-ndeg+1; i<=nf-1; i++)
    {
        result = result&&ae_fp_less_eq(jp.ptr.p_double[i],tol*ae_machineepsilon*jp.ptr.p_double[0]);
    }
    
    /*
     * Test for J optimality
     */
    ae_v_move(&tx.ptr.p_double[0], 1, &wn->ptr.pp_double[0][0], wn->stride, ae_v_len(0,nf-1));
    v = testldaunit_calcj(nf, &st, &sw, &tx, &p, &q, _state);
    for(i=0; i<=nf-1; i++)
    {
        wprev = tx.ptr.p_double[i];
        tx.ptr.p_double[i] = wprev+0.01;
        result = result&&ae_fp_greater_eq(v,(1-tol*ae_machineepsilon)*testldaunit_calcj(nf, &st, &sw, &tx, &p, &q, _state));
        tx.ptr.p_double[i] = wprev-0.01;
        result = result&&ae_fp_greater_eq(v,(1-tol*ae_machineepsilon)*testldaunit_calcj(nf, &st, &sw, &tx, &p, &q, _state));
        tx.ptr.p_double[i] = wprev;
    }
    
    /*
     * Test for linear independence of W
     */
    ae_vector_set_length(&work, nf+1, _state);
    ae_matrix_set_length(&a, nf-1+1, nf-1+1, _state);
    matrixmatrixmultiply(wn, 0, nf-1, 0, nf-1, ae_false, wn, 0, nf-1, 0, nf-1, ae_true, 1.0, &a, 0, nf-1, 0, nf-1, 0.0, &work, _state);
    if( smatrixevd(&a, nf, 1, ae_true, &tx, &z, _state) )
    {
        result = result&&ae_fp_greater(tx.ptr.p_double[0],tx.ptr.p_double[nf-1]*1000*ae_machineepsilon);
    }
    
    /*
     * Test for other properties
     */
    for(j=0; j<=nf-1; j++)
    {
        v = ae_v_dotproduct(&wn->ptr.pp_double[0][j], wn->stride, &wn->ptr.pp_double[0][j], wn->stride, ae_v_len(0,nf-1));
        v = ae_sqrt(v, _state);
        result = result&&ae_fp_less_eq(ae_fabs(v-1, _state),1000*ae_machineepsilon);
        v = (double)(0);
        for(i=0; i<=nf-1; i++)
        {
            v = v+wn->ptr.pp_double[i][j];
        }
        result = result&&ae_fp_greater_eq(v,(double)(0));
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Calculates J
*************************************************************************/
static double testldaunit_calcj(ae_int_t nf,
     /* Real    */ ae_matrix* st,
     /* Real    */ ae_matrix* sw,
     /* Real    */ ae_vector* w,
     double* p,
     double* q,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector tx;
    ae_int_t i;
    double v;
    double result;

    ae_frame_make(_state, &_frame_block);
    memset(&tx, 0, sizeof(tx));
    *p = 0;
    *q = 0;
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&tx, nf-1+1, _state);
    for(i=0; i<=nf-1; i++)
    {
        v = ae_v_dotproduct(&st->ptr.pp_double[i][0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,nf-1));
        tx.ptr.p_double[i] = v;
    }
    v = ae_v_dotproduct(&w->ptr.p_double[0], 1, &tx.ptr.p_double[0], 1, ae_v_len(0,nf-1));
    *p = v;
    for(i=0; i<=nf-1; i++)
    {
        v = ae_v_dotproduct(&sw->ptr.pp_double[i][0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,nf-1));
        tx.ptr.p_double[i] = v;
    }
    v = ae_v_dotproduct(&w->ptr.p_double[0], 1, &tx.ptr.p_double[0], 1, ae_v_len(0,nf-1));
    *q = v;
    result = *p/(*q);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Calculates ST/SW
*************************************************************************/
static void testldaunit_fishers(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nfeatures,
     ae_int_t nclasses,
     /* Real    */ ae_matrix* st,
     /* Real    */ ae_matrix* sw,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    ae_vector c;
    ae_vector mu;
    ae_matrix muc;
    ae_vector nc;
    ae_vector tf;
    ae_vector work;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&mu, 0, sizeof(mu));
    memset(&muc, 0, sizeof(muc));
    memset(&nc, 0, sizeof(nc));
    memset(&tf, 0, sizeof(tf));
    memset(&work, 0, sizeof(work));
    ae_matrix_clear(st);
    ae_matrix_clear(sw);
    ae_vector_init(&c, 0, DT_INT, _state, ae_true);
    ae_vector_init(&mu, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&muc, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&nc, 0, DT_INT, _state, ae_true);
    ae_vector_init(&tf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    
    /*
     * Prepare temporaries
     */
    ae_vector_set_length(&tf, nfeatures-1+1, _state);
    ae_vector_set_length(&work, nfeatures+1, _state);
    
    /*
     * Convert class labels from reals to integers (just for convenience)
     */
    ae_vector_set_length(&c, npoints-1+1, _state);
    for(i=0; i<=npoints-1; i++)
    {
        c.ptr.p_int[i] = ae_round(xy->ptr.pp_double[i][nfeatures], _state);
    }
    
    /*
     * Calculate class sizes and means
     */
    ae_vector_set_length(&mu, nfeatures-1+1, _state);
    ae_matrix_set_length(&muc, nclasses-1+1, nfeatures-1+1, _state);
    ae_vector_set_length(&nc, nclasses-1+1, _state);
    for(j=0; j<=nfeatures-1; j++)
    {
        mu.ptr.p_double[j] = (double)(0);
    }
    for(i=0; i<=nclasses-1; i++)
    {
        nc.ptr.p_int[i] = 0;
        for(j=0; j<=nfeatures-1; j++)
        {
            muc.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(i=0; i<=npoints-1; i++)
    {
        ae_v_add(&mu.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nfeatures-1));
        ae_v_add(&muc.ptr.pp_double[c.ptr.p_int[i]][0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nfeatures-1));
        nc.ptr.p_int[c.ptr.p_int[i]] = nc.ptr.p_int[c.ptr.p_int[i]]+1;
    }
    for(i=0; i<=nclasses-1; i++)
    {
        v = (double)1/(double)nc.ptr.p_int[i];
        ae_v_muld(&muc.ptr.pp_double[i][0], 1, ae_v_len(0,nfeatures-1), v);
    }
    v = (double)1/(double)npoints;
    ae_v_muld(&mu.ptr.p_double[0], 1, ae_v_len(0,nfeatures-1), v);
    
    /*
     * Create ST matrix
     */
    ae_matrix_set_length(st, nfeatures-1+1, nfeatures-1+1, _state);
    for(i=0; i<=nfeatures-1; i++)
    {
        for(j=0; j<=nfeatures-1; j++)
        {
            st->ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(k=0; k<=npoints-1; k++)
    {
        ae_v_move(&tf.ptr.p_double[0], 1, &xy->ptr.pp_double[k][0], 1, ae_v_len(0,nfeatures-1));
        ae_v_sub(&tf.ptr.p_double[0], 1, &mu.ptr.p_double[0], 1, ae_v_len(0,nfeatures-1));
        for(i=0; i<=nfeatures-1; i++)
        {
            v = tf.ptr.p_double[i];
            ae_v_addd(&st->ptr.pp_double[i][0], 1, &tf.ptr.p_double[0], 1, ae_v_len(0,nfeatures-1), v);
        }
    }
    
    /*
     * Create SW matrix
     */
    ae_matrix_set_length(sw, nfeatures-1+1, nfeatures-1+1, _state);
    for(i=0; i<=nfeatures-1; i++)
    {
        for(j=0; j<=nfeatures-1; j++)
        {
            sw->ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(k=0; k<=npoints-1; k++)
    {
        ae_v_move(&tf.ptr.p_double[0], 1, &xy->ptr.pp_double[k][0], 1, ae_v_len(0,nfeatures-1));
        ae_v_sub(&tf.ptr.p_double[0], 1, &muc.ptr.pp_double[c.ptr.p_int[k]][0], 1, ae_v_len(0,nfeatures-1));
        for(i=0; i<=nfeatures-1; i++)
        {
            v = tf.ptr.p_double[i];
            ae_v_addd(&sw->ptr.pp_double[i][0], 1, &tf.ptr.p_double[0], 1, ae_v_len(0,nfeatures-1), v);
        }
    }
    ae_frame_leave(_state);
}



static void testssaunit_testgeneral(ae_bool* errorflag, ae_state *_state);
static void testssaunit_testspecial(ae_bool* errorflag, ae_state *_state);





ae_bool testssa(ae_bool silent, ae_state *_state)
{
    ae_bool specerrors;
    ae_bool generrors;
    ae_bool wereerrors;
    ae_bool result;


    specerrors = ae_false;
    generrors = ae_false;
    testssaunit_testspecial(&specerrors, _state);
    testssaunit_testgeneral(&generrors, _state);
    
    /*
     * Final report
     */
    wereerrors = specerrors||generrors;
    if( !silent )
    {
        printf("SSA TEST\n");
        printf("* GENERAL TEST SUITE                     ");
        if( !generrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* SPECIAL CASES                          ");
        if( !specerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( wereerrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !wereerrors;
    return result;
}


/*************************************************************************
This function tests SSA on several general purpose analysis/prediction
problems.

On failure sets ErrorFlag, on success it is untouched.
*************************************************************************/
static void testssaunit_testgeneral(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double vv;
    ae_int_t pass;
    ssamodel state;
    ssamodel state2;
    ae_int_t ntracks;
    ae_int_t windowwidth;
    ae_int_t nlasttracklen;
    ae_int_t nzeros;
    ae_int_t nlinear;
    double sinefreq;
    double sineoffs;
    double sineamp;
    ae_int_t windowwidth2;
    ae_int_t nbasis;
    ae_int_t nbasis2;
    ae_int_t ninitial;
    ae_vector x;
    ae_vector x2;
    ae_vector trend;
    ae_vector noise;
    ae_vector sv;
    ae_vector sv2;
    ae_vector tmp0;
    ae_vector trend2;
    ae_vector noise2;
    ae_int_t algotype;
    ae_int_t nticks;
    ae_int_t nnoise;
    ae_int_t navg;
    ae_matrix a;
    ae_matrix a2;
    ae_matrix b;
    double tol;
    ae_int_t datalen;
    ae_int_t forecastlen;
    ae_int_t mlimit;
    hqrndstate rs;
    ae_int_t passcount;
    double skipprob;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&state2, 0, sizeof(state2));
    memset(&x, 0, sizeof(x));
    memset(&x2, 0, sizeof(x2));
    memset(&trend, 0, sizeof(trend));
    memset(&noise, 0, sizeof(noise));
    memset(&sv, 0, sizeof(sv));
    memset(&sv2, 0, sizeof(sv2));
    memset(&tmp0, 0, sizeof(tmp0));
    memset(&trend2, 0, sizeof(trend2));
    memset(&noise2, 0, sizeof(noise2));
    memset(&a, 0, sizeof(a));
    memset(&a2, 0, sizeof(a2));
    memset(&b, 0, sizeof(b));
    memset(&rs, 0, sizeof(rs));
    _ssamodel_init(&state, _state, ae_true);
    _ssamodel_init(&state2, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&trend, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&noise, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sv2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&trend2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&noise2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    
    /*
     * Initialize RNG, test pass count and skip probability.
     *
     * When we perform several sequential tests on the same model, we may
     * skip some of them with probability SkipProb in order to make sure
     * that no carry-over effect is observed between tests.
     */
    hqrndrandomize(&rs, _state);
    passcount = 500;
    skipprob = 0.50;
    
    /*
     * Iterate over several algorithm types.
     * Algorithms unsupported by tests are skipped within tests.
     */
    for(algotype=1; algotype<=3; algotype++)
    {
        
        /*
         * Test that on perfectly constant dataset SSA correctly predicts
         * perfectly constant trend. Additionally test that analysis phase
         * correctly returns nearly-zero noise and nearly-constant trend.
         *
         * Dataset is a one/few sequences with different constants; top-1
         * algorithm is used (or precomputed unit-normalized vector of 1's).
         */
        for(pass=1; pass<=passcount; pass++)
        {
            nlasttracklen = -9999999;
            ntracks = 1+hqrnduniformi(&rs, 3, _state);
            windowwidth = 2+hqrnduniformi(&rs, 3, _state);
            ssacreate(&state, _state);
            ssasetwindow(&state, windowwidth, _state);
            for(k=0; k<=ntracks-1; k++)
            {
                nlasttracklen = windowwidth+hqrnduniformi(&rs, windowwidth, _state);
                v = hqrndnormal(&rs, _state);
                ae_vector_set_length(&x, nlasttracklen, _state);
                for(i=0; i<=nlasttracklen-1; i++)
                {
                    x.ptr.p_double[i] = v;
                }
                ssaaddsequence(&state, &x, nlasttracklen, _state);
            }
            if( algotype==1 )
            {
                ae_matrix_set_length(&b, windowwidth, 1, _state);
                for(i=0; i<=windowwidth-1; i++)
                {
                    b.ptr.pp_double[i][0] = 1/ae_sqrt((double)(windowwidth), _state);
                }
                ssasetalgoprecomputed(&state, &b, windowwidth, 1, _state);
            }
            else
            {
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state, 1, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state, 1, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            tol = 1.0E-6;
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                ssaanalyzelastwindow(&state, &trend, &noise, &nticks, _state);
                ae_set_error_flag(errorflag, nticks!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:143");
                ae_set_error_flag(errorflag, noise.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:144");
                ae_set_error_flag(errorflag, trend.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:145");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=windowwidth-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[nlasttracklen+i-windowwidth], _state),tol), __FILE__, __LINE__, "testssaunit.ap:150");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:151");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                ssaanalyzelast(&state, nlasttracklen, &trend, &noise, _state);
                ae_set_error_flag(errorflag, noise.cnt!=nlasttracklen, __FILE__, __LINE__, "testssaunit.ap:157");
                ae_set_error_flag(errorflag, trend.cnt!=nlasttracklen, __FILE__, __LINE__, "testssaunit.ap:158");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=nlasttracklen-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:163");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:164");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 2*windowwidth, _state);
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastlast(&state, nticks, &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:172");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=trend.cnt-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[nlasttracklen-1], _state),tol), __FILE__, __LINE__, "testssaunit.ap:176");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 2*windowwidth, _state);
                navg = 1+hqrnduniformi(&rs, windowwidth+3, _state);
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastavglast(&state, navg, nticks, &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:184");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=trend.cnt-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[nlasttracklen-1], _state),tol), __FILE__, __LINE__, "testssaunit.ap:188");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                datalen = windowwidth+hqrnduniformi(&rs, 10, _state);
                forecastlen = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&x, datalen, _state);
                x.ptr.p_double[0] = hqrndnormal(&rs, _state);
                for(i=1; i<=datalen-1; i++)
                {
                    x.ptr.p_double[i] = x.ptr.p_double[i-1];
                }
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastsequence(&state, &x, datalen, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:200");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=forecastlen-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[datalen-1], _state),tol), __FILE__, __LINE__, "testssaunit.ap:204");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                datalen = windowwidth+hqrnduniformi(&rs, 10, _state);
                navg = 1+hqrnduniformi(&rs, windowwidth+3, _state);
                forecastlen = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&x, datalen, _state);
                x.ptr.p_double[0] = hqrndnormal(&rs, _state);
                for(i=1; i<=datalen-1; i++)
                {
                    x.ptr.p_double[i] = x.ptr.p_double[i-1];
                }
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastavgsequence(&state, &x, datalen, navg, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:217");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=forecastlen-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[datalen-1], _state),tol), __FILE__, __LINE__, "testssaunit.ap:221");
                }
            }
        }
        
        /*
         * Test that on specially designed linear dataset SSA correctly
         * predicts perfectly linear trend. Additionally test that analysis
         * phase correctly returns nearly-zero noise and nearly-linear trend.
         * Also test that correct basis vectors are returned.
         *
         * Dataset consists of many WindowWidth-sized (exactly) sequences with
         * linear trend. Trend coefficients vary accross sequences. Top-2
         * algorithm is used (or precomputed unit-normalized linear trend basis).
         *
         * NOTE: this test requires NTracks>=2, WindowWidth>=3 and TopK=2 to work;
         *       however, in order to improve numerical properties (diversity among
         *       samples, different slopes and offsets) we set NTracks to be
         *       at least 5.
         *
         * NOTE: one more version of this test verifies scaling properties
         *       by solving larger task (WindowWidth=100, NTracks=5,
         *       TrackLen=2*WindowWidth). This version makes just 5 checks because
         *       of higher cost.
         */
        for(pass=1; pass<=passcount; pass++)
        {
            ntracks = 5+hqrnduniformi(&rs, 10, _state);
            windowwidth = 3+hqrnduniformi(&rs, 3, _state);
            ssacreate(&state, _state);
            ssasetwindow(&state, windowwidth, _state);
            ae_vector_set_length(&x, windowwidth, _state);
            for(k=0; k<=ntracks-1; k++)
            {
                v = hqrndnormal(&rs, _state);
                x.ptr.p_double[0] = hqrndnormal(&rs, _state);
                for(i=1; i<=windowwidth-1; i++)
                {
                    x.ptr.p_double[i] = x.ptr.p_double[i-1]+v;
                }
                ssaaddsequence(&state, &x, windowwidth, _state);
            }
            if( algotype==1 )
            {
                ae_matrix_set_length(&b, windowwidth, 2, _state);
                v = 0.0;
                for(i=0; i<=windowwidth-1; i++)
                {
                    b.ptr.pp_double[i][0] = 1/ae_sqrt((double)(windowwidth), _state);
                    b.ptr.pp_double[i][1] = (double)(i);
                    v = v+(double)i/(double)windowwidth;
                }
                vv = 0.0;
                for(i=0; i<=windowwidth-1; i++)
                {
                    b.ptr.pp_double[i][1] = b.ptr.pp_double[i][1]-v;
                    vv = vv+ae_sqr(b.ptr.pp_double[i][1], _state);
                }
                for(i=0; i<=windowwidth-1; i++)
                {
                    b.ptr.pp_double[i][1] = b.ptr.pp_double[i][1]/ae_sqrt(vv, _state);
                }
                ssasetalgoprecomputed(&state, &b, windowwidth, 2, _state);
            }
            else
            {
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state, 2, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state, 2, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            tol = 1.0E-6;
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                
                /*
                 * Basis vectors must be linear/constant functions
                 */
                ssagetbasis(&state, &a, &sv, &windowwidth2, &nbasis, _state);
                ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:293");
                ae_set_error_flag(errorflag, nbasis!=2, __FILE__, __LINE__, "testssaunit.ap:294");
                ae_set_error_flag(errorflag, a.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:295");
                ae_set_error_flag(errorflag, a.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:296");
                ae_set_error_flag(errorflag, sv.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:297");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(j=0; j<=nbasis-1; j++)
                {
                    v = a.ptr.pp_double[1][j]-a.ptr.pp_double[0][j];
                    for(i=2; i<=windowwidth-1; i++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-a.ptr.pp_double[i-1][j]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:304");
                    }
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                ssaanalyzelastwindow(&state, &trend, &noise, &nticks, _state);
                ae_set_error_flag(errorflag, nticks!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:310");
                ae_set_error_flag(errorflag, noise.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:311");
                ae_set_error_flag(errorflag, trend.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:312");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=windowwidth-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:317");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:318");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                ssaanalyzelast(&state, windowwidth, &trend, &noise, _state);
                ae_set_error_flag(errorflag, noise.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:324");
                ae_set_error_flag(errorflag, trend.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:325");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=windowwidth-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:330");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:331");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 2*windowwidth, _state);
                ssaforecastlast(&state, nticks, &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:338");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                v = x.ptr.p_double[windowwidth-1]-x.ptr.p_double[windowwidth-2];
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[0]-x.ptr.p_double[windowwidth-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:342");
                for(i=1; i<=nticks-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend.ptr.p_double[i-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:344");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 2*windowwidth, _state);
                navg = 1+hqrnduniformi(&rs, windowwidth+5, _state);
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastavglast(&state, navg, nticks, &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:352");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                v = x.ptr.p_double[windowwidth-1]-x.ptr.p_double[windowwidth-2];
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[0]-x.ptr.p_double[windowwidth-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:356");
                for(i=1; i<=nticks-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend.ptr.p_double[i-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:358");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                datalen = windowwidth+hqrnduniformi(&rs, 10, _state);
                forecastlen = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&x, datalen, _state);
                v = hqrndnormal(&rs, _state);
                x.ptr.p_double[0] = hqrndnormal(&rs, _state);
                for(i=1; i<=datalen-1; i++)
                {
                    x.ptr.p_double[i] = x.ptr.p_double[i-1]+v;
                }
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastsequence(&state, &x, datalen, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:371");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[0]-x.ptr.p_double[datalen-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:374");
                for(i=1; i<=forecastlen-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend.ptr.p_double[i-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:376");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                datalen = windowwidth+hqrnduniformi(&rs, 10, _state);
                navg = 1+hqrnduniformi(&rs, windowwidth+5, _state);
                forecastlen = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&x, datalen, _state);
                v = hqrndnormal(&rs, _state);
                x.ptr.p_double[0] = hqrndnormal(&rs, _state);
                for(i=1; i<=datalen-1; i++)
                {
                    x.ptr.p_double[i] = x.ptr.p_double[i-1]+v;
                }
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastavgsequence(&state, &x, datalen, navg, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:390");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[0]-x.ptr.p_double[datalen-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:393");
                for(i=1; i<=forecastlen-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend.ptr.p_double[i-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:395");
                }
            }
        }
        for(pass=1; pass<=5; pass++)
        {
            ntracks = 5+hqrnduniformi(&rs, 10, _state);
            windowwidth = 100;
            nlasttracklen = 2*windowwidth;
            ssacreate(&state, _state);
            ssasetwindow(&state, windowwidth, _state);
            ae_vector_set_length(&x, nlasttracklen, _state);
            for(k=0; k<=ntracks-1; k++)
            {
                v = hqrndnormal(&rs, _state);
                x.ptr.p_double[0] = hqrndnormal(&rs, _state);
                for(i=1; i<=nlasttracklen-1; i++)
                {
                    x.ptr.p_double[i] = x.ptr.p_double[i-1]+v;
                }
                ssaaddsequence(&state, &x, nlasttracklen, _state);
            }
            if( algotype==1 )
            {
                ae_matrix_set_length(&b, windowwidth, 2, _state);
                v = 0.0;
                for(i=0; i<=windowwidth-1; i++)
                {
                    b.ptr.pp_double[i][0] = 1/ae_sqrt((double)(windowwidth), _state);
                    b.ptr.pp_double[i][1] = (double)(i);
                    v = v+(double)i/(double)windowwidth;
                }
                vv = 0.0;
                for(i=0; i<=windowwidth-1; i++)
                {
                    b.ptr.pp_double[i][1] = b.ptr.pp_double[i][1]-v;
                    vv = vv+ae_sqr(b.ptr.pp_double[i][1], _state);
                }
                for(i=0; i<=windowwidth-1; i++)
                {
                    b.ptr.pp_double[i][1] = b.ptr.pp_double[i][1]/ae_sqrt(vv, _state);
                }
                ssasetalgoprecomputed(&state, &b, windowwidth, 2, _state);
            }
            else
            {
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state, 2, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state, 2, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            tol = 1.0E-6;
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                ssaanalyzelastwindow(&state, &trend, &noise, &nticks, _state);
                ae_set_error_flag(errorflag, nticks!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:444");
                ae_set_error_flag(errorflag, noise.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:445");
                ae_set_error_flag(errorflag, trend.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:446");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=windowwidth-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[nlasttracklen+i-windowwidth], _state),tol), __FILE__, __LINE__, "testssaunit.ap:451");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:452");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                ssaanalyzelast(&state, nlasttracklen, &trend, &noise, _state);
                ae_set_error_flag(errorflag, noise.cnt!=nlasttracklen, __FILE__, __LINE__, "testssaunit.ap:458");
                ae_set_error_flag(errorflag, trend.cnt!=nlasttracklen, __FILE__, __LINE__, "testssaunit.ap:459");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=nlasttracklen-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:464");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:465");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 10, _state);
                ssaforecastlast(&state, nticks, &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:472");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                v = x.ptr.p_double[nlasttracklen-1]-x.ptr.p_double[nlasttracklen-2];
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[0]-x.ptr.p_double[nlasttracklen-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:476");
                for(i=1; i<=nticks-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend.ptr.p_double[i-1]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:478");
                }
            }
        }
        
        /*
         * Test that on specially designed dataset with two sinusoidal components
         * with significantly different amplitudes, whose periods are integer
         * divisors of window width, SSA correctly separates leading sine from
         * its smaller counterpart. Also test that we can correctly predict future
         * values of the sequence.
         *
         * Dataset consists of many sequences, each of them featuring either
         * sine with period=WindowWidth or sine with period=WindowWidth/2. Such
         * dataset is necessary because sum of two sines is not well separated by
         * SVD (it performs only approximate, asymptotic separation). But when
         * every sequence is either one of the sines, but not two together, we
         * can easily separate them.
         *
         * Sine coefficients are changed from sequence to sequence.
         *
         * NOTE: this test requires large WindowWidth and TopK=2 to work.
         *
         * NOTE: this test uses reduced number of passes because of higher computational complexity
         */
        for(pass=1; pass<=10; pass++)
        {
            
            /*
             * Skip "precomputed basis" algorithm 
             */
            if( algotype==1 )
            {
                continue;
            }
            
            /*
             * Generate dataset
             */
            ntracks = 100+hqrnduniformi(&rs, 2, _state);
            windowwidth = 64;
            nlasttracklen = windowwidth+hqrnduniformi(&rs, 10, _state);
            ssacreate(&state, _state);
            ae_vector_set_length(&x, nlasttracklen, _state);
            for(k=0; k<=ntracks-1; k++)
            {
                sineoffs = (double)(hqrnduniformi(&rs, windowwidth, _state));
                if( k%2==0 )
                {
                    sineamp = 1+hqrnduniformr(&rs, _state);
                    sinefreq = (double)(1);
                }
                else
                {
                    sineamp = 0.1*(1+hqrnduniformr(&rs, _state));
                    sinefreq = (double)(2);
                }
                for(i=0; i<=nlasttracklen-1; i++)
                {
                    x.ptr.p_double[i] = sineamp*ae_sin((i+sineoffs)/windowwidth*2*ae_pi*sinefreq, _state);
                }
                ssaaddsequence(&state, &x, nlasttracklen, _state);
            }
            if( algotype==2 )
            {
                ssasetalgotopkdirect(&state, 2, _state);
            }
            else
            {
                if( algotype==3 )
                {
                    ssasetalgotopkrealtime(&state, 2, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            tol = 1.0E-6;
            
            /*
             * Test analysis with WindowWidth=SinePeriod:
             * * analyze sine with frequency=1, it must be recognized as trend
             * * analyze sine with frequency=2, with smoothing enabled
             *   it must be discarded as noise;
             */
            ssasetwindow(&state, windowwidth, _state);
            nticks = windowwidth+1+hqrnduniformi(&rs, windowwidth, _state);
            sineoffs = (double)(hqrnduniformi(&rs, windowwidth, _state));
            sineamp = 1+hqrnduniformr(&rs, _state);
            sinefreq = (double)(1);
            ae_vector_set_length(&x, nticks, _state);
            for(i=0; i<=nticks-1; i++)
            {
                x.ptr.p_double[i] = sineamp*ae_sin((i+sineoffs)/windowwidth*2*ae_pi*sinefreq, _state);
            }
            ssaanalyzesequence(&state, &x, nticks, &trend, &noise, _state);
            ae_set_error_flag(errorflag, noise.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:558");
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:559");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                v = sineamp*ae_sin((i+sineoffs)/windowwidth*2*ae_pi*sinefreq, _state);
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:565");
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:566");
            }
            forecastlen = 1+hqrnduniformi(&rs, nticks-windowwidth, _state);
            datalen = nticks-forecastlen;
            ae_vector_set_length(&x2, datalen, _state);
            for(i=0; i<=datalen-1; i++)
            {
                x2.ptr.p_double[i] = x.ptr.p_double[i];
            }
            ssaforecastsequence(&state, &x2, datalen, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
            for(i=0; i<=forecastlen-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[datalen+i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:575");
            }
            ae_vector_set_length(&tmp0, 0, _state);
            windowwidth2 = -1;
            ssagetlrr(&state, &tmp0, &windowwidth2, _state);
            ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:579");
            ae_set_error_flag(errorflag, tmp0.cnt!=windowwidth-1, __FILE__, __LINE__, "testssaunit.ap:580");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=windowwidth-1; i<=nticks-1; i++)
            {
                v = ae_v_dotproduct(&x.ptr.p_double[i-(windowwidth-1)], 1, &tmp0.ptr.p_double[0], 1, ae_v_len(i-(windowwidth-1),i-1));
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(v-x.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:586");
            }
            nticks = windowwidth+1+hqrnduniformi(&rs, windowwidth, _state);
            sineoffs = (double)(hqrnduniformi(&rs, windowwidth, _state));
            sineamp = 1+hqrnduniformr(&rs, _state);
            sinefreq = (double)(2);
            ae_vector_set_length(&x, nticks, _state);
            for(i=0; i<=nticks-1; i++)
            {
                x.ptr.p_double[i] = sineamp*ae_sin((i+sineoffs)/windowwidth*2*ae_pi*sinefreq, _state);
            }
            ssaanalyzesequence(&state, &x, nticks, &trend, &noise, _state);
            ae_set_error_flag(errorflag, noise.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:597");
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:598");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                v = sineamp*ae_sin((i+sineoffs)/windowwidth*2*ae_pi*sinefreq, _state);
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:604");
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i]-v, _state),tol), __FILE__, __LINE__, "testssaunit.ap:605");
            }
            forecastlen = 1+hqrnduniformi(&rs, nticks-windowwidth, _state);
            datalen = nticks-forecastlen;
            ae_vector_set_length(&x2, datalen, _state);
            for(i=0; i<=datalen-1; i++)
            {
                x2.ptr.p_double[i] = x.ptr.p_double[i];
            }
            ssaforecastsequence(&state, &x2, datalen, forecastlen, ae_true, &trend, _state);
            for(i=0; i<=forecastlen-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:614");
            }
        }
        
        /*
         * Test appendPoint() functionality.
         *
         * We have specially designed dataset:
         * * NZeros ticks of exactly zero values
         * * NLinear ticks of series which linearly grow from 0 to 1
         * * NLinear ticks of series which linearly decrease from 1 to 0
         * * NZeros ticks of exactly zero values
         * * NZeros=100
         * * NLinear=20
         *
         * SSA settings have following values:
         * * WindowWidth=4 or 25 (large widths are more problematic for
         *   iterative solvers, so they help to debug incremental updates)
         * * NBasis=2
         *
         * We choose number of initial values to start with NInitial
         * in [1..2*WindowWidth], then add points one
         * by one with SSAAppendPointAndUpdate(). In the end we compare
         * results returned by SSAGetBasis() with that returned by model
         * which was created from full dataset.
         *
         * NOTE: we perform limited amount of passes because this test
         *       has high cost.
         */
        for(pass=1; pass<=25; pass++)
        {
            
            /*
             * Generate dataset
             */
            tol = 1.0E-6;
            nzeros = 100;
            nlinear = 50;
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                windowwidth = 25;
            }
            else
            {
                windowwidth = 4;
            }
            nbasis = 2;
            ninitial = 1+hqrnduniformi(&rs, 2*windowwidth, _state);
            ae_vector_set_length(&x, 2*nzeros+2*nlinear, _state);
            for(i=0; i<=x.cnt-1; i++)
            {
                x.ptr.p_double[i] = 0.0;
            }
            for(i=0; i<=nlinear-1; i++)
            {
                x.ptr.p_double[nzeros+i] = (double)i/(double)nlinear;
                x.ptr.p_double[nzeros+nlinear+i] = 1-(double)i/(double)nlinear;
            }
            rmatrixrndorthogonal(windowwidth, &b, _state);
            
            /*
             * Build model using many sequential appends
             *
             * NOTE: for NInitial>=WindowWidth with probability 50%
             *       we enforce basis calculation before first
             *       append() call. It helps to debug different
             *       branches of algorithms.
             *
             * NOTE: we may also request delayed power-up of the
             *       algorithm. It also checks various branches of
             *       the algo, although with such settings delayed
             *       power-up is hard to check (initial dataset is
             *       just zeros, zeros, zeros..).
             */
            ae_vector_set_length(&x2, ninitial, _state);
            for(i=0; i<=x2.cnt-1; i++)
            {
                x2.ptr.p_double[i] = x.ptr.p_double[i];
            }
            ssacreate(&state, _state);
            ssasetwindow(&state, windowwidth, _state);
            ssaaddsequence(&state, &x2, x2.cnt, _state);
            if( algotype==1 )
            {
                ssasetalgoprecomputed(&state, &b, windowwidth, nbasis, _state);
            }
            else
            {
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state, nbasis, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state, nbasis, _state);
                        if( ae_fp_greater(hqrnduniformr(&rs, _state),0.5) )
                        {
                            ssasetpoweruplength(&state, 10, _state);
                        }
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            if( ninitial>=windowwidth&&ae_fp_greater(hqrnduniformr(&rs, _state),0.5) )
            {
                ae_matrix_set_length(&a, 0, 0, _state);
                ae_vector_set_length(&sv, 0, _state);
                windowwidth2 = -1;
                nbasis2 = -1;
                ssagetbasis(&state, &a, &sv, &windowwidth2, &nbasis2, _state);
                ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:705");
                ae_set_error_flag(errorflag, nbasis2!=nbasis, __FILE__, __LINE__, "testssaunit.ap:706");
                ae_set_error_flag(errorflag, a.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:707");
                ae_set_error_flag(errorflag, a.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:708");
                ae_set_error_flag(errorflag, sv.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:709");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
            }
            for(i=x2.cnt; i<=x.cnt-1; i++)
            {
                ssaappendpointandupdate(&state, x.ptr.p_double[i], 1.0, _state);
            }
            ae_matrix_set_length(&a, 0, 0, _state);
            ae_vector_set_length(&sv, 0, _state);
            windowwidth2 = -1;
            nbasis2 = -1;
            ssagetbasis(&state, &a, &sv, &windowwidth2, &nbasis2, _state);
            ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:720");
            ae_set_error_flag(errorflag, nbasis2!=nbasis, __FILE__, __LINE__, "testssaunit.ap:721");
            ae_set_error_flag(errorflag, a.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:722");
            ae_set_error_flag(errorflag, a.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:723");
            ae_set_error_flag(errorflag, sv.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:724");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Build model using one big sequence
             */
            ssacreate(&state2, _state);
            ssasetwindow(&state2, windowwidth, _state);
            ssaaddsequence(&state2, &x, x.cnt, _state);
            if( algotype==1 )
            {
                ssasetalgoprecomputed(&state2, &b, windowwidth, nbasis, _state);
            }
            else
            {
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state2, nbasis, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state2, nbasis, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            ae_matrix_set_length(&a2, 0, 0, _state);
            ae_vector_set_length(&sv2, 0, _state);
            windowwidth2 = -1;
            nbasis2 = -1;
            ssagetbasis(&state2, &a2, &sv2, &windowwidth2, &nbasis2, _state);
            ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:747");
            ae_set_error_flag(errorflag, nbasis2!=nbasis, __FILE__, __LINE__, "testssaunit.ap:748");
            ae_set_error_flag(errorflag, a2.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:749");
            ae_set_error_flag(errorflag, a2.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:750");
            ae_set_error_flag(errorflag, sv2.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:751");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Compare results
             */
            for(i=0; i<=nbasis-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(sv.ptr.p_double[i]-sv2.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:759");
            }
            for(j=0; j<=nbasis-1; j++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[0][j], a.stride, &a2.ptr.pp_double[0][j], a2.stride, ae_v_len(0,windowwidth-1));
                for(i=0; i<=windowwidth-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-ae_sign(v, _state)*a2.ptr.pp_double[i][j], _state),tol), __FILE__, __LINE__, "testssaunit.ap:764");
                }
            }
        }
        
        /*
         * Test appendSequence() functionality.
         *
         * We have specially designed dataset:
         * * NNoise sequences of Gaussian noise
         * * NLinear linear sequences partially corrupted by noise
         * * NZeros exactly zero sequences (used to let incremental algo converge)
         * * all sequences have random size in [WindowWidth-2,WindowWidth+2]
         * * NNoise=20
         * * NLinear=20
         *
         * SSA settings have following values:
         * * WindowWidth=4 or 25 (large widths are more problematic for
         *   iterative solvers, so they help to debug incremental updates)
         * * NBasis=2
         *
         * We have two solvers:
         * * one is trained on complete dataset
         * * another one starts from NNoise noisy sequences, linear/zero sequenes
         *   are incrementally appended with AppendSequence
         *
         * NOTE: we perform limited amount of passes because this test
         *       has high cost.
         */
        for(pass=1; pass<=25; pass++)
        {
            
            /*
             * Problem metrics
             */
            tol = 1.0E-5;
            nnoise = 20;
            nlinear = 20;
            nzeros = 50;
            if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
            {
                windowwidth = 25;
            }
            else
            {
                windowwidth = 4;
            }
            nbasis = 2;
            
            /*
             * Initialize solvers
             *
             * NOTE: we set State.DefaultSubspaceIts to large value in order
             *       to ensure convergence to same basis.
             */
            rmatrixrndorthogonal(windowwidth, &b, _state);
            ssacreate(&state, _state);
            ssacreate(&state2, _state);
            state.defaultsubspaceits = 50;
            ssasetwindow(&state, windowwidth, _state);
            ssasetwindow(&state2, windowwidth, _state);
            if( algotype==1 )
            {
                ssasetalgoprecomputed(&state, &b, windowwidth, nbasis, _state);
                ssasetalgoprecomputed(&state2, &b, windowwidth, nbasis, _state);
            }
            else
            {
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state, nbasis, _state);
                    ssasetalgotopkdirect(&state2, nbasis, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state, nbasis, _state);
                        ssasetalgotopkrealtime(&state2, nbasis, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            
            /*
             * Feed noisy sequences
             */
            for(i=0; i<=nnoise-1; i++)
            {
                k = windowwidth+(hqrnduniformi(&rs, 5, _state)-2);
                ae_vector_set_length(&x, k, _state);
                for(j=0; j<=k-1; j++)
                {
                    x.ptr.p_double[j] = hqrndnormal(&rs, _state);
                }
                ssaaddsequence(&state, &x, x.cnt, _state);
                ssaaddsequence(&state2, &x, x.cnt, _state);
            }
            
            /*
             * Feed linear and zero sequences.
             *
             * NOTE: with probability 50% we call SSAGetBasis(State2).
             *       Ideally, SSA should be able to handle appends correctly
             *       with or without preceeding call which requires basis
             *       to be evaluated.
             */
            if( ae_fp_greater(hqrnduniformr(&rs, _state),0.5) )
            {
                ae_matrix_set_length(&a2, 0, 0, _state);
                ae_vector_set_length(&sv2, 0, _state);
                windowwidth2 = -1;
                nbasis2 = -1;
                ssagetbasis(&state2, &a2, &sv2, &windowwidth2, &nbasis2, _state);
                ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:865");
                ae_set_error_flag(errorflag, nbasis2!=nbasis, __FILE__, __LINE__, "testssaunit.ap:866");
                ae_set_error_flag(errorflag, a2.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:867");
                ae_set_error_flag(errorflag, a2.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:868");
                ae_set_error_flag(errorflag, sv2.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:869");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
            }
            for(i=0; i<=nlinear-1; i++)
            {
                k = windowwidth+(hqrnduniformi(&rs, 5, _state)-2);
                v = hqrndnormal(&rs, _state);
                vv = hqrndnormal(&rs, _state);
                ae_vector_set_length(&x, k, _state);
                x.ptr.p_double[0] = v;
                for(j=1; j<=k-1; j++)
                {
                    x.ptr.p_double[j] = x.ptr.p_double[j-1]+vv+0.1*hqrndnormal(&rs, _state);
                }
                ssaaddsequence(&state, &x, x.cnt, _state);
                ssaappendsequenceandupdate(&state2, &x, x.cnt, 1.0, _state);
            }
            for(i=0; i<=nzeros-1; i++)
            {
                k = windowwidth+(hqrnduniformi(&rs, 5, _state)-2);
                ae_vector_set_length(&x, k, _state);
                for(j=0; j<=k-1; j++)
                {
                    x.ptr.p_double[j] = (double)(0);
                }
                ssaaddsequence(&state, &x, x.cnt, _state);
                ssaappendsequenceandupdate(&state2, &x, x.cnt, 1.0, _state);
            }
            
            /*
             * Compare results
             */
            ae_matrix_set_length(&a, 0, 0, _state);
            ae_vector_set_length(&sv, 0, _state);
            windowwidth2 = -1;
            nbasis2 = -1;
            ssagetbasis(&state, &a, &sv, &windowwidth2, &nbasis2, _state);
            ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:903");
            ae_set_error_flag(errorflag, nbasis2!=nbasis, __FILE__, __LINE__, "testssaunit.ap:904");
            ae_set_error_flag(errorflag, a.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:905");
            ae_set_error_flag(errorflag, a.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:906");
            ae_set_error_flag(errorflag, sv.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:907");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            ae_matrix_set_length(&a2, 0, 0, _state);
            ae_vector_set_length(&sv2, 0, _state);
            windowwidth2 = -1;
            nbasis2 = -1;
            ssagetbasis(&state2, &a2, &sv2, &windowwidth2, &nbasis2, _state);
            ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:915");
            ae_set_error_flag(errorflag, nbasis2!=nbasis, __FILE__, __LINE__, "testssaunit.ap:916");
            ae_set_error_flag(errorflag, a2.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:917");
            ae_set_error_flag(errorflag, a2.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:918");
            ae_set_error_flag(errorflag, sv2.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:919");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nbasis-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(sv.ptr.p_double[i]-sv2.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:923");
            }
            for(j=0; j<=nbasis-1; j++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[0][j], a.stride, &a2.ptr.pp_double[0][j], a2.stride, ae_v_len(0,windowwidth-1));
                for(i=0; i<=windowwidth-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-ae_sign(v, _state)*a2.ptr.pp_double[i][j], _state),tol), __FILE__, __LINE__, "testssaunit.ap:928");
                }
            }
        }
        
        /*
         * Test memory limit functionality.
         *
         * Compare results obtained with HUGE limit vs ones obtained with small limit.
         */
        for(windowwidth=1; windowwidth<=20; windowwidth++)
        {
            for(mlimit=-1; mlimit<=16; mlimit++)
            {
                
                /*
                 * Not tested
                 */
                if( algotype==1 )
                {
                    continue;
                }
                
                /*
                 * Problem metrics
                 */
                tol = 1.0E-5;
                nticks = 1000+hqrnduniformi(&rs, 1000, _state);
                if( windowwidth>5 )
                {
                    nbasis = 1+hqrnduniformi(&rs, 5, _state);
                }
                else
                {
                    nbasis = 1;
                }
                
                /*
                 * Create solvers, dataset, set limits
                 */
                ssacreate(&state, _state);
                ssacreate(&state2, _state);
                if( mlimit>=0 )
                {
                    ssasetmemorylimit(&state, ae_round(ae_pow((double)(2), (double)(mlimit), _state), _state), _state);
                }
                else
                {
                    ssasetmemorylimit(&state, 0, _state);
                }
                ssasetmemorylimit(&state2, 999999999, _state);
                ssasetwindow(&state, windowwidth, _state);
                ssasetwindow(&state2, windowwidth, _state);
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state, nbasis, _state);
                    ssasetalgotopkdirect(&state2, nbasis, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state, nbasis, _state);
                        ssasetalgotopkrealtime(&state2, nbasis, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
                ae_vector_set_length(&x, nticks, _state);
                for(i=0; i<=nticks-1; i++)
                {
                    x.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                ssaaddsequence(&state, &x, x.cnt, _state);
                ssaaddsequence(&state2, &x, x.cnt, _state);
                
                /*
                 * Reset internal temporaries for this test.
                 * Implementation-dependent, may fail due to future changes in the core.
                 */
                ae_matrix_set_length(&state.uxbatch, 0, 0, _state);
                ae_matrix_set_length(&state.aseqtrajectory, 0, 0, _state);
                ae_matrix_set_length(&state.aseqtbproduct, 0, 0, _state);
                
                /*
                 * Test
                 */
                ssaanalyzelast(&state, nticks, &trend, &noise, _state);
                ssaanalyzelast(&state2, nticks, &trend2, &noise2, _state);
                for(i=0; i<=nticks-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend2.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:1001");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i]-noise2.ptr.p_double[i], _state),tol), __FILE__, __LINE__, "testssaunit.ap:1002");
                }
                
                /*
                 * Additional tests for sizes of internal arrays.
                 * Implementation-dependent, may fail due to future changes in the core.
                 */
                if( mlimit>=0 )
                {
                    k = ae_maxint(4*windowwidth*windowwidth, ae_round(ae_pow((double)(2), (double)(mlimit), _state), _state), _state);
                    ae_set_error_flag(errorflag, state.uxbatch.cols*state.uxbatch.rows>k, __FILE__, __LINE__, "testssaunit.ap:1012");
                    ae_set_error_flag(errorflag, state.aseqtrajectory.cols*state.aseqtrajectory.rows>k, __FILE__, __LINE__, "testssaunit.ap:1013");
                    ae_set_error_flag(errorflag, state.aseqtbproduct.cols*state.aseqtbproduct.rows>k, __FILE__, __LINE__, "testssaunit.ap:1014");
                }
                else
                {
                    ae_set_error_flag(errorflag, state.uxbatch.rows!=nticks-windowwidth+1, __FILE__, __LINE__, "testssaunit.ap:1018");
                    ae_set_error_flag(errorflag, state.aseqtrajectory.rows!=nticks-windowwidth+1, __FILE__, __LINE__, "testssaunit.ap:1019");
                    ae_set_error_flag(errorflag, state.aseqtbproduct.rows!=nticks-windowwidth+1, __FILE__, __LINE__, "testssaunit.ap:1020");
                }
            }
        }
    }
    
    /*
     * Test power-up ability of the real-time algorithm.
     *
     * We have specially designed dataset:
     * * NTicks ticks of linearly descending from 1 to 0 linear trend,
     *   corrupted by random Gaussian noise
     * * NZeros ticks of exactly zero values
     * * NTicks=100
     * * NZeros=100
     *
     * SSA settings have following values:
     * * WindowWidth=4 or 25 (large widths are more problematic for
     *   iterative solvers, so they help to debug incremental updates)
     * * NBasis=1..2 (only top vectors converge stable enough for unit testing)
     * * powerup length is 10
     *
     * We perform two SSAs:
     * * one with full dataset and no powerup
     * * one with powerup, NTicks+WindowWidth first elements, followed by appending of NZeros zeros
     *
     * We check that:
     * * basis found by second model (right after initialization) is different
     *   from basis of the first one
     * * basis found by second model after last append is same as the first one
     *
     * NOTE: we perform limited amount of passes because this test
     *       has high cost.
     */
    for(pass=1; pass<=25; pass++)
    {
        
        /*
         * Generate dataset
         */
        tol = 1.0E-3;
        nticks = 100;
        nzeros = 50;
        if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
        {
            windowwidth = 25;
        }
        else
        {
            windowwidth = 4;
        }
        nbasis = 1+hqrnduniformi(&rs, 2, _state);
        ae_vector_set_length(&x, nticks+nzeros, _state);
        for(i=0; i<=x.cnt-1; i++)
        {
            x.ptr.p_double[i] = 0.0;
        }
        for(i=0; i<=nticks-1; i++)
        {
            x.ptr.p_double[i] = 1-(double)i/(double)nticks+0.05*hqrndnormal(&rs, _state);
        }
        
        /*
         * Build complete model
         *
         * NOTE: we tweak S.DefaultSubspaceIts in order to enforce convergence to
         *       same basis; for this test we need extra-precise convergence.
         */
        ssacreate(&state2, _state);
        ssasetwindow(&state2, windowwidth, _state);
        ssaaddsequence(&state2, &x, x.cnt, _state);
        ssasetalgotopkrealtime(&state2, nbasis, _state);
        ae_matrix_set_length(&a2, 0, 0, _state);
        ae_vector_set_length(&sv2, 0, _state);
        windowwidth2 = -1;
        nbasis2 = -1;
        ssagetbasis(&state2, &a2, &sv2, &windowwidth2, &nbasis2, _state);
        ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1088");
        ae_set_error_flag(errorflag, nbasis2!=nbasis, __FILE__, __LINE__, "testssaunit.ap:1089");
        ae_set_error_flag(errorflag, a2.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1090");
        ae_set_error_flag(errorflag, a2.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:1091");
        ae_set_error_flag(errorflag, sv2.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:1092");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Build model with power-up cycle
         *
         * NOTE: with probability 50% we enforce basis calculation
         *       before first append() call and compare basis with
         *       one returned by full analysis. We do it only in 50%
         *       of the cases because randomness helps to debug different
         *       branches of algorithms.
         */
        ae_vector_set_length(&x2, nticks+windowwidth, _state);
        for(i=0; i<=x2.cnt-1; i++)
        {
            x2.ptr.p_double[i] = x.ptr.p_double[i];
        }
        ssacreate(&state, _state);
        ssasetwindow(&state, windowwidth, _state);
        ssaaddsequence(&state, &x2, x2.cnt, _state);
        ssasetalgotopkrealtime(&state, nbasis, _state);
        ssasetpoweruplength(&state, 10, _state);
        if( ae_fp_greater(hqrnduniformr(&rs, _state),0.5) )
        {
            ae_matrix_set_length(&a, 0, 0, _state);
            ae_vector_set_length(&sv, 0, _state);
            windowwidth2 = -1;
            nbasis2 = -1;
            ssagetbasis(&state, &a, &sv, &windowwidth2, &nbasis2, _state);
            ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1120");
            ae_set_error_flag(errorflag, nbasis2!=nbasis, __FILE__, __LINE__, "testssaunit.ap:1121");
            ae_set_error_flag(errorflag, a.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1122");
            ae_set_error_flag(errorflag, a.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:1123");
            ae_set_error_flag(errorflag, sv.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:1124");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            vv = 0.0;
            for(i=0; i<=nbasis-1; i++)
            {
                vv = ae_maxreal(vv, ae_fabs(sv.ptr.p_double[i]-sv2.ptr.p_double[i], _state), _state);
            }
            for(j=0; j<=nbasis-1; j++)
            {
                v = ae_v_dotproduct(&a.ptr.pp_double[0][j], a.stride, &a2.ptr.pp_double[0][j], a2.stride, ae_v_len(0,windowwidth-1));
                for(i=0; i<=windowwidth-1; i++)
                {
                    vv = ae_maxreal(vv, ae_fabs(a.ptr.pp_double[i][j]-ae_sign(v, _state)*a2.ptr.pp_double[i][j], _state), _state);
                }
            }
            ae_set_error_flag(errorflag, ae_fp_less(ae_fabs(vv, _state),tol), __FILE__, __LINE__, "testssaunit.ap:1136");
        }
        for(i=x2.cnt; i<=x.cnt-1; i++)
        {
            ssaappendpointandupdate(&state, x.ptr.p_double[i], 1.0, _state);
        }
        ae_matrix_set_length(&a, 0, 0, _state);
        ae_vector_set_length(&sv, 0, _state);
        windowwidth2 = -1;
        nbasis2 = -1;
        ssagetbasis(&state, &a, &sv, &windowwidth2, &nbasis2, _state);
        ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1145");
        ae_set_error_flag(errorflag, nbasis2!=nbasis, __FILE__, __LINE__, "testssaunit.ap:1146");
        ae_set_error_flag(errorflag, a.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1147");
        ae_set_error_flag(errorflag, a.cols!=nbasis, __FILE__, __LINE__, "testssaunit.ap:1148");
        ae_set_error_flag(errorflag, sv.cnt!=nbasis, __FILE__, __LINE__, "testssaunit.ap:1149");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        vv = 0.0;
        for(i=0; i<=nbasis-1; i++)
        {
            vv = ae_maxreal(vv, ae_fabs(sv.ptr.p_double[i]-sv2.ptr.p_double[i], _state), _state);
        }
        for(j=0; j<=nbasis-1; j++)
        {
            v = ae_v_dotproduct(&a.ptr.pp_double[0][j], a.stride, &a2.ptr.pp_double[0][j], a2.stride, ae_v_len(0,windowwidth-1));
            for(i=0; i<=windowwidth-1; i++)
            {
                vv = ae_maxreal(vv, ae_fabs(a.ptr.pp_double[i][j]-ae_sign(v, _state)*a2.ptr.pp_double[i][j], _state), _state);
            }
        }
        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(vv, _state),tol), __FILE__, __LINE__, "testssaunit.ap:1161");
    }
    
    /*
     * Test that SSAForecastAvgLast/Sequence() actually performs averaging.
     * We test it by comparing its results vs manually averaged predictions.
     *
     * Dataset is a small linear trend + Gaussian noise. We are not interested
     * in getting meaningful components, we just want to check correctness of math.
     */
    for(pass=1; pass<=100; pass++)
    {
        nticks = 75+hqrnduniformi(&rs, 75, _state);
        windowwidth = 5+hqrnduniformi(&rs, 5, _state);
        navg = 1+hqrnduniformi(&rs, 2*windowwidth, _state);
        forecastlen = 1+hqrnduniformi(&rs, 2*windowwidth, _state);
        ssacreate(&state, _state);
        ssasetwindow(&state, windowwidth, _state);
        ae_vector_set_length(&x, nticks, _state);
        for(i=0; i<=nticks-1; i++)
        {
            x.ptr.p_double[i] = 0.1*i+hqrndnormal(&rs, _state);
        }
        ssaaddsequence(&state, &x, nticks, _state);
        ssasetalgotopkdirect(&state, 1+hqrnduniformi(&rs, 3, _state), _state);
        tol = 1.0E-9;
        ssaforecastavglast(&state, navg, forecastlen, &trend, _state);
        ae_vector_set_length(&trend2, forecastlen, _state);
        for(i=0; i<=forecastlen-1; i++)
        {
            trend2.ptr.p_double[i] = 0.0;
        }
        for(i=0; i<=navg-1; i++)
        {
            ssaforecastsequence(&state, &x, nticks-i, forecastlen+i, ae_true, &tmp0, _state);
            for(j=0; j<=forecastlen-1; j++)
            {
                trend2.ptr.p_double[j] = trend2.ptr.p_double[j]+tmp0.ptr.p_double[i+j]/navg;
            }
        }
        for(i=0; i<=forecastlen-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend2.ptr.p_double[i], _state),tol*maxreal3(ae_fabs(trend.ptr.p_double[i], _state), ae_fabs(trend2.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testssaunit.ap:1198");
        }
        nticks = 75+hqrnduniformi(&rs, 75, _state);
        j = hqrnduniformi(&rs, 150, _state)-75;
        ae_vector_set_length(&x2, nticks, _state);
        for(i=0; i<=nticks-1; i++)
        {
            x2.ptr.p_double[i] = 0.1*(i+j)+hqrndnormal(&rs, _state);
        }
        ssaforecastavgsequence(&state, &x2, nticks, navg, forecastlen, ae_true, &trend, _state);
        ae_vector_set_length(&trend2, forecastlen, _state);
        for(i=0; i<=forecastlen-1; i++)
        {
            trend2.ptr.p_double[i] = 0.0;
        }
        for(i=0; i<=navg-1; i++)
        {
            ssaforecastsequence(&state, &x2, nticks-i, forecastlen+i, ae_true, &tmp0, _state);
            for(j=0; j<=forecastlen-1; j++)
            {
                trend2.ptr.p_double[j] = trend2.ptr.p_double[j]+tmp0.ptr.p_double[i+j]/navg;
            }
        }
        for(i=0; i<=forecastlen-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend2.ptr.p_double[i], _state),tol*maxreal3(ae_fabs(trend.ptr.p_double[i], _state), ae_fabs(trend2.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testssaunit.ap:1217");
        }
        nticks = 75+hqrnduniformi(&rs, 75, _state);
        j = hqrnduniformi(&rs, 150, _state)-75;
        ae_vector_set_length(&x2, nticks, _state);
        for(i=0; i<=nticks-1; i++)
        {
            x2.ptr.p_double[i] = 0.1*(i+j)+hqrndnormal(&rs, _state);
        }
        ssaforecastavgsequence(&state, &x2, nticks, navg, forecastlen, ae_false, &trend, _state);
        ae_vector_set_length(&trend2, forecastlen, _state);
        for(i=0; i<=forecastlen-1; i++)
        {
            trend2.ptr.p_double[i] = 0.0;
        }
        for(i=0; i<=navg-1; i++)
        {
            ssaforecastsequence(&state, &x2, nticks-i, forecastlen+i, ae_false, &tmp0, _state);
            for(j=0; j<=forecastlen-1; j++)
            {
                trend2.ptr.p_double[j] = trend2.ptr.p_double[j]+tmp0.ptr.p_double[i+j]/navg;
            }
        }
        for(i=0; i<=forecastlen-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend2.ptr.p_double[i], _state),tol*maxreal3(ae_fabs(trend.ptr.p_double[i], _state), ae_fabs(trend2.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testssaunit.ap:1236");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests different special cases (mostly - degenerate ones).

On failure sets ErrorFlag, on success it is untouched.
*************************************************************************/
static void testssaunit_testspecial(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    ae_int_t pass;
    ssamodel state;
    ssamodel state2;
    ae_matrix a;
    ae_matrix b;
    ae_vector x;
    ae_vector x2;
    ae_vector sv;
    ae_vector trend;
    ae_vector noise;
    ae_vector trend2;
    ae_vector noise2;
    ae_int_t algotype;
    ae_int_t nticks;
    ae_int_t nanalyzed;
    ae_int_t nlasttracklen;
    ae_int_t ntracks;
    ae_int_t maxtracklen;
    ae_int_t mintracklen;
    ae_int_t datalen;
    ae_int_t forecastlen;
    ae_int_t windowwidth;
    ae_int_t nbasis;
    ae_int_t windowwidth2;
    ae_matrix tracksmatrix;
    ae_vector trackssizes;
    hqrndstate rs;
    ae_int_t passcount;
    double skipprob;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&state2, 0, sizeof(state2));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&x, 0, sizeof(x));
    memset(&x2, 0, sizeof(x2));
    memset(&sv, 0, sizeof(sv));
    memset(&trend, 0, sizeof(trend));
    memset(&noise, 0, sizeof(noise));
    memset(&trend2, 0, sizeof(trend2));
    memset(&noise2, 0, sizeof(noise2));
    memset(&tracksmatrix, 0, sizeof(tracksmatrix));
    memset(&trackssizes, 0, sizeof(trackssizes));
    memset(&rs, 0, sizeof(rs));
    _ssamodel_init(&state, _state, ae_true);
    _ssamodel_init(&state2, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&trend, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&noise, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&trend2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&noise2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&tracksmatrix, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&trackssizes, 0, DT_INT, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    
    /*
     * Initialize RNG, test pass count and skip probability.
     *
     * When we perform several sequential tests on the same model, we may
     * skip some of them with probability SkipProb in order to make sure
     * that no carry-over effect is observed between tests.
     */
    hqrndrandomize(&rs, _state);
    passcount = 500;
    skipprob = 0.50;
    
    /*
     * Test that for empty model in default state:
     * * SSAGetBasis() returns zero 1x1 basis
     * * SSAAnalyzeLastWindow() returns zeros as trend/noise 
     * * SSAAnalyzeLast() returns zeros as trend/noise
     * * SSAAnalyzeSequence() returns zeros as trend, sequence as noise
     * * SSAForecastLast() returns zero trend
     * * SSAForecastSequence() returns zero trend
     */
    for(pass=1; pass<=passcount; pass++)
    {
        ssacreate(&state, _state);
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            ssagetbasis(&state, &a, &sv, &windowwidth, &nbasis, _state);
            ae_set_error_flag(errorflag, windowwidth!=1, __FILE__, __LINE__, "testssaunit.ap:1299");
            ae_set_error_flag(errorflag, nbasis!=1, __FILE__, __LINE__, "testssaunit.ap:1300");
            ae_set_error_flag(errorflag, a.rows!=1, __FILE__, __LINE__, "testssaunit.ap:1301");
            ae_set_error_flag(errorflag, a.cols!=1, __FILE__, __LINE__, "testssaunit.ap:1302");
            ae_set_error_flag(errorflag, sv.cnt!=1, __FILE__, __LINE__, "testssaunit.ap:1303");
            for(i=0; i<=a.rows-1; i++)
            {
                for(j=0; j<=a.cols-1; j++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(a.ptr.pp_double[i][j],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1306");
                }
            }
            for(i=0; i<=sv.cnt-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(sv.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1308");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            windowwidth = -1;
            ae_vector_set_length(&trend, 0, _state);
            ae_vector_set_length(&noise, 0, _state);
            ssaanalyzelastwindow(&state, &trend, &noise, &windowwidth, _state);
            ae_set_error_flag(errorflag, windowwidth!=1, __FILE__, __LINE__, "testssaunit.ap:1316");
            ae_set_error_flag(errorflag, trend.cnt!=1||ae_fp_neq(trend.ptr.p_double[0],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1317");
            ae_set_error_flag(errorflag, noise.cnt!=1||ae_fp_neq(noise.ptr.p_double[0],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1318");
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            nticks = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&trend, 0, _state);
            ae_vector_set_length(&noise, 0, _state);
            ssaanalyzelast(&state, nticks, &trend, &noise, _state);
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1326");
            ae_set_error_flag(errorflag, noise.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1327");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1332");
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1333");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            nticks = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&x, nticks, _state);
            for(i=0; i<=nticks-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_vector_set_length(&trend, 0, _state);
            ae_vector_set_length(&noise, 0, _state);
            ssaanalyzesequence(&state, &x, nticks, &trend, &noise, _state);
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1345");
            ae_set_error_flag(errorflag, noise.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1346");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1351");
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],x.ptr.p_double[i]), __FILE__, __LINE__, "testssaunit.ap:1352");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            nticks = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&trend, 0, _state);
            ssaforecastlast(&state, nticks, &trend, _state);
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1360");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1364");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            datalen = 1+hqrnduniformi(&rs, 10, _state);
            forecastlen = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&x, datalen, _state);
            for(i=0; i<=datalen-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_vector_set_length(&trend, 0, _state);
            ssaforecastsequence(&state, &x, datalen, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
            ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:1375");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=forecastlen-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1379");
            }
        }
    }
    
    /*
     * Test that for empty model with non-default window, but default
     * algorithm and no data:
     * * SSAGetBasis() returns zero WINDOWx1 basis
     * * SSAAnalyzeLastWindow() returns zeros as trend/noise 
     * * SSAAnalyzeLast() returns zeros as trend/noise
     * * SSAAnalyzeSequence() returns zeros as trend, sequence as noise
     * * SSAForecastLast() returns zero trend
     * * SSAForecastSequence() returns zero trend
     */
    for(pass=1; pass<=passcount; pass++)
    {
        windowwidth2 = 1+hqrnduniformi(&rs, 10, _state);
        ssacreate(&state, _state);
        ssasetwindow(&state, windowwidth2, _state);
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            ssagetbasis(&state, &a, &sv, &windowwidth, &nbasis, _state);
            ae_set_error_flag(errorflag, windowwidth!=windowwidth2, __FILE__, __LINE__, "testssaunit.ap:1401");
            ae_set_error_flag(errorflag, nbasis!=1, __FILE__, __LINE__, "testssaunit.ap:1402");
            ae_set_error_flag(errorflag, a.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1403");
            ae_set_error_flag(errorflag, a.cols!=1, __FILE__, __LINE__, "testssaunit.ap:1404");
            ae_set_error_flag(errorflag, sv.cnt!=1, __FILE__, __LINE__, "testssaunit.ap:1405");
            for(i=0; i<=a.rows-1; i++)
            {
                for(j=0; j<=a.cols-1; j++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(a.ptr.pp_double[i][j],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1408");
                }
            }
            for(i=0; i<=sv.cnt-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(sv.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1410");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            windowwidth = -1;
            ae_vector_set_length(&trend, 0, _state);
            ae_vector_set_length(&noise, 0, _state);
            ssaanalyzelastwindow(&state, &trend, &noise, &windowwidth, _state);
            ae_set_error_flag(errorflag, windowwidth!=windowwidth2, __FILE__, __LINE__, "testssaunit.ap:1418");
            ae_set_error_flag(errorflag, trend.cnt!=windowwidth2, __FILE__, __LINE__, "testssaunit.ap:1419");
            ae_set_error_flag(errorflag, noise.cnt!=windowwidth2, __FILE__, __LINE__, "testssaunit.ap:1420");
            for(i=0; i<=trend.cnt-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1422");
            }
            for(i=0; i<=noise.cnt-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1424");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            nticks = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&trend, 0, _state);
            ae_vector_set_length(&noise, 0, _state);
            ssaanalyzelast(&state, nticks, &trend, &noise, _state);
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1432");
            ae_set_error_flag(errorflag, noise.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1433");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1438");
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1439");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            nticks = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&x, nticks, _state);
            for(i=0; i<=nticks-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_vector_set_length(&trend, 0, _state);
            ae_vector_set_length(&noise, 0, _state);
            ssaanalyzesequence(&state, &x, nticks, &trend, &noise, _state);
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1451");
            ae_set_error_flag(errorflag, noise.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1452");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1457");
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],x.ptr.p_double[i]), __FILE__, __LINE__, "testssaunit.ap:1458");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            nticks = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&trend, 0, _state);
            ssaforecastlast(&state, nticks, &trend, _state);
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1466");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1470");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            datalen = 1+hqrnduniformi(&rs, 10, _state);
            forecastlen = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&x, datalen, _state);
            for(i=0; i<=datalen-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_vector_set_length(&trend, 0, _state);
            ssaforecastsequence(&state, &x, datalen, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
            ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:1481");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=forecastlen-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1485");
            }
        }
    }
    
    /*
     * Test that for empty model with default algorithm and one/few tracks
     * (which are sometimes shorter than window, sometimes longer than window)
     * * SSAGetBasis() returns zero WINDOWx1 basis
     * * SSAAnalyzeLastWindow() returns zeros as trend and correctly aligned
     *   cropped/padded copy of X in the noise
     * * SSAAnalyzeLast() returns zeros as trend and correctly aligned
     *   cropped/padded copy of X in the noise
     * * SSAAnalyzeSequence() returns zeros as trend, sequence as noise
     * * SSAForecastLast() returns zero trend
     * * SSAForecastSequence() returns zero trend
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Generate task; last track is stored in X, its length in NLastTrackLen
         */
        nlasttracklen = -999999;
        windowwidth2 = 1+hqrnduniformi(&rs, 10, _state);
        ntracks = 1+hqrnduniformi(&rs, 3, _state);
        ssacreate(&state, _state);
        ssasetwindow(&state, windowwidth2, _state);
        ae_matrix_set_length(&tracksmatrix, ntracks, 2*windowwidth2, _state);
        ae_vector_set_length(&trackssizes, ntracks, _state);
        for(k=0; k<=ntracks-1; k++)
        {
            nlasttracklen = hqrnduniformi(&rs, 2*windowwidth2, _state);
            ae_vector_set_length(&x, nlasttracklen, _state);
            for(i=0; i<=nlasttracklen-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
                tracksmatrix.ptr.pp_double[k][i] = x.ptr.p_double[i];
            }
            trackssizes.ptr.p_int[k] = nlasttracklen;
            ssaaddsequence(&state, &x, nlasttracklen, _state);
        }
        
        /*
         * Test
         */
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            ssagetbasis(&state, &a, &sv, &windowwidth, &nbasis, _state);
            ae_set_error_flag(errorflag, windowwidth!=windowwidth2, __FILE__, __LINE__, "testssaunit.ap:1532");
            ae_set_error_flag(errorflag, nbasis!=1, __FILE__, __LINE__, "testssaunit.ap:1533");
            ae_set_error_flag(errorflag, a.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1534");
            ae_set_error_flag(errorflag, a.cols!=1, __FILE__, __LINE__, "testssaunit.ap:1535");
            ae_set_error_flag(errorflag, sv.cnt!=1, __FILE__, __LINE__, "testssaunit.ap:1536");
            for(i=0; i<=a.rows-1; i++)
            {
                for(j=0; j<=a.cols-1; j++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(a.ptr.pp_double[i][j],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1539");
                }
            }
            for(i=0; i<=sv.cnt-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(sv.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1541");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            windowwidth = -1;
            ae_vector_set_length(&trend, 0, _state);
            ae_vector_set_length(&noise, 0, _state);
            ssaanalyzelastwindow(&state, &trend, &noise, &windowwidth, _state);
            ae_set_error_flag(errorflag, windowwidth!=windowwidth2, __FILE__, __LINE__, "testssaunit.ap:1549");
            ae_set_error_flag(errorflag, trend.cnt!=windowwidth2, __FILE__, __LINE__, "testssaunit.ap:1550");
            ae_set_error_flag(errorflag, noise.cnt!=windowwidth2, __FILE__, __LINE__, "testssaunit.ap:1551");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=windowwidth-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1555");
            }
            for(i=0; i<=ae_maxint(windowwidth-nlasttracklen, 0, _state)-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1557");
            }
            for(i=ae_maxint(windowwidth-nlasttracklen, 0, _state); i<=windowwidth-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],x.ptr.p_double[nlasttracklen+(i-windowwidth)]), __FILE__, __LINE__, "testssaunit.ap:1559");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            nanalyzed = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&trend, 0, _state);
            ae_vector_set_length(&noise, 0, _state);
            ssaanalyzelast(&state, nanalyzed, &trend, &noise, _state);
            ae_set_error_flag(errorflag, trend.cnt!=nanalyzed, __FILE__, __LINE__, "testssaunit.ap:1567");
            ae_set_error_flag(errorflag, noise.cnt!=nanalyzed, __FILE__, __LINE__, "testssaunit.ap:1568");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nanalyzed-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1572");
            }
            for(i=0; i<=ae_maxint(nanalyzed-nlasttracklen, 0, _state)-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1574");
            }
            for(i=ae_maxint(nanalyzed-nlasttracklen, 0, _state); i<=nanalyzed-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],x.ptr.p_double[nlasttracklen+(i-nanalyzed)]), __FILE__, __LINE__, "testssaunit.ap:1576");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            nticks = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&x, nticks, _state);
            for(i=0; i<=nticks-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_vector_set_length(&trend, 0, _state);
            ae_vector_set_length(&noise, 0, _state);
            ssaanalyzesequence(&state, &x, nticks, &trend, &noise, _state);
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1587");
            ae_set_error_flag(errorflag, noise.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1588");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1593");
                ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],x.ptr.p_double[i]), __FILE__, __LINE__, "testssaunit.ap:1594");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            nticks = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&trend, 0, _state);
            ssaforecastlast(&state, nticks, &trend, _state);
            ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1602");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nticks-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1606");
            }
        }
        if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
        {
            datalen = 1+hqrnduniformi(&rs, 10, _state);
            forecastlen = 1+hqrnduniformi(&rs, 10, _state);
            ae_vector_set_length(&x, datalen, _state);
            for(i=0; i<=datalen-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_vector_set_length(&trend, 0, _state);
            ssaforecastsequence(&state, &x, datalen, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
            ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:1617");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=forecastlen-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1621");
            }
        }
    }
    
    /*
     * Tests below are performed for all algorithms supported.
     */
    for(algotype=1; algotype<=3; algotype++)
    {
        
        /*
         * Test that SSAClearData() actually clears data
         */
        for(pass=1; pass<=passcount; pass++)
        {
            if( algotype==1 )
            {
                continue;
            }
            
            /*
             * Create two models, one is created after cleardata() call
             * which should erase all traces of the previous dataset.
             */
            ssacreate(&state, _state);
            ssacreate(&state2, _state);
            ssasetwindow(&state, 5, _state);
            ssasetwindow(&state2, 5, _state);
            if( algotype==2 )
            {
                ssasetalgotopkdirect(&state, 2, _state);
                ssasetalgotopkdirect(&state2, 2, _state);
            }
            else
            {
                if( algotype==3 )
                {
                    ssasetalgotopkrealtime(&state, 2, _state);
                    ssasetalgotopkrealtime(&state2, 2, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            ae_vector_set_length(&x, 10, _state);
            for(i=0; i<=x.cnt-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ssaaddsequence(&state, &x, x.cnt, _state);
            ssacleardata(&state, _state);
            for(i=0; i<=x.cnt-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ssaaddsequence(&state, &x, x.cnt, _state);
            ssaaddsequence(&state2, &x, x.cnt, _state);
            
            /*
             * Test
             */
            ssaanalyzelast(&state, x.cnt, &trend, &noise, _state);
            ssaanalyzelast(&state2, x.cnt, &trend2, &noise2, _state);
            for(i=0; i<=x.cnt-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-trend2.ptr.p_double[i], _state),1.0E-5), __FILE__, __LINE__, "testssaunit.ap:1675");
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i]-noise2.ptr.p_double[i], _state),1.0E-5), __FILE__, __LINE__, "testssaunit.ap:1676");
            }
        }
        
        /*
         * Test that for model with some algo being set, one/few tracks and unit window length:
         * * SSAGetBasis() returns 1x1 basis (unit matrix)
         * * SSAAnalyzeLastWindow() returns last element of track as trend and zeros
         *   as noise (with minor rounding error)
         * * SSAAnalyzeLast() returns tracks as trend and zeros as noise (with
         *   minor rounding error possible). For NTicks>TrackLength result is correctly
         *   prepended with zeros.
         * * SSAAnalyzeSequence() returns sequence as trend, zeros as noise (up to
         *   machine precision)
         * * SSAForecastLast() returns just copies of the last element
         * * SSAForecastSequence() returns just copies of the last element
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Generate task; last track is stored in X, its length in NLastTrackLen
             */
            nlasttracklen = -999999;
            maxtracklen = 10;
            ntracks = 1+hqrnduniformi(&rs, 3, _state);
            ssacreate(&state, _state);
            ssasetwindow(&state, 1, _state);
            ae_matrix_set_length(&tracksmatrix, ntracks, maxtracklen, _state);
            ae_vector_set_length(&trackssizes, ntracks, _state);
            for(k=0; k<=ntracks-1; k++)
            {
                nlasttracklen = 1+hqrnduniformi(&rs, maxtracklen, _state);
                ae_vector_set_length(&x, nlasttracklen, _state);
                for(i=0; i<=nlasttracklen-1; i++)
                {
                    x.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    tracksmatrix.ptr.pp_double[k][i] = x.ptr.p_double[i];
                }
                trackssizes.ptr.p_int[k] = nlasttracklen;
                ssaaddsequence(&state, &x, nlasttracklen, _state);
            }
            if( algotype==1 )
            {
                ae_matrix_set_length(&b, 1, 1, _state);
                b.ptr.pp_double[0][0] = (double)(2*hqrnduniformi(&rs, 2, _state)-1);
                ssasetalgoprecomputed(&state, &b, 1, 1, _state);
            }
            else
            {
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state, 1, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state, 1, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            
            /*
             * Test
             */
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                ssagetbasis(&state, &a, &sv, &windowwidth, &nbasis, _state);
                ae_set_error_flag(errorflag, windowwidth!=1, __FILE__, __LINE__, "testssaunit.ap:1736");
                ae_set_error_flag(errorflag, nbasis!=1, __FILE__, __LINE__, "testssaunit.ap:1737");
                ae_set_error_flag(errorflag, a.rows!=1, __FILE__, __LINE__, "testssaunit.ap:1738");
                ae_set_error_flag(errorflag, a.cols!=1, __FILE__, __LINE__, "testssaunit.ap:1739");
                ae_set_error_flag(errorflag, sv.cnt!=1, __FILE__, __LINE__, "testssaunit.ap:1740");
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(ae_fabs(a.ptr.pp_double[0][0], _state)-1, _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1741");
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                windowwidth = -1;
                ae_vector_set_length(&trend, 0, _state);
                ae_vector_set_length(&noise, 0, _state);
                ssaanalyzelastwindow(&state, &trend, &noise, &windowwidth, _state);
                ae_set_error_flag(errorflag, windowwidth!=1, __FILE__, __LINE__, "testssaunit.ap:1749");
                ae_set_error_flag(errorflag, trend.cnt!=1, __FILE__, __LINE__, "testssaunit.ap:1750");
                ae_set_error_flag(errorflag, noise.cnt!=1, __FILE__, __LINE__, "testssaunit.ap:1751");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[0]-x.ptr.p_double[nlasttracklen-1], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1754");
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[0], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1755");
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nanalyzed = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&trend, 0, _state);
                ae_vector_set_length(&noise, 0, _state);
                ssaanalyzelast(&state, nanalyzed, &trend, &noise, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nanalyzed, __FILE__, __LINE__, "testssaunit.ap:1763");
                ae_set_error_flag(errorflag, noise.cnt!=nanalyzed, __FILE__, __LINE__, "testssaunit.ap:1764");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=ae_maxint(nanalyzed-nlasttracklen, 0, _state)-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1769");
                    ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1770");
                }
                for(i=ae_maxint(nanalyzed-nlasttracklen, 0, _state); i<=nanalyzed-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[i-nanalyzed+nlasttracklen], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1774");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1775");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&x2, nticks, _state);
                for(i=0; i<=nticks-1; i++)
                {
                    x2.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                ae_vector_set_length(&trend, 0, _state);
                ae_vector_set_length(&noise, 0, _state);
                ssaanalyzesequence(&state, &x2, nticks, &trend, &noise, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1787");
                ae_set_error_flag(errorflag, noise.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1788");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=nticks-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x2.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1793");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1794");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastlast(&state, nticks, &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1802");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=nticks-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],x.ptr.p_double[nlasttracklen-1]), __FILE__, __LINE__, "testssaunit.ap:1806");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                datalen = 1+hqrnduniformi(&rs, 10, _state);
                forecastlen = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&x, datalen, _state);
                for(i=0; i<=datalen-1; i++)
                {
                    x.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastsequence(&state, &x, datalen, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:1817");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=forecastlen-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],x.ptr.p_double[datalen-1]), __FILE__, __LINE__, "testssaunit.ap:1821");
                }
            }
        }
        
        /*
         * Test that for model with non-unit window, some algo being set to calculate ALL
         * components, and one/few tracks which have at least window length ticks:
         * * SSAGetBasis() returns full orthonormal basis
         * * SSAAnalyzeLastWindow() returns last elements of track as trend and zeros
         *   as noise (with minor rounding error)
         * * SSAAnalyzeLast() returns track as trend and zeros as noise (with
         *   minor rounding error possible). For NTicks>TrackLength result is correctly
         *   prepended with zeros.
         * * SSAAnalyzeSequence() returns:
         *   * for sequences with length at least window width - sequence as trend,
         *     zeros as noise (up to machine precision)
         *   * for sequences shorter than window width - zeros as trend,
         *     sequence as noise (exactly)
         * * SSAForecastLast() returns just copies of the last element
         * * SSAForecastSequence() returns just copies of the last element for long enough
         *   sequences, zeros for shorter sequences
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Generate task; last track is stored in X, its length in NLastTrackLen
             */
            windowwidth = 1+hqrnduniformi(&rs, 5, _state);
            ntracks = 1+hqrnduniformi(&rs, 3, _state);
            mintracklen = windowwidth;
            maxtracklen = mintracklen+2*windowwidth;
            ssacreate(&state, _state);
            ssasetwindow(&state, windowwidth, _state);
            ae_matrix_set_length(&tracksmatrix, ntracks, maxtracklen, _state);
            ae_vector_set_length(&trackssizes, ntracks, _state);
            nlasttracklen = 0;
            for(k=0; k<=ntracks-1; k++)
            {
                nlasttracklen = mintracklen+hqrnduniformi(&rs, maxtracklen-mintracklen+1, _state);
                ae_vector_set_length(&x, nlasttracklen, _state);
                for(i=0; i<=nlasttracklen-1; i++)
                {
                    x.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    tracksmatrix.ptr.pp_double[k][i] = x.ptr.p_double[i];
                }
                trackssizes.ptr.p_int[k] = nlasttracklen;
                ssaaddsequence(&state, &x, nlasttracklen, _state);
            }
            if( algotype==1 )
            {
                rmatrixrndorthogonal(windowwidth, &b, _state);
                ssasetalgoprecomputed(&state, &b, windowwidth, windowwidth, _state);
            }
            else
            {
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state, windowwidth, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state, windowwidth, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            
            /*
             * Test
             */
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                ssagetbasis(&state, &a, &sv, &windowwidth2, &nbasis, _state);
                ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1888");
                ae_set_error_flag(errorflag, nbasis!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1889");
                ae_set_error_flag(errorflag, a.rows!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1890");
                ae_set_error_flag(errorflag, a.cols!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1891");
                ae_set_error_flag(errorflag, sv.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1892");
                for(i=0; i<=windowwidth-1; i++)
                {
                    for(j=0; j<=windowwidth-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &a.ptr.pp_double[j][0], 1, ae_v_len(0,windowwidth-1));
                        if( i==j )
                        {
                            v = v-1;
                        }
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(v, _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1899");
                    }
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                windowwidth2 = -1;
                ae_vector_set_length(&trend, 0, _state);
                ae_vector_set_length(&noise, 0, _state);
                ssaanalyzelastwindow(&state, &trend, &noise, &windowwidth2, _state);
                ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1908");
                ae_set_error_flag(errorflag, trend.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1909");
                ae_set_error_flag(errorflag, noise.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:1910");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=windowwidth-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[i-windowwidth+nlasttracklen], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1915");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1916");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nanalyzed = 1+hqrnduniformi(&rs, 2*maxtracklen, _state);
                ae_vector_set_length(&trend, 0, _state);
                ae_vector_set_length(&noise, 0, _state);
                ssaanalyzelast(&state, nanalyzed, &trend, &noise, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nanalyzed, __FILE__, __LINE__, "testssaunit.ap:1925");
                ae_set_error_flag(errorflag, noise.cnt!=nanalyzed, __FILE__, __LINE__, "testssaunit.ap:1926");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=ae_maxint(nanalyzed-nlasttracklen, 0, _state)-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1931");
                    ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:1932");
                }
                for(i=ae_maxint(nanalyzed-nlasttracklen, 0, _state); i<=nanalyzed-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x.ptr.p_double[i-nanalyzed+nlasttracklen], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1936");
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1937");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 2*windowwidth, _state);
                ae_vector_set_length(&x2, nticks, _state);
                for(i=0; i<=nticks-1; i++)
                {
                    x2.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                ae_vector_set_length(&trend, 0, _state);
                ae_vector_set_length(&noise, 0, _state);
                ssaanalyzesequence(&state, &x2, nticks, &trend, &noise, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1949");
                ae_set_error_flag(errorflag, noise.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1950");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=nticks-1; i++)
                {
                    if( nticks>=windowwidth )
                    {
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i]-x2.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1957");
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1958");
                    }
                    else
                    {
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(trend.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1962");
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(noise.ptr.p_double[i]-x2.ptr.p_double[i], _state),100*ae_machineepsilon), __FILE__, __LINE__, "testssaunit.ap:1963");
                    }
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastlast(&state, nticks, &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:1972");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=nticks-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],x.ptr.p_double[nlasttracklen-1]), __FILE__, __LINE__, "testssaunit.ap:1976");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                if( windowwidth>2 )
                {
                    datalen = 1+hqrnduniformi(&rs, windowwidth-1, _state);
                    forecastlen = 1+hqrnduniformi(&rs, 10, _state);
                    ae_vector_set_length(&x, datalen, _state);
                    for(i=0; i<=datalen-1; i++)
                    {
                        x.ptr.p_double[i] = hqrndnormal(&rs, _state);
                    }
                    ae_vector_set_length(&trend, 0, _state);
                    ssaforecastsequence(&state, &x, datalen, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
                    ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:1990");
                    if( *errorflag )
                    {
                        ae_frame_leave(_state);
                        return;
                    }
                    for(i=0; i<=forecastlen-1; i++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],0.0), __FILE__, __LINE__, "testssaunit.ap:1994");
                    }
                }
                datalen = windowwidth+hqrnduniformi(&rs, windowwidth, _state);
                forecastlen = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&x, datalen, _state);
                for(i=0; i<=datalen-1; i++)
                {
                    x.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastsequence(&state, &x, datalen, forecastlen, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=forecastlen, __FILE__, __LINE__, "testssaunit.ap:2005");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=forecastlen-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],x.ptr.p_double[datalen-1]), __FILE__, __LINE__, "testssaunit.ap:2009");
                }
            }
        }
        
        /*
         * Test that for model with
         *
         * * strictly non-unit window
         * * some algo being set to calculate one leading component
         * * a few tracks which have at least window length ticks
         * * and last track which has LESS than window length minus 1 ticks
         *
         * we have:
         *
         * * SSAAnalyzeLastWindow() returns zero trend and sequence as noise,
         *   correctly padded by zeros
         * * SSAAnalyzeLast() returns zero trend and sequence as noise,
         *   correctly padded by zeros
         * * SSAForecastLast() returns zero trend
         *
         * This test checks correct handling of the situatuon when last sequence
         * stored in the dataset is too short.
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Generate task; last track is stored in X, its length in NLastTrackLen
             */
            windowwidth = 3+hqrnduniformi(&rs, 5, _state);
            ntracks = 1+hqrnduniformi(&rs, 3, _state);
            mintracklen = windowwidth;
            maxtracklen = mintracklen+2*windowwidth;
            ssacreate(&state, _state);
            ssasetwindow(&state, windowwidth, _state);
            for(k=0; k<=ntracks-1; k++)
            {
                nlasttracklen = mintracklen+hqrnduniformi(&rs, maxtracklen-mintracklen+1, _state);
                ae_vector_set_length(&x, nlasttracklen, _state);
                for(i=0; i<=nlasttracklen-1; i++)
                {
                    x.ptr.p_double[i] = hqrndnormal(&rs, _state);
                }
                ssaaddsequence(&state, &x, nlasttracklen, _state);
            }
            nlasttracklen = windowwidth-2;
            ae_vector_set_length(&x, nlasttracklen, _state);
            for(i=0; i<=nlasttracklen-1; i++)
            {
                x.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ssaaddsequence(&state, &x, nlasttracklen, _state);
            if( algotype==1 )
            {
                rmatrixrndorthogonal(windowwidth, &b, _state);
                ssasetalgoprecomputed(&state, &b, windowwidth, 1, _state);
            }
            else
            {
                if( algotype==2 )
                {
                    ssasetalgotopkdirect(&state, 1, _state);
                }
                else
                {
                    if( algotype==3 )
                    {
                        ssasetalgotopkrealtime(&state, 1, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            
            /*
             * Test
             */
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                windowwidth2 = -1;
                ae_vector_set_length(&trend, 0, _state);
                ae_vector_set_length(&noise, 0, _state);
                ssaanalyzelastwindow(&state, &trend, &noise, &windowwidth2, _state);
                ae_set_error_flag(errorflag, windowwidth2!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:2078");
                ae_set_error_flag(errorflag, trend.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:2079");
                ae_set_error_flag(errorflag, noise.cnt!=windowwidth, __FILE__, __LINE__, "testssaunit.ap:2080");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=windowwidth-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],0.0), __FILE__, __LINE__, "testssaunit.ap:2085");
                    j = nlasttracklen+(i-windowwidth);
                    if( j>=0 )
                    {
                        ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],x.ptr.p_double[j]), __FILE__, __LINE__, "testssaunit.ap:2088");
                    }
                    else
                    {
                        ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],0.0), __FILE__, __LINE__, "testssaunit.ap:2090");
                    }
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nanalyzed = 1+hqrnduniformi(&rs, 2*windowwidth, _state);
                ae_vector_set_length(&trend, 0, _state);
                ae_vector_set_length(&noise, 0, _state);
                ssaanalyzelast(&state, nanalyzed, &trend, &noise, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nanalyzed, __FILE__, __LINE__, "testssaunit.ap:2099");
                ae_set_error_flag(errorflag, noise.cnt!=nanalyzed, __FILE__, __LINE__, "testssaunit.ap:2100");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=ae_maxint(nanalyzed-nlasttracklen, 0, _state)-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:2105");
                    ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:2106");
                }
                for(i=ae_maxint(nanalyzed-nlasttracklen, 0, _state); i<=nanalyzed-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],(double)(0)), __FILE__, __LINE__, "testssaunit.ap:2110");
                    ae_set_error_flag(errorflag, ae_fp_neq(noise.ptr.p_double[i],x.ptr.p_double[nlasttracklen+(i-nanalyzed)]), __FILE__, __LINE__, "testssaunit.ap:2111");
                }
            }
            if( ae_fp_greater(hqrnduniformr(&rs, _state),skipprob) )
            {
                nticks = 1+hqrnduniformi(&rs, 10, _state);
                ae_vector_set_length(&trend, 0, _state);
                ssaforecastlast(&state, nticks, &trend, _state);
                ae_set_error_flag(errorflag, trend.cnt!=nticks, __FILE__, __LINE__, "testssaunit.ap:2119");
                if( *errorflag )
                {
                    ae_frame_leave(_state);
                    return;
                }
                for(i=0; i<=nticks-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(trend.ptr.p_double[i],0.0), __FILE__, __LINE__, "testssaunit.ap:2123");
                }
            }
        }
    }
    ae_frame_leave(_state);
}








ae_bool testgammafunc(ae_bool silent, ae_state *_state)
{
    double threshold;
    double v;
    double s;
    ae_bool waserrors;
    ae_bool gammaerrors;
    ae_bool lngammaerrors;
    ae_bool result;


    gammaerrors = ae_false;
    lngammaerrors = ae_false;
    waserrors = ae_false;
    threshold = 100*ae_machineepsilon;
    
    /*
     *
     */
    gammaerrors = gammaerrors||ae_fp_greater(ae_fabs(gammafunction(0.5, _state)-ae_sqrt(ae_pi, _state), _state),threshold);
    gammaerrors = gammaerrors||ae_fp_greater(ae_fabs(gammafunction(1.5, _state)-0.5*ae_sqrt(ae_pi, _state), _state),threshold);
    v = lngamma(0.5, &s, _state);
    lngammaerrors = (lngammaerrors||ae_fp_greater(ae_fabs(v-ae_log(ae_sqrt(ae_pi, _state), _state), _state),threshold))||ae_fp_neq(s,(double)(1));
    v = lngamma(1.5, &s, _state);
    lngammaerrors = (lngammaerrors||ae_fp_greater(ae_fabs(v-ae_log(0.5*ae_sqrt(ae_pi, _state), _state), _state),threshold))||ae_fp_neq(s,(double)(1));
    
    /*
     * report
     */
    waserrors = gammaerrors||lngammaerrors;
    if( !silent )
    {
        printf("TESTING GAMMA FUNCTION\n");
        printf("GAMMA:                                   ");
        if( gammaerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LN GAMMA:                                ");
        if( lngammaerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    return result;
}



static void testlinregunit_generaterandomtask(double xl,
     double xr,
     ae_bool randomx,
     double ymin,
     double ymax,
     double smin,
     double smax,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state);
static void testlinregunit_generatetask(double a,
     double b,
     double xl,
     double xr,
     ae_bool randomx,
     double smin,
     double smax,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state);
static void testlinregunit_filltaskwithy(double a,
     double b,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state);
static double testlinregunit_generatenormal(double mean,
     double sigma,
     ae_state *_state);
static void testlinregunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state);
static void testlinregunit_unsetlr(linearmodel* lr, ae_state *_state);





ae_bool testlinreg(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double sigmathreshold;
    ae_int_t maxn;
    ae_int_t passcount;
    ae_int_t estpasscount;
    double threshold;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t tmpi;
    ae_int_t pass;
    ae_int_t epass;
    ae_int_t m;
    ae_int_t tasktype;
    ae_int_t modeltype;
    ae_int_t m1;
    ae_int_t m2;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t info;
    ae_int_t info2;
    ae_matrix xy;
    ae_matrix xy2;
    ae_vector s;
    ae_vector s2;
    ae_vector w2;
    ae_vector x;
    ae_vector ta;
    ae_vector tb;
    ae_vector tc;
    ae_vector xy0;
    ae_vector tmpweights;
    linearmodel w;
    linearmodel wt;
    linearmodel wt2;
    ae_vector x1;
    ae_vector x2;
    double y1;
    double y2;
    ae_bool allsame;
    double ea;
    double eb;
    double varatested;
    double varbtested;
    double a;
    double b;
    double vara;
    double varb;
    double a2;
    double b2;
    double covab;
    double corrab;
    double p;
    ae_int_t qcnt;
    ae_vector qtbl;
    ae_vector qvals;
    ae_vector qsigma;
    lrreport ar;
    lrreport ar2;
    double f;
    double fp;
    double fm;
    double v;
    double vv;
    double cvrmserror;
    double cvavgerror;
    double cvavgrelerror;
    double rmserror;
    double avgerror;
    double avgrelerror;
    ae_bool nondefect;
    double sinshift;
    double tasklevel;
    double noiselevel;
    double hstep;
    double sigma;
    double mean;
    double means;
    double stddev;
    double stddevs;
    ae_bool slcerrors;
    ae_bool slerrors;
    ae_bool grcoverrors;
    ae_bool gropterrors;
    ae_bool gresterrors;
    ae_bool grothererrors;
    ae_bool grconverrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&xy2, 0, sizeof(xy2));
    memset(&s, 0, sizeof(s));
    memset(&s2, 0, sizeof(s2));
    memset(&w2, 0, sizeof(w2));
    memset(&x, 0, sizeof(x));
    memset(&ta, 0, sizeof(ta));
    memset(&tb, 0, sizeof(tb));
    memset(&tc, 0, sizeof(tc));
    memset(&xy0, 0, sizeof(xy0));
    memset(&tmpweights, 0, sizeof(tmpweights));
    memset(&w, 0, sizeof(w));
    memset(&wt, 0, sizeof(wt));
    memset(&wt2, 0, sizeof(wt2));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&qtbl, 0, sizeof(qtbl));
    memset(&qvals, 0, sizeof(qvals));
    memset(&qsigma, 0, sizeof(qsigma));
    memset(&ar, 0, sizeof(ar));
    memset(&ar2, 0, sizeof(ar2));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ta, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xy0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpweights, 0, DT_REAL, _state, ae_true);
    _linearmodel_init(&w, _state, ae_true);
    _linearmodel_init(&wt, _state, ae_true);
    _linearmodel_init(&wt2, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qtbl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qvals, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qsigma, 0, DT_REAL, _state, ae_true);
    _lrreport_init(&ar, _state, ae_true);
    _lrreport_init(&ar2, _state, ae_true);

    
    /*
     * Primary settings
     */
    maxn = 40;
    passcount = 3;
    estpasscount = 1000;
    sigmathreshold = (double)(7);
    threshold = 1000000*ae_machineepsilon;
    slerrors = ae_false;
    slcerrors = ae_false;
    grcoverrors = ae_false;
    gropterrors = ae_false;
    gresterrors = ae_false;
    grothererrors = ae_false;
    grconverrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Quantiles table setup
     */
    qcnt = 5;
    ae_vector_set_length(&qtbl, qcnt-1+1, _state);
    ae_vector_set_length(&qvals, qcnt-1+1, _state);
    ae_vector_set_length(&qsigma, qcnt-1+1, _state);
    qtbl.ptr.p_double[0] = 0.5;
    qtbl.ptr.p_double[1] = 0.25;
    qtbl.ptr.p_double[2] = 0.10;
    qtbl.ptr.p_double[3] = 0.05;
    qtbl.ptr.p_double[4] = 0.025;
    for(i=0; i<=qcnt-1; i++)
    {
        qsigma.ptr.p_double[i] = ae_sqrt(qtbl.ptr.p_double[i]*(1-qtbl.ptr.p_double[i])/estpasscount, _state);
    }
    
    /*
     * Other setup
     */
    ae_vector_set_length(&ta, estpasscount-1+1, _state);
    ae_vector_set_length(&tb, estpasscount-1+1, _state);
    
    /*
     * Test straight line regression
     */
    for(n=2; n<=maxn; n++)
    {
        
        /*
         * Fail/pass test
         */
        testlinregunit_generaterandomtask((double)(-1), (double)(1), ae_false, (double)(-1), (double)(1), (double)(1), (double)(2), n, &xy, &s, _state);
        lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        slcerrors = slcerrors||info!=1;
        testlinregunit_generaterandomtask((double)(1), (double)(1), ae_false, (double)(-1), (double)(1), (double)(1), (double)(2), n, &xy, &s, _state);
        lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        slcerrors = slcerrors||info!=-3;
        testlinregunit_generaterandomtask((double)(-1), (double)(1), ae_false, (double)(-1), (double)(1), (double)(-1), (double)(-1), n, &xy, &s, _state);
        lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        slcerrors = slcerrors||info!=-2;
        testlinregunit_generaterandomtask((double)(-1), (double)(1), ae_false, (double)(-1), (double)(1), (double)(2), (double)(1), 2, &xy, &s, _state);
        lrlines(&xy, &s, 1, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        slcerrors = slcerrors||info!=-1;
        
        /*
         * Multipass tests
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Test S variant against non-S variant
             */
            ea = 2*ae_randomreal(_state)-1;
            eb = 2*ae_randomreal(_state)-1;
            testlinregunit_generatetask(ea, eb, -5*ae_randomreal(_state), 5*ae_randomreal(_state), ae_fp_greater(ae_randomreal(_state),0.5), (double)(1), (double)(1), n, &xy, &s, _state);
            lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
            lrline(&xy, n, &info2, &a2, &b2, _state);
            if( info!=1||info2!=1 )
            {
                slcerrors = ae_true;
            }
            else
            {
                slerrors = (slerrors||ae_fp_greater(ae_fabs(a-a2, _state),threshold))||ae_fp_greater(ae_fabs(b-b2, _state),threshold);
            }
            
            /*
             * Test for A/B
             *
             * Generate task with exact, non-perturbed y[i],
             * then make non-zero s[i]
             */
            ea = 2*ae_randomreal(_state)-1;
            eb = 2*ae_randomreal(_state)-1;
            testlinregunit_generatetask(ea, eb, -5*ae_randomreal(_state), 5*ae_randomreal(_state), n>4, 0.0, 0.0, n, &xy, &s, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = 1+ae_randomreal(_state);
            }
            lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
            if( info!=1 )
            {
                slcerrors = ae_true;
            }
            else
            {
                slerrors = (slerrors||ae_fp_greater(ae_fabs(a-ea, _state),0.001))||ae_fp_greater(ae_fabs(b-eb, _state),0.001);
            }
            
            /*
             * Test for VarA, VarB, P (P is being tested only for N>2)
             */
            for(i=0; i<=qcnt-1; i++)
            {
                qvals.ptr.p_double[i] = (double)(0);
            }
            ea = 2*ae_randomreal(_state)-1;
            eb = 2*ae_randomreal(_state)-1;
            testlinregunit_generatetask(ea, eb, -5*ae_randomreal(_state), 5*ae_randomreal(_state), n>4, 1.0, 2.0, n, &xy, &s, _state);
            lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
            if( info!=1 )
            {
                slcerrors = ae_true;
                continue;
            }
            varatested = vara;
            varbtested = varb;
            for(epass=0; epass<=estpasscount-1; epass++)
            {
                
                /*
                 * Generate
                 */
                testlinregunit_filltaskwithy(ea, eb, n, &xy, &s, _state);
                lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
                if( info!=1 )
                {
                    slcerrors = ae_true;
                    continue;
                }
                
                /*
                 * A, B, P
                 * (P is being tested for uniformity, additional p-tests are below)
                 */
                ta.ptr.p_double[epass] = a;
                tb.ptr.p_double[epass] = b;
                for(i=0; i<=qcnt-1; i++)
                {
                    if( ae_fp_less_eq(p,qtbl.ptr.p_double[i]) )
                    {
                        qvals.ptr.p_double[i] = qvals.ptr.p_double[i]+(double)1/(double)estpasscount;
                    }
                }
            }
            testlinregunit_calculatemv(&ta, estpasscount, &mean, &means, &stddev, &stddevs, _state);
            slerrors = slerrors||ae_fp_greater_eq(ae_fabs(mean-ea, _state)/means,sigmathreshold);
            slerrors = slerrors||ae_fp_greater_eq(ae_fabs(stddev-ae_sqrt(varatested, _state), _state)/stddevs,sigmathreshold);
            testlinregunit_calculatemv(&tb, estpasscount, &mean, &means, &stddev, &stddevs, _state);
            slerrors = slerrors||ae_fp_greater_eq(ae_fabs(mean-eb, _state)/means,sigmathreshold);
            slerrors = slerrors||ae_fp_greater_eq(ae_fabs(stddev-ae_sqrt(varbtested, _state), _state)/stddevs,sigmathreshold);
            if( n>2 )
            {
                for(i=0; i<=qcnt-1; i++)
                {
                    if( ae_fp_greater(ae_fabs(qtbl.ptr.p_double[i]-qvals.ptr.p_double[i], _state)/qsigma.ptr.p_double[i],sigmathreshold) )
                    {
                        slerrors = ae_true;
                    }
                }
            }
            
            /*
             * Additional tests for P: correlation with fit quality
             */
            if( n>2 )
            {
                testlinregunit_generatetask(ea, eb, -5*ae_randomreal(_state), 5*ae_randomreal(_state), ae_false, 0.0, 0.0, n, &xy, &s, _state);
                for(i=0; i<=n-1; i++)
                {
                    s.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
                if( info!=1 )
                {
                    slcerrors = ae_true;
                    continue;
                }
                slerrors = slerrors||ae_fp_less(p,0.999);
                testlinregunit_generatetask((double)(0), (double)(0), -5*ae_randomreal(_state), 5*ae_randomreal(_state), ae_false, 1.0, 1.0, n, &xy, &s, _state);
                for(i=0; i<=n-1; i++)
                {
                    if( i%2==0 )
                    {
                        xy.ptr.pp_double[i][1] = 5.0;
                    }
                    else
                    {
                        xy.ptr.pp_double[i][1] = -5.0;
                    }
                }
                if( n%2!=0 )
                {
                    xy.ptr.pp_double[n-1][1] = (double)(0);
                }
                lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
                if( info!=1 )
                {
                    slcerrors = ae_true;
                    continue;
                }
                slerrors = slerrors||ae_fp_greater(p,0.001);
            }
        }
    }
    
    /*
     * General regression tests:
     */
    
    /*
     * Simple linear tests (small sample, optimum point, covariance)
     */
    for(n=3; n<=maxn; n++)
    {
        ae_vector_set_length(&s, n-1+1, _state);
        
        /*
         * Linear tests:
         * a. random points, sigmas
         * b. no sigmas
         */
        ae_matrix_set_length(&xy, n-1+1, 1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            xy.ptr.pp_double[i][0] = 2*ae_randomreal(_state)-1;
            xy.ptr.pp_double[i][1] = 2*ae_randomreal(_state)-1;
            s.ptr.p_double[i] = 1+ae_randomreal(_state);
        }
        lrbuilds(&xy, &s, n, 1, &info, &wt, &ar, _state);
        if( info!=1 )
        {
            grconverrors = ae_true;
            continue;
        }
        lrunpack(&wt, &tmpweights, &tmpi, _state);
        lrlines(&xy, &s, n, &info2, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        gropterrors = gropterrors||ae_fp_greater(ae_fabs(a-tmpweights.ptr.p_double[1], _state),threshold);
        gropterrors = gropterrors||ae_fp_greater(ae_fabs(b-tmpweights.ptr.p_double[0], _state),threshold);
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(vara-ar.c.ptr.pp_double[1][1], _state),threshold);
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(varb-ar.c.ptr.pp_double[0][0], _state),threshold);
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(covab-ar.c.ptr.pp_double[1][0], _state),threshold);
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(covab-ar.c.ptr.pp_double[0][1], _state),threshold);
        lrbuild(&xy, n, 1, &info, &wt, &ar, _state);
        if( info!=1 )
        {
            grconverrors = ae_true;
            continue;
        }
        lrunpack(&wt, &tmpweights, &tmpi, _state);
        lrline(&xy, n, &info2, &a, &b, _state);
        gropterrors = gropterrors||ae_fp_greater(ae_fabs(a-tmpweights.ptr.p_double[1], _state),threshold);
        gropterrors = gropterrors||ae_fp_greater(ae_fabs(b-tmpweights.ptr.p_double[0], _state),threshold);
    }
    
    /*
     * S covariance versus S-less covariance.
     * Slightly skewed task, large sample size.
     * Will S-less subroutine estimate covariance matrix good enough?
     */
    n = 1000+ae_randominteger(3000, _state);
    sigma = 0.1+ae_randomreal(_state)*1.9;
    ae_matrix_set_length(&xy, n-1+1, 1+1, _state);
    ae_vector_set_length(&s, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        xy.ptr.pp_double[i][0] = 1.5*ae_randomreal(_state)-0.5;
        xy.ptr.pp_double[i][1] = 1.2*xy.ptr.pp_double[i][0]-0.3+testlinregunit_generatenormal((double)(0), sigma, _state);
        s.ptr.p_double[i] = sigma;
    }
    lrbuild(&xy, n, 1, &info, &wt, &ar, _state);
    lrlines(&xy, &s, n, &info2, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
    if( info!=1||info2!=1 )
    {
        grconverrors = ae_true;
    }
    else
    {
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(ae_log(ar.c.ptr.pp_double[0][0]/varb, _state), _state),ae_log(1.2, _state));
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(ae_log(ar.c.ptr.pp_double[1][1]/vara, _state), _state),ae_log(1.2, _state));
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(ae_log(ar.c.ptr.pp_double[0][1]/covab, _state), _state),ae_log(1.2, _state));
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(ae_log(ar.c.ptr.pp_double[1][0]/covab, _state), _state),ae_log(1.2, _state));
    }
    
    /*
     * General tests:
     * * basis functions - up to cubic
     * * task types:
     * * data set is noisy sine half-period with random shift
     * * tests:
     *   unpacking/packing
     *   optimality
     *   error estimates
     * * tasks:
     *   0 = noised sine
     *   1 = degenerate task with 1-of-n encoded categorical variables
     *   2 = random task with large variation (for 1-type models)
     *   3 = random task with small variation (for 1-type models)
     *
     *   Additional tasks TODO
     *   specially designed task with defective vectors which leads to
     *   the failure of the fast CV formula.
     *
     */
    m1 = 0;
    m2 = -1;
    n1 = 0;
    n2 = -1;
    for(modeltype=0; modeltype<=1; modeltype++)
    {
        for(tasktype=0; tasktype<=3; tasktype++)
        {
            if( tasktype==0 )
            {
                m1 = 1;
                m2 = 3;
            }
            if( tasktype==1 )
            {
                m1 = 9;
                m2 = 9;
            }
            if( tasktype==2||tasktype==3 )
            {
                m1 = 9;
                m2 = 9;
            }
            for(m=m1; m<=m2; m++)
            {
                if( tasktype==0 )
                {
                    n1 = m+3;
                    n2 = m+20;
                }
                if( tasktype==1 )
                {
                    n1 = 70+ae_randominteger(70, _state);
                    n2 = n1;
                }
                if( tasktype==2||tasktype==3 )
                {
                    n1 = 100;
                    n2 = n1;
                }
                for(n=n1; n<=n2; n++)
                {
                    ae_matrix_set_length(&xy, n-1+1, m+1, _state);
                    ae_vector_set_length(&xy0, n-1+1, _state);
                    ae_vector_set_length(&s, n-1+1, _state);
                    hstep = 0.001;
                    noiselevel = 0.2;
                    
                    /*
                     * Prepare task
                     */
                    if( tasktype==0 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            xy.ptr.pp_double[i][0] = 2*ae_randomreal(_state)-1;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=1; j<=m-1; j++)
                            {
                                xy.ptr.pp_double[i][j] = xy.ptr.pp_double[i][0]*xy.ptr.pp_double[i][j-1];
                            }
                        }
                        sinshift = ae_randomreal(_state)*ae_pi;
                        for(i=0; i<=n-1; i++)
                        {
                            xy0.ptr.p_double[i] = ae_sin(sinshift+ae_pi*0.5*(xy.ptr.pp_double[i][0]+1), _state);
                            xy.ptr.pp_double[i][m] = xy0.ptr.p_double[i]+noiselevel*testlinregunit_generatenormal((double)(0), (double)(1), _state);
                        }
                    }
                    if( tasktype==1 )
                    {
                        ae_assert(m==9, "Assertion failed", _state);
                        ae_vector_set_length(&ta, 8+1, _state);
                        ta.ptr.p_double[0] = (double)(1);
                        ta.ptr.p_double[1] = (double)(2);
                        ta.ptr.p_double[2] = (double)(3);
                        ta.ptr.p_double[3] = 0.25;
                        ta.ptr.p_double[4] = 0.5;
                        ta.ptr.p_double[5] = 0.75;
                        ta.ptr.p_double[6] = 0.06;
                        ta.ptr.p_double[7] = 0.12;
                        ta.ptr.p_double[8] = 0.18;
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                xy.ptr.pp_double[i][j] = (double)(0);
                            }
                            xy.ptr.pp_double[i][0+i%3] = (double)(1);
                            xy.ptr.pp_double[i][3+i/3%3] = (double)(1);
                            xy.ptr.pp_double[i][6+i/9%3] = (double)(1);
                            v = ae_v_dotproduct(&xy.ptr.pp_double[i][0], 1, &ta.ptr.p_double[0], 1, ae_v_len(0,8));
                            xy0.ptr.p_double[i] = v;
                            xy.ptr.pp_double[i][m] = v+noiselevel*testlinregunit_generatenormal((double)(0), (double)(1), _state);
                        }
                    }
                    if( tasktype==2||tasktype==3 )
                    {
                        ae_assert(m==9, "Assertion failed", _state);
                        ae_vector_set_length(&ta, 8+1, _state);
                        ta.ptr.p_double[0] = (double)(1);
                        ta.ptr.p_double[1] = (double)(-2);
                        ta.ptr.p_double[2] = (double)(3);
                        ta.ptr.p_double[3] = 0.25;
                        ta.ptr.p_double[4] = -0.5;
                        ta.ptr.p_double[5] = 0.75;
                        ta.ptr.p_double[6] = -0.06;
                        ta.ptr.p_double[7] = 0.12;
                        ta.ptr.p_double[8] = -0.18;
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                if( tasktype==2 )
                                {
                                    xy.ptr.pp_double[i][j] = 1+testlinregunit_generatenormal((double)(0), (double)(3), _state);
                                }
                                else
                                {
                                    xy.ptr.pp_double[i][j] = 1+testlinregunit_generatenormal((double)(0), 0.05, _state);
                                }
                            }
                            v = ae_v_dotproduct(&xy.ptr.pp_double[i][0], 1, &ta.ptr.p_double[0], 1, ae_v_len(0,8));
                            xy0.ptr.p_double[i] = v;
                            xy.ptr.pp_double[i][m] = v+noiselevel*testlinregunit_generatenormal((double)(0), (double)(1), _state);
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        s.ptr.p_double[i] = 1+ae_randomreal(_state);
                    }
                    
                    /*
                     * Solve (using S-variant, non-S-variant is not tested)
                     */
                    if( modeltype==0 )
                    {
                        lrbuilds(&xy, &s, n, m, &info, &wt, &ar, _state);
                    }
                    else
                    {
                        lrbuildzs(&xy, &s, n, m, &info, &wt, &ar, _state);
                    }
                    if( info!=1 )
                    {
                        grconverrors = ae_true;
                        continue;
                    }
                    lrunpack(&wt, &tmpweights, &tmpi, _state);
                    
                    /*
                     * LRProcess test
                     */
                    ae_vector_set_length(&x, m-1+1, _state);
                    v = tmpweights.ptr.p_double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        v = v+tmpweights.ptr.p_double[i]*x.ptr.p_double[i];
                    }
                    grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-lrprocess(&wt, &x, _state), _state)/ae_maxreal(ae_fabs(v, _state), (double)(1), _state),threshold);
                    
                    /*
                     * LRPack test
                     */
                    lrpack(&tmpweights, m, &wt2, _state);
                    ae_vector_set_length(&x, m-1+1, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    v = lrprocess(&wt, &x, _state);
                    grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-lrprocess(&wt2, &x, _state), _state)/ae_fabs(v, _state),threshold);
                    
                    /*
                     * Optimality test
                     */
                    for(k=0; k<=m; k++)
                    {
                        if( modeltype==1&&k==m )
                        {
                            
                            /*
                             * 0-type models (with non-zero constant term)
                             * are tested for optimality of all coefficients.
                             *
                             * 1-type models (with zero constant term)
                             * are tested for optimality of non-constant terms only.
                             */
                            continue;
                        }
                        f = (double)(0);
                        fp = (double)(0);
                        fm = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            v = tmpweights.ptr.p_double[m];
                            for(j=0; j<=m-1; j++)
                            {
                                v = v+xy.ptr.pp_double[i][j]*tmpweights.ptr.p_double[j];
                            }
                            f = f+ae_sqr((v-xy.ptr.pp_double[i][m])/s.ptr.p_double[i], _state);
                            if( k<m )
                            {
                                vv = xy.ptr.pp_double[i][k];
                            }
                            else
                            {
                                vv = (double)(1);
                            }
                            fp = fp+ae_sqr((v+vv*hstep-xy.ptr.pp_double[i][m])/s.ptr.p_double[i], _state);
                            fm = fm+ae_sqr((v-vv*hstep-xy.ptr.pp_double[i][m])/s.ptr.p_double[i], _state);
                        }
                        gropterrors = (gropterrors||ae_fp_greater(f,fp))||ae_fp_greater(f,fm);
                    }
                    
                    /*
                     * Covariance matrix test:
                     * generate random vector, project coefficients on it,
                     * compare variance of projection with estimate provided
                     * by cov.matrix
                     */
                    ae_vector_set_length(&ta, estpasscount-1+1, _state);
                    ae_vector_set_length(&tb, m+1, _state);
                    ae_vector_set_length(&tc, m+1, _state);
                    ae_matrix_set_length(&xy2, n-1+1, m+1, _state);
                    for(i=0; i<=m; i++)
                    {
                        tb.ptr.p_double[i] = testlinregunit_generatenormal((double)(0), (double)(1), _state);
                    }
                    for(epass=0; epass<=estpasscount-1; epass++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            ae_v_move(&xy2.ptr.pp_double[i][0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
                            xy2.ptr.pp_double[i][m] = xy0.ptr.p_double[i]+s.ptr.p_double[i]*testlinregunit_generatenormal((double)(0), (double)(1), _state);
                        }
                        if( modeltype==0 )
                        {
                            lrbuilds(&xy2, &s, n, m, &info, &wt, &ar2, _state);
                        }
                        else
                        {
                            lrbuildzs(&xy2, &s, n, m, &info, &wt, &ar2, _state);
                        }
                        if( info!=1 )
                        {
                            ta.ptr.p_double[epass] = (double)(0);
                            grconverrors = ae_true;
                            continue;
                        }
                        lrunpack(&wt, &w2, &tmpi, _state);
                        v = ae_v_dotproduct(&tb.ptr.p_double[0], 1, &w2.ptr.p_double[0], 1, ae_v_len(0,m));
                        ta.ptr.p_double[epass] = v;
                    }
                    testlinregunit_calculatemv(&ta, estpasscount, &mean, &means, &stddev, &stddevs, _state);
                    for(i=0; i<=m; i++)
                    {
                        v = ae_v_dotproduct(&tb.ptr.p_double[0], 1, &ar.c.ptr.pp_double[0][i], ar.c.stride, ae_v_len(0,m));
                        tc.ptr.p_double[i] = v;
                    }
                    v = ae_v_dotproduct(&tc.ptr.p_double[0], 1, &tb.ptr.p_double[0], 1, ae_v_len(0,m));
                    grcoverrors = grcoverrors||ae_fp_greater_eq(ae_fabs((ae_sqrt(v, _state)-stddev)/stddevs, _state),sigmathreshold);
                    
                    /*
                     * Test for the fast CV error:
                     * calculate CV error by definition (leaving out N
                     * points and recalculating solution).
                     *
                     * Test for the training set error
                     */
                    cvrmserror = (double)(0);
                    cvavgerror = (double)(0);
                    cvavgrelerror = (double)(0);
                    rmserror = (double)(0);
                    avgerror = (double)(0);
                    avgrelerror = (double)(0);
                    ae_matrix_set_length(&xy2, n-2+1, m+1, _state);
                    ae_vector_set_length(&s2, n-2+1, _state);
                    for(i=0; i<=n-2; i++)
                    {
                        ae_v_move(&xy2.ptr.pp_double[i][0], 1, &xy.ptr.pp_double[i+1][0], 1, ae_v_len(0,m));
                        s2.ptr.p_double[i] = s.ptr.p_double[i+1];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        
                        /*
                         * Trn
                         */
                        v = ae_v_dotproduct(&xy.ptr.pp_double[i][0], 1, &tmpweights.ptr.p_double[0], 1, ae_v_len(0,m-1));
                        v = v+tmpweights.ptr.p_double[m];
                        rmserror = rmserror+ae_sqr(v-xy.ptr.pp_double[i][m], _state);
                        avgerror = avgerror+ae_fabs(v-xy.ptr.pp_double[i][m], _state);
                        avgrelerror = avgrelerror+ae_fabs((v-xy.ptr.pp_double[i][m])/xy.ptr.pp_double[i][m], _state);
                        
                        /*
                         * CV: non-defect vectors only
                         */
                        nondefect = ae_true;
                        for(k=0; k<=ar.ncvdefects-1; k++)
                        {
                            if( ar.cvdefects.ptr.p_int[k]==i )
                            {
                                nondefect = ae_false;
                            }
                        }
                        if( nondefect )
                        {
                            if( modeltype==0 )
                            {
                                lrbuilds(&xy2, &s2, n-1, m, &info2, &wt, &ar2, _state);
                            }
                            else
                            {
                                lrbuildzs(&xy2, &s2, n-1, m, &info2, &wt, &ar2, _state);
                            }
                            if( info2!=1 )
                            {
                                grconverrors = ae_true;
                                continue;
                            }
                            lrunpack(&wt, &w2, &tmpi, _state);
                            v = ae_v_dotproduct(&xy.ptr.pp_double[i][0], 1, &w2.ptr.p_double[0], 1, ae_v_len(0,m-1));
                            v = v+w2.ptr.p_double[m];
                            cvrmserror = cvrmserror+ae_sqr(v-xy.ptr.pp_double[i][m], _state);
                            cvavgerror = cvavgerror+ae_fabs(v-xy.ptr.pp_double[i][m], _state);
                            cvavgrelerror = cvavgrelerror+ae_fabs((v-xy.ptr.pp_double[i][m])/xy.ptr.pp_double[i][m], _state);
                        }
                        
                        /*
                         * Next set
                         */
                        if( i!=n-1 )
                        {
                            ae_v_move(&xy2.ptr.pp_double[i][0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,m));
                            s2.ptr.p_double[i] = s.ptr.p_double[i];
                        }
                    }
                    cvrmserror = ae_sqrt(cvrmserror/(n-ar.ncvdefects), _state);
                    cvavgerror = cvavgerror/(n-ar.ncvdefects);
                    cvavgrelerror = cvavgrelerror/(n-ar.ncvdefects);
                    rmserror = ae_sqrt(rmserror/n, _state);
                    avgerror = avgerror/n;
                    avgrelerror = avgrelerror/n;
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.cvrmserror/cvrmserror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.cvavgerror/cvavgerror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.cvavgrelerror/cvavgrelerror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.rmserror/rmserror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.avgerror/avgerror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.avgrelerror/avgrelerror, _state), _state),ae_log(1+1.0E-5, _state));
                }
            }
        }
    }
    
    /*
     * Additional subroutines
     */
    for(pass=1; pass<=50; pass++)
    {
        n = 2;
        do
        {
            noiselevel = ae_randomreal(_state)+0.1;
            tasklevel = 2*ae_randomreal(_state)-1;
        }
        while(ae_fp_less_eq(ae_fabs(noiselevel-tasklevel, _state),0.05));
        ae_matrix_set_length(&xy, 3*n-1+1, 1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            xy.ptr.pp_double[3*i+0][0] = (double)(i);
            xy.ptr.pp_double[3*i+1][0] = (double)(i);
            xy.ptr.pp_double[3*i+2][0] = (double)(i);
            xy.ptr.pp_double[3*i+0][1] = tasklevel-noiselevel;
            xy.ptr.pp_double[3*i+1][1] = tasklevel;
            xy.ptr.pp_double[3*i+2][1] = tasklevel+noiselevel;
        }
        lrbuild(&xy, 3*n, 1, &info, &wt, &ar, _state);
        if( info==1 )
        {
            lrunpack(&wt, &tmpweights, &tmpi, _state);
            v = lrrmserror(&wt, &xy, 3*n, _state);
            grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-noiselevel*ae_sqrt((double)2/(double)3, _state), _state),threshold);
            v = lravgerror(&wt, &xy, 3*n, _state);
            grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-noiselevel*((double)2/(double)3), _state),threshold);
            v = lravgrelerror(&wt, &xy, 3*n, _state);
            vv = (ae_fabs(noiselevel/(tasklevel-noiselevel), _state)+ae_fabs(noiselevel/(tasklevel+noiselevel), _state))/3;
            grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-vv, _state),threshold*vv);
        }
        else
        {
            grothererrors = ae_true;
        }
        for(i=0; i<=n-1; i++)
        {
            xy.ptr.pp_double[3*i+0][0] = (double)(i);
            xy.ptr.pp_double[3*i+1][0] = (double)(i);
            xy.ptr.pp_double[3*i+2][0] = (double)(i);
            xy.ptr.pp_double[3*i+0][1] = -noiselevel;
            xy.ptr.pp_double[3*i+1][1] = (double)(0);
            xy.ptr.pp_double[3*i+2][1] = noiselevel;
        }
        lrbuild(&xy, 3*n, 1, &info, &wt, &ar, _state);
        if( info==1 )
        {
            lrunpack(&wt, &tmpweights, &tmpi, _state);
            v = lravgrelerror(&wt, &xy, 3*n, _state);
            grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-1, _state),threshold);
        }
        else
        {
            grothererrors = ae_true;
        }
    }
    for(pass=1; pass<=10; pass++)
    {
        m = 1+ae_randominteger(5, _state);
        n = 10+ae_randominteger(10, _state);
        ae_matrix_set_length(&xy, n-1+1, m+1, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m; j++)
            {
                xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        lrbuild(&xy, n, m, &info, &w, &ar, _state);
        if( info<0 )
        {
            grothererrors = ae_true;
            break;
        }
        ae_vector_set_length(&x1, m-1+1, _state);
        ae_vector_set_length(&x2, m-1+1, _state);
        
        /*
         * Same inputs on original leads to same outputs
         * on copy created using LRCopy
         */
        testlinregunit_unsetlr(&wt, _state);
        lrcopy(&w, &wt, _state);
        for(i=0; i<=m-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        y1 = lrprocess(&w, &x1, _state);
        y2 = lrprocess(&wt, &x2, _state);
        allsame = ae_fp_eq(y1,y2);
        grothererrors = grothererrors||!allsame;
    }
    
    /*
     * TODO: Degenerate tests (when design matrix and right part are zero)
     */
    
    /*
     * Final report
     */
    waserrors = (((((slerrors||slcerrors)||gropterrors)||grcoverrors)||gresterrors)||grothererrors)||grconverrors;
    if( !silent )
    {
        printf("REGRESSION TEST\n");
        printf("STRAIGHT LINE REGRESSION:                ");
        if( !slerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("STRAIGHT LINE REGRESSION CONVERGENCE:    ");
        if( !slcerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("GENERAL LINEAR REGRESSION:               ");
        if( !((((gropterrors||grcoverrors)||gresterrors)||grothererrors)||grconverrors) )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OPTIMALITY:                            ");
        if( !gropterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* COV. MATRIX:                           ");
        if( !grcoverrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* ERROR ESTIMATES:                       ");
        if( !gresterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* CONVERGENCE:                           ");
        if( !grconverrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OTHER SUBROUTINES:                     ");
        if( !grothererrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Task generation. Meaningless task, just random numbers.
*************************************************************************/
static void testlinregunit_generaterandomtask(double xl,
     double xr,
     ae_bool randomx,
     double ymin,
     double ymax,
     double smin,
     double smax,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_matrix_set_length(xy, n-1+1, 1+1, _state);
    ae_vector_set_length(s, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        if( randomx )
        {
            xy->ptr.pp_double[i][0] = xl+(xr-xl)*ae_randomreal(_state);
        }
        else
        {
            xy->ptr.pp_double[i][0] = xl+(xr-xl)*i/(n-1);
        }
        xy->ptr.pp_double[i][1] = ymin+(ymax-ymin)*ae_randomreal(_state);
        s->ptr.p_double[i] = smin+(smax-smin)*ae_randomreal(_state);
    }
}


/*************************************************************************
Task generation.
*************************************************************************/
static void testlinregunit_generatetask(double a,
     double b,
     double xl,
     double xr,
     ae_bool randomx,
     double smin,
     double smax,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_matrix_set_length(xy, n-1+1, 1+1, _state);
    ae_vector_set_length(s, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        if( randomx )
        {
            xy->ptr.pp_double[i][0] = xl+(xr-xl)*ae_randomreal(_state);
        }
        else
        {
            xy->ptr.pp_double[i][0] = xl+(xr-xl)*i/(n-1);
        }
        s->ptr.p_double[i] = smin+(smax-smin)*ae_randomreal(_state);
        xy->ptr.pp_double[i][1] = a+b*xy->ptr.pp_double[i][0]+testlinregunit_generatenormal((double)(0), s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
Task generation.
y[i] are filled based on A, B, X[I], S[I]
*************************************************************************/
static void testlinregunit_filltaskwithy(double a,
     double b,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    for(i=0; i<=n-1; i++)
    {
        xy->ptr.pp_double[i][1] = a+b*xy->ptr.pp_double[i][0]+testlinregunit_generatenormal((double)(0), s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
Normal random numbers
*************************************************************************/
static double testlinregunit_generatenormal(double mean,
     double sigma,
     ae_state *_state)
{
    double u;
    double v;
    double sum;
    double result;


    result = mean;
    for(;;)
    {
        u = (2*ae_randominteger(2, _state)-1)*ae_randomreal(_state);
        v = (2*ae_randominteger(2, _state)-1)*ae_randomreal(_state);
        sum = u*u+v*v;
        if( ae_fp_less(sum,(double)(1))&&ae_fp_greater(sum,(double)(0)) )
        {
            sum = ae_sqrt(-2*ae_log(sum, _state)/sum, _state);
            result = sigma*u*sum+mean;
            break;
        }
    }
    return result;
}


/*************************************************************************
Moments estimates and their errors
*************************************************************************/
static void testlinregunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state)
{
    ae_int_t i;
    double v1;
    double v2;
    double variance;

    *mean = 0;
    *means = 0;
    *stddev = 0;
    *stddevs = 0;

    *mean = (double)(0);
    *means = (double)(1);
    *stddev = (double)(0);
    *stddevs = (double)(1);
    variance = (double)(0);
    if( n<=1 )
    {
        return;
    }
    
    /*
     * Mean
     */
    for(i=0; i<=n-1; i++)
    {
        *mean = *mean+x->ptr.p_double[i];
    }
    *mean = *mean/n;
    
    /*
     * Variance (using corrected two-pass algorithm)
     */
    if( n!=1 )
    {
        v1 = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v1 = v1+ae_sqr(x->ptr.p_double[i]-(*mean), _state);
        }
        v2 = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            v2 = v2+(x->ptr.p_double[i]-(*mean));
        }
        v2 = ae_sqr(v2, _state)/n;
        variance = (v1-v2)/(n-1);
        if( ae_fp_less(variance,(double)(0)) )
        {
            variance = (double)(0);
        }
        *stddev = ae_sqrt(variance, _state);
    }
    
    /*
     * Errors
     */
    *means = *stddev/ae_sqrt((double)(n), _state);
    *stddevs = *stddev*ae_sqrt((double)(2), _state)/ae_sqrt((double)(n-1), _state);
}


/*************************************************************************
Unsets LR
*************************************************************************/
static void testlinregunit_unsetlr(linearmodel* lr, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t info;
    lrreport rep;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _lrreport_init(&rep, _state, ae_true);

    ae_matrix_set_length(&xy, 5+1, 1+1, _state);
    for(i=0; i<=5; i++)
    {
        xy.ptr.pp_double[i][0] = (double)(0);
        xy.ptr.pp_double[i][1] = (double)(0);
    }
    lrbuild(&xy, 6, 1, &info, lr, &rep, _state);
    ae_assert(info>0, "Assertion failed", _state);
    ae_frame_leave(_state);
}








ae_bool testfilters(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool smaerrors;
    ae_bool emaerrors;
    ae_bool lrmaerrors;
    ae_bool result;


    smaerrors = testsma(ae_true, _state);
    emaerrors = testema(ae_true, _state);
    lrmaerrors = testlrma(ae_true, _state);
    
    /*
     * Final report
     */
    waserrors = (smaerrors||emaerrors)||lrmaerrors;
    if( !silent )
    {
        printf("FILTERS TEST\n");
        printf("* SMA:                                   ");
        if( !smaerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* EMA:                                   ");
        if( !emaerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* LRMA:                                  ");
        if( !lrmaerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
This function tests SMA(k) filter. It returns True on error.

Additional IsSilent parameter controls detailed error reporting.
*************************************************************************/
ae_bool testsma(ae_bool issilent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_bool precomputederrors;
    ae_bool zerohandlingerrors;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    threshold = 1000*ae_machineepsilon;
    if( !issilent )
    {
        printf("SMA(K) TEST\n");
    }
    
    /*
     * Test several pre-computed problems.
     *
     * NOTE: tests below rely on the fact that floating point
     *       additions and subtractions are exact when dealing
     *       with integer values.
     */
    precomputederrors = ae_false;
    ae_vector_set_length(&x, 1, _state);
    x.ptr.p_double[0] = (double)(7);
    filtersma(&x, 1, 1, _state);
    precomputederrors = precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7));
    ae_vector_set_length(&x, 3, _state);
    x.ptr.p_double[0] = (double)(7);
    x.ptr.p_double[1] = (double)(8);
    x.ptr.p_double[2] = (double)(9);
    filtersma(&x, 3, 1, _state);
    precomputederrors = ((precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7)))||ae_fp_neq(x.ptr.p_double[1],(double)(8)))||ae_fp_neq(x.ptr.p_double[2],(double)(9));
    filtersma(&x, 3, 2, _state);
    precomputederrors = ((precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7)))||ae_fp_neq(x.ptr.p_double[1],7.5))||ae_fp_neq(x.ptr.p_double[2],8.5);
    ae_vector_set_length(&x, 3, _state);
    x.ptr.p_double[0] = (double)(7);
    x.ptr.p_double[1] = (double)(8);
    x.ptr.p_double[2] = (double)(9);
    filtersma(&x, 3, 4, _state);
    precomputederrors = ((precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7)))||ae_fp_neq(x.ptr.p_double[1],7.5))||ae_fp_neq(x.ptr.p_double[2],(double)(8));
    
    /*
     * Test zero-handling:
     * a) when we have non-zero sequence (N1 elements) followed by zero sequence
     *    (N2 elements), then first N1+K-1 elements of the processed sequence are
     *    non-zero, but elements since (N1+K)th must be exactly zero.
     * b) similar property holds for zero sequence followed by non-zero one
     *
     * Naive implementation of SMA does not have such property.
     *
     * NOTE: it is important to initialize X with non-integer elements with long
     * binary mantissas, because this test tries to test behaviour in the presence
     * of roundoff errors, and it will be useless when used with integer inputs.
     */
    zerohandlingerrors = ae_false;
    ae_vector_set_length(&x, 10, _state);
    x.ptr.p_double[0] = ae_sqrt((double)(2), _state);
    x.ptr.p_double[1] = ae_sqrt((double)(3), _state);
    x.ptr.p_double[2] = ae_sqrt((double)(5), _state);
    x.ptr.p_double[3] = ae_sqrt((double)(6), _state);
    x.ptr.p_double[4] = ae_sqrt((double)(7), _state);
    x.ptr.p_double[5] = (double)(0);
    x.ptr.p_double[6] = (double)(0);
    x.ptr.p_double[7] = (double)(0);
    x.ptr.p_double[8] = (double)(0);
    x.ptr.p_double[9] = (double)(0);
    filtersma(&x, 10, 3, _state);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_sqrt((double)(2), _state), _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[1]-(ae_sqrt((double)(2), _state)+ae_sqrt((double)(3), _state))/2, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-(ae_sqrt((double)(2), _state)+ae_sqrt((double)(3), _state)+ae_sqrt((double)(5), _state))/3, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[3]-(ae_sqrt((double)(3), _state)+ae_sqrt((double)(5), _state)+ae_sqrt((double)(6), _state))/3, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[4]-(ae_sqrt((double)(5), _state)+ae_sqrt((double)(6), _state)+ae_sqrt((double)(7), _state))/3, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[5]-(ae_sqrt((double)(6), _state)+ae_sqrt((double)(7), _state))/3, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[6]-ae_sqrt((double)(7), _state)/3, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_neq(x.ptr.p_double[7],(double)(0));
    zerohandlingerrors = zerohandlingerrors||ae_fp_neq(x.ptr.p_double[8],(double)(0));
    zerohandlingerrors = zerohandlingerrors||ae_fp_neq(x.ptr.p_double[9],(double)(0));
    x.ptr.p_double[0] = (double)(0);
    x.ptr.p_double[1] = (double)(0);
    x.ptr.p_double[2] = (double)(0);
    x.ptr.p_double[3] = (double)(0);
    x.ptr.p_double[4] = (double)(0);
    x.ptr.p_double[5] = ae_sqrt((double)(2), _state);
    x.ptr.p_double[6] = ae_sqrt((double)(3), _state);
    x.ptr.p_double[7] = ae_sqrt((double)(5), _state);
    x.ptr.p_double[8] = ae_sqrt((double)(6), _state);
    x.ptr.p_double[9] = ae_sqrt((double)(7), _state);
    filtersma(&x, 10, 3, _state);
    zerohandlingerrors = zerohandlingerrors||ae_fp_neq(x.ptr.p_double[0],(double)(0));
    zerohandlingerrors = zerohandlingerrors||ae_fp_neq(x.ptr.p_double[1],(double)(0));
    zerohandlingerrors = zerohandlingerrors||ae_fp_neq(x.ptr.p_double[2],(double)(0));
    zerohandlingerrors = zerohandlingerrors||ae_fp_neq(x.ptr.p_double[3],(double)(0));
    zerohandlingerrors = zerohandlingerrors||ae_fp_neq(x.ptr.p_double[4],(double)(0));
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[5]-ae_sqrt((double)(2), _state)/3, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[6]-(ae_sqrt((double)(2), _state)+ae_sqrt((double)(3), _state))/3, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[7]-(ae_sqrt((double)(2), _state)+ae_sqrt((double)(3), _state)+ae_sqrt((double)(5), _state))/3, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[8]-(ae_sqrt((double)(3), _state)+ae_sqrt((double)(5), _state)+ae_sqrt((double)(6), _state))/3, _state),threshold);
    zerohandlingerrors = zerohandlingerrors||ae_fp_greater(ae_fabs(x.ptr.p_double[9]-(ae_sqrt((double)(5), _state)+ae_sqrt((double)(6), _state)+ae_sqrt((double)(7), _state))/3, _state),threshold);
    
    /*
     * Final result
     */
    result = precomputederrors||zerohandlingerrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests EMA(alpha) filter. It returns True on error.

Additional IsSilent parameter controls detailed error reporting.
*************************************************************************/
ae_bool testema(ae_bool issilent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_bool precomputederrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    if( !issilent )
    {
        printf("EMA(alpha) TEST\n");
    }
    
    /*
     * Test several pre-computed problems.
     *
     * NOTE: tests below rely on the fact that floating point
     *       additions and subtractions are exact when dealing
     *       with integer values.
     */
    precomputederrors = ae_false;
    ae_vector_set_length(&x, 1, _state);
    x.ptr.p_double[0] = (double)(7);
    filterema(&x, 1, 1.0, _state);
    precomputederrors = precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7));
    filterema(&x, 1, 0.5, _state);
    precomputederrors = precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7));
    ae_vector_set_length(&x, 3, _state);
    x.ptr.p_double[0] = (double)(7);
    x.ptr.p_double[1] = (double)(8);
    x.ptr.p_double[2] = (double)(9);
    filterema(&x, 3, 1.0, _state);
    precomputederrors = ((precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7)))||ae_fp_neq(x.ptr.p_double[1],(double)(8)))||ae_fp_neq(x.ptr.p_double[2],(double)(9));
    filterema(&x, 3, 0.5, _state);
    precomputederrors = ((precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7)))||ae_fp_neq(x.ptr.p_double[1],7.5))||ae_fp_neq(x.ptr.p_double[2],8.25);
    
    /*
     * Final result
     */
    result = precomputederrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests LRMA(k) filter. It returns True on error.

Additional IsSilent parameter controls detailed error reporting.
*************************************************************************/
ae_bool testlrma(ae_bool issilent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_bool precomputederrors;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    threshold = 1000*ae_machineepsilon;
    if( !issilent )
    {
        printf("LRMA(K) TEST\n");
    }
    precomputederrors = ae_false;
    
    /*
     * First, check that filter does not changes points for K=1 or K=2
     */
    ae_vector_set_length(&x, 1, _state);
    x.ptr.p_double[0] = (double)(7);
    filterlrma(&x, 1, 1, _state);
    precomputederrors = precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7));
    ae_vector_set_length(&x, 6, _state);
    x.ptr.p_double[0] = (double)(7);
    x.ptr.p_double[1] = (double)(8);
    x.ptr.p_double[2] = (double)(9);
    x.ptr.p_double[3] = (double)(10);
    x.ptr.p_double[4] = (double)(11);
    x.ptr.p_double[5] = (double)(12);
    filterlrma(&x, 6, 1, _state);
    precomputederrors = (((((precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7)))||ae_fp_neq(x.ptr.p_double[1],(double)(8)))||ae_fp_neq(x.ptr.p_double[2],(double)(9)))||ae_fp_neq(x.ptr.p_double[3],(double)(10)))||ae_fp_neq(x.ptr.p_double[4],(double)(11)))||ae_fp_neq(x.ptr.p_double[5],(double)(12));
    filterlrma(&x, 6, 2, _state);
    precomputederrors = (((((precomputederrors||ae_fp_neq(x.ptr.p_double[0],(double)(7)))||ae_fp_neq(x.ptr.p_double[1],(double)(8)))||ae_fp_neq(x.ptr.p_double[2],(double)(9)))||ae_fp_neq(x.ptr.p_double[3],(double)(10)))||ae_fp_neq(x.ptr.p_double[4],(double)(11)))||ae_fp_neq(x.ptr.p_double[5],(double)(12));
    
    /*
     * Check several precomputed problems
     */
    ae_vector_set_length(&x, 6, _state);
    x.ptr.p_double[0] = (double)(7);
    x.ptr.p_double[1] = (double)(8);
    x.ptr.p_double[2] = (double)(9);
    x.ptr.p_double[3] = (double)(10);
    x.ptr.p_double[4] = (double)(11);
    x.ptr.p_double[5] = (double)(12);
    filterlrma(&x, 6, 3, _state);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-7, _state),threshold);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[1]-8, _state),threshold);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-9, _state),threshold);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[3]-10, _state),threshold);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[4]-11, _state),threshold);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[5]-12, _state),threshold);
    ae_vector_set_length(&x, 6, _state);
    x.ptr.p_double[0] = (double)(7);
    x.ptr.p_double[1] = (double)(8);
    x.ptr.p_double[2] = (double)(8);
    x.ptr.p_double[3] = (double)(9);
    x.ptr.p_double[4] = (double)(12);
    x.ptr.p_double[5] = (double)(12);
    filterlrma(&x, 6, 3, _state);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-7.0000000000, _state),1.0E-5);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[1]-8.0000000000, _state),1.0E-5);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-8.1666666667, _state),1.0E-5);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[3]-8.8333333333, _state),1.0E-5);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[4]-11.6666666667, _state),1.0E-5);
    precomputederrors = precomputederrors||ae_fp_greater(ae_fabs(x.ptr.p_double[5]-12.5000000000, _state),1.0E-5);
    
    /*
     * Final result
     */
    result = precomputederrors;
    ae_frame_leave(_state);
    return result;
}



static void testmcpdunit_testsimple(ae_bool* err, ae_state *_state);
static void testmcpdunit_testentryexit(ae_bool* err, ae_state *_state);
static void testmcpdunit_testec(ae_bool* err, ae_state *_state);
static void testmcpdunit_testbc(ae_bool* err, ae_state *_state);
static void testmcpdunit_testlc(ae_bool* err, ae_state *_state);
static void testmcpdunit_createee(ae_int_t n,
     ae_int_t entrystate,
     ae_int_t exitstate,
     mcpdstate* s,
     ae_state *_state);





ae_bool testmcpd(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool simpleerrors;
    ae_bool entryexiterrors;
    ae_bool ecerrors;
    ae_bool bcerrors;
    ae_bool lcerrors;
    ae_bool othererrors;
    ae_bool result;


    
    /*
     * Init
     */
    waserrors = ae_false;
    othererrors = ae_false;
    simpleerrors = ae_false;
    entryexiterrors = ae_false;
    ecerrors = ae_false;
    bcerrors = ae_false;
    lcerrors = ae_false;
    
    /*
     * Test
     */
    testmcpdunit_testsimple(&simpleerrors, _state);
    testmcpdunit_testentryexit(&entryexiterrors, _state);
    testmcpdunit_testec(&ecerrors, _state);
    testmcpdunit_testbc(&bcerrors, _state);
    testmcpdunit_testlc(&lcerrors, _state);
    
    /*
     * Final report
     */
    waserrors = ((((othererrors||simpleerrors)||entryexiterrors)||ecerrors)||bcerrors)||lcerrors;
    if( !silent )
    {
        printf("MCPD TEST\n");
        printf("TOTAL RESULTS:                           ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* SIMPLE:                                ");
        if( !simpleerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* ENTRY/EXIT:                            ");
        if( !entryexiterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* EQUALITY CONSTRAINTS:                  ");
        if( !ecerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* BOUND CONSTRAINTS:                     ");
        if( !bcerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* LINEAR CONSTRAINTS:                    ");
        if( !lcerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OTHER PROPERTIES:                      ");
        if( !othererrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Simple test with no "entry"/"exit" states

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testsimple(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix pexact;
    ae_matrix xy;
    double threshold;
    ae_int_t i;
    ae_int_t j;
    double v;
    double v0;
    ae_matrix p;
    mcpdstate s;
    mcpdreport rep;
    double offdiagonal;

    ae_frame_make(_state, &_frame_block);
    memset(&pexact, 0, sizeof(pexact));
    memset(&xy, 0, sizeof(xy));
    memset(&p, 0, sizeof(p));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&pexact, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    threshold = 1.0E-2;
    
    /*
     * First test:
     * * N-dimensional problem
     * * proportional data
     * * no "entry"/"exit" states
     * * N tracks, each includes only two states
     * * first record in I-th track is [0 ... 1 ... 0] with 1 is in I-th position
     * * all tracks are modelled using randomly generated transition matrix P
     */
    for(n=1; n<=5; n++)
    {
        
        /*
         * Initialize "exact" P:
         * * fill by random values
         * * make sure that each column sums to non-zero value
         * * normalize
         */
        ae_matrix_set_length(&pexact, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                pexact.ptr.pp_double[i][j] = ae_randomreal(_state);
            }
        }
        for(j=0; j<=n-1; j++)
        {
            i = ae_randominteger(n, _state);
            pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]+0.1;
        }
        for(j=0; j<=n-1; j++)
        {
            v = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                v = v+pexact.ptr.pp_double[i][j];
            }
            for(i=0; i<=n-1; i++)
            {
                pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]/v;
            }
        }
        
        /*
         * Initialize solver:
         * * create object
         * * add tracks
         */
        mcpdcreate(n, &s, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_matrix_set_length(&xy, 2, n, _state);
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[0][j] = (double)(0);
            }
            xy.ptr.pp_double[0][i] = (double)(1);
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[1][j] = pexact.ptr.pp_double[j][i];
            }
            mcpdaddtrack(&s, &xy, 2, _state);
        }
        
        /*
         * Solve and test
         */
        mcpdsolve(&s, _state);
        mcpdresults(&s, &p, &rep, _state);
        if( rep.terminationtype>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    *err = *err||ae_fp_greater(ae_fabs(p.ptr.pp_double[i][j]-pexact.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
        else
        {
            *err = ae_true;
        }
    }
    
    /*
     * Second test:
     * * N-dimensional problem
     * * proportional data
     * * no "entry"/"exit" states
     * * N tracks, each includes only two states
     * * first record in I-th track is [0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position
     * * all tracks are modelled using randomly generated transition matrix P
     */
    offdiagonal = 0.1;
    for(n=1; n<=5; n++)
    {
        
        /*
         * Initialize "exact" P:
         * * fill by random values
         * * make sure that each column sums to non-zero value
         * * normalize
         */
        ae_matrix_set_length(&pexact, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                pexact.ptr.pp_double[i][j] = ae_randomreal(_state);
            }
        }
        for(j=0; j<=n-1; j++)
        {
            i = ae_randominteger(n, _state);
            pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]+0.1;
        }
        for(j=0; j<=n-1; j++)
        {
            v = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                v = v+pexact.ptr.pp_double[i][j];
            }
            for(i=0; i<=n-1; i++)
            {
                pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]/v;
            }
        }
        
        /*
         * Initialize solver:
         * * create object
         * * add tracks
         */
        mcpdcreate(n, &s, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_matrix_set_length(&xy, 2, n, _state);
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[0][j] = (double)(0);
            }
            
            /*
             * "main" element
             */
            xy.ptr.pp_double[0][i] = 1.0-2*offdiagonal;
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[1][j] = (1.0-2*offdiagonal)*pexact.ptr.pp_double[j][i];
            }
            
            /*
             * off-diagonal ones
             */
            if( i>0 )
            {
                xy.ptr.pp_double[0][i-1] = offdiagonal;
                for(j=0; j<=n-1; j++)
                {
                    xy.ptr.pp_double[1][j] = xy.ptr.pp_double[1][j]+offdiagonal*pexact.ptr.pp_double[j][i-1];
                }
            }
            if( i<n-1 )
            {
                xy.ptr.pp_double[0][i+1] = offdiagonal;
                for(j=0; j<=n-1; j++)
                {
                    xy.ptr.pp_double[1][j] = xy.ptr.pp_double[1][j]+offdiagonal*pexact.ptr.pp_double[j][i+1];
                }
            }
            mcpdaddtrack(&s, &xy, 2, _state);
        }
        
        /*
         * Solve and test
         */
        mcpdsolve(&s, _state);
        mcpdresults(&s, &p, &rep, _state);
        if( rep.terminationtype>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    *err = *err||ae_fp_greater(ae_fabs(p.ptr.pp_double[i][j]-pexact.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
        else
        {
            *err = ae_true;
        }
    }
    
    /*
     * Third test:
     * * N-dimensional problem
     * * population data
     * * no "entry"/"exit" states
     * * N tracks, each includes only two states
     * * first record in I-th track is V*[0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position, V in [1,10]
     * * all tracks are modelled using randomly generated transition matrix P
     */
    offdiagonal = 0.1;
    for(n=1; n<=5; n++)
    {
        
        /*
         * Initialize "exact" P:
         * * fill by random values
         * * make sure that each column sums to non-zero value
         * * normalize
         */
        ae_matrix_set_length(&pexact, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                pexact.ptr.pp_double[i][j] = ae_randomreal(_state);
            }
        }
        for(j=0; j<=n-1; j++)
        {
            i = ae_randominteger(n, _state);
            pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]+0.1;
        }
        for(j=0; j<=n-1; j++)
        {
            v = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                v = v+pexact.ptr.pp_double[i][j];
            }
            for(i=0; i<=n-1; i++)
            {
                pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]/v;
            }
        }
        
        /*
         * Initialize solver:
         * * create object
         * * add tracks
         */
        mcpdcreate(n, &s, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_matrix_set_length(&xy, 2, n, _state);
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[0][j] = (double)(0);
            }
            
            /*
             * "main" element
             */
            v0 = 9*ae_randomreal(_state)+1;
            xy.ptr.pp_double[0][i] = v0*(1.0-2*offdiagonal);
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[1][j] = v0*(1.0-2*offdiagonal)*pexact.ptr.pp_double[j][i];
            }
            
            /*
             * off-diagonal ones
             */
            if( i>0 )
            {
                xy.ptr.pp_double[0][i-1] = v0*offdiagonal;
                for(j=0; j<=n-1; j++)
                {
                    xy.ptr.pp_double[1][j] = xy.ptr.pp_double[1][j]+v0*offdiagonal*pexact.ptr.pp_double[j][i-1];
                }
            }
            if( i<n-1 )
            {
                xy.ptr.pp_double[0][i+1] = v0*offdiagonal;
                for(j=0; j<=n-1; j++)
                {
                    xy.ptr.pp_double[1][j] = xy.ptr.pp_double[1][j]+v0*offdiagonal*pexact.ptr.pp_double[j][i+1];
                }
            }
            mcpdaddtrack(&s, &xy, 2, _state);
        }
        
        /*
         * Solve and test
         */
        mcpdsolve(&s, _state);
        mcpdresults(&s, &p, &rep, _state);
        if( rep.terminationtype>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    *err = *err||ae_fp_greater(ae_fabs(p.ptr.pp_double[i][j]-pexact.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
        else
        {
            *err = ae_true;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test for different combinations of "entry"/"exit" models

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testentryexit(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix p;
    ae_matrix pexact;
    ae_matrix xy;
    double threshold;
    ae_int_t entrystate;
    ae_int_t exitstate;
    ae_int_t entrykind;
    ae_int_t exitkind;
    ae_int_t popkind;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    mcpdstate s;
    mcpdreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&p, 0, sizeof(p));
    memset(&pexact, 0, sizeof(pexact));
    memset(&xy, 0, sizeof(xy));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&pexact, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    threshold = 1.0E-3;
    
    /*
     *
     */
    for(n=2; n<=5; n++)
    {
        for(entrykind=0; entrykind<=1; entrykind++)
        {
            for(exitkind=0; exitkind<=1; exitkind++)
            {
                for(popkind=0; popkind<=1; popkind++)
                {
                    
                    /*
                     * Generate EntryState/ExitState such that one of the following is True:
                     * * EntryState<>ExitState
                     * * EntryState=-1 or ExitState=-1
                     */
                    do
                    {
                        if( entrykind==0 )
                        {
                            entrystate = -1;
                        }
                        else
                        {
                            entrystate = ae_randominteger(n, _state);
                        }
                        if( exitkind==0 )
                        {
                            exitstate = -1;
                        }
                        else
                        {
                            exitstate = ae_randominteger(n, _state);
                        }
                    }
                    while(!((entrystate==-1||exitstate==-1)||entrystate!=exitstate));
                    
                    /*
                     * Generate transition matrix P such that:
                     * * columns corresponding to non-exit states sums to 1.0
                     * * columns corresponding to exit states sums to 0.0
                     * * rows corresponding to entry states are zero
                     */
                    ae_matrix_set_length(&pexact, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            pexact.ptr.pp_double[i][j] = (double)(1+ae_randominteger(5, _state));
                            if( i==entrystate )
                            {
                                pexact.ptr.pp_double[i][j] = 0.0;
                            }
                            if( j==exitstate )
                            {
                                pexact.ptr.pp_double[i][j] = 0.0;
                            }
                        }
                    }
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            v = v+pexact.ptr.pp_double[i][j];
                        }
                        if( ae_fp_neq(v,(double)(0)) )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]/v;
                            }
                        }
                    }
                    
                    /*
                     * Create MCPD solver
                     */
                    if( entrystate<0&&exitstate<0 )
                    {
                        mcpdcreate(n, &s, _state);
                    }
                    if( entrystate>=0&&exitstate<0 )
                    {
                        mcpdcreateentry(n, entrystate, &s, _state);
                    }
                    if( entrystate<0&&exitstate>=0 )
                    {
                        mcpdcreateexit(n, exitstate, &s, _state);
                    }
                    if( entrystate>=0&&exitstate>=0 )
                    {
                        mcpdcreateentryexit(n, entrystate, exitstate, &s, _state);
                    }
                    
                    /*
                     * Add N tracks.
                     *
                     * K-th track starts from vector with large value of
                     * K-th component and small random noise in other components.
                     *
                     * Track contains from 2 to 4 elements.
                     *
                     * Tracks contain proportional (normalized) or
                     * population data, depending on PopKind variable.
                     */
                    for(k=0; k<=n-1; k++)
                    {
                        
                        /*
                         * Generate track whose length is in 2..4
                         */
                        ae_matrix_set_length(&xy, 2+ae_randominteger(3, _state), n, _state);
                        for(j=0; j<=n-1; j++)
                        {
                            xy.ptr.pp_double[0][j] = 0.05*ae_randomreal(_state);
                        }
                        xy.ptr.pp_double[0][k] = 1+ae_randomreal(_state);
                        for(i=1; i<=xy.rows-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( j!=entrystate )
                                {
                                    v = ae_v_dotproduct(&pexact.ptr.pp_double[j][0], 1, &xy.ptr.pp_double[i-1][0], 1, ae_v_len(0,n-1));
                                    xy.ptr.pp_double[i][j] = v;
                                }
                                else
                                {
                                    xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                                }
                            }
                        }
                        
                        /*
                         * Normalize, if needed
                         */
                        if( popkind==1 )
                        {
                            for(i=0; i<=xy.rows-1; i++)
                            {
                                v = 0.0;
                                for(j=0; j<=n-1; j++)
                                {
                                    v = v+xy.ptr.pp_double[i][j];
                                }
                                if( ae_fp_greater(v,(double)(0)) )
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        xy.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]/v;
                                    }
                                }
                            }
                        }
                        
                        /*
                         * Add track
                         */
                        mcpdaddtrack(&s, &xy, xy.rows, _state);
                    }
                    
                    /*
                     * Solve and test
                     */
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    if( rep.terminationtype>0 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                *err = *err||ae_fp_greater(ae_fabs(p.ptr.pp_double[i][j]-pexact.ptr.pp_double[i][j], _state),threshold);
                            }
                        }
                    }
                    else
                    {
                        *err = ae_true;
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test equality constraints.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testec(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix p;
    ae_matrix ec;
    ae_matrix xy;
    ae_int_t entrystate;
    ae_int_t exitstate;
    ae_int_t entrykind;
    ae_int_t exitkind;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ic;
    ae_int_t jc;
    double vc;
    mcpdstate s;
    mcpdreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&p, 0, sizeof(p));
    memset(&ec, 0, sizeof(ec));
    memset(&xy, 0, sizeof(xy));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ec, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    
    /*
     * We try different problems with following properties:
     * * N is large enough - we won't have problems with inconsistent constraints
     * * first state is either "entry" or "normal"
     * * last state is either "exit" or "normal"
     * * we have one long random track
     *
     * We test several properties which are described in comments below
     */
    for(n=4; n<=6; n++)
    {
        for(entrykind=0; entrykind<=1; entrykind++)
        {
            for(exitkind=0; exitkind<=1; exitkind++)
            {
                
                /*
                 * Prepare problem
                 */
                if( entrykind==0 )
                {
                    entrystate = -1;
                }
                else
                {
                    entrystate = 0;
                }
                if( exitkind==0 )
                {
                    exitstate = -1;
                }
                else
                {
                    exitstate = n-1;
                }
                ae_matrix_set_length(&xy, 2*n, n, _state);
                for(i=0; i<=xy.rows-1; i++)
                {
                    for(j=0; j<=xy.cols-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                    }
                }
                
                /*
                 * Test that single equality constraint on non-entry
                 * non-exit elements of P is satisfied.
                 *
                 * NOTE: this test needs N>=4 because smaller values
                 * can give us inconsistent constraints
                 */
                ae_assert(n>=4, "TestEC: expectation failed", _state);
                ic = 1+ae_randominteger(n-2, _state);
                jc = 1+ae_randominteger(n-2, _state);
                vc = ae_randomreal(_state);
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdaddec(&s, ic, jc, vc, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    *err = *err||ae_fp_neq(p.ptr.pp_double[ic][jc],vc);
                }
                else
                {
                    *err = ae_true;
                }
                
                /*
                 * Test interaction with default "sum-to-one" constraint
                 * on columns of P.
                 *
                 * We set N-1 equality constraints on random non-exit column
                 * of P, which are inconsistent with this default constraint
                 * (sum will be greater that 1.0).
                 *
                 * Algorithm must detect inconsistency.
                 *
                 * NOTE:
                 * 1. we do not set constraints for the first element of
                 *    the column, because this element may be constrained by
                 *    "exit state" constraint.
                 * 2. this test needs N>=3
                 */
                ae_assert(n>=3, "TestEC: expectation failed", _state);
                jc = ae_randominteger(n-1, _state);
                vc = 0.95;
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                for(i=1; i<=n-1; i++)
                {
                    mcpdaddec(&s, i, jc, vc, _state);
                }
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                *err = *err||rep.terminationtype!=-3;
                
                /*
                 * Test interaction with constrains on entry states.
                 *
                 * When model has entry state, corresponding row of P
                 * must be zero. We try to set two kinds of constraints
                 * on random element of this row:
                 * * zero equality constraint, which must be consistent
                 * * non-zero equality constraint, which must be inconsistent
                 */
                if( entrystate>=0 )
                {
                    jc = ae_randominteger(n, _state);
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddec(&s, entrystate, jc, 0.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddec(&s, entrystate, jc, 0.5, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test interaction with constrains on exit states.
                 *
                 * When model has exit state, corresponding column of P
                 * must be zero. We try to set two kinds of constraints
                 * on random element of this column:
                 * * zero equality constraint, which must be consistent
                 * * non-zero equality constraint, which must be inconsistent
                 */
                if( exitstate>=0 )
                {
                    ic = ae_randominteger(n, _state);
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddec(&s, ic, exitstate, 0.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddec(&s, ic, exitstate, 0.5, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test SetEC() call - we constrain subset of non-entry
                 * non-exit elements and test it.
                 */
                ae_assert(n>=4, "TestEC: expectation failed", _state);
                ae_matrix_set_length(&ec, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ec.ptr.pp_double[i][j] = _state->v_nan;
                    }
                }
                for(j=1; j<=n-2; j++)
                {
                    ec.ptr.pp_double[1+ae_randominteger(n-2, _state)][j] = 0.1+0.1*ae_randomreal(_state);
                }
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetec(&s, &ec, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( ae_isfinite(ec.ptr.pp_double[i][j], _state) )
                            {
                                *err = *err||ae_fp_neq(p.ptr.pp_double[i][j],ec.ptr.pp_double[i][j]);
                            }
                        }
                    }
                }
                else
                {
                    *err = ae_true;
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test bound constraints.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testbc(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix p;
    ae_matrix bndl;
    ae_matrix bndu;
    ae_matrix xy;
    ae_int_t entrystate;
    ae_int_t exitstate;
    ae_int_t entrykind;
    ae_int_t exitkind;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ic;
    ae_int_t jc;
    double vl;
    double vu;
    mcpdstate s;
    mcpdreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&p, 0, sizeof(p));
    memset(&bndl, 0, sizeof(bndl));
    memset(&bndu, 0, sizeof(bndu));
    memset(&xy, 0, sizeof(xy));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&bndl, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&bndu, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    
    /*
     * We try different problems with following properties:
     * * N is large enough - we won't have problems with inconsistent constraints
     * * first state is either "entry" or "normal"
     * * last state is either "exit" or "normal"
     * * we have one long random track
     *
     * We test several properties which are described in comments below
     */
    for(n=4; n<=6; n++)
    {
        for(entrykind=0; entrykind<=1; entrykind++)
        {
            for(exitkind=0; exitkind<=1; exitkind++)
            {
                
                /*
                 * Prepare problem
                 */
                if( entrykind==0 )
                {
                    entrystate = -1;
                }
                else
                {
                    entrystate = 0;
                }
                if( exitkind==0 )
                {
                    exitstate = -1;
                }
                else
                {
                    exitstate = n-1;
                }
                ae_matrix_set_length(&xy, 2*n, n, _state);
                for(i=0; i<=xy.rows-1; i++)
                {
                    for(j=0; j<=xy.cols-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                    }
                }
                
                /*
                 * Test that single bound constraint on non-entry
                 * non-exit elements of P is satisfied.
                 *
                 * NOTE 1: this test needs N>=4 because smaller values
                 * can give us inconsistent constraints
                 */
                ae_assert(n>=4, "TestBC: expectation failed", _state);
                ic = 1+ae_randominteger(n-2, _state);
                jc = 1+ae_randominteger(n-2, _state);
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    vl = 0.3*ae_randomreal(_state);
                }
                else
                {
                    vl = _state->v_neginf;
                }
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    vu = 0.5+0.3*ae_randomreal(_state);
                }
                else
                {
                    vu = _state->v_posinf;
                }
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdaddbc(&s, ic, jc, vl, vu, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    *err = *err||ae_fp_less(p.ptr.pp_double[ic][jc],vl);
                    *err = *err||ae_fp_greater(p.ptr.pp_double[ic][jc],vu);
                }
                else
                {
                    *err = ae_true;
                }
                
                /*
                 * Test interaction with default "sum-to-one" constraint
                 * on columns of P.
                 *
                 * We set N-1 bound constraints on random non-exit column
                 * of P, which are inconsistent with this default constraint
                 * (sum will be greater that 1.0).
                 *
                 * Algorithm must detect inconsistency.
                 *
                 * NOTE:
                 * 1. we do not set constraints for the first element of
                 *    the column, because this element may be constrained by
                 *    "exit state" constraint.
                 * 2. this test needs N>=3
                 */
                ae_assert(n>=3, "TestEC: expectation failed", _state);
                jc = ae_randominteger(n-1, _state);
                vl = 0.85;
                vu = 0.95;
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                for(i=1; i<=n-1; i++)
                {
                    mcpdaddbc(&s, i, jc, vl, vu, _state);
                }
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                *err = *err||rep.terminationtype!=-3;
                
                /*
                 * Test interaction with constrains on entry states.
                 *
                 * When model has entry state, corresponding row of P
                 * must be zero. We try to set two kinds of constraints
                 * on random element of this row:
                 * * bound constraint with zero lower bound, which must be consistent
                 * * bound constraint with non-zero lower bound, which must be inconsistent
                 */
                if( entrystate>=0 )
                {
                    jc = ae_randominteger(n, _state);
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddbc(&s, entrystate, jc, 0.0, 1.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddbc(&s, entrystate, jc, 0.5, 1.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test interaction with constrains on exit states.
                 *
                 * When model has exit state, corresponding column of P
                 * must be zero. We try to set two kinds of constraints
                 * on random element of this column:
                 * * bound constraint with zero lower bound, which must be consistent
                 * * bound constraint with non-zero lower bound, which must be inconsistent
                 */
                if( exitstate>=0 )
                {
                    ic = ae_randominteger(n, _state);
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddbc(&s, ic, exitstate, 0.0, 1.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddbc(&s, ic, exitstate, 0.5, 1.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test SetBC() call - we constrain subset of non-entry
                 * non-exit elements and test it.
                 */
                ae_assert(n>=4, "TestBC: expectation failed", _state);
                ae_matrix_set_length(&bndl, n, n, _state);
                ae_matrix_set_length(&bndu, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        bndl.ptr.pp_double[i][j] = _state->v_neginf;
                        bndu.ptr.pp_double[i][j] = _state->v_posinf;
                    }
                }
                for(j=1; j<=n-2; j++)
                {
                    i = 1+ae_randominteger(n-2, _state);
                    bndl.ptr.pp_double[i][j] = 0.5-0.1*ae_randomreal(_state);
                    bndu.ptr.pp_double[i][j] = 0.5+0.1*ae_randomreal(_state);
                }
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetbc(&s, &bndl, &bndu, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            *err = *err||ae_fp_less(p.ptr.pp_double[i][j],bndl.ptr.pp_double[i][j]);
                            *err = *err||ae_fp_greater(p.ptr.pp_double[i][j],bndu.ptr.pp_double[i][j]);
                        }
                    }
                }
                else
                {
                    *err = ae_true;
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test bound constraints.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testlc(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix p;
    ae_matrix c;
    ae_matrix xy;
    ae_vector ct;
    ae_int_t entrystate;
    ae_int_t exitstate;
    ae_int_t entrykind;
    ae_int_t exitkind;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t t;
    ae_int_t jc;
    double v;
    double threshold;
    mcpdstate s;
    mcpdreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&p, 0, sizeof(p));
    memset(&c, 0, sizeof(c));
    memset(&xy, 0, sizeof(xy));
    memset(&ct, 0, sizeof(ct));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    threshold = 1.0E5*ae_machineepsilon;
    
    /*
     * We try different problems with following properties:
     * * N is large enough - we won't have problems with inconsistent constraints
     * * first state is either "entry" or "normal"
     * * last state is either "exit" or "normal"
     * * we have one long random track
     *
     * We test several properties which are described in comments below
     */
    for(n=4; n<=6; n++)
    {
        for(entrykind=0; entrykind<=1; entrykind++)
        {
            for(exitkind=0; exitkind<=1; exitkind++)
            {
                
                /*
                 * Prepare problem
                 */
                if( entrykind==0 )
                {
                    entrystate = -1;
                }
                else
                {
                    entrystate = 0;
                }
                if( exitkind==0 )
                {
                    exitstate = -1;
                }
                else
                {
                    exitstate = n-1;
                }
                ae_matrix_set_length(&xy, 2*n, n, _state);
                for(i=0; i<=xy.rows-1; i++)
                {
                    for(j=0; j<=xy.cols-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                    }
                }
                
                /*
                 * Test that single linear equality/inequality constraint
                 * on non-entry non-exit elements of P is satisfied.
                 *
                 * NOTE 1: this test needs N>=4 because smaller values
                 *         can give us inconsistent constraints
                 * NOTE 2: Constraints are generated is such a way that P=(1/N ... 1/N)
                 *         is always feasible. It guarantees that there always exists
                 *         at least one feasible point
                 * NOTE 3: If we have inequality constraint, we "shift" right part
                 *         in order to make feasible some neighborhood of P=(1/N ... 1/N).
                 */
                ae_assert(n>=4, "TestLC: expectation failed", _state);
                ae_matrix_set_length(&c, 1, n*n+1, _state);
                ae_vector_set_length(&ct, 1, _state);
                v = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ((i==0||i==n-1)||j==0)||j==n-1 )
                        {
                            c.ptr.pp_double[0][i*n+j] = (double)(0);
                        }
                        else
                        {
                            c.ptr.pp_double[0][i*n+j] = ae_randomreal(_state);
                            v = v+c.ptr.pp_double[0][i*n+j]*((double)1/(double)n);
                        }
                    }
                }
                c.ptr.pp_double[0][n*n] = v;
                ct.ptr.p_int[0] = ae_randominteger(3, _state)-1;
                if( ct.ptr.p_int[0]<0 )
                {
                    c.ptr.pp_double[0][n*n] = c.ptr.pp_double[0][n*n]+0.1;
                }
                if( ct.ptr.p_int[0]>0 )
                {
                    c.ptr.pp_double[0][n*n] = c.ptr.pp_double[0][n*n]-0.1;
                }
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetlc(&s, &c, &ct, 1, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    v = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            v = v+p.ptr.pp_double[i][j]*c.ptr.pp_double[0][i*n+j];
                        }
                    }
                    if( ct.ptr.p_int[0]<0 )
                    {
                        *err = *err||ae_fp_greater_eq(v,c.ptr.pp_double[0][n*n]+threshold);
                    }
                    if( ct.ptr.p_int[0]==0 )
                    {
                        *err = *err||ae_fp_greater_eq(ae_fabs(v-c.ptr.pp_double[0][n*n], _state),threshold);
                    }
                    if( ct.ptr.p_int[0]>0 )
                    {
                        *err = *err||ae_fp_less_eq(v,c.ptr.pp_double[0][n*n]-threshold);
                    }
                }
                else
                {
                    *err = ae_true;
                }
                
                /*
                 * Test interaction with default "sum-to-one" constraint
                 * on columns of P.
                 *
                 * We set linear constraint which has for "sum-to-X" on
                 * on random non-exit column of P. This constraint can be
                 * either consistent (X=1.0) or inconsistent (X<>1.0) with
                 * this default constraint.
                 *
                 * Algorithm must detect inconsistency.
                 *
                 * NOTE:
                 * 1. this test needs N>=2
                 */
                ae_assert(n>=2, "TestLC: expectation failed", _state);
                jc = ae_randominteger(n-1, _state);
                ae_matrix_set_length(&c, 1, n*n+1, _state);
                ae_vector_set_length(&ct, 1, _state);
                for(i=0; i<=n*n-1; i++)
                {
                    c.ptr.pp_double[0][i] = 0.0;
                }
                for(i=0; i<=n-1; i++)
                {
                    c.ptr.pp_double[0][n*i+jc] = 1.0;
                }
                c.ptr.pp_double[0][n*n] = 1.0;
                ct.ptr.p_int[0] = 0;
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetlc(&s, &c, &ct, 1, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                *err = *err||rep.terminationtype<=0;
                c.ptr.pp_double[0][n*n] = 2.0;
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetlc(&s, &c, &ct, 1, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                *err = *err||rep.terminationtype!=-3;
                
                /*
                 * Test interaction with constrains on entry states.
                 *
                 * When model has entry state, corresponding row of P
                 * must be zero. We try to set two kinds of constraints
                 * on elements of this row:
                 * * sums-to-zero constraint, which must be consistent
                 * * sums-to-one constraint, which must be inconsistent
                 */
                if( entrystate>=0 )
                {
                    ae_matrix_set_length(&c, 1, n*n+1, _state);
                    ae_vector_set_length(&ct, 1, _state);
                    for(i=0; i<=n*n-1; i++)
                    {
                        c.ptr.pp_double[0][i] = 0.0;
                    }
                    for(j=0; j<=n-1; j++)
                    {
                        c.ptr.pp_double[0][n*entrystate+j] = 1.0;
                    }
                    ct.ptr.p_int[0] = 0;
                    c.ptr.pp_double[0][n*n] = 0.0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdsetlc(&s, &c, &ct, 1, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    c.ptr.pp_double[0][n*n] = 1.0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdsetlc(&s, &c, &ct, 1, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test interaction with constrains on exit states.
                 *
                 * When model has exit state, corresponding column of P
                 * must be zero. We try to set two kinds of constraints
                 * on elements of this column:
                 * * sums-to-zero constraint, which must be consistent
                 * * sums-to-one constraint, which must be inconsistent
                 */
                if( exitstate>=0 )
                {
                    ae_matrix_set_length(&c, 1, n*n+1, _state);
                    ae_vector_set_length(&ct, 1, _state);
                    for(i=0; i<=n*n-1; i++)
                    {
                        c.ptr.pp_double[0][i] = 0.0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        c.ptr.pp_double[0][n*i+exitstate] = 1.0;
                    }
                    ct.ptr.p_int[0] = 0;
                    c.ptr.pp_double[0][n*n] = 0.0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdsetlc(&s, &c, &ct, 1, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    c.ptr.pp_double[0][n*n] = 1.0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdsetlc(&s, &c, &ct, 1, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
            }
        }
    }
    
    /*
     * Final test - we generate several random constraints and
     * test SetLC() function.
     *
     * NOTES:
     *
     * 1. Constraints are generated is such a way that P=(1/N ... 1/N)
     *    is always feasible. It guarantees that there always exists
     *    at least one feasible point
     * 2. For simplicity of the test we do not use entry/exit states
     *    in our model
     */
    for(n=1; n<=4; n++)
    {
        for(k=1; k<=2*n; k++)
        {
            
            /*
             * Generate track
             */
            ae_matrix_set_length(&xy, 2*n, n, _state);
            for(i=0; i<=xy.rows-1; i++)
            {
                for(j=0; j<=xy.cols-1; j++)
                {
                    xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                }
            }
            
            /*
             * Generate random constraints
             */
            ae_matrix_set_length(&c, k, n*n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=k-1; i++)
            {
                
                /*
                 * Generate constraint and its right part
                 */
                c.ptr.pp_double[i][n*n] = (double)(0);
                for(j=0; j<=n*n-1; j++)
                {
                    c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    c.ptr.pp_double[i][n*n] = c.ptr.pp_double[i][n*n]+c.ptr.pp_double[i][j]*((double)1/(double)n);
                }
                ct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
                
                /*
                 * If we have inequality constraint, we "shift" right part
                 * in order to make feasible some neighborhood of P=(1/N ... 1/N).
                 */
                if( ct.ptr.p_int[i]<0 )
                {
                    c.ptr.pp_double[i][n*n] = c.ptr.pp_double[i][n*n]+0.1;
                }
                if( ct.ptr.p_int[i]>0 )
                {
                    c.ptr.pp_double[i][n*n] = c.ptr.pp_double[i][n*n]-0.1;
                }
            }
            
            /*
             * Test
             */
            testmcpdunit_createee(n, -1, -1, &s, _state);
            mcpdaddtrack(&s, &xy, xy.rows, _state);
            mcpdsetlc(&s, &c, &ct, k, _state);
            mcpdsolve(&s, _state);
            mcpdresults(&s, &p, &rep, _state);
            if( rep.terminationtype>0 )
            {
                for(t=0; t<=k-1; t++)
                {
                    v = (double)(0);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            v = v+p.ptr.pp_double[i][j]*c.ptr.pp_double[t][i*n+j];
                        }
                    }
                    if( ct.ptr.p_int[t]<0 )
                    {
                        *err = *err||ae_fp_greater_eq(v,c.ptr.pp_double[t][n*n]+threshold);
                    }
                    if( ct.ptr.p_int[t]==0 )
                    {
                        *err = *err||ae_fp_greater_eq(ae_fabs(v-c.ptr.pp_double[t][n*n], _state),threshold);
                    }
                    if( ct.ptr.p_int[t]>0 )
                    {
                        *err = *err||ae_fp_less_eq(v,c.ptr.pp_double[t][n*n]-threshold);
                    }
                }
            }
            else
            {
                *err = ae_true;
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function is used to create MCPD object with arbitrary combination of
entry and exit states
*************************************************************************/
static void testmcpdunit_createee(ae_int_t n,
     ae_int_t entrystate,
     ae_int_t exitstate,
     mcpdstate* s,
     ae_state *_state)
{

    _mcpdstate_clear(s);

    if( entrystate<0&&exitstate<0 )
    {
        mcpdcreate(n, s, _state);
    }
    if( entrystate>=0&&exitstate<0 )
    {
        mcpdcreateentry(n, entrystate, s, _state);
    }
    if( entrystate<0&&exitstate>=0 )
    {
        mcpdcreateexit(n, exitstate, s, _state);
    }
    if( entrystate>=0&&exitstate>=0 )
    {
        mcpdcreateentryexit(n, entrystate, exitstate, s, _state);
    }
}



static void testmlpeunit_createensemble(mlpensemble* ensemble,
     ae_int_t nkind,
     double a1,
     double a2,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_state *_state);
static void testmlpeunit_unsetensemble(mlpensemble* ensemble,
     ae_state *_state);
static void testmlpeunit_testinformational(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testmlpeunit_testprocessing(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testmlpeunit_testerr(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_int_t sizemin,
     ae_int_t sizemax,
     ae_bool* err,
     ae_state *_state);





ae_bool testmlpe(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool inferrors;
    ae_bool procerrors;
    ae_bool errerrors;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_int_t maxhid;
    ae_int_t nf;
    ae_int_t nl;
    ae_int_t nhid1;
    ae_int_t nhid2;
    ae_int_t ec;
    ae_int_t nkind;
    ae_int_t sizemin;
    ae_int_t sizemax;
    ae_bool result;


    waserrors = ae_false;
    inferrors = ae_false;
    procerrors = ae_false;
    errerrors = ae_false;
    passcount = 5;
    maxn = 3;
    maxhid = 3;
    
    /*
     * General MLP ensembles tests
     * These tests are performed with small dataset, whose size is in [0,10].
     * We test correctness of functions on small sets, but do not test code
     * which splits large dataset into smaller chunks.
     */
    sizemin = 0;
    sizemax = 10;
    for(nf=1; nf<=maxn; nf++)
    {
        for(nl=1; nl<=maxn; nl++)
        {
            for(nhid1=0; nhid1<=maxhid; nhid1++)
            {
                for(nhid2=0; nhid2<=maxhid; nhid2++)
                {
                    for(nkind=0; nkind<=3; nkind++)
                    {
                        for(ec=1; ec<=3; ec++)
                        {
                            
                            /*
                             *  Skip meaningless parameters combinations
                             */
                            if( nkind==1&&nl<2 )
                            {
                                continue;
                            }
                            if( nhid1==0&&nhid2!=0 )
                            {
                                continue;
                            }
                            
                            /*
                             * Tests
                             */
                            testmlpeunit_testinformational(nkind, nf, nhid1, nhid2, nl, ec, passcount, &inferrors, _state);
                            testmlpeunit_testprocessing(nkind, nf, nhid1, nhid2, nl, ec, passcount, &procerrors, _state);
                            testmlpeunit_testerr(nkind, nf, nhid1, nhid2, nl, ec, passcount, sizemin, sizemax, &errerrors, _state);
                        }
                    }
                }
            }
        }
    }
    
    /*
     * Special tests on large datasets: test ability to correctly split
     * work into smaller chunks.
     */
    nf = 2;
    nhid1 = 10;
    nhid2 = 10;
    nl = 2;
    ec = 10;
    sizemin = 1000;
    sizemax = 1000;
    testmlpeunit_testerr(0, nf, nhid1, nhid2, nl, ec, 1, sizemin, sizemax, &errerrors, _state);
    
    /*
     * Final report
     */
    waserrors = (inferrors||procerrors)||errerrors;
    if( !silent )
    {
        printf("MLP ENSEMBLE TEST\n");
        printf("INFORMATIONAL FUNCTIONS:                 ");
        if( !inferrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("BASIC PROCESSING:                        ");
        if( !procerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("ERROR FUNCTIONS:                         ");
        if( !errerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Network creation
*************************************************************************/
static void testmlpeunit_createensemble(mlpensemble* ensemble,
     ae_int_t nkind,
     double a1,
     double a2,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_state *_state)
{


    ae_assert(((nin>0&&nhid1>=0)&&nhid2>=0)&&nout>0, "CreateNetwork error", _state);
    ae_assert(nhid1!=0||nhid2==0, "CreateNetwork error", _state);
    ae_assert(nkind!=1||nout>=2, "CreateNetwork error", _state);
    if( nhid1==0 )
    {
        
        /*
         * No hidden layers
         */
        if( nkind==0 )
        {
            mlpecreate0(nin, nout, ec, ensemble, _state);
        }
        else
        {
            if( nkind==1 )
            {
                mlpecreatec0(nin, nout, ec, ensemble, _state);
            }
            else
            {
                if( nkind==2 )
                {
                    mlpecreateb0(nin, nout, a1, a2, ec, ensemble, _state);
                }
                else
                {
                    if( nkind==3 )
                    {
                        mlpecreater0(nin, nout, a1, a2, ec, ensemble, _state);
                    }
                }
            }
        }
        return;
    }
    if( nhid2==0 )
    {
        
        /*
         * One hidden layer
         */
        if( nkind==0 )
        {
            mlpecreate1(nin, nhid1, nout, ec, ensemble, _state);
        }
        else
        {
            if( nkind==1 )
            {
                mlpecreatec1(nin, nhid1, nout, ec, ensemble, _state);
            }
            else
            {
                if( nkind==2 )
                {
                    mlpecreateb1(nin, nhid1, nout, a1, a2, ec, ensemble, _state);
                }
                else
                {
                    if( nkind==3 )
                    {
                        mlpecreater1(nin, nhid1, nout, a1, a2, ec, ensemble, _state);
                    }
                }
            }
        }
        return;
    }
    
    /*
     * Two hidden layers
     */
    if( nkind==0 )
    {
        mlpecreate2(nin, nhid1, nhid2, nout, ec, ensemble, _state);
    }
    else
    {
        if( nkind==1 )
        {
            mlpecreatec2(nin, nhid1, nhid2, nout, ec, ensemble, _state);
        }
        else
        {
            if( nkind==2 )
            {
                mlpecreateb2(nin, nhid1, nhid2, nout, a1, a2, ec, ensemble, _state);
            }
            else
            {
                if( nkind==3 )
                {
                    mlpecreater2(nin, nhid1, nhid2, nout, a1, a2, ec, ensemble, _state);
                }
            }
        }
    }
}


/*************************************************************************
Unsets network (initialize it to smallest network possible
*************************************************************************/
static void testmlpeunit_unsetensemble(mlpensemble* ensemble,
     ae_state *_state)
{


    mlpecreate0(1, 1, 1, ensemble, _state);
}


/*************************************************************************
Iformational functions test
*************************************************************************/
static void testmlpeunit_testinformational(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    mlpensemble ensemble;
    ae_int_t n1;
    ae_int_t n2;

    ae_frame_make(_state, &_frame_block);
    memset(&ensemble, 0, sizeof(ensemble));
    _mlpensemble_init(&ensemble, _state, ae_true);

    testmlpeunit_createensemble(&ensemble, nkind, -1.0, 1.0, nin, nhid1, nhid2, nout, ec, _state);
    mlpeproperties(&ensemble, &n1, &n2, _state);
    *err = (*err||n1!=nin)||n2!=nout;
    ae_frame_leave(_state);
}


/*************************************************************************
Processing functions test
*************************************************************************/
static void testmlpeunit_testprocessing(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    mlpensemble ensemble;
    mlpensemble ensemble2;
    double a1;
    double a2;
    ae_int_t pass;
    ae_int_t rkind;
    ae_int_t i;
    ae_bool allsame;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    ae_vector ra;
    ae_vector ra2;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&ensemble, 0, sizeof(ensemble));
    memset(&ensemble2, 0, sizeof(ensemble2));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&y1, 0, sizeof(y1));
    memset(&y2, 0, sizeof(y2));
    memset(&ra, 0, sizeof(ra));
    memset(&ra2, 0, sizeof(ra2));
    _mlpensemble_init(&ensemble, _state, ae_true);
    _mlpensemble_init(&ensemble2, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ra, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ra2, 0, DT_REAL, _state, ae_true);

    
    /*
     * Prepare network
     */
    a1 = (double)(0);
    a2 = (double)(0);
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    
    /*
     * Initialize arrays
     */
    ae_vector_set_length(&x1, nin-1+1, _state);
    ae_vector_set_length(&x2, nin-1+1, _state);
    ae_vector_set_length(&y1, nout-1+1, _state);
    ae_vector_set_length(&y2, nout-1+1, _state);
    
    /*
     * Main cycle:
     * * Pass is a number of repeated test
     * * RKind is a "replication kind":
     *   * RKind=0 means that we work with original ensemble
     *   * RKind=1 means that we work with replica created with MLPECopy()
     *   * RKind=2 means that we work with replica created with serialization/unserialization
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(rkind=0; rkind<=2; rkind++)
        {
            
            /*
             * Create network, pass through replication in order to test that replicated network works correctly.
             */
            testmlpeunit_createensemble(&ensemble, nkind, a1, a2, nin, nhid1, nhid2, nout, ec, _state);
            if( rkind==1 )
            {
                mlpecopy(&ensemble, &ensemble2, _state);
                testmlpeunit_unsetensemble(&ensemble, _state);
                mlpecopy(&ensemble2, &ensemble, _state);
                testmlpeunit_unsetensemble(&ensemble2, _state);
            }
            if( rkind==2 )
            {
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    mlpealloc(&_local_serializer, &ensemble, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                    ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpeserialize(&_local_serializer, &ensemble, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpeunserialize(&_local_serializer, &ensemble2, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
                testmlpeunit_unsetensemble(&ensemble, _state);
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    mlpealloc(&_local_serializer, &ensemble2, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                    ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpeserialize(&_local_serializer, &ensemble2, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpeunserialize(&_local_serializer, &ensemble, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
                testmlpeunit_unsetensemble(&ensemble2, _state);
            }
            
            /*
             * Same inputs leads to same outputs
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = x1.ptr.p_double[i];
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpeprocess(&ensemble, &x1, &y1, _state);
            mlpeprocess(&ensemble, &x2, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||!allsame;
            
            /*
             * Same inputs on original network leads to same outputs
             * on copy created using MLPCopy
             */
            testmlpeunit_unsetensemble(&ensemble2, _state);
            mlpecopy(&ensemble, &ensemble2, _state);
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = x1.ptr.p_double[i];
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpeprocess(&ensemble, &x1, &y1, _state);
            mlpeprocess(&ensemble2, &x2, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||!allsame;
            
            /*
             * Same inputs on original network leads to same outputs
             * on copy created using MLPSerialize
             */
            {
                /*
                 * This code passes data structure through serializers
                 * (serializes it to string and loads back)
                 */
                ae_serializer _local_serializer;
                ae_int_t _local_ssize;
                ae_frame _local_frame_block;
                ae_dyn_block _local_dynamic_block;
                
                ae_frame_make(_state, &_local_frame_block);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_alloc_start(&_local_serializer);
                mlpealloc(&_local_serializer, &ensemble, _state);
                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                mlpeserialize(&_local_serializer, &ensemble, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                mlpeunserialize(&_local_serializer, &ensemble2, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_frame_leave(_state);
            }
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = x1.ptr.p_double[i];
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpeprocess(&ensemble, &x1, &y1, _state);
            mlpeprocess(&ensemble2, &x2, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||!allsame;
            
            /*
             * Different inputs leads to different outputs (non-zero network)
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = y1.ptr.p_double[i];
            }
            mlpeprocess(&ensemble, &x1, &y1, _state);
            mlpeprocess(&ensemble, &x2, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||allsame;
            
            /*
             * Randomization changes outputs (when inputs are unchanged, non-zero network)
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = y1.ptr.p_double[i];
            }
            mlpecopy(&ensemble, &ensemble2, _state);
            mlperandomize(&ensemble2, _state);
            mlpeprocess(&ensemble, &x1, &y1, _state);
            mlpeprocess(&ensemble2, &x1, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||allsame;
            
            /*
             * Normalization properties
             */
            if( nkind==1 )
            {
                
                /*
                 * Classifier network outputs are normalized
                 */
                for(i=0; i<=nin-1; i++)
                {
                    x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mlpeprocess(&ensemble, &x1, &y1, _state);
                v = (double)(0);
                for(i=0; i<=nout-1; i++)
                {
                    v = v+y1.ptr.p_double[i];
                    *err = *err||ae_fp_less(y1.ptr.p_double[i],(double)(0));
                }
                *err = *err||ae_fp_greater(ae_fabs(v-1, _state),1000*ae_machineepsilon);
            }
            if( nkind==2 )
            {
                
                /*
                 * B-type network outputs are bounded from above/below
                 */
                for(i=0; i<=nin-1; i++)
                {
                    x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mlpeprocess(&ensemble, &x1, &y1, _state);
                for(i=0; i<=nout-1; i++)
                {
                    if( ae_fp_greater_eq(a2,(double)(0)) )
                    {
                        *err = *err||ae_fp_less(y1.ptr.p_double[i],a1);
                    }
                    else
                    {
                        *err = *err||ae_fp_greater(y1.ptr.p_double[i],a1);
                    }
                }
            }
            if( nkind==3 )
            {
                
                /*
                 * R-type network outputs are within [A1,A2] (or [A2,A1])
                 */
                for(i=0; i<=nin-1; i++)
                {
                    x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mlpeprocess(&ensemble, &x1, &y1, _state);
                for(i=0; i<=nout-1; i++)
                {
                    *err = (*err||ae_fp_less(y1.ptr.p_double[i],ae_minreal(a1, a2, _state)))||ae_fp_greater(y1.ptr.p_double[i],ae_maxreal(a1, a2, _state));
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Error functions

Ensemble of type NKind is created, with  NIn  inputs,  NHid1*NHid2  hidden
layers (one layer if NHid2=0), NOut outputs. PassCount  random  passes  is
performed. Dataset has random size in [SizeMin,SizeMax].
*************************************************************************/
static void testmlpeunit_testerr(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_int_t sizemin,
     ae_int_t sizemax,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    mlpensemble ensemble;
    sparsematrix sparsexy;
    sparsematrix sparsexy2;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    double etol;
    double escale;
    double a1;
    double a2;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ssize;
    ae_matrix xy;
    ae_matrix xy2;
    ae_vector y;
    ae_vector x1;
    ae_vector y1;
    ae_vector idx;
    ae_vector dummy;
    double refrmserror;
    double refclserror;
    double refavgce;
    double refavgerror;
    double refavgrelerror;
    ae_int_t avgrelcnt;
    modelerrors allerrors;
    ae_int_t nnmax;
    ae_int_t dsmax;

    ae_frame_make(_state, &_frame_block);
    memset(&ensemble, 0, sizeof(ensemble));
    memset(&sparsexy, 0, sizeof(sparsexy));
    memset(&sparsexy2, 0, sizeof(sparsexy2));
    memset(&xy, 0, sizeof(xy));
    memset(&xy2, 0, sizeof(xy2));
    memset(&y, 0, sizeof(y));
    memset(&x1, 0, sizeof(x1));
    memset(&y1, 0, sizeof(y1));
    memset(&idx, 0, sizeof(idx));
    memset(&dummy, 0, sizeof(dummy));
    memset(&allerrors, 0, sizeof(allerrors));
    _mlpensemble_init(&ensemble, _state, ae_true);
    _sparsematrix_init(&sparsexy, _state, ae_true);
    _sparsematrix_init(&sparsexy2, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&idx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&dummy, 0, DT_INT, _state, ae_true);
    _modelerrors_init(&allerrors, _state, ae_true);

    a1 = (double)(0);
    a2 = (double)(0);
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    testmlpeunit_createensemble(&ensemble, nkind, a1, a2, nin, nhid1, nhid2, nout, ec, _state);
    mlpproperties(&ensemble.network, &n1, &n2, &wcount, _state);
    etol = 1.0E-4;
    escale = 1.0E-2;
    
    /*
     * Initialize
     */
    ae_vector_set_length(&x1, nin, _state);
    ae_vector_set_length(&y, nout, _state);
    ae_vector_set_length(&y1, nout, _state);
    
    /*
     * Process
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Randomize Ensemble, then re-randomaze weights manually.
         *
         * NOTE: weights magnitude is chosen to be small, about 0.1,
         *       which allows us to avoid oversaturated Ensemble.
         *       In 10% of cases we use zero weights.
         */
        mlperandomize(&ensemble, _state);
        if( ae_fp_less_eq(ae_randomreal(_state),0.1) )
        {
            for(i=0; i<=wcount*ec-1; i++)
            {
                ensemble.weights.ptr.p_double[i] = 0.0;
            }
        }
        else
        {
            for(i=0; i<=wcount*ec-1; i++)
            {
                ensemble.weights.ptr.p_double[i] = 0.2*ae_randomreal(_state)-0.1;
            }
        }
        
        /*
         * Generate random dataset.
         * Calculate reference errors.
         *
         * NOTE: about 10% of tests are performed with zero SSize
         */
        ssize = sizemin+ae_randominteger(sizemax-sizemin+1, _state);
        if( mlpeissoftmax(&ensemble, _state) )
        {
            ae_matrix_set_length(&xy, ae_maxint(ssize, 1, _state), nin+1, _state);
            sparsecreate(ae_maxint(ssize, 1, _state), nin+1, 0, &sparsexy, _state);
        }
        else
        {
            ae_matrix_set_length(&xy, ae_maxint(ssize, 1, _state), nin+nout, _state);
            sparsecreate(ae_maxint(ssize, 1, _state), nin+nout, 0, &sparsexy, _state);
        }
        refrmserror = 0.0;
        refclserror = 0.0;
        refavgce = 0.0;
        refavgerror = 0.0;
        refavgrelerror = 0.0;
        avgrelcnt = 0;
        for(i=0; i<=ssize-1; i++)
        {
            
            /*
             * Fill I-th row
             */
            for(j=0; j<=nin-1; j++)
            {
                x1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                sparseset(&sparsexy, i, j, x1.ptr.p_double[j], _state);
            }
            ae_v_move(&xy.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,nin-1));
            if( mlpeissoftmax(&ensemble, _state) )
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = (double)(0);
                }
                xy.ptr.pp_double[i][nin] = (double)(ae_randominteger(nout, _state));
                sparseset(&sparsexy, i, nin, xy.ptr.pp_double[i][nin], _state);
                y1.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)] = (double)(1);
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                    sparseset(&sparsexy, i, nin+j, y1.ptr.p_double[j], _state);
                }
                ae_v_move(&xy.ptr.pp_double[i][nin], 1, &y1.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
            }
            
            /*
             * Process
             */
            mlpeprocess(&ensemble, &x1, &y, _state);
            
            /*
             * Update reference errors
             */
            nnmax = 0;
            if( mlpeissoftmax(&ensemble, _state) )
            {
                if( ae_fp_greater(y.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)],(double)(0)) )
                {
                    refavgce = refavgce+ae_log(1/y.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)], _state);
                }
                else
                {
                    refavgce = refavgce+ae_log(ae_maxrealnumber, _state);
                }
            }
            if( mlpeissoftmax(&ensemble, _state) )
            {
                dsmax = ae_round(xy.ptr.pp_double[i][nin], _state);
            }
            else
            {
                dsmax = 0;
            }
            for(j=0; j<=nout-1; j++)
            {
                refrmserror = refrmserror+ae_sqr(y.ptr.p_double[j]-y1.ptr.p_double[j], _state);
                refavgerror = refavgerror+ae_fabs(y.ptr.p_double[j]-y1.ptr.p_double[j], _state);
                if( ae_fp_neq(y1.ptr.p_double[j],(double)(0)) )
                {
                    refavgrelerror = refavgrelerror+ae_fabs(y.ptr.p_double[j]-y1.ptr.p_double[j], _state)/ae_fabs(y1.ptr.p_double[j], _state);
                    avgrelcnt = avgrelcnt+1;
                }
                if( ae_fp_greater(y.ptr.p_double[j],y.ptr.p_double[nnmax]) )
                {
                    nnmax = j;
                }
                if( !mlpeissoftmax(&ensemble, _state)&&ae_fp_greater(y1.ptr.p_double[j],y1.ptr.p_double[dsmax]) )
                {
                    dsmax = j;
                }
            }
            if( nnmax!=dsmax )
            {
                refclserror = refclserror+1;
            }
        }
        sparseconverttocrs(&sparsexy, _state);
        if( ssize>0 )
        {
            refrmserror = ae_sqrt(refrmserror/(ssize*nout), _state);
            refavgerror = refavgerror/(ssize*nout);
            refavgce = refavgce/(ssize*ae_log((double)(2), _state));
        }
        if( avgrelcnt>0 )
        {
            refavgrelerror = refavgrelerror/avgrelcnt;
        }
        
        /*
         * Test "continuous" errors on full dataset
         */
        seterrorflagdiff(err, mlpermserror(&ensemble, &xy, ssize, _state), refrmserror, etol, escale, _state);
        seterrorflagdiff(err, mlpeavgce(&ensemble, &xy, ssize, _state), refavgce, etol, escale, _state);
        seterrorflagdiff(err, mlpeavgerror(&ensemble, &xy, ssize, _state), refavgerror, etol, escale, _state);
        seterrorflagdiff(err, mlpeavgrelerror(&ensemble, &xy, ssize, _state), refavgrelerror, etol, escale, _state);
    }
    ae_frame_leave(_state);
}



static ae_bool testmlptrainunit_testmlptraines(ae_state *_state);
static ae_bool testmlptrainunit_testmlptrainregr(ae_state *_state);
static ae_bool testmlptrainunit_testmlpxorregr(ae_state *_state);
static ae_bool testmlptrainunit_testmlptrainclass(ae_state *_state);
static ae_bool testmlptrainunit_testmlpxorcls(ae_state *_state);
static ae_bool testmlptrainunit_testmlpzeroweights(ae_state *_state);
static ae_bool testmlptrainunit_testmlprestarts(ae_state *_state);
static ae_bool testmlptrainunit_testmlpcverror(ae_state *_state);
static ae_bool testmlptrainunit_testmlptrainens(ae_state *_state);
static ae_bool testmlptrainunit_testmlptrainensregr(ae_state *_state);
static ae_bool testmlptrainunit_testmlptrainenscls(ae_state *_state);





ae_bool testmlptrain(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_int_t info;
    multilayerperceptron network;
    multilayerperceptron network2;
    mlpreport rep;
    mlpcvreport cvrep;
    ae_matrix xy;
    ae_matrix valxy;
    ae_bool trnerrors;
    ae_bool mlpcverrorerr;
    ae_bool mlptrainregrerr;
    ae_bool mlptrainclasserr;
    ae_bool mlprestartserr;
    ae_bool mlpxorregrerr;
    ae_bool mlpxorclserr;
    ae_bool mlptrainenserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&network, 0, sizeof(network));
    memset(&network2, 0, sizeof(network2));
    memset(&rep, 0, sizeof(rep));
    memset(&cvrep, 0, sizeof(cvrep));
    memset(&xy, 0, sizeof(xy));
    memset(&valxy, 0, sizeof(valxy));
    _multilayerperceptron_init(&network, _state, ae_true);
    _multilayerperceptron_init(&network2, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    _mlpcvreport_init(&cvrep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&valxy, 0, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    trnerrors = ae_false;
    mlpcverrorerr = ae_false;
    mlptrainregrerr = ae_false;
    mlptrainclasserr = ae_false;
    mlprestartserr = ae_false;
    mlpxorregrerr = ae_false;
    mlpxorclserr = ae_false;
    mlptrainenserrors = ae_false;
    
    /*
     * Test network training on simple XOR problem
     */
    ae_matrix_set_length(&xy, 3+1, 2+1, _state);
    xy.ptr.pp_double[0][0] = (double)(-1);
    xy.ptr.pp_double[0][1] = (double)(-1);
    xy.ptr.pp_double[0][2] = (double)(-1);
    xy.ptr.pp_double[1][0] = (double)(1);
    xy.ptr.pp_double[1][1] = (double)(-1);
    xy.ptr.pp_double[1][2] = (double)(1);
    xy.ptr.pp_double[2][0] = (double)(-1);
    xy.ptr.pp_double[2][1] = (double)(1);
    xy.ptr.pp_double[2][2] = (double)(1);
    xy.ptr.pp_double[3][0] = (double)(1);
    xy.ptr.pp_double[3][1] = (double)(1);
    xy.ptr.pp_double[3][2] = (double)(-1);
    mlpcreate1(2, 2, 1, &network, _state);
    mlptrainlm(&network, &xy, 4, 0.001, 10, &info, &rep, _state);
    trnerrors = trnerrors||ae_fp_greater(mlprmserror(&network, &xy, 4, _state),0.1);
    
    /*
     * Test early stopping
     */
    trnerrors = trnerrors||testmlptrainunit_testmlptraines(_state);
    
    /*
     * Test for function MLPFoldCV()
     */
    mlpcverrorerr = testmlptrainunit_testmlpcverror(_state);
    
    /*
     * Test for training functions
     */
    mlptrainregrerr = testmlptrainunit_testmlptrainregr(_state)||testmlptrainunit_testmlpzeroweights(_state);
    mlptrainclasserr = testmlptrainunit_testmlptrainclass(_state);
    mlprestartserr = testmlptrainunit_testmlprestarts(_state);
    mlpxorregrerr = testmlptrainunit_testmlpxorregr(_state);
    mlpxorclserr = testmlptrainunit_testmlpxorcls(_state);
    
    /*
     * Training for ensembles
     */
    mlptrainenserrors = (testmlptrainunit_testmlptrainens(_state)||testmlptrainunit_testmlptrainensregr(_state))||testmlptrainunit_testmlptrainenscls(_state);
    
    /*
     * Final report
     */
    waserrors = ((((((trnerrors||mlptrainregrerr)||mlptrainclasserr)||mlprestartserr)||mlpxorregrerr)||mlpxorclserr)||mlpcverrorerr)||mlptrainenserrors;
    if( !silent )
    {
        printf("MLP TEST\n");
        printf("CROSS-VALIDATION ERRORS:                 ");
        if( !mlpcverrorerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("TRAINING:                                ");
        if( !trnerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("TRAIN -LM -LBFGS FOR REGRESSION:         ");
        if( mlptrainregrerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TRAIN -LM -LBFGS FOR CLASSIFIER:         ");
        if( mlptrainclasserr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("PARAMETER RESTARTS IN TRAIN -LBFGS:      ");
        if( mlprestartserr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TRAINIG WITH TRAINER FOR REGRESSION:     ");
        if( mlpxorregrerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TRAINIG WITH TRAINER FOR CLASSIFIER:     ");
        if( mlpxorclserr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TRAINING ENSEMBLES:                      ");
        if( mlptrainenserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests MLPTrainES().

It returns True in case of errors, False when no errors were detected
*************************************************************************/
static ae_bool testmlptrainunit_testmlptraines(ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    multilayerperceptron network;
    ae_matrix trnxy;
    ae_matrix valxy;
    ae_vector x;
    ae_vector y;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t nrestarts;
    ae_int_t info;
    mlpreport rep;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&network, 0, sizeof(network));
    memset(&trnxy, 0, sizeof(trnxy));
    memset(&valxy, 0, sizeof(valxy));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rep, 0, sizeof(rep));
    _multilayerperceptron_init(&network, _state, ae_true);
    ae_matrix_init(&trnxy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&valxy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);

    result = ae_false;
    
    /*
     * First test checks that MLPTrainES() - when training set is equal to the validation
     * set, MLPTrainES() behaves just like a "normal" training algorithm.
     *
     * Test sequence:
     * * generate training set - 100 random points from 2D square [-1,+1]*[-1,+1]
     * * generate network with 2 inputs, no hidden layers, nonlinear output layer,
     *   use its outputs as target values for the test set
     * * randomize network
     * * train with MLPTrainES(), using original set as both training and validation set
     * * trained network must reproduce training set with good precision
     *
     * NOTE: it is important to test algorithm on nonlinear network because linear
     *       problems converge too fast. Slow convergence is important to detect
     *       some kinds of bugs.
     *
     * NOTE: it is important to have NRestarts at least equal to 5, because with just
     *       one restart algorithm fails test about once in several thousands of passes.
     */
    passcount = 10;
    nrestarts = 5;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Create network, generate training/validation sets
         */
        mlpcreater0(2, 1, -2.0, 2.0, &network, _state);
        mlprandomize(&network, _state);
        n = 100;
        ae_matrix_set_length(&trnxy, n, 3, _state);
        ae_matrix_set_length(&valxy, n, 3, _state);
        ae_vector_set_length(&x, 2, _state);
        ae_vector_set_length(&y, 1, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=1; j++)
            {
                trnxy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                valxy.ptr.pp_double[i][j] = trnxy.ptr.pp_double[i][j];
                x.ptr.p_double[j] = trnxy.ptr.pp_double[i][j];
            }
            mlpprocess(&network, &x, &y, _state);
            trnxy.ptr.pp_double[i][2] = y.ptr.p_double[0];
            valxy.ptr.pp_double[i][2] = y.ptr.p_double[0];
        }
        mlprandomize(&network, _state);
        mlptraines(&network, &trnxy, n, &valxy, n, 0.0001, nrestarts, &info, &rep, _state);
        if( info<=0 )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        if( ae_fp_greater(ae_sqrt(mlperror(&network, &valxy, n, _state)/n, _state),0.01) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This  function   tests   MLPTrainLM,  MLPTrainLBFGS   and  MLPTrainNetwork
functions  for regression.  It check that  train functions  work correctly.
Test use Create1 with 10 neurons.
Test function is f(x,y)=X^2+cos(3*Pi*y).
*************************************************************************/
static ae_bool testmlptrainunit_testmlptrainregr(ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron net;
    mlptrainer trainer;
    mlpreport rep;
    ae_int_t info;
    ae_matrix xy;
    sparsematrix sm;
    ae_vector x;
    ae_vector y;
    ae_int_t n;
    ae_int_t sn;
    ae_int_t nneurons;
    double vdecay;
    double averr;
    double st;
    double eps;
    double traineps;
    ae_int_t nneedrest;
    ae_int_t trainits;
    ae_int_t shift;
    ae_int_t i;
    ae_int_t j;
    ae_int_t vtrain;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&net, 0, sizeof(net));
    memset(&trainer, 0, sizeof(trainer));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&sm, 0, sizeof(sm));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    _multilayerperceptron_init(&net, _state, ae_true);
    _mlptrainer_init(&trainer, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sm, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);

    eps = 0.01;
    vdecay = 0.001;
    nneurons = 10;
    nneedrest = 5;
    traineps = 1.0E-3;
    trainits = 0;
    sn = 5;
    n = sn*sn;
    st = (double)2/(double)(sn-1);
    sparsecreate(n, 3, n*3, &sm, _state);
    ae_matrix_set_length(&xy, n, 3, _state);
    ae_vector_set_length(&x, 2, _state);
    for(vtrain=0; vtrain<=3; vtrain++)
    {
        averr = (double)(0);
        
        /*
         * Create a train set(uniformly distributed set of points).
         */
        for(i=0; i<=sn-1; i++)
        {
            for(j=0; j<=sn-1; j++)
            {
                shift = i*sn+j;
                xy.ptr.pp_double[shift][0] = i*st-1.0;
                xy.ptr.pp_double[shift][1] = j*st-1.0;
                xy.ptr.pp_double[shift][2] = xy.ptr.pp_double[shift][0]*xy.ptr.pp_double[shift][0]+ae_cos(3*ae_pi*xy.ptr.pp_double[shift][1], _state);
            }
        }
        
        /*
         * Create and train a neural network
         */
        mlpcreate1(2, nneurons, 1, &net, _state);
        if( vtrain==0 )
        {
            mlptrainlm(&net, &xy, n, vdecay, nneedrest, &info, &rep, _state);
        }
        if( vtrain==1 )
        {
            mlptrainlbfgs(&net, &xy, n, vdecay, nneedrest, traineps, trainits, &info, &rep, _state);
        }
        
        /*
         * Train with trainer, using:
         *  * dense matrix;
         */
        if( vtrain==2 )
        {
            mlpcreatetrainer(2, 1, &trainer, _state);
            mlpsetdataset(&trainer, &xy, n, _state);
            mlpsetdecay(&trainer, vdecay, _state);
            mlpsetcond(&trainer, traineps, trainits, _state);
            mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
        }
        
        /*
         *  * sparse matrix.
         */
        if( vtrain==3 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=2; j++)
                {
                    sparseset(&sm, i, j, xy.ptr.pp_double[i][j], _state);
                }
            }
            mlpcreatetrainer(2, 1, &trainer, _state);
            mlpsetsparsedataset(&trainer, &sm, n, _state);
            mlpsetdecay(&trainer, vdecay, _state);
            mlpsetcond(&trainer, traineps, trainits, _state);
            mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
        }
        
        /*
         * Check that network is trained correctly
         */
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[0] = xy.ptr.pp_double[i][0];
            x.ptr.p_double[1] = xy.ptr.pp_double[i][1];
            mlpprocess(&net, &x, &y, _state);
            
            /*
             * Calculate average error
             */
            averr = averr+ae_fabs(y.ptr.p_double[0]-xy.ptr.pp_double[i][2], _state);
        }
        if( ae_fp_greater(averr/n,eps) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This  function tests  MLPTrainNetwork/MLPStartTraining/MLPContinueTraining
functions for  regression.  It check  that train  functions work correctly.
Test use Create1 with 2 neurons.
Test function is XOR(x,y).
*************************************************************************/
static ae_bool testmlptrainunit_testmlpxorregr(ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron net;
    mlptrainer trainer;
    mlpreport rep;
    ae_matrix xy;
    sparsematrix sm;
    ae_vector x;
    ae_vector y;
    ae_int_t n;
    ae_int_t sn;
    ae_int_t nneurons;
    double vdecay;
    double averr;
    double eps;
    ae_int_t numxp;
    double traineps;
    ae_int_t nneedrest;
    ae_int_t trainits;
    ae_int_t shift;
    ae_int_t i;
    ae_int_t j;
    ae_int_t vtrain;
    ae_int_t xp;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&net, 0, sizeof(net));
    memset(&trainer, 0, sizeof(trainer));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&sm, 0, sizeof(sm));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    _multilayerperceptron_init(&net, _state, ae_true);
    _mlptrainer_init(&trainer, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sm, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);

    eps = 0.01;
    numxp = 15;
    vdecay = 0.001;
    nneurons = 3;
    nneedrest = 1;
    traineps = 1.0E-4;
    trainits = 0;
    sn = 2;
    n = sn*sn;
    sparsecreate(n, 3, n*3, &sm, _state);
    ae_matrix_set_length(&xy, n, 3, _state);
    ae_vector_set_length(&x, 2, _state);
    for(xp=1; xp<=numxp; xp++)
    {
        for(vtrain=0; vtrain<=3; vtrain++)
        {
            averr = (double)(0);
            
            /*
             * Create a train set(uniformly distributed set of points).
             */
            for(i=0; i<=sn-1; i++)
            {
                for(j=0; j<=sn-1; j++)
                {
                    shift = i*sn+j;
                    xy.ptr.pp_double[shift][0] = (double)(i);
                    xy.ptr.pp_double[shift][1] = (double)(j);
                    if( ae_fp_eq(xy.ptr.pp_double[shift][0],xy.ptr.pp_double[shift][1]) )
                    {
                        xy.ptr.pp_double[shift][2] = (double)(0);
                    }
                    else
                    {
                        xy.ptr.pp_double[shift][2] = (double)(1);
                    }
                }
            }
            
            /*
             * Create and train a neural network
             */
            mlpcreate1(2, nneurons, 1, &net, _state);
            
            /*
             * Train with trainer, using:
             *  * dense matrix;
             */
            if( vtrain==0 )
            {
                mlpcreatetrainer(2, 1, &trainer, _state);
                mlpsetdataset(&trainer, &xy, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
            }
            if( vtrain==1 )
            {
                mlpcreatetrainer(2, 1, &trainer, _state);
                mlpsetdataset(&trainer, &xy, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlpstarttraining(&trainer, &net, ae_true, _state);
                while(mlpcontinuetraining(&trainer, &net, _state))
                {
                }
            }
            
            /*
             *  * sparse matrix.
             */
            if( vtrain==2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=2; j++)
                    {
                        sparseset(&sm, i, j, xy.ptr.pp_double[i][j], _state);
                    }
                }
                mlpcreatetrainer(2, 1, &trainer, _state);
                mlpsetsparsedataset(&trainer, &sm, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
            }
            if( vtrain==3 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=2; j++)
                    {
                        sparseset(&sm, i, j, xy.ptr.pp_double[i][j], _state);
                    }
                }
                mlpcreatetrainer(2, 1, &trainer, _state);
                mlpsetsparsedataset(&trainer, &sm, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlpstarttraining(&trainer, &net, ae_true, _state);
                while(mlpcontinuetraining(&trainer, &net, _state))
                {
                }
            }
            
            /*
             * Check that network is trained correctly
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[0] = xy.ptr.pp_double[i][0];
                x.ptr.p_double[1] = xy.ptr.pp_double[i][1];
                mlpprocess(&net, &x, &y, _state);
                
                /*
                 * Calculate average error
                 */
                averr = averr+ae_fabs(y.ptr.p_double[0]-xy.ptr.pp_double[i][2], _state);
            }
            if( ae_fp_greater(averr/n,eps) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This  function   tests   MLPTrainLM,  MLPTrainLBFGS  and   MLPTrainNetwork
functions for classification problems. It check that train  functions work
correctly  when  is used CreateC1  function.  Here  the network  tries  to
distinguish positive from negative numbers.
*************************************************************************/
static ae_bool testmlptrainunit_testmlptrainclass(ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron net;
    mlptrainer trainer;
    mlpreport rep;
    ae_int_t info;
    ae_matrix xy;
    sparsematrix sm;
    ae_vector x;
    ae_vector y;
    ae_int_t n;
    double vdecay;
    double traineps;
    ae_int_t nneedrest;
    ae_int_t trainits;
    double tmp;
    double mnc;
    double mxc;
    ae_int_t nxp;
    ae_int_t i;
    ae_int_t rndind;
    ae_int_t vtrain;
    ae_int_t xp;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&net, 0, sizeof(net));
    memset(&trainer, 0, sizeof(trainer));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&sm, 0, sizeof(sm));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    _multilayerperceptron_init(&net, _state, ae_true);
    _mlptrainer_init(&trainer, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sm, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);

    mnc = (double)(10);
    mxc = (double)(11);
    nxp = 15;
    vdecay = 0.001;
    nneedrest = 10;
    traineps = 1.0E-4;
    trainits = 0;
    n = 100;
    sparsecreate(n, 2, n*2, &sm, _state);
    ae_vector_set_length(&x, 1, _state);
    ae_matrix_set_length(&xy, n, 2, _state);
    for(xp=1; xp<=nxp; xp++)
    {
        for(vtrain=0; vtrain<=3; vtrain++)
        {
            
            /*
             * Initialization:
             *  * create negative part of the set;
             */
            for(i=0; i<=n/2-1; i++)
            {
                xy.ptr.pp_double[i][0] = -1*((mxc-mnc)*ae_randomreal(_state)+mnc);
                xy.ptr.pp_double[i][1] = (double)(0);
            }
            
            /*
             *  * create positive part of the set;
             */
            for(i=n/2; i<=n-1; i++)
            {
                xy.ptr.pp_double[i][0] = (mxc-mnc)*ae_randomreal(_state)+mnc;
                xy.ptr.pp_double[i][1] = (double)(1);
            }
            
            /*
             *  * mix two parts.
             */
            for(i=0; i<=n-1; i++)
            {
                do
                {
                    rndind = ae_randominteger(n, _state);
                }
                while(rndind==i);
                tmp = xy.ptr.pp_double[i][0];
                xy.ptr.pp_double[i][0] = xy.ptr.pp_double[rndind][0];
                xy.ptr.pp_double[rndind][0] = tmp;
                tmp = xy.ptr.pp_double[i][1];
                xy.ptr.pp_double[i][1] = xy.ptr.pp_double[rndind][1];
                xy.ptr.pp_double[rndind][1] = tmp;
            }
            
            /*
             * Create and train a neural network
             */
            mlpcreatec0(1, 2, &net, _state);
            if( vtrain==0 )
            {
                mlptrainlm(&net, &xy, n, vdecay, nneedrest, &info, &rep, _state);
            }
            if( vtrain==1 )
            {
                mlptrainlbfgs(&net, &xy, n, vdecay, nneedrest, traineps, trainits, &info, &rep, _state);
            }
            
            /*
             * Train with trainer, using:
             *  * dense matrix;
             */
            if( vtrain==2 )
            {
                mlpcreatetrainercls(1, 2, &trainer, _state);
                mlpsetdataset(&trainer, &xy, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
            }
            
            /*
             *  * sparse matrix.
             */
            if( vtrain==3 )
            {
                for(i=0; i<=n-1; i++)
                {
                    sparseset(&sm, i, 0, xy.ptr.pp_double[i][0], _state);
                    sparseset(&sm, i, 1, xy.ptr.pp_double[i][1], _state);
                }
                mlpcreatetrainercls(1, 2, &trainer, _state);
                mlpsetsparsedataset(&trainer, &sm, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
            }
            
            /*
             * Test on training set
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[0] = xy.ptr.pp_double[i][0];
                mlpprocess(&net, &x, &y, _state);
                
                /*
                 * Negative number has to be negative and
                 * positive number has to be positive.
                 */
                if( ((ae_fp_less(x.ptr.p_double[0],(double)(0))&&ae_fp_less(y.ptr.p_double[0],0.95))&&ae_fp_greater(y.ptr.p_double[1],0.05))||((ae_fp_greater_eq(x.ptr.p_double[0],(double)(0))&&ae_fp_greater(y.ptr.p_double[0],0.05))&&ae_fp_less(y.ptr.p_double[1],0.95)) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            
            /*
             * Test on random set
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[0] = ae_pow((double)(-1), (double)(ae_randominteger(2, _state)), _state)*((mxc-mnc)*ae_randomreal(_state)+mnc);
                mlpprocess(&net, &x, &y, _state);
                if( ((ae_fp_less(x.ptr.p_double[0],(double)(0))&&ae_fp_less(y.ptr.p_double[0],0.95))&&ae_fp_greater(y.ptr.p_double[1],0.05))||((ae_fp_greater_eq(x.ptr.p_double[0],(double)(0))&&ae_fp_greater(y.ptr.p_double[0],0.05))&&ae_fp_less(y.ptr.p_double[1],0.95)) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests   MLPTrainNetwork/MLPStartTraining/MLPContinueTraining
functions for classification problems. It check that train  functions work
correctly  when  is used CreateC1  function.  Here  the network  tries  to
distinguish positive from negative numbers.
*************************************************************************/
static ae_bool testmlptrainunit_testmlpxorcls(ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron net;
    mlptrainer trainer;
    mlpreport rep;
    ae_matrix xy;
    sparsematrix sm;
    ae_vector x;
    ae_vector y;
    ae_int_t n;
    ae_int_t nin;
    ae_int_t nout;
    ae_int_t wcount;
    double e;
    double ebest;
    double v;
    ae_vector wbest;
    double vdecay;
    double traineps;
    ae_int_t nneurons;
    ae_int_t nneedrest;
    ae_int_t trainits;
    ae_int_t nxp;
    ae_int_t i;
    ae_int_t vtrain;
    ae_int_t xp;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&net, 0, sizeof(net));
    memset(&trainer, 0, sizeof(trainer));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&sm, 0, sizeof(sm));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&wbest, 0, sizeof(wbest));
    _multilayerperceptron_init(&net, _state, ae_true);
    _mlptrainer_init(&trainer, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sm, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wbest, 0, DT_REAL, _state, ae_true);

    nxp = 15;
    nneurons = 3;
    vdecay = 0.001;
    nneedrest = 3;
    traineps = 1.0E-4;
    trainits = 0;
    n = 4;
    sparsecreate(n, 3, n*3, &sm, _state);
    ae_vector_set_length(&x, 2, _state);
    ae_matrix_set_length(&xy, n, 3, _state);
    
    /*
     * Initialization:
     */
    xy.ptr.pp_double[0][0] = (double)(0);
    xy.ptr.pp_double[0][1] = (double)(0);
    xy.ptr.pp_double[0][2] = (double)(0);
    xy.ptr.pp_double[1][0] = (double)(0);
    xy.ptr.pp_double[1][1] = (double)(1);
    xy.ptr.pp_double[1][2] = (double)(1);
    xy.ptr.pp_double[2][0] = (double)(1);
    xy.ptr.pp_double[2][1] = (double)(0);
    xy.ptr.pp_double[2][2] = (double)(1);
    xy.ptr.pp_double[3][0] = (double)(1);
    xy.ptr.pp_double[3][1] = (double)(1);
    xy.ptr.pp_double[3][2] = (double)(0);
    
    /*
     * Create a neural network
     */
    mlpcreatec1(2, nneurons, 2, &net, _state);
    mlpproperties(&net, &nin, &nout, &wcount, _state);
    ae_vector_set_length(&wbest, wcount, _state);
    
    /*
     * Test
     */
    for(xp=1; xp<=nxp; xp++)
    {
        for(vtrain=0; vtrain<=3; vtrain++)
        {
            
            /*
             * Train with trainer, using:
             *  * dense matrix;
             */
            if( vtrain==0 )
            {
                mlpcreatetrainercls(2, 2, &trainer, _state);
                mlpsetdataset(&trainer, &xy, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
            }
            if( vtrain==1 )
            {
                mlpcreatetrainercls(2, 2, &trainer, _state);
                mlpsetdataset(&trainer, &xy, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                ebest = ae_maxrealnumber;
                for(i=1; i<=nneedrest; i++)
                {
                    mlpstarttraining(&trainer, &net, ae_true, _state);
                    while(mlpcontinuetraining(&trainer, &net, _state))
                    {
                    }
                    v = ae_v_dotproduct(&net.weights.ptr.p_double[0], 1, &net.weights.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
                    e = mlperror(&net, &xy, n, _state)+0.5*vdecay*v;
                    
                    /*
                     * Compare with the best answer.
                     */
                    if( ae_fp_less(e,ebest) )
                    {
                        ae_v_move(&wbest.ptr.p_double[0], 1, &net.weights.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
                        ebest = e;
                    }
                }
                
                /*
                 * The best result
                 */
                ae_v_move(&net.weights.ptr.p_double[0], 1, &wbest.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
            }
            
            /*
             *  * sparse matrix.
             */
            if( vtrain==2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    sparseset(&sm, i, 0, xy.ptr.pp_double[i][0], _state);
                    sparseset(&sm, i, 1, xy.ptr.pp_double[i][1], _state);
                    sparseset(&sm, i, 2, xy.ptr.pp_double[i][2], _state);
                }
                mlpcreatetrainercls(2, 2, &trainer, _state);
                mlpsetsparsedataset(&trainer, &sm, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
            }
            if( vtrain==3 )
            {
                for(i=0; i<=n-1; i++)
                {
                    sparseset(&sm, i, 0, xy.ptr.pp_double[i][0], _state);
                    sparseset(&sm, i, 1, xy.ptr.pp_double[i][1], _state);
                    sparseset(&sm, i, 2, xy.ptr.pp_double[i][2], _state);
                }
                mlpcreatetrainercls(2, 2, &trainer, _state);
                mlpsetsparsedataset(&trainer, &sm, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                ebest = ae_maxrealnumber;
                for(i=1; i<=nneedrest; i++)
                {
                    mlpstarttraining(&trainer, &net, ae_true, _state);
                    while(mlpcontinuetraining(&trainer, &net, _state))
                    {
                    }
                    v = ae_v_dotproduct(&net.weights.ptr.p_double[0], 1, &net.weights.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
                    e = mlperror(&net, &xy, n, _state)+0.5*vdecay*v;
                    
                    /*
                     * Compare with the best answer.
                     */
                    if( ae_fp_less(e,ebest) )
                    {
                        ae_v_move(&wbest.ptr.p_double[0], 1, &net.weights.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
                        ebest = e;
                    }
                }
                
                /*
                 * The best result
                 */
                ae_v_move(&net.weights.ptr.p_double[0], 1, &wbest.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
            }
            
            /*
             * Test on training set
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[0] = xy.ptr.pp_double[i][0];
                x.ptr.p_double[1] = xy.ptr.pp_double[i][1];
                mlpprocess(&net, &x, &y, _state);
                if( ((ae_fp_eq(x.ptr.p_double[0],x.ptr.p_double[1])&&ae_fp_less(y.ptr.p_double[0],0.95))&&ae_fp_greater(y.ptr.p_double[1],0.05))||((ae_fp_neq(x.ptr.p_double[0],x.ptr.p_double[1])&&ae_fp_greater(y.ptr.p_double[0],0.05))&&ae_fp_less(y.ptr.p_double[1],0.95)) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The  test  check,  that  all weights are zero after training with trainer
using empty dataset(either zero size or is't used MLPSetDataSet function).
Test  on  regression and classification problems given by dense or sparse
matrix.

NOTE: Result of the function is written in MLPTrainRegrErr variable in
      unit test.
*************************************************************************/
static ae_bool testmlptrainunit_testmlpzeroweights(ae_state *_state)
{
    ae_frame _frame_block;
    mlptrainer trainer;
    multilayerperceptron net;
    mlpreport rep;
    ae_int_t nin;
    ae_int_t nout;
    ae_int_t wcount;
    ae_int_t mxnin;
    ae_int_t mxnout;
    double vdecay;
    double traineps;
    ae_int_t trainits;
    ae_int_t nneedrest;
    ae_matrix dds;
    sparsematrix sds;
    ae_bool iscls;
    ae_bool issparse;
    ae_int_t c;
    ae_int_t n;
    ae_int_t xp;
    ae_int_t nxp;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&trainer, 0, sizeof(trainer));
    memset(&net, 0, sizeof(net));
    memset(&rep, 0, sizeof(rep));
    memset(&dds, 0, sizeof(dds));
    memset(&sds, 0, sizeof(sds));
    _mlptrainer_init(&trainer, _state, ae_true);
    _multilayerperceptron_init(&net, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    ae_matrix_init(&dds, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sds, _state, ae_true);

    mxnin = 10;
    mxnout = 10;
    vdecay = 1.0E-3;
    nneedrest = 1;
    traineps = 1.0E-3;
    trainits = 0;
    sparsecreate(1, 1, 0, &sds, _state);
    sparseconverttocrs(&sds, _state);
    nxp = 10;
    for(xp=1; xp<=nxp; xp++)
    {
        c = ae_randominteger(2, _state);
        iscls = c==1;
        c = ae_randominteger(2, _state);
        issparse = c==1;
        
        /*
         * Create trainer and network
         */
        if( !iscls )
        {
            
            /*
             * Regression
             */
            nin = ae_randominteger(mxnin, _state)+1;
            nout = ae_randominteger(mxnout, _state)+1;
            mlpcreatetrainer(nin, nout, &trainer, _state);
            mlpcreate0(nin, nout, &net, _state);
        }
        else
        {
            
            /*
             * Classification
             */
            nin = ae_randominteger(mxnin, _state)+1;
            nout = ae_randominteger(mxnout, _state)+2;
            mlpcreatetrainercls(nin, nout, &trainer, _state);
            mlpcreatec0(nin, nout, &net, _state);
        }
        n = ae_randominteger(2, _state)-1;
        if( n==0 )
        {
            if( !issparse )
            {
                mlpsetdataset(&trainer, &dds, n, _state);
            }
            else
            {
                mlpsetsparsedataset(&trainer, &sds, n, _state);
            }
        }
        mlpsetdecay(&trainer, vdecay, _state);
        mlpsetcond(&trainer, traineps, trainits, _state);
        c = ae_randominteger(2, _state);
        if( c==0 )
        {
            mlpstarttraining(&trainer, &net, ae_true, _state);
            while(mlpcontinuetraining(&trainer, &net, _state))
            {
            }
        }
        if( c==1 )
        {
            mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
        }
        
        /*
         * Check weights
         */
        mlpproperties(&net, &nin, &nout, &wcount, _state);
        for(c=0; c<=wcount-1; c++)
        {
            if( ae_fp_neq(net.weights.ptr.p_double[c],(double)(0)) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests that increasing numbers of restarts lead to statistical
improvement quality of solution.
Neural network created by Create1(10 neurons) and trained by MLPTrainLBFGS.

TEST's DISCRIPTION:

Net0   -   network trained with one restart (denoted as R1)
Net1   -   network trained with more than one restart (denoted as Rn)

We must refuse hypothesis that R1 equivalent to Rn.
Here Mean = N/2, Sigma = Sqrt(N)/2.
       _
      | 0   -   R1 worse than Rn;
 ri = |
      |_1   -   Rn same or worse then R1.
    
If Sum(ri)<Mean-5*Sigma then hypothesis is refused and test is passed.
In another case if Mean-5*Sigma<=Sum(ri)<=Mean+5*Sigma then hypothesis
is't refused and test is broken; and if Mean+5*Sigma<Sum(ri) then test
broken too hard!
*************************************************************************/
static ae_bool testmlptrainunit_testmlprestarts(ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron net0;
    multilayerperceptron net1;
    mlptrainer trainer;
    mlpreport rep;
    ae_int_t info;
    sparsematrix sm;
    ae_matrix xy;
    ae_vector x;
    ae_vector y;
    ae_int_t n;
    ae_int_t nneurons;
    double vdecay;
    ae_int_t wcount0;
    ae_int_t wcount1;
    ae_int_t nin;
    ae_int_t nout;
    double avval;
    double e0;
    double e1;
    double mean;
    double numsigma;
    ae_int_t numxp;
    double traineps;
    ae_int_t nneedrest;
    ae_int_t trainits;
    ae_int_t i;
    ae_int_t vtrain;
    ae_int_t xp;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&net0, 0, sizeof(net0));
    memset(&net1, 0, sizeof(net1));
    memset(&trainer, 0, sizeof(trainer));
    memset(&rep, 0, sizeof(rep));
    memset(&sm, 0, sizeof(sm));
    memset(&xy, 0, sizeof(xy));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    _multilayerperceptron_init(&net0, _state, ae_true);
    _multilayerperceptron_init(&net1, _state, ae_true);
    _mlptrainer_init(&trainer, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    _sparsematrix_init(&sm, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);

    vdecay = 0.001;
    nneurons = 4;
    nneedrest = 3;
    traineps = 0.00;
    trainits = 2;
    n = 20;
    numxp = 400;
    ae_matrix_set_length(&xy, n, 2, _state);
    ae_vector_set_length(&x, 1, _state);
    sparsecreate(n, 2, n*2, &sm, _state);
    mean = numxp/2.0;
    numsigma = 5.0*ae_sqrt((double)(numxp), _state)/2.0;
    for(vtrain=0; vtrain<=2; vtrain++)
    {
        avval = (double)(0);
        for(xp=1; xp<=numxp; xp++)
        {
            
            /*
             * Create a train set
             */
            for(i=0; i<=n-1; i++)
            {
                xy.ptr.pp_double[i][0] = 2*ae_randomreal(_state)-1;
                xy.ptr.pp_double[i][1] = 2*ae_randomreal(_state)-1;
            }
            
            /*
             * Create and train a neural network
             */
            mlpcreate1(1, nneurons, 1, &net0, _state);
            mlpcreate1(1, nneurons, 1, &net1, _state);
            if( vtrain==0 )
            {
                mlptrainlbfgs(&net0, &xy, n, vdecay, 1, traineps, trainits, &info, &rep, _state);
                mlptrainlbfgs(&net1, &xy, n, vdecay, nneedrest, traineps, trainits, &info, &rep, _state);
            }
            if( vtrain==1 )
            {
                mlpcreatetrainer(1, 1, &trainer, _state);
                mlpsetdataset(&trainer, &xy, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlptrainnetwork(&trainer, &net0, 1, &rep, _state);
                mlptrainnetwork(&trainer, &net1, nneedrest, &rep, _state);
            }
            if( vtrain==2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    sparseset(&sm, i, 0, xy.ptr.pp_double[i][0], _state);
                    sparseset(&sm, i, 1, xy.ptr.pp_double[i][1], _state);
                }
                mlpcreatetrainer(1, 1, &trainer, _state);
                mlpsetsparsedataset(&trainer, &sm, n, _state);
                mlpsetdecay(&trainer, vdecay, _state);
                mlpsetcond(&trainer, traineps, trainits, _state);
                mlptrainnetwork(&trainer, &net0, 1, &rep, _state);
                mlptrainnetwork(&trainer, &net1, nneedrest, &rep, _state);
            }
            
            /*
             * Calculate errors for...
             *
             * ...for Net0, trained with 1 restart.
             */
            mlpproperties(&net0, &nin, &nout, &wcount0, _state);
            e0 = ae_v_dotproduct(&net0.weights.ptr.p_double[0], 1, &net0.weights.ptr.p_double[0], 1, ae_v_len(0,wcount0-1));
            e0 = mlperrorn(&net0, &xy, n, _state)+0.5*vdecay*e0;
            
            /*
             * ...for Net1, trained with NNeedRest>1 restarts.
             */
            mlpproperties(&net1, &nin, &nout, &wcount1, _state);
            e1 = ae_v_dotproduct(&net1.weights.ptr.p_double[0], 1, &net1.weights.ptr.p_double[0], 1, ae_v_len(0,wcount1-1));
            e1 = mlperrorn(&net1, &xy, n, _state)+0.5*vdecay*e1;
            if( ae_fp_less_eq(e0,e1) )
            {
                avval = avval+1;
            }
        }
        if( ae_fp_less(mean-numsigma,avval) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The function test function MLPKFoldCV.
*************************************************************************/
static ae_bool testmlptrainunit_testmlpcverror(ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron net;
    mlptrainer trainer;
    mlpreport rep;
    mlpreport cvrep;
    ae_int_t nin;
    ae_int_t nout;
    ae_int_t nneurons;
    ae_int_t rowsz;
    double decay;
    double wstep;
    ae_int_t maxits;
    ae_int_t foldscount;
    ae_int_t nneedrest;
    sparsematrix sptrainingset;
    ae_matrix trainingset;
    ae_matrix testset;
    ae_int_t npoints;
    ae_int_t ntstpoints;
    double mean;
    double numsigma;
    double diffms;
    double tstrelclserror;
    double tstavgce;
    double tstrmserror;
    double tstavgerror;
    double tstavgrelerror;
    ae_int_t r0;
    ae_int_t r1;
    ae_int_t r2;
    ae_int_t r3;
    ae_int_t r4;
    ae_int_t ntest;
    ae_int_t xp;
    ae_int_t nxp;
    ae_bool isregr;
    ae_int_t issparse;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&net, 0, sizeof(net));
    memset(&trainer, 0, sizeof(trainer));
    memset(&rep, 0, sizeof(rep));
    memset(&cvrep, 0, sizeof(cvrep));
    memset(&sptrainingset, 0, sizeof(sptrainingset));
    memset(&trainingset, 0, sizeof(trainingset));
    memset(&testset, 0, sizeof(testset));
    _multilayerperceptron_init(&net, _state, ae_true);
    _mlptrainer_init(&trainer, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    _mlpreport_init(&cvrep, _state, ae_true);
    _sparsematrix_init(&sptrainingset, _state, ae_true);
    ae_matrix_init(&trainingset, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&testset, 0, 0, DT_REAL, _state, ae_true);

    decay = 1.0E-6;
    wstep = 0.0;
    foldscount = 5;
    nneedrest = 1;
    ntest = ae_randominteger(4, _state);
    nxp = 1000;
    maxits = 50;
    nin = 1;
    nout = 1;
    npoints = 5;
    ntstpoints = 100;
    isregr = ae_true;
    nneurons = 3;
    if( ntest==1 )
    {
        nxp = 1000;
        maxits = 50;
        nin = 1;
        nout = 10;
        npoints = 5;
        ntstpoints = 100;
        isregr = ae_true;
    }
    if( ntest==2 )
    {
        nxp = 1000;
        maxits = 50;
        nin = 10;
        nout = 1;
        npoints = 20;
        ntstpoints = 100;
        isregr = ae_true;
    }
    if( ntest==3 )
    {
        nxp = 2000;
        maxits = 10;
        nin = 1;
        nneurons = 3;
        nout = 3;
        npoints = 10;
        ntstpoints = 100;
        isregr = ae_false;
    }
    mean = nxp/2.0;
    numsigma = 5.0*ae_sqrt((double)(nxp), _state)/2.0;
    diffms = mean-numsigma;
    issparse = ae_randominteger(2, _state);
    if( isregr )
    {
        mlpcreate0(nin, nout, &net, _state);
        mlpcreatetrainer(nin, nout, &trainer, _state);
    }
    else
    {
        mlpcreatec1(nin, nneurons, nout, &net, _state);
        mlpcreatetrainercls(nin, nout, &trainer, _state);
    }
    mlpsetcond(&trainer, wstep, maxits, _state);
    mlpsetdecay(&trainer, decay, _state);
    if( isregr )
    {
        rowsz = nin+nout;
    }
    else
    {
        rowsz = nin+1;
    }
    r0 = 0;
    r1 = 0;
    r2 = 0;
    r3 = 0;
    r4 = 0;
    for(xp=1; xp<=nxp; xp++)
    {
        
        /*
         * Dense matrix
         */
        if( issparse==0 )
        {
            rmatrixsetlengthatleast(&trainingset, npoints, rowsz, _state);
            
            /*
             * Create training set
             */
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=nin-1; j++)
                {
                    trainingset.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
            }
            if( isregr )
            {
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=nin; j<=rowsz-1; j++)
                    {
                        trainingset.ptr.pp_double[i][j] = 2*ae_randomreal(_state)+1;
                    }
                }
            }
            else
            {
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=nin; j<=rowsz-1; j++)
                    {
                        trainingset.ptr.pp_double[i][j] = (double)(ae_randominteger(nout, _state));
                    }
                }
            }
            mlpsetdataset(&trainer, &trainingset, npoints, _state);
        }
        
        /*
         * Sparse matrix
         */
        if( issparse==1 )
        {
            sparsecreate(npoints, rowsz, npoints*rowsz, &sptrainingset, _state);
            
            /*
             * Create training set
             */
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=nin-1; j++)
                {
                    sparseset(&sptrainingset, i, j, 2*ae_randomreal(_state)-1, _state);
                }
            }
            if( isregr )
            {
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=nin; j<=rowsz-1; j++)
                    {
                        sparseset(&sptrainingset, i, j, 2*ae_randomreal(_state)+1, _state);
                    }
                }
            }
            else
            {
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=nin; j<=rowsz-1; j++)
                    {
                        sparseset(&sptrainingset, i, j, (double)(ae_randominteger(nout, _state)), _state);
                    }
                }
            }
            sparseconverttocrs(&sptrainingset, _state);
            mlpsetsparsedataset(&trainer, &sptrainingset, npoints, _state);
        }
        rmatrixsetlengthatleast(&testset, ntstpoints, rowsz, _state);
        
        /*
         * Create test set
         */
        for(i=0; i<=ntstpoints-1; i++)
        {
            for(j=0; j<=nin-1; j++)
            {
                testset.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        if( isregr )
        {
            for(i=0; i<=ntstpoints-1; i++)
            {
                for(j=nin; j<=rowsz-1; j++)
                {
                    testset.ptr.pp_double[i][j] = 2*ae_randomreal(_state)+1;
                }
            }
        }
        else
        {
            for(i=0; i<=ntstpoints-1; i++)
            {
                for(j=nin; j<=rowsz-1; j++)
                {
                    testset.ptr.pp_double[i][j] = (double)(ae_randominteger(nout, _state));
                }
            }
        }
        mlptrainnetwork(&trainer, &net, nneedrest, &rep, _state);
        tstrelclserror = (double)(0);
        tstavgce = (double)(0);
        tstrmserror = (double)(0);
        tstavgerror = (double)(0);
        tstavgrelerror = (double)(0);
        if( !isregr )
        {
            tstrelclserror = mlprelclserror(&net, &testset, ntstpoints, _state);
            tstavgce = mlpavgce(&net, &testset, ntstpoints, _state);
        }
        tstrmserror = mlprmserror(&net, &testset, ntstpoints, _state);
        tstavgerror = mlpavgerror(&net, &testset, ntstpoints, _state);
        tstavgrelerror = mlpavgrelerror(&net, &testset, ntstpoints, _state);
        
        /*
         * Cross-validation
         */
        mlpkfoldcv(&trainer, &net, nneedrest, foldscount, &cvrep, _state);
        if( !isregr )
        {
            if( ae_fp_less(ae_fabs(tstrelclserror-rep.relclserror, _state),ae_fabs(tstrelclserror-cvrep.relclserror, _state)) )
            {
                r0 = r0+1;
            }
            if( ae_fp_less(ae_fabs(tstavgce-rep.avgce, _state),ae_fabs(tstavgce-cvrep.avgce, _state)) )
            {
                r1 = r1+1;
            }
        }
        if( ae_fp_less(ae_fabs(tstrmserror-rep.rmserror, _state),ae_fabs(tstrmserror-cvrep.rmserror, _state)) )
        {
            r2 = r2+1;
        }
        if( ae_fp_less(ae_fabs(tstavgerror-rep.avgerror, _state),ae_fabs(tstavgerror-cvrep.avgerror, _state)) )
        {
            r3 = r3+1;
        }
        if( ae_fp_less(ae_fabs(tstavgrelerror-rep.avgrelerror, _state),ae_fabs(tstavgrelerror-cvrep.avgrelerror, _state)) )
        {
            r4 = r4+1;
        }
    }
    if( !isregr )
    {
        if( ae_fp_less_eq(diffms,(double)(r0))||ae_fp_less_eq(diffms,(double)(r1)) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    if( (ae_fp_less_eq(diffms,(double)(r2))||ae_fp_less_eq(diffms,(double)(r3)))||ae_fp_less_eq(diffms,(double)(r4)) )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test FoldCV when  no dataset  was specified with
     * MLPSetDataset/SetSparseDataset(), or subset with
     * only one point  was  given.
     *
     * NPoints values:
     *  * -1 - don't set dataset with using MLPSetDataset..;
     *  *  0 - zero dataset;
     *  *  1 - dataset with one point.
     */
    for(npoints=-1; npoints<=1; npoints++)
    {
        if( isregr )
        {
            mlpcreatetrainer(nin, nout, &trainer, _state);
        }
        else
        {
            mlpcreatetrainercls(nin, nout, &trainer, _state);
        }
        if( npoints>-1 )
        {
            if( issparse==0 )
            {
                mlpsetdataset(&trainer, &trainingset, npoints, _state);
            }
            if( issparse==1 )
            {
                mlpsetsparsedataset(&trainer, &sptrainingset, npoints, _state);
            }
        }
        mlpkfoldcv(&trainer, &net, nneedrest, foldscount, &cvrep, _state);
        if( ((((((ae_fp_neq(cvrep.relclserror,(double)(0))||ae_fp_neq(cvrep.avgce,(double)(0)))||ae_fp_neq(cvrep.rmserror,(double)(0)))||ae_fp_neq(cvrep.avgerror,(double)(0)))||ae_fp_neq(cvrep.avgrelerror,(double)(0)))||cvrep.ngrad!=0)||cvrep.nhess!=0)||cvrep.ncholesky!=0 )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The  function  tests  functions  for  training  ensembles:  MLPEBaggingLM,
MLPEBaggingLBFGS.
*************************************************************************/
static ae_bool testmlptrainunit_testmlptrainens(ae_state *_state)
{
    ae_frame _frame_block;
    mlpensemble ensemble;
    mlpreport rep;
    mlpcvreport oobrep;
    ae_int_t info;
    ae_matrix xy;
    ae_int_t nin;
    ae_int_t nout;
    ae_int_t npoints;
    ae_int_t nhid;
    ae_int_t algtype;
    ae_int_t tasktype;
    ae_int_t pass;
    double e;
    ae_int_t nless;
    ae_int_t nall;
    ae_int_t nclasses;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ensemble, 0, sizeof(ensemble));
    memset(&rep, 0, sizeof(rep));
    memset(&oobrep, 0, sizeof(oobrep));
    memset(&xy, 0, sizeof(xy));
    _mlpensemble_init(&ensemble, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    _mlpcvreport_init(&oobrep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    
    /*
     * network training must reduce error
     * test on random regression task
     */
    nin = 3;
    nout = 2;
    nhid = 5;
    npoints = 100;
    nless = 0;
    nall = 0;
    for(pass=1; pass<=10; pass++)
    {
        for(algtype=0; algtype<=1; algtype++)
        {
            for(tasktype=0; tasktype<=1; tasktype++)
            {
                if( tasktype==0 )
                {
                    ae_matrix_set_length(&xy, npoints, nin+nout, _state);
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=nin+nout-1; j++)
                        {
                            xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    mlpecreate1(nin, nhid, nout, 1+ae_randominteger(3, _state), &ensemble, _state);
                }
                else
                {
                    ae_matrix_set_length(&xy, npoints, nin+1, _state);
                    nclasses = 2+ae_randominteger(2, _state);
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=nin-1; j++)
                        {
                            xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                        xy.ptr.pp_double[i][nin] = (double)(ae_randominteger(nclasses, _state));
                    }
                    mlpecreatec1(nin, nhid, nclasses, 1+ae_randominteger(3, _state), &ensemble, _state);
                }
                e = mlpermserror(&ensemble, &xy, npoints, _state);
                if( algtype==0 )
                {
                    mlpebagginglm(&ensemble, &xy, npoints, 0.001, 1, &info, &rep, &oobrep, _state);
                }
                else
                {
                    mlpebagginglbfgs(&ensemble, &xy, npoints, 0.001, 1, 0.01, 0, &info, &rep, &oobrep, _state);
                }
                if( info<0 )
                {
                    result = ae_true;
                }
                else
                {
                    if( ae_fp_less(mlpermserror(&ensemble, &xy, npoints, _state),e) )
                    {
                        nless = nless+1;
                    }
                }
                nall = nall+1;
            }
        }
    }
    result = result||ae_fp_greater((double)(nall-nless),0.3*nall);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Testing  for  functions  MLPETrainES and MLPTrainEnsembleES  on regression
problems. Returns TRUE for errors, FALSE for success.
*************************************************************************/
static ae_bool testmlptrainunit_testmlptrainensregr(ae_state *_state)
{
    ae_frame _frame_block;
    mlptrainer trainer;
    mlpensemble netens;
    mlpreport rep;
    modelerrors repx;
    ae_int_t info;
    sparsematrix xytrainsp;
    ae_matrix xytrain;
    ae_matrix xytest;
    ae_int_t nin;
    ae_int_t nout;
    ae_int_t nneurons;
    ae_vector x;
    ae_vector y;
    double decay;
    double wstep;
    ae_int_t maxits;
    ae_int_t nneedrest;
    ae_int_t enssize;
    double mnval;
    double mxval;
    ae_int_t ntrain;
    ae_int_t ntest;
    double avgerr;
    ae_int_t issparse;
    ae_int_t withtrainer;
    double eps;
    ae_int_t xp;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&trainer, 0, sizeof(trainer));
    memset(&netens, 0, sizeof(netens));
    memset(&rep, 0, sizeof(rep));
    memset(&repx, 0, sizeof(repx));
    memset(&xytrainsp, 0, sizeof(xytrainsp));
    memset(&xytrain, 0, sizeof(xytrain));
    memset(&xytest, 0, sizeof(xytest));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    _mlptrainer_init(&trainer, _state, ae_true);
    _mlpensemble_init(&netens, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    _modelerrors_init(&repx, _state, ae_true);
    _sparsematrix_init(&xytrainsp, _state, ae_true);
    ae_matrix_init(&xytrain, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xytest, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    
    /*
     * This test checks ability to train ensemble on simple regression
     * problem "f(x0,x1,x2,...) = x0 + x1 + x2 + ...".
     */
    eps = 5.0E-2;
    mnval = (double)(-1);
    mxval = (double)(1);
    ntrain = 40;
    ntest = 20;
    decay = 1.0E-3;
    wstep = 1.0E-3;
    maxits = 20;
    nneedrest = 1;
    nneurons = 20;
    nout = 1;
    enssize = 100;
    for(xp=1; xp<=2; xp++)
    {
        nin = ae_randominteger(3, _state)+1;
        rvectorsetlengthatleast(&x, nin, _state);
        mlpcreatetrainer(nin, nout, &trainer, _state);
        mlpsetdecay(&trainer, decay, _state);
        mlpsetcond(&trainer, wstep, maxits, _state);
        rmatrixsetlengthatleast(&xytrain, ntrain, nin+nout, _state);
        rmatrixsetlengthatleast(&xytest, ntest, nin+nout, _state);
        withtrainer = ae_randominteger(2, _state);
        issparse = 0;
        if( withtrainer==0 )
        {
            issparse = 0;
        }
        if( withtrainer==1 )
        {
            issparse = ae_randominteger(2, _state);
        }
        
        /*
         * Training set
         */
        for(i=0; i<=ntrain-1; i++)
        {
            for(j=0; j<=nin-1; j++)
            {
                xytrain.ptr.pp_double[i][j] = (mxval-mnval)*ae_randomreal(_state)+mnval;
            }
            xytrain.ptr.pp_double[i][nin] = (double)(0);
            for(j=0; j<=nin-1; j++)
            {
                xytrain.ptr.pp_double[i][nin] = xytrain.ptr.pp_double[i][nin]+xytrain.ptr.pp_double[i][j];
            }
        }
        if( withtrainer==1 )
        {
            
            /*
             * Dense matrix
             */
            if( issparse==0 )
            {
                mlpsetdataset(&trainer, &xytrain, ntrain, _state);
            }
            
            /*
             * Sparse matrix
             */
            if( issparse==1 )
            {
                sparsecreate(ntrain, nin+nout, ntrain*(nin+nout), &xytrainsp, _state);
                
                /*
                 * Just copy dense matrix to sparse matrix(using SparseGet() is too expensive).
                 */
                for(i=0; i<=ntrain-1; i++)
                {
                    for(j=0; j<=nin+nout-1; j++)
                    {
                        sparseset(&xytrainsp, i, j, xytrain.ptr.pp_double[i][j], _state);
                    }
                }
                sparseconverttocrs(&xytrainsp, _state);
                mlpsetsparsedataset(&trainer, &xytrainsp, ntrain, _state);
            }
        }
        
        /*
         * Test set
         */
        for(i=0; i<=ntest-1; i++)
        {
            for(j=0; j<=nin-1; j++)
            {
                xytest.ptr.pp_double[i][j] = (mxval-mnval)*ae_randomreal(_state)+mnval;
            }
            xytest.ptr.pp_double[i][nin] = (double)(0);
            for(j=0; j<=nin-1; j++)
            {
                xytest.ptr.pp_double[i][nin] = xytest.ptr.pp_double[i][nin]+xytest.ptr.pp_double[i][j];
            }
        }
        
        /*
         * Create ensemble
         */
        mlpecreate1(nin, nneurons, nout, enssize, &netens, _state);
        
        /*
         * Train ensembles:
         *  * without trainer;
         */
        if( withtrainer==0 )
        {
            mlpetraines(&netens, &xytrain, ntrain, decay, nneedrest, &info, &rep, _state);
        }
        
        /*
         *  * with trainer.
         */
        if( withtrainer==1 )
        {
            mlptrainensemblees(&trainer, &netens, nneedrest, &rep, _state);
        }
        
        /*
         * Test that Rep contains correct error values
         */
        mlpeallerrorsx(&netens, &xytrain, &xytrainsp, ntrain, 0, &netens.network.dummyidx, 0, ntrain, 0, &netens.network.buf, &repx, _state);
        seterrorflagdiff(&result, rep.relclserror, repx.relclserror, 1.0E-4, 1.0E-2, _state);
        seterrorflagdiff(&result, rep.avgce, repx.avgce, 1.0E-4, 1.0E-2, _state);
        seterrorflagdiff(&result, rep.rmserror, repx.rmserror, 1.0E-4, 1.0E-2, _state);
        seterrorflagdiff(&result, rep.avgerror, repx.avgerror, 1.0E-4, 1.0E-2, _state);
        seterrorflagdiff(&result, rep.avgrelerror, repx.avgrelerror, 1.0E-4, 1.0E-2, _state);
        
        /*
         * Test that network fits data well. Calculate average error:
         *  * on training dataset;
         *  * on test dataset. (here we reduce the accuracy
         *    requirements - average error is compared with 2*Eps).
         */
        avgerr = (double)(0);
        for(i=0; i<=ntrain-1; i++)
        {
            if( issparse==0 )
            {
                ae_v_move(&x.ptr.p_double[0], 1, &xytrain.ptr.pp_double[i][0], 1, ae_v_len(0,nin-1));
            }
            if( issparse==1 )
            {
                sparsegetrow(&xytrainsp, i, &x, _state);
            }
            mlpeprocess(&netens, &x, &y, _state);
            avgerr = avgerr+ae_fabs(y.ptr.p_double[0]-xytrain.ptr.pp_double[i][nin], _state);
        }
        avgerr = avgerr/ntrain;
        ae_set_error_flag(&result, ae_fp_greater(avgerr,eps), __FILE__, __LINE__, "testmlptrainunit.ap:1799");
        avgerr = (double)(0);
        for(i=0; i<=ntest-1; i++)
        {
            ae_v_move(&x.ptr.p_double[0], 1, &xytest.ptr.pp_double[i][0], 1, ae_v_len(0,nin-1));
            mlpeprocess(&netens, &x, &y, _state);
            avgerr = avgerr+ae_fabs(y.ptr.p_double[0]-xytest.ptr.pp_double[i][nin], _state);
        }
        avgerr = avgerr/ntest;
        ae_set_error_flag(&result, ae_fp_greater(avgerr,2*eps), __FILE__, __LINE__, "testmlptrainunit.ap:1808");
    }
    
    /*
     * Catch bug in implementation of MLPTrainEnsembleX:
     * test ensemble training on empty dataset.
     *
     * Unfixed version should crash with violation of array
     * bounds (at least in C#).
     */
    nin = 2;
    nout = 2;
    nneurons = 3;
    enssize = 3;
    nneedrest = 2;
    wstep = 0.001;
    maxits = 2;
    mlpcreatetrainer(nin, nout, &trainer, _state);
    mlpsetcond(&trainer, wstep, maxits, _state);
    mlpecreate1(nin, nneurons, nout, enssize, &netens, _state);
    mlptrainensemblees(&trainer, &netens, nneedrest, &rep, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Testing for functions MLPETrainES and MLPTrainEnsembleES on classification
problems.
*************************************************************************/
static ae_bool testmlptrainunit_testmlptrainenscls(ae_state *_state)
{
    ae_frame _frame_block;
    mlptrainer trainer;
    mlpensemble netens;
    mlpreport rep;
    ae_int_t info;
    sparsematrix xytrainsp;
    ae_matrix xytrain;
    ae_matrix xytest;
    ae_int_t nin;
    ae_int_t nout;
    ae_vector x;
    ae_vector y;
    double decay;
    double wstep;
    ae_int_t maxits;
    ae_int_t nneedrest;
    ae_int_t enssize;
    ae_int_t val;
    ae_int_t ntrain;
    ae_int_t ntest;
    double avgerr;
    double eps;
    double delta;
    ae_int_t issparse;
    ae_int_t withtrainer;
    ae_int_t xp;
    ae_int_t nxp;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&trainer, 0, sizeof(trainer));
    memset(&netens, 0, sizeof(netens));
    memset(&rep, 0, sizeof(rep));
    memset(&xytrainsp, 0, sizeof(xytrainsp));
    memset(&xytrain, 0, sizeof(xytrain));
    memset(&xytest, 0, sizeof(xytest));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    _mlptrainer_init(&trainer, _state, ae_true);
    _mlpensemble_init(&netens, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    _sparsematrix_init(&xytrainsp, _state, ae_true);
    ae_matrix_init(&xytrain, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xytest, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);

    eps = 5.0E-2;
    delta = 0.1;
    ntrain = 90;
    ntest = 90;
    nin = 3;
    nout = 3;
    rvectorsetlengthatleast(&x, nin, _state);
    rmatrixsetlengthatleast(&xytrain, ntrain, nin+1, _state);
    rmatrixsetlengthatleast(&xytest, ntest, nin+1, _state);
    decay = 1.0E-3;
    wstep = 1.0E-3;
    maxits = 100;
    nneedrest = 1;
    mlpcreatetrainercls(nin, nout, &trainer, _state);
    mlpsetdecay(&trainer, decay, _state);
    mlpsetcond(&trainer, wstep, maxits, _state);
    nxp = 5;
    for(xp=1; xp<=nxp; xp++)
    {
        enssize = ae_round(ae_pow((double)(10), (double)(ae_randominteger(2, _state)+1), _state), _state);
        withtrainer = ae_randominteger(2, _state);
        issparse = 0;
        if( withtrainer==0 )
        {
            issparse = 0;
        }
        if( withtrainer==1 )
        {
            issparse = ae_randominteger(2, _state);
        }
        for(i=0; i<=ntrain-1; i++)
        {
            val = i%nin;
            for(j=0; j<=nin-1; j++)
            {
                xytrain.ptr.pp_double[i][j] = delta*(ae_randomreal(_state)-1);
            }
            xytrain.ptr.pp_double[i][val] = xytrain.ptr.pp_double[i][val]+1;
            xytrain.ptr.pp_double[i][nin] = (double)(val);
        }
        
        /*
         * Set dense dataset in trainer
         */
        if( issparse==0 )
        {
            mlpsetdataset(&trainer, &xytrain, ntrain, _state);
        }
        
        /*
         *  * Sparse dataset(create it with using dense dataset).
         */
        if( issparse==1 )
        {
            sparsecreate(ntrain, nin+1, ntrain*(nin+1), &xytrainsp, _state);
            for(i=0; i<=ntrain-1; i++)
            {
                for(j=0; j<=nin-1; j++)
                {
                    sparseset(&xytrainsp, i, j, xytrain.ptr.pp_double[i][j], _state);
                }
                sparseset(&xytrainsp, i, nin, xytrain.ptr.pp_double[i][nin], _state);
            }
            sparseconverttocrs(&xytrainsp, _state);
            
            /*
             * Set sparse dataset in trainer
             */
            mlpsetsparsedataset(&trainer, &xytrainsp, ntrain, _state);
        }
        
        /*
         * Create test set
         */
        for(i=0; i<=ntest-1; i++)
        {
            val = ae_randominteger(nin, _state);
            for(j=0; j<=nin-1; j++)
            {
                xytest.ptr.pp_double[i][j] = delta*(ae_randomreal(_state)-1);
            }
            xytest.ptr.pp_double[i][val] = xytest.ptr.pp_double[i][val]+1;
            xytest.ptr.pp_double[i][nin] = (double)(val);
        }
        
        /*
         * Create ensemble
         */
        mlpecreatec0(nin, nout, enssize, &netens, _state);
        
        /*
         * Train ensembles:
         *  * without trainer;
         */
        if( withtrainer==0 )
        {
            mlpetraines(&netens, &xytrain, ntrain, decay, nneedrest, &info, &rep, _state);
        }
        
        /*
         *  * with trainer.
         */
        if( withtrainer==1 )
        {
            mlptrainensemblees(&trainer, &netens, nneedrest, &rep, _state);
        }
        
        /*
         * Calculate average error:
         *  * on training dataset;
         */
        avgerr = (double)(0);
        for(i=0; i<=ntrain-1; i++)
        {
            if( issparse==0 )
            {
                ae_v_move(&x.ptr.p_double[0], 1, &xytrain.ptr.pp_double[i][0], 1, ae_v_len(0,nin-1));
            }
            if( issparse==1 )
            {
                sparsegetrow(&xytrainsp, i, &x, _state);
            }
            mlpeprocess(&netens, &x, &y, _state);
            for(j=0; j<=nout-1; j++)
            {
                if( ae_fp_neq((double)(j),xytrain.ptr.pp_double[i][nin]) )
                {
                    avgerr = avgerr+y.ptr.p_double[j];
                }
                else
                {
                    avgerr = avgerr+(1-y.ptr.p_double[j]);
                }
            }
        }
        avgerr = avgerr/(ntrain*nout);
        if( ae_fp_greater(avgerr,eps) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         *  * on test dataset.
         */
        avgerr = (double)(0);
        for(i=0; i<=ntest-1; i++)
        {
            ae_v_move(&x.ptr.p_double[0], 1, &xytest.ptr.pp_double[i][0], 1, ae_v_len(0,nin-1));
            mlpeprocess(&netens, &x, &y, _state);
            for(j=0; j<=nout-1; j++)
            {
                if( ae_fp_neq((double)(j),xytest.ptr.pp_double[i][nin]) )
                {
                    avgerr = avgerr+y.ptr.p_double[j];
                }
                else
                {
                    avgerr = avgerr+(1-y.ptr.p_double[j]);
                }
            }
        }
        avgerr = avgerr/(ntest*nout);
        if( ae_fp_greater(avgerr,eps) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}



static ae_bool testclusteringunit_basicahctests(ae_state *_state);
static ae_bool testclusteringunit_advancedahctests(ae_state *_state);
static void testclusteringunit_kmeanssimpletest1(ae_int_t nvars,
     ae_int_t nc,
     ae_int_t passcount,
     ae_bool* converrors,
     ae_bool* othererrors,
     ae_bool* simpleerrors,
     ae_state *_state);
static void testclusteringunit_kmeansspecialtests(ae_bool* othererrors,
     ae_state *_state);
static void testclusteringunit_kmeansinfinitelooptest(ae_bool* othererrors,
     ae_state *_state);
static void testclusteringunit_kmeansrestartstest(ae_bool* converrors,
     ae_bool* restartserrors,
     ae_state *_state);
static double testclusteringunit_rnormal(ae_state *_state);
static void testclusteringunit_rsphere(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t i,
     ae_state *_state);
static double testclusteringunit_distfunc(/* Real    */ ae_vector* x0,
     /* Real    */ ae_vector* x1,
     ae_int_t d,
     ae_int_t disttype,
     ae_state *_state);
static ae_bool testclusteringunit_errorsinmerges(/* Real    */ ae_matrix* d,
     /* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nf,
     ahcreport* rep,
     ae_int_t ahcalgo,
     ae_state *_state);
static void testclusteringunit_kmeansreferenceupdatedistances(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nvars,
     /* Real    */ ae_matrix* ct,
     ae_int_t k,
     /* Integer */ ae_vector* xyc,
     /* Real    */ ae_vector* xydist2,
     ae_state *_state);





/*************************************************************************
Testing clustering
*************************************************************************/
ae_bool testclustering(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool basicahcerrors;
    ae_bool ahcerrors;
    ae_bool kmeansconverrors;
    ae_bool kmeanssimpleerrors;
    ae_bool kmeansothererrors;
    ae_bool kmeansrestartserrors;
    ae_int_t passcount;
    ae_int_t nf;
    ae_int_t nc;
    ae_bool result;


    
    /*
     * AHC tests
     */
    basicahcerrors = testclusteringunit_basicahctests(_state);
    ahcerrors = testclusteringunit_advancedahctests(_state);
    
    /*
     * k-means tests
     */
    passcount = 10;
    kmeansconverrors = ae_false;
    kmeansothererrors = ae_false;
    kmeanssimpleerrors = ae_false;
    kmeansrestartserrors = ae_false;
    testclusteringunit_kmeansspecialtests(&kmeansothererrors, _state);
    testclusteringunit_kmeansinfinitelooptest(&kmeansothererrors, _state);
    testclusteringunit_kmeansrestartstest(&kmeansconverrors, &kmeansrestartserrors, _state);
    for(nf=1; nf<=5; nf++)
    {
        for(nc=1; nc<=5; nc++)
        {
            testclusteringunit_kmeanssimpletest1(nf, nc, passcount, &kmeansconverrors, &kmeansothererrors, &kmeanssimpleerrors, _state);
        }
    }
    
    /*
     * Results
     */
    waserrors = ae_false;
    waserrors = waserrors||(basicahcerrors||ahcerrors);
    waserrors = waserrors||(((kmeansconverrors||kmeansothererrors)||kmeanssimpleerrors)||kmeansrestartserrors);
    if( !silent )
    {
        printf("TESTING CLUSTERING\n");
        printf("AHC:                                \n");
        printf("* BASIC TESTS                       ");
        if( !basicahcerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* GENERAL TESTS                     ");
        if( !ahcerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("K-MEANS:                            \n");
        printf("* CONVERGENCE                       ");
        if( !kmeansconverrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* SIMPLE TASKS                      ");
        if( !kmeanssimpleerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OTHER PROPERTIES                  ");
        if( !kmeansothererrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* RESTARTS PROPERTIES               ");
        if( !kmeansrestartserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Basic agglomerative hierarchical clustering tests:
returns True on failure, False on success.

Basic tests study algorithm behavior on simple,  hand-made  datasets  with
small number of points (1..10).
*************************************************************************/
static ae_bool testclusteringunit_basicahctests(ae_state *_state)
{
    ae_frame _frame_block;
    clusterizerstate s;
    ahcreport rep;
    ae_matrix xy;
    ae_matrix d;
    ae_matrix c;
    ae_bool berr;
    ae_int_t ahcalgo;
    ae_int_t i;
    ae_int_t j;
    ae_int_t npoints;
    ae_int_t k;
    ae_vector cidx;
    ae_vector cz;
    ae_vector cidx2;
    ae_vector cz2;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&d, 0, sizeof(d));
    memset(&c, 0, sizeof(c));
    memset(&cidx, 0, sizeof(cidx));
    memset(&cz, 0, sizeof(cz));
    memset(&cidx2, 0, sizeof(cidx2));
    memset(&cz2, 0, sizeof(cz2));
    _clusterizerstate_init(&s, _state, ae_true);
    _ahcreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&d, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cidx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&cz, 0, DT_INT, _state, ae_true);
    ae_vector_init(&cidx2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&cz2, 0, DT_INT, _state, ae_true);

    result = ae_true;
    
    /*
     * Test on empty problem
     */
    clusterizercreate(&s, _state);
    clusterizerrunahc(&s, &rep, _state);
    if( rep.npoints!=0 )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test on problem with one point
     */
    ae_matrix_set_length(&xy, 1, 2, _state);
    xy.ptr.pp_double[0][0] = ae_randomreal(_state);
    xy.ptr.pp_double[0][1] = ae_randomreal(_state);
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, 1, 2, 0, _state);
    clusterizerrunahc(&s, &rep, _state);
    if( rep.npoints!=1 )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test on problem with two points
     */
    ae_matrix_set_length(&xy, 2, 2, _state);
    xy.ptr.pp_double[0][0] = ae_randomreal(_state);
    xy.ptr.pp_double[0][1] = ae_randomreal(_state);
    xy.ptr.pp_double[1][0] = ae_randomreal(_state);
    xy.ptr.pp_double[1][1] = ae_randomreal(_state);
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, 2, 2, 0, _state);
    clusterizerrunahc(&s, &rep, _state);
    if( (rep.npoints!=2||rep.z.rows!=1)||rep.z.cols!=2 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( rep.z.ptr.pp_int[0][0]!=0||rep.z.ptr.pp_int[0][1]!=1 )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test on specially designed problem which should have
     * following dendrogram:
     *
     *   ------
     *   |    |
     * ----  ----
     * |  |  |  |
     * 0  1  2  3
     *
     * ...with first merge performed on 0 and 1, second merge
     * performed on 2 and 3. Complete linkage is used.
     *
     * Additionally we test ClusterizerSeparatedByDist() on this
     * problem for different distances. Test is performed by
     * comparing function result with ClusterizerGetKClusters()
     * for known K.
     */
    ae_matrix_set_length(&xy, 4, 1, _state);
    xy.ptr.pp_double[0][0] = 0.0;
    xy.ptr.pp_double[1][0] = 1.0;
    xy.ptr.pp_double[2][0] = 3.0;
    xy.ptr.pp_double[3][0] = 4.1;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, 4, 1, 0, _state);
    clusterizersetahcalgo(&s, 0, _state);
    clusterizerrunahc(&s, &rep, _state);
    if( (((rep.npoints!=4||rep.z.rows!=3)||rep.z.cols!=2)||rep.pz.rows!=3)||rep.pz.cols!=2 )
    {
        ae_frame_leave(_state);
        return result;
    }
    berr = ae_false;
    berr = (berr||rep.z.ptr.pp_int[0][0]!=0)||rep.z.ptr.pp_int[0][1]!=1;
    berr = (berr||rep.z.ptr.pp_int[1][0]!=2)||rep.z.ptr.pp_int[1][1]!=3;
    berr = (berr||rep.z.ptr.pp_int[2][0]!=4)||rep.z.ptr.pp_int[2][1]!=5;
    berr = (((berr||rep.p.ptr.p_int[0]!=0)||rep.p.ptr.p_int[1]!=1)||rep.p.ptr.p_int[2]!=2)||rep.p.ptr.p_int[3]!=3;
    berr = (berr||rep.pz.ptr.pp_int[0][0]!=0)||rep.pz.ptr.pp_int[0][1]!=1;
    berr = (berr||rep.pz.ptr.pp_int[1][0]!=2)||rep.pz.ptr.pp_int[1][1]!=3;
    berr = (berr||rep.pz.ptr.pp_int[2][0]!=4)||rep.pz.ptr.pp_int[2][1]!=5;
    berr = (((berr||rep.pm.ptr.pp_int[0][0]!=0)||rep.pm.ptr.pp_int[0][1]!=0)||rep.pm.ptr.pp_int[0][2]!=1)||rep.pm.ptr.pp_int[0][3]!=1;
    berr = (((berr||rep.pm.ptr.pp_int[1][0]!=2)||rep.pm.ptr.pp_int[1][1]!=2)||rep.pm.ptr.pp_int[1][2]!=3)||rep.pm.ptr.pp_int[1][3]!=3;
    berr = (((berr||rep.pm.ptr.pp_int[2][0]!=0)||rep.pm.ptr.pp_int[2][1]!=1)||rep.pm.ptr.pp_int[2][2]!=2)||rep.pm.ptr.pp_int[2][3]!=3;
    if( berr )
    {
        ae_frame_leave(_state);
        return result;
    }
    clusterizerseparatedbydist(&rep, 0.5, &k, &cidx, &cz, _state);
    clusterizergetkclusters(&rep, 4, &cidx2, &cz2, _state);
    if( k!=4 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ((cidx.ptr.p_int[0]!=cidx2.ptr.p_int[0]||cidx.ptr.p_int[1]!=cidx2.ptr.p_int[1])||cidx.ptr.p_int[2]!=cidx2.ptr.p_int[2])||cidx.ptr.p_int[3]!=cidx2.ptr.p_int[3] )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ((cz.ptr.p_int[0]!=cz2.ptr.p_int[0]||cz.ptr.p_int[1]!=cz2.ptr.p_int[1])||cz.ptr.p_int[2]!=cz2.ptr.p_int[2])||cz.ptr.p_int[3]!=cz2.ptr.p_int[3] )
    {
        ae_frame_leave(_state);
        return result;
    }
    clusterizerseparatedbydist(&rep, 1.05, &k, &cidx, &cz, _state);
    clusterizergetkclusters(&rep, 3, &cidx2, &cz2, _state);
    if( k!=3 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ((cidx.ptr.p_int[0]!=cidx2.ptr.p_int[0]||cidx.ptr.p_int[1]!=cidx2.ptr.p_int[1])||cidx.ptr.p_int[2]!=cidx2.ptr.p_int[2])||cidx.ptr.p_int[3]!=cidx2.ptr.p_int[3] )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( (cz.ptr.p_int[0]!=cz2.ptr.p_int[0]||cz.ptr.p_int[1]!=cz2.ptr.p_int[1])||cz.ptr.p_int[2]!=cz2.ptr.p_int[2] )
    {
        ae_frame_leave(_state);
        return result;
    }
    clusterizerseparatedbydist(&rep, 1.15, &k, &cidx, &cz, _state);
    clusterizergetkclusters(&rep, 2, &cidx2, &cz2, _state);
    if( k!=2 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ((cidx.ptr.p_int[0]!=cidx2.ptr.p_int[0]||cidx.ptr.p_int[1]!=cidx2.ptr.p_int[1])||cidx.ptr.p_int[2]!=cidx2.ptr.p_int[2])||cidx.ptr.p_int[3]!=cidx2.ptr.p_int[3] )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( cz.ptr.p_int[0]!=cz2.ptr.p_int[0]||cz.ptr.p_int[1]!=cz2.ptr.p_int[1] )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test on specially designed problem with Pearson distance
     * which should have following dendrogram:
     *
     *   ------
     *   |    |
     * ----  ----
     * |  |  |  |
     * 0  1  2  3
     *
     * This problem is used to test ClusterizerSeparatedByDist().
     * The test is performed by comparing function result with
     * ClusterizerGetKClusters() for known K.
     *
     * NOTE:
     * * corr(a0,a1) = 0.866
     * * corr(a2,a3) = 0.990
     * * corr(a0/a1, a2/a3)<=0.5
     */
    ae_matrix_set_length(&xy, 4, 3, _state);
    xy.ptr.pp_double[0][0] = 0.3;
    xy.ptr.pp_double[0][1] = 0.5;
    xy.ptr.pp_double[0][2] = 0.3;
    xy.ptr.pp_double[1][0] = 0.3;
    xy.ptr.pp_double[1][1] = 0.5;
    xy.ptr.pp_double[1][2] = 0.4;
    xy.ptr.pp_double[2][0] = 0.1;
    xy.ptr.pp_double[2][1] = 0.5;
    xy.ptr.pp_double[2][2] = 0.9;
    xy.ptr.pp_double[3][0] = 0.1;
    xy.ptr.pp_double[3][1] = 0.4;
    xy.ptr.pp_double[3][2] = 0.9;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, 4, 3, 10, _state);
    clusterizersetahcalgo(&s, 1, _state);
    clusterizerrunahc(&s, &rep, _state);
    clusterizerseparatedbycorr(&rep, 0.999, &k, &cidx, &cz, _state);
    clusterizergetkclusters(&rep, 4, &cidx2, &cz2, _state);
    if( k!=4 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ((cidx.ptr.p_int[0]!=cidx2.ptr.p_int[0]||cidx.ptr.p_int[1]!=cidx2.ptr.p_int[1])||cidx.ptr.p_int[2]!=cidx2.ptr.p_int[2])||cidx.ptr.p_int[3]!=cidx2.ptr.p_int[3] )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ((cz.ptr.p_int[0]!=cz2.ptr.p_int[0]||cz.ptr.p_int[1]!=cz2.ptr.p_int[1])||cz.ptr.p_int[2]!=cz2.ptr.p_int[2])||cz.ptr.p_int[3]!=cz2.ptr.p_int[3] )
    {
        ae_frame_leave(_state);
        return result;
    }
    clusterizerseparatedbycorr(&rep, 0.900, &k, &cidx, &cz, _state);
    clusterizergetkclusters(&rep, 3, &cidx2, &cz2, _state);
    if( k!=3 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ((cidx.ptr.p_int[0]!=cidx2.ptr.p_int[0]||cidx.ptr.p_int[1]!=cidx2.ptr.p_int[1])||cidx.ptr.p_int[2]!=cidx2.ptr.p_int[2])||cidx.ptr.p_int[3]!=cidx2.ptr.p_int[3] )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( (cz.ptr.p_int[0]!=cz2.ptr.p_int[0]||cz.ptr.p_int[1]!=cz2.ptr.p_int[1])||cz.ptr.p_int[2]!=cz2.ptr.p_int[2] )
    {
        ae_frame_leave(_state);
        return result;
    }
    clusterizerseparatedbycorr(&rep, 0.600, &k, &cidx, &cz, _state);
    clusterizergetkclusters(&rep, 2, &cidx2, &cz2, _state);
    if( k!=2 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ((cidx.ptr.p_int[0]!=cidx2.ptr.p_int[0]||cidx.ptr.p_int[1]!=cidx2.ptr.p_int[1])||cidx.ptr.p_int[2]!=cidx2.ptr.p_int[2])||cidx.ptr.p_int[3]!=cidx2.ptr.p_int[3] )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( cz.ptr.p_int[0]!=cz2.ptr.p_int[0]||cz.ptr.p_int[1]!=cz2.ptr.p_int[1] )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Single linkage vs. complete linkage
     */
    ae_matrix_set_length(&xy, 6, 1, _state);
    xy.ptr.pp_double[0][0] = 0.0;
    xy.ptr.pp_double[1][0] = 1.0;
    xy.ptr.pp_double[2][0] = 2.1;
    xy.ptr.pp_double[3][0] = 3.3;
    xy.ptr.pp_double[4][0] = 6.0;
    xy.ptr.pp_double[5][0] = 4.6;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, 6, 1, 0, _state);
    clusterizersetahcalgo(&s, 0, _state);
    clusterizerrunahc(&s, &rep, _state);
    if( rep.npoints!=6||rep.p.cnt!=6 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ((rep.z.rows!=5||rep.z.cols!=2)||rep.pz.rows!=5)||rep.pz.cols!=2 )
    {
        ae_frame_leave(_state);
        return result;
    }
    berr = ae_false;
    berr = berr||rep.p.ptr.p_int[0]!=2;
    berr = berr||rep.p.ptr.p_int[1]!=3;
    berr = berr||rep.p.ptr.p_int[2]!=4;
    berr = berr||rep.p.ptr.p_int[3]!=5;
    berr = berr||rep.p.ptr.p_int[4]!=0;
    berr = berr||rep.p.ptr.p_int[5]!=1;
    berr = (berr||rep.z.ptr.pp_int[0][0]!=0)||rep.z.ptr.pp_int[0][1]!=1;
    berr = (berr||rep.z.ptr.pp_int[1][0]!=2)||rep.z.ptr.pp_int[1][1]!=3;
    berr = (berr||rep.z.ptr.pp_int[2][0]!=4)||rep.z.ptr.pp_int[2][1]!=5;
    berr = (berr||rep.z.ptr.pp_int[3][0]!=6)||rep.z.ptr.pp_int[3][1]!=7;
    berr = (berr||rep.z.ptr.pp_int[4][0]!=8)||rep.z.ptr.pp_int[4][1]!=9;
    berr = (berr||rep.pz.ptr.pp_int[0][0]!=2)||rep.pz.ptr.pp_int[0][1]!=3;
    berr = (berr||rep.pz.ptr.pp_int[1][0]!=4)||rep.pz.ptr.pp_int[1][1]!=5;
    berr = (berr||rep.pz.ptr.pp_int[2][0]!=0)||rep.pz.ptr.pp_int[2][1]!=1;
    berr = (berr||rep.pz.ptr.pp_int[3][0]!=6)||rep.pz.ptr.pp_int[3][1]!=7;
    berr = (berr||rep.pz.ptr.pp_int[4][0]!=8)||rep.pz.ptr.pp_int[4][1]!=9;
    if( berr )
    {
        ae_frame_leave(_state);
        return result;
    }
    clusterizersetahcalgo(&s, 1, _state);
    clusterizerrunahc(&s, &rep, _state);
    if( (rep.npoints!=6||rep.z.rows!=5)||rep.z.cols!=2 )
    {
        ae_frame_leave(_state);
        return result;
    }
    berr = ae_false;
    berr = (berr||rep.z.ptr.pp_int[0][0]!=0)||rep.z.ptr.pp_int[0][1]!=1;
    berr = (berr||rep.z.ptr.pp_int[1][0]!=2)||rep.z.ptr.pp_int[1][1]!=6;
    berr = (berr||rep.z.ptr.pp_int[2][0]!=3)||rep.z.ptr.pp_int[2][1]!=7;
    berr = (berr||rep.z.ptr.pp_int[3][0]!=5)||rep.z.ptr.pp_int[3][1]!=8;
    berr = (berr||rep.z.ptr.pp_int[4][0]!=4)||rep.z.ptr.pp_int[4][1]!=9;
    if( berr )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test which differentiates complete linkage and average linkage from
     * single linkage:
     * * we have cluster C0={(-0.5), (0)},
     *   cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)},
     *   and point P between them - (10.0)
     * * we try three different strategies - single linkage, complete
     *   linkage, average linkage.
     * * any strategy will merge C0 first, then merge points of C1,
     *   and then merge P with C0 or C1 (depending on linkage type)
     * * we test that:
     *   a) C0 is merged first
     *   b) after 5 merges (including merge of C0), P is merged with C0 or C1
     *   c) P is merged with C1 when we have single linkage, with C0 otherwise
     */
    ae_matrix_set_length(&xy, 8, 1, _state);
    xy.ptr.pp_double[0][0] = -0.5;
    xy.ptr.pp_double[1][0] = 0.0;
    xy.ptr.pp_double[2][0] = 10.0;
    xy.ptr.pp_double[3][0] = 19.0;
    xy.ptr.pp_double[4][0] = 20.0;
    xy.ptr.pp_double[5][0] = 21.0;
    xy.ptr.pp_double[6][0] = 22.0;
    xy.ptr.pp_double[7][0] = 23.0;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, 8, 1, 0, _state);
    for(ahcalgo=0; ahcalgo<=2; ahcalgo++)
    {
        clusterizersetahcalgo(&s, ahcalgo, _state);
        clusterizerrunahc(&s, &rep, _state);
        if( (rep.npoints!=8||rep.z.rows!=7)||rep.z.cols!=2 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[0][0]!=0||rep.z.ptr.pp_int[0][1]!=1 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[5][0]!=2&&rep.z.ptr.pp_int[5][1]!=2 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[5][0]!=2&&rep.z.ptr.pp_int[5][1]!=2 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( (ahcalgo==0||ahcalgo==2)&&(rep.z.ptr.pp_int[5][0]!=8&&rep.z.ptr.pp_int[5][1]!=8) )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( ahcalgo==1&&(rep.z.ptr.pp_int[5][0]==8||rep.z.ptr.pp_int[5][1]==8) )
        {
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * Test which differentiates single linkage and average linkage from
     * complete linkage:
     * * we have cluster C0={(-2.5), (-2.0)},
     *   cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)},
     *   and point P between them - (10.0)
     * * we try three different strategies - single linkage, complete
     *   linkage, average linkage.
     * * any strategy will merge C0 first, then merge points of C1,
     *   and then merge P with C0 or C1 (depending on linkage type)
     * * we test that:
     *   a) C0 is merged first
     *   b) after 5 merges (including merge of C0), P is merged with C0 or C1
     *   c) P is merged with C0 when we have complete linkage, with C1 otherwise
     */
    ae_matrix_set_length(&xy, 8, 1, _state);
    xy.ptr.pp_double[0][0] = -2.5;
    xy.ptr.pp_double[1][0] = -2.0;
    xy.ptr.pp_double[2][0] = 10.0;
    xy.ptr.pp_double[3][0] = 19.0;
    xy.ptr.pp_double[4][0] = 20.0;
    xy.ptr.pp_double[5][0] = 21.0;
    xy.ptr.pp_double[6][0] = 22.0;
    xy.ptr.pp_double[7][0] = 23.0;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, 8, 1, 0, _state);
    for(ahcalgo=0; ahcalgo<=2; ahcalgo++)
    {
        clusterizersetahcalgo(&s, ahcalgo, _state);
        clusterizerrunahc(&s, &rep, _state);
        if( (rep.npoints!=8||rep.z.rows!=7)||rep.z.cols!=2 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[0][0]!=0||rep.z.ptr.pp_int[0][1]!=1 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[5][0]!=2&&rep.z.ptr.pp_int[5][1]!=2 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[5][0]!=2&&rep.z.ptr.pp_int[5][1]!=2 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( ahcalgo==0&&(rep.z.ptr.pp_int[5][0]!=8&&rep.z.ptr.pp_int[5][1]!=8) )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( (ahcalgo==1||ahcalgo==2)&&(rep.z.ptr.pp_int[5][0]==8||rep.z.ptr.pp_int[5][1]==8) )
        {
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * Test which differentiates weighred average linkage from unweighted average linkage:
     * * we have cluster C0={(0.0), (1.5), (2.5)},
     *   cluster C1={(7.5), (7.99)},
     *   and point P between them - (4.5)
     * * we try two different strategies - weighted average linkage and unweighted average linkage
     * * any strategy will merge C1 first, then merge points of C0,
     *   and then merge P with C0 or C1 (depending on linkage type)
     * * we test that:
     *   a) C1 is merged first, C0 is merged after that
     *   b) after first 3 merges P is merged with C0 or C1
     *   c) P is merged with C1 when we have weighted average linkage, with C0 otherwise
     */
    ae_matrix_set_length(&xy, 6, 1, _state);
    xy.ptr.pp_double[0][0] = 0.0;
    xy.ptr.pp_double[1][0] = 1.5;
    xy.ptr.pp_double[2][0] = 2.5;
    xy.ptr.pp_double[3][0] = 4.5;
    xy.ptr.pp_double[4][0] = 7.5;
    xy.ptr.pp_double[5][0] = 7.99;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, 6, 1, 0, _state);
    for(ahcalgo=2; ahcalgo<=3; ahcalgo++)
    {
        clusterizersetahcalgo(&s, ahcalgo, _state);
        clusterizerrunahc(&s, &rep, _state);
        if( (rep.npoints!=6||rep.z.rows!=5)||rep.z.cols!=2 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[0][0]!=4||rep.z.ptr.pp_int[0][1]!=5 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[1][0]!=1||rep.z.ptr.pp_int[1][1]!=2 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[2][0]!=0||rep.z.ptr.pp_int[2][1]!=7 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( rep.z.ptr.pp_int[3][0]!=3 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( ahcalgo==2&&rep.z.ptr.pp_int[3][1]!=8 )
        {
            ae_frame_leave(_state);
            return result;
        }
        if( ahcalgo==3&&rep.z.ptr.pp_int[3][1]!=6 )
        {
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * Test which checks correctness of Ward's method on very basic problem
     */
    ae_matrix_set_length(&xy, 4, 1, _state);
    xy.ptr.pp_double[0][0] = 0.0;
    xy.ptr.pp_double[1][0] = 1.0;
    xy.ptr.pp_double[2][0] = 3.1;
    xy.ptr.pp_double[3][0] = 4.0;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, xy.rows, xy.cols, 2, _state);
    clusterizergetdistances(&xy, xy.rows, xy.cols, 2, &d, _state);
    clusterizersetahcalgo(&s, 4, _state);
    clusterizerrunahc(&s, &rep, _state);
    if( testclusteringunit_errorsinmerges(&d, &xy, xy.rows, xy.cols, &rep, 4, _state) )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * One more Ward's test
     */
    ae_matrix_set_length(&xy, 8, 2, _state);
    xy.ptr.pp_double[0][0] = 0.4700566262;
    xy.ptr.pp_double[0][1] = 0.4565938448;
    xy.ptr.pp_double[1][0] = 0.2394499506;
    xy.ptr.pp_double[1][1] = 0.1750209592;
    xy.ptr.pp_double[2][0] = 0.6518417019;
    xy.ptr.pp_double[2][1] = 0.6151370746;
    xy.ptr.pp_double[3][0] = 0.9863942841;
    xy.ptr.pp_double[3][1] = 0.7855012189;
    xy.ptr.pp_double[4][0] = 0.1517812919;
    xy.ptr.pp_double[4][1] = 0.2600174758;
    xy.ptr.pp_double[5][0] = 0.7840203638;
    xy.ptr.pp_double[5][1] = 0.9023597604;
    xy.ptr.pp_double[6][0] = 0.2604194835;
    xy.ptr.pp_double[6][1] = 0.9792704661;
    xy.ptr.pp_double[7][0] = 0.6353096042;
    xy.ptr.pp_double[7][1] = 0.8252606906;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, xy.rows, xy.cols, 2, _state);
    clusterizergetdistances(&xy, xy.rows, xy.cols, 2, &d, _state);
    clusterizersetahcalgo(&s, 4, _state);
    clusterizerrunahc(&s, &rep, _state);
    if( rep.z.ptr.pp_int[0][0]!=1||rep.z.ptr.pp_int[0][1]!=4 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( rep.z.ptr.pp_int[1][0]!=5||rep.z.ptr.pp_int[1][1]!=7 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( rep.z.ptr.pp_int[2][0]!=0||rep.z.ptr.pp_int[2][1]!=2 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( rep.z.ptr.pp_int[3][0]!=3||rep.z.ptr.pp_int[3][1]!=9 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( rep.z.ptr.pp_int[4][0]!=10||rep.z.ptr.pp_int[4][1]!=11 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( rep.z.ptr.pp_int[5][0]!=6||rep.z.ptr.pp_int[5][1]!=12 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( rep.z.ptr.pp_int[6][0]!=8||rep.z.ptr.pp_int[6][1]!=13 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( testclusteringunit_errorsinmerges(&d, &xy, xy.rows, xy.cols, &rep, 4, _state) )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Ability to solve problems with zero distance matrix
     */
    npoints = 20;
    ae_matrix_set_length(&d, npoints, npoints, _state);
    for(i=0; i<=npoints-1; i++)
    {
        for(j=0; j<=npoints-1; j++)
        {
            d.ptr.pp_double[i][j] = 0.0;
        }
    }
    for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
    {
        clusterizercreate(&s, _state);
        clusterizersetdistances(&s, &d, npoints, ae_true, _state);
        clusterizersetahcalgo(&s, ahcalgo, _state);
        clusterizerrunahc(&s, &rep, _state);
        if( (rep.npoints!=npoints||rep.z.rows!=npoints-1)||rep.z.cols!=2 )
        {
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * Test GetKClusters()
     */
    ae_matrix_set_length(&xy, 8, 1, _state);
    xy.ptr.pp_double[0][0] = -2.5;
    xy.ptr.pp_double[1][0] = -2.0;
    xy.ptr.pp_double[2][0] = 10.0;
    xy.ptr.pp_double[3][0] = 19.0;
    xy.ptr.pp_double[4][0] = 20.0;
    xy.ptr.pp_double[5][0] = 21.0;
    xy.ptr.pp_double[6][0] = 22.0;
    xy.ptr.pp_double[7][0] = 23.0;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, 8, 1, 0, _state);
    clusterizersetahcalgo(&s, 0, _state);
    clusterizerrunahc(&s, &rep, _state);
    clusterizergetkclusters(&rep, 3, &cidx, &cz, _state);
    if( ((((((cidx.ptr.p_int[0]!=1||cidx.ptr.p_int[1]!=1)||cidx.ptr.p_int[2]!=0)||cidx.ptr.p_int[3]!=2)||cidx.ptr.p_int[4]!=2)||cidx.ptr.p_int[5]!=2)||cidx.ptr.p_int[6]!=2)||cidx.ptr.p_int[7]!=2 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( (cz.ptr.p_int[0]!=2||cz.ptr.p_int[1]!=8)||cz.ptr.p_int[2]!=12 )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test is done
     */
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Advanced  agglomerative  hierarchical  clustering  tests : returns True on
failure, False on success.

Advanced testing subroutine perform several automatically generated tests.
*************************************************************************/
static ae_bool testclusteringunit_advancedahctests(ae_state *_state)
{
    ae_frame _frame_block;
    clusterizerstate s;
    ahcreport rep;
    ae_matrix xy;
    ae_matrix dm;
    ae_matrix dm2;
    ae_vector idx;
    ae_vector disttypes;
    ae_vector x0;
    ae_vector x1;
    ae_int_t d;
    ae_int_t n;
    ae_int_t npoints;
    ae_int_t ahcalgo;
    ae_int_t disttype;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    ae_int_t t;
    ae_int_t euclidean;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&dm, 0, sizeof(dm));
    memset(&dm2, 0, sizeof(dm2));
    memset(&idx, 0, sizeof(idx));
    memset(&disttypes, 0, sizeof(disttypes));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    _clusterizerstate_init(&s, _state, ae_true);
    _ahcreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&dm, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&dm2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&idx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&disttypes, 0, DT_INT, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    euclidean = 2;
    
    /*
     * Test on D-dimensional problem:
     * * D = 2...5
     * * D clusters, each has N points;
     *   centers are located at x=(0 ... 1 ... 0);
     *   cluster radii are approximately 0.1
     * * single/complete/unweighted_average/weighted_average linkage/Ward's method are tested
     * * Euclidean distance is used, either:
     *   a) one given by distance matrix (ClusterizerSetDistances)
     *   b) one calculated from dataset (ClusterizerSetPoints)
     * * we have N*D points, and N*D-1 merges in total
     * * points are randomly rearranged after generation
     *
     * For all kinds of linkage we perform following test:
     * * for each point we remember index of its cluster
     *   (one which is determined during dataset generation)
     * * we clusterize points with ALGLIB capabilities
     * * we scan Rep.Z and perform first D*(N-1) merges
     * * for each merge we check that it merges points
     *   from same cluster;
     *
     * Additonally, we call ErrorsInMerges(). See function comments 
     * for more information about specific tests performed. This function
     * allows us to check that clusters are built exactly as specified by
     * definition of the clustering algorithm.
     */
    for(d=2; d<=5; d++)
    {
        for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
        {
            n = ae_round(ae_pow((double)(3), (double)(ae_randominteger(3, _state)), _state), _state);
            npoints = d*n;
            
            /*
             * 1. generate dataset.
             * 2. fill Idx (array of cluster indexes):
             *    * first N*D elements store cluster indexes
             *    * next D*(N-1) elements are filled during merges
             * 3. build distance matrix DM
             */
            ae_matrix_set_length(&xy, n*d, d, _state);
            ae_vector_set_length(&idx, n*d+d*(n-1), _state);
            for(i=0; i<=n*d-1; i++)
            {
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][j] = 0.2*ae_randomreal(_state)-0.1;
                }
                xy.ptr.pp_double[i][i%d] = xy.ptr.pp_double[i][i%d]+1.0;
                idx.ptr.p_int[i] = i%d;
            }
            for(i=0; i<=n*d-1; i++)
            {
                k = ae_randominteger(n*d, _state);
                if( k!=i )
                {
                    for(j=0; j<=d-1; j++)
                    {
                        v = xy.ptr.pp_double[i][j];
                        xy.ptr.pp_double[i][j] = xy.ptr.pp_double[k][j];
                        xy.ptr.pp_double[k][j] = v;
                    }
                    t = idx.ptr.p_int[k];
                    idx.ptr.p_int[k] = idx.ptr.p_int[i];
                    idx.ptr.p_int[i] = t;
                }
            }
            ae_matrix_set_length(&dm, npoints, npoints, _state);
            ae_vector_set_length(&x0, d, _state);
            ae_vector_set_length(&x1, d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=npoints-1; j++)
                {
                    ae_v_move(&x0.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,d-1));
                    ae_v_move(&x1.ptr.p_double[0], 1, &xy.ptr.pp_double[j][0], 1, ae_v_len(0,d-1));
                    dm.ptr.pp_double[i][j] = testclusteringunit_distfunc(&x0, &x1, d, euclidean, _state);
                }
            }
            
            /*
             * Clusterize with SetPoints()
             */
            clusterizercreate(&s, _state);
            clusterizersetpoints(&s, &xy, n*d, d, euclidean, _state);
            clusterizersetahcalgo(&s, ahcalgo, _state);
            clusterizerrunahc(&s, &rep, _state);
            
            /*
             * Tests:
             * * replay first D*(N-1) merges; these merges should take place
             *   within clusters, intercluster merges will be performed at the
             *   last stages of the processing.
             * * test with ErrorsInMerges()
             */
            if( rep.npoints!=npoints )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(i=0; i<=d*(n-1)-1; i++)
            {
                
                /*
                 * Check correctness of I-th row of Z
                 */
                if( (rep.z.ptr.pp_int[i][0]<0||rep.z.ptr.pp_int[i][0]>=rep.z.ptr.pp_int[i][1])||rep.z.ptr.pp_int[i][1]>=d*n+i )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                
                /*
                 * Check that merge is performed within cluster
                 */
                if( idx.ptr.p_int[rep.z.ptr.pp_int[i][0]]!=idx.ptr.p_int[rep.z.ptr.pp_int[i][1]] )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                
                /*
                 * Write new entry of Idx.
                 * Both points from the same cluster, so result of the merge
                 * belongs to the same cluster
                 */
                idx.ptr.p_int[n*d+i] = idx.ptr.p_int[rep.z.ptr.pp_int[i][1]];
            }
            if( ((ahcalgo==0||ahcalgo==1)||ahcalgo==2)||ahcalgo==4 )
            {
                if( testclusteringunit_errorsinmerges(&dm, &xy, d*n, d, &rep, ahcalgo, _state) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            
            /*
             * Clusterize one more time, now with distance matrix
             */
            clusterizercreate(&s, _state);
            clusterizersetdistances(&s, &dm, n*d, ae_fp_greater(ae_randomreal(_state),0.5), _state);
            clusterizersetahcalgo(&s, ahcalgo, _state);
            clusterizerrunahc(&s, &rep, _state);
            
            /*
             * Tests:
             * * replay first D*(N-1) merges; these merges should take place
             *   within clusters, intercluster merges will be performed at the
             *   last stages of the processing.
             * * test with ErrorsInMerges()
             */
            if( rep.npoints!=npoints )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(i=0; i<=d*(n-1)-1; i++)
            {
                
                /*
                 * Check correctness of I-th row of Z
                 */
                if( (rep.z.ptr.pp_int[i][0]<0||rep.z.ptr.pp_int[i][0]>=rep.z.ptr.pp_int[i][1])||rep.z.ptr.pp_int[i][1]>=d*n+i )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                
                /*
                 * Check that merge is performed within cluster
                 */
                if( idx.ptr.p_int[rep.z.ptr.pp_int[i][0]]!=idx.ptr.p_int[rep.z.ptr.pp_int[i][1]] )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                
                /*
                 * Write new entry of Idx.
                 * Both points from the same cluster, so result of the merge
                 * belongs to the same cluster
                 */
                idx.ptr.p_int[n*d+i] = idx.ptr.p_int[rep.z.ptr.pp_int[i][1]];
            }
            if( ((ahcalgo==0||ahcalgo==1)||ahcalgo==2)||ahcalgo==4 )
            {
                if( testclusteringunit_errorsinmerges(&dm, &xy, d*n, d, &rep, ahcalgo, _state) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    
    /*
     * Test on random D-dimensional problem:
     * * D = 2...5
     * * N=1..16 random points from unit hypercube
     * * single/complete/unweighted_average linkage/Ward's method are tested
     * * different distance functions are tested
     * * we call ErrorsInMerges() and we check distance matrix
     *   calculated by unit test against one returned by GetDistances()
     */
    ae_vector_set_length(&disttypes, 9, _state);
    disttypes.ptr.p_int[0] = 0;
    disttypes.ptr.p_int[1] = 1;
    disttypes.ptr.p_int[2] = 2;
    disttypes.ptr.p_int[3] = 10;
    disttypes.ptr.p_int[4] = 11;
    disttypes.ptr.p_int[5] = 12;
    disttypes.ptr.p_int[6] = 13;
    disttypes.ptr.p_int[7] = 20;
    disttypes.ptr.p_int[8] = 21;
    for(disttype=0; disttype<=disttypes.cnt-1; disttype++)
    {
        for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
        {
            if( ahcalgo==3 )
            {
                continue;
            }
            if( ahcalgo==4&&disttype!=2 )
            {
                continue;
            }
            npoints = ae_round(ae_pow((double)(2), (double)(ae_randominteger(5, _state)), _state), _state);
            d = 2+ae_randominteger(4, _state);
            
            /*
             * Generate dataset and distance matrix
             */
            ae_matrix_set_length(&xy, npoints, d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                }
            }
            ae_matrix_set_length(&dm, npoints, npoints, _state);
            ae_vector_set_length(&x0, d, _state);
            ae_vector_set_length(&x1, d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=npoints-1; j++)
                {
                    ae_v_move(&x0.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,d-1));
                    ae_v_move(&x1.ptr.p_double[0], 1, &xy.ptr.pp_double[j][0], 1, ae_v_len(0,d-1));
                    dm.ptr.pp_double[i][j] = testclusteringunit_distfunc(&x0, &x1, d, disttypes.ptr.p_int[disttype], _state);
                }
            }
            
            /*
             * Clusterize
             */
            clusterizercreate(&s, _state);
            clusterizersetpoints(&s, &xy, npoints, d, disttypes.ptr.p_int[disttype], _state);
            clusterizersetahcalgo(&s, ahcalgo, _state);
            clusterizerrunahc(&s, &rep, _state);
            
            /*
             * Test with ErrorsInMerges()
             */
            if( testclusteringunit_errorsinmerges(&dm, &xy, npoints, d, &rep, ahcalgo, _state) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Test distance matrix
             */
            clusterizergetdistances(&xy, npoints, d, disttypes.ptr.p_int[disttype], &dm2, _state);
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=npoints-1; j++)
                {
                    if( !ae_isfinite(dm2.ptr.pp_double[i][j], _state)||ae_fp_greater(ae_fabs(dm.ptr.pp_double[i][j]-dm2.ptr.pp_double[i][j], _state),1.0E5*ae_machineepsilon) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Simple test 1: ellipsoid in NF-dimensional space.
compare k-means centers with random centers
*************************************************************************/
static void testclusteringunit_kmeanssimpletest1(ae_int_t nvars,
     ae_int_t nc,
     ae_int_t passcount,
     ae_bool* converrors,
     ae_bool* othererrors,
     ae_bool* simpleerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t npoints;
    ae_int_t majoraxis;
    ae_matrix xy;
    ae_vector tmp;
    double v;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_int_t restarts;
    double ekmeans;
    double erandom;
    double dclosest;
    ae_int_t cclosest;
    clusterizerstate s;
    kmeansreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&tmp, 0, sizeof(tmp));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    _clusterizerstate_init(&s, _state, ae_true);
    _kmeansreport_init(&rep, _state, ae_true);

    npoints = nc*100;
    restarts = 5;
    passcount = 10;
    ae_vector_set_length(&tmp, nvars-1+1, _state);
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Fill
         */
        ae_matrix_set_length(&xy, npoints-1+1, nvars-1+1, _state);
        majoraxis = ae_randominteger(nvars, _state);
        for(i=0; i<=npoints-1; i++)
        {
            testclusteringunit_rsphere(&xy, nvars, i, _state);
            xy.ptr.pp_double[i][majoraxis] = nc*xy.ptr.pp_double[i][majoraxis];
        }
        
        /*
         * Test
         */
        clusterizercreate(&s, _state);
        clusterizersetpoints(&s, &xy, npoints, nvars, 2, _state);
        clusterizersetkmeanslimits(&s, restarts, 0, _state);
        clusterizerrunkmeans(&s, nc, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            *converrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Test that XYC is correct mapping to cluster centers
         */
        for(i=0; i<=npoints-1; i++)
        {
            cclosest = -1;
            dclosest = ae_maxrealnumber;
            for(j=0; j<=nc-1; j++)
            {
                ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
                ae_v_sub(&tmp.ptr.p_double[0], 1, &rep.c.ptr.pp_double[j][0], 1, ae_v_len(0,nvars-1));
                v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
                if( ae_fp_less(v,dclosest) )
                {
                    cclosest = j;
                    dclosest = v;
                }
            }
            if( cclosest!=rep.cidx.ptr.p_int[i] )
            {
                *othererrors = ae_true;
                ae_frame_leave(_state);
                return;
            }
        }
        
        /*
         * Use first NC rows of XY as random centers
         * (XY is totally random, so it is as good as any other choice).
         *
         * Compare potential functions.
         */
        ekmeans = (double)(0);
        for(i=0; i<=npoints-1; i++)
        {
            ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
            ae_v_sub(&tmp.ptr.p_double[0], 1, &rep.c.ptr.pp_double[rep.cidx.ptr.p_int[i]][0], 1, ae_v_len(0,nvars-1));
            v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
            ekmeans = ekmeans+v;
        }
        erandom = (double)(0);
        for(i=0; i<=npoints-1; i++)
        {
            dclosest = ae_maxrealnumber;
            v = (double)(0);
            for(j=0; j<=nc-1; j++)
            {
                ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
                ae_v_sub(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[j][0], 1, ae_v_len(0,nvars-1));
                v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
                if( ae_fp_less(v,dclosest) )
                {
                    dclosest = v;
                }
            }
            erandom = erandom+v;
        }
        if( ae_fp_less(erandom,ekmeans) )
        {
            *simpleerrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This test perform several checks for special properties

On failure sets error flag, on success leaves it unchanged.
*************************************************************************/
static void testclusteringunit_kmeansspecialtests(ae_bool* othererrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t npoints;
    ae_int_t nfeatures;
    ae_int_t nclusters;
    ae_int_t initalgo;
    ae_matrix xy;
    ae_matrix c;
    ae_int_t idx0;
    ae_int_t idx1;
    ae_int_t idx2;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t separation;
    ae_vector xyc;
    ae_vector xycref;
    ae_vector xydist2;
    ae_vector xydist2ref;
    ae_vector energies;
    hqrndstate rs;
    clusterizerstate s;
    kmeansreport rep;
    ae_shared_pool bufferpool;
    apbuffers bufferseed;
    ae_vector pointslist;
    ae_vector featureslist;
    ae_vector clusterslist;
    ae_bool allsame;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&c, 0, sizeof(c));
    memset(&xyc, 0, sizeof(xyc));
    memset(&xycref, 0, sizeof(xycref));
    memset(&xydist2, 0, sizeof(xydist2));
    memset(&xydist2ref, 0, sizeof(xydist2ref));
    memset(&energies, 0, sizeof(energies));
    memset(&rs, 0, sizeof(rs));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&bufferpool, 0, sizeof(bufferpool));
    memset(&bufferseed, 0, sizeof(bufferseed));
    memset(&pointslist, 0, sizeof(pointslist));
    memset(&featureslist, 0, sizeof(featureslist));
    memset(&clusterslist, 0, sizeof(clusterslist));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xyc, 0, DT_INT, _state, ae_true);
    ae_vector_init(&xycref, 0, DT_INT, _state, ae_true);
    ae_vector_init(&xydist2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xydist2ref, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&energies, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);
    _clusterizerstate_init(&s, _state, ae_true);
    _kmeansreport_init(&rep, _state, ae_true);
    ae_shared_pool_init(&bufferpool, _state, ae_true);
    _apbuffers_init(&bufferseed, _state, ae_true);
    ae_vector_init(&pointslist, 0, DT_INT, _state, ae_true);
    ae_vector_init(&featureslist, 0, DT_INT, _state, ae_true);
    ae_vector_init(&clusterslist, 0, DT_INT, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Compare different initialization algorithms:
     * * dataset is K balls, chosen at random gaussian points, with
     *   radius equal to 2^(-Separation).
     * * we generate random sample, run k-means initialization algorithm
     *   and calculate mean energy for each initialization algorithm.
     *   In order to suppress Lloyd's iteration we use KmeansDbgNoIts
     *   debug flag.
     * * then, we compare mean energies; kmeans++ must be best one,
     *   random initialization must be worst one.
     */
    ae_vector_set_length(&energies, 4, _state);
    passcount = 1000;
    npoints = 100;
    nfeatures = 3;
    nclusters = 6;
    ae_matrix_set_length(&xy, npoints, nfeatures, _state);
    ae_matrix_set_length(&c, nclusters, nfeatures, _state);
    clusterizercreate(&s, _state);
    s.kmeansdbgnoits = ae_true;
    for(separation=2; separation<=5; separation++)
    {
        
        /*
         * Try different init algorithms
         */
        for(initalgo=1; initalgo<=3; initalgo++)
        {
            energies.ptr.p_double[initalgo] = 0.0;
            clusterizersetkmeansinit(&s, initalgo, _state);
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Generate centers of balls
                 */
                for(i=0; i<=nclusters-1; i++)
                {
                    for(j=0; j<=nfeatures-1; j++)
                    {
                        c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                    }
                }
                
                /*
                 * Generate points
                 */
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=0; j<=nfeatures-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = hqrndnormal(&rs, _state)*ae_pow((double)(2), (double)(-separation), _state)+c.ptr.pp_double[i%nclusters][j];
                    }
                }
                
                /*
                 * Run clusterization
                 */
                clusterizersetpoints(&s, &xy, npoints, nfeatures, 2, _state);
                clusterizerrunkmeans(&s, nclusters, &rep, _state);
                ae_set_error_flag(othererrors, rep.terminationtype<=0, __FILE__, __LINE__, "testclusteringunit.ap:1069");
                energies.ptr.p_double[initalgo] = energies.ptr.p_double[initalgo]+rep.energy/passcount;
            }
        }
        
        /*
         * Compare
         */
        ae_set_error_flag(othererrors, !ae_fp_less(energies.ptr.p_double[2],energies.ptr.p_double[1]), __FILE__, __LINE__, "testclusteringunit.ap:1077");
        ae_set_error_flag(othererrors, !ae_fp_less(energies.ptr.p_double[3],energies.ptr.p_double[1]), __FILE__, __LINE__, "testclusteringunit.ap:1078");
    }
    
    /*
     * Test distance calculation algorithm
     */
    ae_vector_set_length(&pointslist, 6, _state);
    pointslist.ptr.p_int[0] = 1;
    pointslist.ptr.p_int[1] = 10;
    pointslist.ptr.p_int[2] = 32;
    pointslist.ptr.p_int[3] = 100;
    pointslist.ptr.p_int[4] = 512;
    pointslist.ptr.p_int[5] = 8000;
    ae_vector_set_length(&featureslist, 5, _state);
    featureslist.ptr.p_int[0] = 1;
    featureslist.ptr.p_int[1] = 5;
    featureslist.ptr.p_int[2] = 32;
    featureslist.ptr.p_int[3] = 50;
    featureslist.ptr.p_int[4] = 96;
    ae_vector_set_length(&clusterslist, 5, _state);
    clusterslist.ptr.p_int[0] = 1;
    clusterslist.ptr.p_int[1] = 5;
    clusterslist.ptr.p_int[2] = 32;
    clusterslist.ptr.p_int[3] = 50;
    clusterslist.ptr.p_int[4] = 96;
    ae_shared_pool_set_seed(&bufferpool, &bufferseed, sizeof(bufferseed), _apbuffers_init, _apbuffers_init_copy, _apbuffers_destroy, _state);
    for(idx0=0; idx0<=pointslist.cnt-1; idx0++)
    {
        for(idx1=0; idx1<=featureslist.cnt-1; idx1++)
        {
            for(idx2=0; idx2<=clusterslist.cnt-1; idx2++)
            {
                npoints = pointslist.ptr.p_int[idx0];
                nfeatures = featureslist.ptr.p_int[idx1];
                nclusters = clusterslist.ptr.p_int[idx2];
                ae_matrix_set_length(&xy, npoints, nfeatures, _state);
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=0; j<=nfeatures-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                    }
                }
                ae_matrix_set_length(&c, nclusters, nfeatures, _state);
                for(i=0; i<=nclusters-1; i++)
                {
                    for(j=0; j<=nfeatures-1; j++)
                    {
                        c.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                    }
                }
                ae_vector_set_length(&xyc, npoints, _state);
                ae_vector_set_length(&xycref, npoints, _state);
                ae_vector_set_length(&xydist2, npoints, _state);
                ae_vector_set_length(&xydist2ref, npoints, _state);
                
                /*
                 * Test
                 */
                kmeansupdatedistances(&xy, 0, npoints, nfeatures, &c, 0, nclusters, &xyc, &xydist2, &bufferpool, _state);
                testclusteringunit_kmeansreferenceupdatedistances(&xy, npoints, nfeatures, &c, nclusters, &xycref, &xydist2ref, _state);
                for(i=0; i<=npoints-1; i++)
                {
                    ae_set_error_flag(othererrors, xyc.ptr.p_int[i]!=xycref.ptr.p_int[i], __FILE__, __LINE__, "testclusteringunit.ap:1137");
                    ae_set_error_flag(othererrors, ae_fp_greater(ae_fabs(xydist2.ptr.p_double[i]-xydist2ref.ptr.p_double[i], _state),1.0E-6), __FILE__, __LINE__, "testclusteringunit.ap:1138");
                }
            }
        }
    }
    
    /*
     * Test degenerate dataset (less than NClusters distinct points)
     */
    for(nclusters=2; nclusters<=10; nclusters++)
    {
        for(initalgo=0; initalgo<=3; initalgo++)
        {
            for(pass=1; pass<=10; pass++)
            {
                
                /*
                 * Initialize points. Two algorithms are used:
                 * * initialization by small integers (no rounding problems)
                 * * initialization by "long" fraction
                 */
                npoints = 100;
                nfeatures = 10;
                ae_matrix_set_length(&xy, npoints, nfeatures, _state);
                if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
                {
                    for(i=0; i<=nclusters-2; i++)
                    {
                        for(j=0; j<=nfeatures-1; j++)
                        {
                            xy.ptr.pp_double[i][j] = ae_sin(hqrndnormal(&rs, _state), _state);
                        }
                    }
                }
                else
                {
                    for(i=0; i<=nclusters-2; i++)
                    {
                        for(j=0; j<=nfeatures-1; j++)
                        {
                            xy.ptr.pp_double[i][j] = (double)(hqrnduniformi(&rs, 50, _state));
                        }
                    }
                }
                for(i=nclusters-1; i<=npoints-1; i++)
                {
                    idx0 = hqrnduniformi(&rs, nclusters-1, _state);
                    for(j=0; j<=nfeatures-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = xy.ptr.pp_double[idx0][j];
                    }
                }
                
                /*
                 * Clusterize with unlimited number of iterations.
                 * Correct error code must be returned.
                 */
                clusterizercreate(&s, _state);
                clusterizersetpoints(&s, &xy, npoints, nfeatures, 2, _state);
                clusterizersetkmeanslimits(&s, 1, 0, _state);
                clusterizersetkmeansinit(&s, initalgo, _state);
                clusterizerrunkmeans(&s, nclusters, &rep, _state);
                ae_set_error_flag(othererrors, rep.terminationtype!=-3, __FILE__, __LINE__, "testclusteringunit.ap:1185");
            }
        }
    }
    
    /*
     * Test deterministic seed:
     * * specyfying zero seed, or not specyfind seed at all = nondeterministic algo
     * * nonzero zeed = deterministic algo
     */
    for(initalgo=0; initalgo<=3; initalgo++)
    {
        
        /*
         * Initialize points.
         */
        npoints = 100;
        nfeatures = 3;
        ae_matrix_set_length(&xy, npoints, nfeatures, _state);
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nfeatures-1; j++)
            {
                xy.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
            }
        }
        
        /*
         * Clusterize with negative seed.
         *
         * Perform multiple runs, compare results with
         * first one returned - at least one result must
         * be different.
         */
        clusterizercreate(&s, _state);
        clusterizersetpoints(&s, &xy, npoints, nfeatures, 2, _state);
        clusterizersetkmeanslimits(&s, 1, 1, _state);
        clusterizersetkmeansinit(&s, initalgo, _state);
        clusterizersetseed(&s, -ae_randominteger(3, _state), _state);
        allsame = ae_true;
        for(pass=0; pass<=10; pass++)
        {
            clusterizerrunkmeans(&s, nclusters, &rep, _state);
            ae_set_error_flag(othererrors, rep.terminationtype<=0, __FILE__, __LINE__, "testclusteringunit.ap:1221");
            if( *othererrors )
            {
                ae_frame_leave(_state);
                return;
            }
            if( pass==0 )
            {
                
                /*
                 * Save clusters
                 */
                ae_matrix_set_length(&c, rep.c.rows, rep.c.cols, _state);
                for(i=0; i<=rep.c.rows-1; i++)
                {
                    for(j=0; j<=rep.c.cols-1; j++)
                    {
                        c.ptr.pp_double[i][j] = rep.c.ptr.pp_double[i][j];
                    }
                }
            }
            else
            {
                
                /*
                 * Compare clusters with ones returned from first run.
                 */
                for(i=0; i<=rep.c.rows-1; i++)
                {
                    for(j=0; j<=rep.c.cols-1; j++)
                    {
                        allsame = allsame&&ae_fp_eq(c.ptr.pp_double[i][j],rep.c.ptr.pp_double[i][j]);
                    }
                }
            }
        }
        ae_set_error_flag(othererrors, allsame, __FILE__, __LINE__, "testclusteringunit.ap:1244");
        
        /*
         * Clusterize with positive seed.
         *
         * Perform multiple runs, compare results with
         * first one returned - all results must be same.
         */
        clusterizercreate(&s, _state);
        clusterizersetpoints(&s, &xy, npoints, nfeatures, 2, _state);
        clusterizersetkmeanslimits(&s, 1, 1, _state);
        clusterizersetkmeansinit(&s, initalgo, _state);
        clusterizersetseed(&s, 1+ae_randominteger(3, _state), _state);
        allsame = ae_true;
        for(pass=0; pass<=10; pass++)
        {
            clusterizerrunkmeans(&s, nclusters, &rep, _state);
            ae_set_error_flag(othererrors, rep.terminationtype<=0, __FILE__, __LINE__, "testclusteringunit.ap:1261");
            if( *othererrors )
            {
                ae_frame_leave(_state);
                return;
            }
            if( pass==0 )
            {
                
                /*
                 * Save clusters
                 */
                ae_matrix_set_length(&c, rep.c.rows, rep.c.cols, _state);
                for(i=0; i<=rep.c.rows-1; i++)
                {
                    for(j=0; j<=rep.c.cols-1; j++)
                    {
                        c.ptr.pp_double[i][j] = rep.c.ptr.pp_double[i][j];
                    }
                }
            }
            else
            {
                
                /*
                 * Compare clusters with ones returned from first run.
                 */
                for(i=0; i<=rep.c.rows-1; i++)
                {
                    for(j=0; j<=rep.c.cols-1; j++)
                    {
                        allsame = allsame&&ae_fp_eq(c.ptr.pp_double[i][j],rep.c.ptr.pp_double[i][j]);
                    }
                }
            }
        }
        ae_set_error_flag(othererrors, !allsame, __FILE__, __LINE__, "testclusteringunit.ap:1284");
        
        /*
         * Clusterize with default seed.
         *
         * Perform multiple runs, compare results with
         * first one returned - all results must be same.
         */
        clusterizercreate(&s, _state);
        clusterizersetpoints(&s, &xy, npoints, nfeatures, 2, _state);
        clusterizersetkmeanslimits(&s, 1, 1, _state);
        clusterizersetkmeansinit(&s, initalgo, _state);
        allsame = ae_true;
        for(pass=0; pass<=10; pass++)
        {
            clusterizerrunkmeans(&s, nclusters, &rep, _state);
            ae_set_error_flag(othererrors, rep.terminationtype<=0, __FILE__, __LINE__, "testclusteringunit.ap:1301");
            if( *othererrors )
            {
                ae_frame_leave(_state);
                return;
            }
            if( pass==0 )
            {
                
                /*
                 * Save clusters
                 */
                ae_matrix_set_length(&c, rep.c.rows, rep.c.cols, _state);
                for(i=0; i<=rep.c.rows-1; i++)
                {
                    for(j=0; j<=rep.c.cols-1; j++)
                    {
                        c.ptr.pp_double[i][j] = rep.c.ptr.pp_double[i][j];
                    }
                }
            }
            else
            {
                
                /*
                 * Compare clusters with ones returned from first run.
                 */
                for(i=0; i<=rep.c.rows-1; i++)
                {
                    for(j=0; j<=rep.c.cols-1; j++)
                    {
                        allsame = allsame&&ae_fp_eq(c.ptr.pp_double[i][j],rep.c.ptr.pp_double[i][j]);
                    }
                }
            }
        }
        ae_set_error_flag(othererrors, !allsame, __FILE__, __LINE__, "testclusteringunit.ap:1324");
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This test checks algorithm ability to handle degenerate problems without
causing infinite loop.
*************************************************************************/
static void testclusteringunit_kmeansinfinitelooptest(ae_bool* othererrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t npoints;
    ae_int_t nfeatures;
    ae_int_t nclusters;
    ae_int_t restarts;
    ae_matrix xy;
    ae_int_t i;
    ae_int_t j;
    clusterizerstate s;
    kmeansreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _clusterizerstate_init(&s, _state, ae_true);
    _kmeansreport_init(&rep, _state, ae_true);

    
    /*
     * Problem 1: all points are same.
     *
     * For NClusters=1 we must get correct solution, for NClusters>1 we must get failure.
     */
    npoints = 100;
    nfeatures = 1;
    restarts = 5;
    ae_matrix_set_length(&xy, npoints, nfeatures, _state);
    for(j=0; j<=nfeatures-1; j++)
    {
        xy.ptr.pp_double[0][j] = ae_randomreal(_state);
    }
    for(i=1; i<=npoints-1; i++)
    {
        for(j=0; j<=nfeatures-1; j++)
        {
            xy.ptr.pp_double[i][j] = xy.ptr.pp_double[0][j];
        }
    }
    nclusters = 1;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, npoints, nfeatures, 2, _state);
    clusterizersetkmeanslimits(&s, restarts, 0, _state);
    clusterizerrunkmeans(&s, nclusters, &rep, _state);
    *othererrors = *othererrors||rep.terminationtype<=0;
    for(i=0; i<=nfeatures-1; i++)
    {
        *othererrors = *othererrors||ae_fp_greater(ae_fabs(rep.c.ptr.pp_double[0][i]-xy.ptr.pp_double[0][i], _state),1000*ae_machineepsilon);
    }
    for(i=0; i<=npoints-1; i++)
    {
        *othererrors = *othererrors||rep.cidx.ptr.p_int[i]!=0;
    }
    nclusters = 5;
    clusterizerrunkmeans(&s, nclusters, &rep, _state);
    *othererrors = *othererrors||rep.terminationtype>0;
    
    /*
     * Problem 2: degenerate dataset (report by Andreas).
     */
    npoints = 57;
    nfeatures = 1;
    restarts = 1;
    nclusters = 4;
    ae_matrix_set_length(&xy, npoints, nfeatures, _state);
    xy.ptr.pp_double[0][0] = 12.244689632138986;
    xy.ptr.pp_double[1][0] = 12.244689632138982;
    xy.ptr.pp_double[2][0] = 12.244689632138986;
    xy.ptr.pp_double[3][0] = 12.244689632138982;
    xy.ptr.pp_double[4][0] = 12.244689632138986;
    xy.ptr.pp_double[5][0] = 12.244689632138986;
    xy.ptr.pp_double[6][0] = 12.244689632138986;
    xy.ptr.pp_double[7][0] = 12.244689632138986;
    xy.ptr.pp_double[8][0] = 12.244689632138986;
    xy.ptr.pp_double[9][0] = 12.244689632138986;
    xy.ptr.pp_double[10][0] = 12.244689632138989;
    xy.ptr.pp_double[11][0] = 12.244689632138984;
    xy.ptr.pp_double[12][0] = 12.244689632138986;
    xy.ptr.pp_double[13][0] = 12.244689632138986;
    xy.ptr.pp_double[14][0] = 12.244689632138989;
    xy.ptr.pp_double[15][0] = 12.244689632138986;
    xy.ptr.pp_double[16][0] = 12.244689632138986;
    xy.ptr.pp_double[17][0] = 12.244689632138986;
    xy.ptr.pp_double[18][0] = 12.244689632138986;
    xy.ptr.pp_double[19][0] = 12.244689632138989;
    xy.ptr.pp_double[20][0] = 12.244689632138972;
    xy.ptr.pp_double[21][0] = 12.244689632138986;
    xy.ptr.pp_double[22][0] = 12.244689632138986;
    xy.ptr.pp_double[23][0] = 12.244689632138986;
    xy.ptr.pp_double[24][0] = 12.244689632138984;
    xy.ptr.pp_double[25][0] = 12.244689632138982;
    xy.ptr.pp_double[26][0] = 12.244689632138986;
    xy.ptr.pp_double[27][0] = 12.244689632138986;
    xy.ptr.pp_double[28][0] = 12.244689632138986;
    xy.ptr.pp_double[29][0] = 12.244689632138986;
    xy.ptr.pp_double[30][0] = 12.244689632138986;
    xy.ptr.pp_double[31][0] = 12.244689632138986;
    xy.ptr.pp_double[32][0] = 12.244689632138986;
    xy.ptr.pp_double[33][0] = 12.244689632138986;
    xy.ptr.pp_double[34][0] = 12.244689632138986;
    xy.ptr.pp_double[35][0] = 12.244689632138982;
    xy.ptr.pp_double[36][0] = 12.244689632138989;
    xy.ptr.pp_double[37][0] = 12.244689632138986;
    xy.ptr.pp_double[38][0] = 12.244689632138986;
    xy.ptr.pp_double[39][0] = 12.244689632138986;
    xy.ptr.pp_double[40][0] = 12.244689632138986;
    xy.ptr.pp_double[41][0] = 12.244689632138986;
    xy.ptr.pp_double[42][0] = 12.244689632138986;
    xy.ptr.pp_double[43][0] = 12.244689632138986;
    xy.ptr.pp_double[44][0] = 12.244689632138986;
    xy.ptr.pp_double[45][0] = 12.244689632138986;
    xy.ptr.pp_double[46][0] = 12.244689632138986;
    xy.ptr.pp_double[47][0] = 12.244689632138986;
    xy.ptr.pp_double[48][0] = 12.244689632138986;
    xy.ptr.pp_double[49][0] = 12.244689632138986;
    xy.ptr.pp_double[50][0] = 12.244689632138984;
    xy.ptr.pp_double[51][0] = 12.244689632138986;
    xy.ptr.pp_double[52][0] = 12.244689632138986;
    xy.ptr.pp_double[53][0] = 12.244689632138986;
    xy.ptr.pp_double[54][0] = 12.244689632138986;
    xy.ptr.pp_double[55][0] = 12.244689632138986;
    xy.ptr.pp_double[56][0] = 12.244689632138986;
    clusterizercreate(&s, _state);
    clusterizersetpoints(&s, &xy, npoints, nfeatures, 2, _state);
    clusterizersetkmeanslimits(&s, restarts, 0, _state);
    clusterizerrunkmeans(&s, nclusters, &rep, _state);
    *othererrors = *othererrors||rep.terminationtype<=0;
    ae_frame_leave(_state);
}


/*************************************************************************
This non-deterministic test checks that Restarts>1 significantly  improves
quality of results.

Subroutine generates random task 3 unit balls in 2D, each with 20  points,
separated by 5 units wide gaps, and solves it  with  Restarts=1  and  with
Restarts=5. Potential functions are compared,  outcome  of  the  trial  is
either 0 or 1 (depending on what is better).

Sequence of 1000 such tasks is  solved.  If  Restarts>1  actually  improve
quality of solution, sum of outcome will be non-binomial.  If  it  doesn't
matter, it will be binomially distributed.

P.S. This test was added after report from Gianluca  Borello  who  noticed
error in the handling of multiple restarts.
*************************************************************************/
static void testclusteringunit_kmeansrestartstest(ae_bool* converrors,
     ae_bool* restartserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t npoints;
    ae_int_t nvars;
    ae_int_t nclusters;
    ae_int_t clustersize;
    ae_int_t restarts;
    ae_int_t passcount;
    double sigmathreshold;
    double p;
    double s;
    ae_matrix xy;
    ae_vector tmp;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    double ea;
    double eb;
    double v;
    clusterizerstate state;
    kmeansreport rep1;
    kmeansreport rep2;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&tmp, 0, sizeof(tmp));
    memset(&state, 0, sizeof(state));
    memset(&rep1, 0, sizeof(rep1));
    memset(&rep2, 0, sizeof(rep2));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    _clusterizerstate_init(&state, _state, ae_true);
    _kmeansreport_init(&rep1, _state, ae_true);
    _kmeansreport_init(&rep2, _state, ae_true);

    restarts = 5;
    passcount = 1000;
    clustersize = 20;
    nclusters = 3;
    nvars = 2;
    npoints = nclusters*clustersize;
    sigmathreshold = (double)(5);
    ae_matrix_set_length(&xy, npoints, nvars, _state);
    ae_vector_set_length(&tmp, nvars, _state);
    p = (double)(0);
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Fill
         */
        for(i=0; i<=npoints-1; i++)
        {
            testclusteringunit_rsphere(&xy, nvars, i, _state);
            for(j=0; j<=nvars-1; j++)
            {
                xy.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]+(double)i/(double)clustersize*5;
            }
        }
        clusterizercreate(&state, _state);
        clusterizersetpoints(&state, &xy, npoints, nvars, 2, _state);
        
        /*
         * Test: Restarts=1
         */
        clusterizersetkmeanslimits(&state, 1, 0, _state);
        clusterizerrunkmeans(&state, nclusters, &rep1, _state);
        if( rep1.terminationtype<=0 )
        {
            *converrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        ea = (double)(0);
        for(i=0; i<=npoints-1; i++)
        {
            ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
            ae_v_sub(&tmp.ptr.p_double[0], 1, &rep1.c.ptr.pp_double[rep1.cidx.ptr.p_int[i]][0], 1, ae_v_len(0,nvars-1));
            v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
            ea = ea+v;
        }
        
        /*
         * Test: Restarts>1
         */
        clusterizersetkmeanslimits(&state, restarts, 0, _state);
        clusterizerrunkmeans(&state, nclusters, &rep2, _state);
        if( rep2.terminationtype<=0 )
        {
            *converrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        eb = (double)(0);
        for(i=0; i<=npoints-1; i++)
        {
            ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
            ae_v_sub(&tmp.ptr.p_double[0], 1, &rep2.c.ptr.pp_double[rep2.cidx.ptr.p_int[i]][0], 1, ae_v_len(0,nvars-1));
            v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
            eb = eb+v;
        }
        
        /*
         * Calculate statistic.
         */
        if( ae_fp_less(ea,eb) )
        {
            p = p+1;
        }
        if( ae_fp_eq(ea,eb) )
        {
            p = p+0.5;
        }
    }
    
    /*
     * If Restarts doesn't influence quality of centers found, P must be
     * binomially distributed random value with mean 0.5*PassCount and
     * standard deviation Sqrt(PassCount/4).
     *
     * If Restarts do influence quality of solution, P must be significantly
     * lower than 0.5*PassCount.
     */
    s = (p-0.5*passcount)/ae_sqrt((double)passcount/(double)4, _state);
    *restartserrors = *restartserrors||ae_fp_greater(s,-sigmathreshold);
    ae_frame_leave(_state);
}


/*************************************************************************
Random normal number
*************************************************************************/
static double testclusteringunit_rnormal(ae_state *_state)
{
    double u;
    double v;
    double s;
    double x1;
    double result;


    for(;;)
    {
        u = 2*ae_randomreal(_state)-1;
        v = 2*ae_randomreal(_state)-1;
        s = ae_sqr(u, _state)+ae_sqr(v, _state);
        if( ae_fp_greater(s,(double)(0))&&ae_fp_less(s,(double)(1)) )
        {
            s = ae_sqrt(-2*ae_log(s, _state)/s, _state);
            x1 = u*s;
            break;
        }
    }
    result = x1;
    return result;
}


/*************************************************************************
Random point from sphere
*************************************************************************/
static void testclusteringunit_rsphere(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t i,
     ae_state *_state)
{
    ae_int_t j;
    double v;


    for(j=0; j<=n-1; j++)
    {
        xy->ptr.pp_double[i][j] = testclusteringunit_rnormal(_state);
    }
    v = ae_v_dotproduct(&xy->ptr.pp_double[i][0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
    v = ae_randomreal(_state)/ae_sqrt(v, _state);
    ae_v_muld(&xy->ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
}


/*************************************************************************
Distance function: distance between X0 and X1

X0, X1 - array[D], points
DistType - distance type
*************************************************************************/
static double testclusteringunit_distfunc(/* Real    */ ae_vector* x0,
     /* Real    */ ae_vector* x1,
     ae_int_t d,
     ae_int_t disttype,
     ae_state *_state)
{
    ae_int_t i;
    double s0;
    double s1;
    double result;


    ae_assert((((((((disttype==0||disttype==1)||disttype==2)||disttype==10)||disttype==11)||disttype==12)||disttype==13)||disttype==20)||disttype==21, "Assertion failed", _state);
    if( disttype==0 )
    {
        result = 0.0;
        for(i=0; i<=d-1; i++)
        {
            result = ae_maxreal(result, ae_fabs(x0->ptr.p_double[i]-x1->ptr.p_double[i], _state), _state);
        }
        return result;
    }
    if( disttype==1 )
    {
        result = 0.0;
        for(i=0; i<=d-1; i++)
        {
            result = result+ae_fabs(x0->ptr.p_double[i]-x1->ptr.p_double[i], _state);
        }
        return result;
    }
    if( disttype==2 )
    {
        result = 0.0;
        for(i=0; i<=d-1; i++)
        {
            result = result+ae_sqr(x0->ptr.p_double[i]-x1->ptr.p_double[i], _state);
        }
        result = ae_sqrt(result, _state);
        return result;
    }
    if( disttype==10 )
    {
        result = ae_maxreal(1-pearsoncorr2(x0, x1, d, _state), 0.0, _state);
        return result;
    }
    if( disttype==11 )
    {
        result = ae_maxreal(1-ae_fabs(pearsoncorr2(x0, x1, d, _state), _state), 0.0, _state);
        return result;
    }
    if( disttype==12||disttype==13 )
    {
        s0 = 0.0;
        s1 = 0.0;
        for(i=0; i<=d-1; i++)
        {
            s0 = s0+ae_sqr(x0->ptr.p_double[i], _state)/d;
            s1 = s1+ae_sqr(x1->ptr.p_double[i], _state)/d;
        }
        s0 = ae_sqrt(s0, _state);
        s1 = ae_sqrt(s1, _state);
        result = (double)(0);
        for(i=0; i<=d-1; i++)
        {
            result = result+x0->ptr.p_double[i]/s0*(x1->ptr.p_double[i]/s1)/d;
        }
        if( disttype==12 )
        {
            result = ae_maxreal(1-result, 0.0, _state);
        }
        else
        {
            result = ae_maxreal(1-ae_fabs(result, _state), 0.0, _state);
        }
        return result;
    }
    if( disttype==20 )
    {
        result = ae_maxreal(1-spearmancorr2(x0, x1, d, _state), 0.0, _state);
        return result;
    }
    if( disttype==21 )
    {
        result = ae_maxreal(1-ae_fabs(spearmancorr2(x0, x1, d, _state), _state), 0.0, _state);
        return result;
    }
    result = (double)(0);
    return result;
}


/*************************************************************************
This function replays merges and checks that:
* Rep.NPoints, Rep.Z, Rep.PZ and Rep.PM are consistent and correct
* Rep.MergeDist is consistent with distance between clusters being merged
* clusters with minimal distance are merged at each step
* GetKClusters() correctly unpacks clusters for each K

NOTE: this algorithm correctly handle ties, i.e. situations where several
      pairs  of  clusters  have  same intercluster distance, and we can't
      unambiguously choose clusters to merge.

INPUT PARAMETERS
    D           -   distance matrix, array[NPoints,NPoints], full matrix
                    is given (including both triangles and zeros on the
                    main diagonal)
    XY          -   dataset matrix, array[NPoints,NF]
    NPoints     -   dataset size
    NF          -   number of features
    Rep         -   clusterizer report
    AHCAlgo     -   AHC algorithm:
                    * 0 - complete linkage
                    * 1 - single linkage
                    * 2 - unweighted average linkage

This function returns True on failure, False on success.
*************************************************************************/
static ae_bool testclusteringunit_errorsinmerges(/* Real    */ ae_matrix* d,
     /* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nf,
     ahcreport* rep,
     ae_int_t ahcalgo,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix dm;
    ae_matrix cm;
    ae_vector clustersizes;
    ae_vector clusterheights;
    ae_vector b;
    ae_vector x0;
    ae_vector x1;
    ae_bool bflag;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t i0;
    ae_int_t i1;
    ae_int_t c0;
    ae_int_t c1;
    ae_int_t s0;
    ae_int_t s1;
    double v;
    ae_int_t t;
    ae_int_t mergeidx;
    ae_vector kidx;
    ae_vector kidxz;
    ae_int_t currentelement;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&dm, 0, sizeof(dm));
    memset(&cm, 0, sizeof(cm));
    memset(&clustersizes, 0, sizeof(clustersizes));
    memset(&clusterheights, 0, sizeof(clusterheights));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&kidx, 0, sizeof(kidx));
    memset(&kidxz, 0, sizeof(kidxz));
    ae_matrix_init(&dm, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cm, 0, 0, DT_INT, _state, ae_true);
    ae_vector_init(&clustersizes, 0, DT_INT, _state, ae_true);
    ae_vector_init(&clusterheights, 0, DT_INT, _state, ae_true);
    ae_vector_init(&b, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&kidx, 0, DT_INT, _state, ae_true);
    ae_vector_init(&kidxz, 0, DT_INT, _state, ae_true);

    ae_assert(ahcalgo!=3, "integrity error", _state);
    result = ae_false;
    ae_vector_set_length(&x0, nf, _state);
    ae_vector_set_length(&x1, nf, _state);
    
    /*
     * Basic checks:
     * * positive completion code
     * * sizes of arrays
     * * Rep.P is correct permutation
     * * Rep.Z contains correct cluster indexes
     * * Rep.PZ is consistent with Rep.P/Rep.Z
     * * Rep.PM contains consistent indexes
     * * GetKClusters() for K=NPoints
     */
    bflag = ae_false;
    bflag = bflag||rep->terminationtype<=0;
    if( bflag )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    bflag = bflag||rep->npoints!=npoints;
    bflag = (bflag||rep->z.rows!=npoints-1)||(npoints>1&&rep->z.cols!=2);
    bflag = (bflag||rep->pz.rows!=npoints-1)||(npoints>1&&rep->pz.cols!=2);
    bflag = (bflag||rep->pm.rows!=npoints-1)||(npoints>1&&rep->pm.cols!=6);
    bflag = bflag||rep->mergedist.cnt!=npoints-1;
    bflag = bflag||rep->p.cnt!=npoints;
    if( bflag )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    ae_vector_set_length(&b, npoints, _state);
    for(i=0; i<=npoints-1; i++)
    {
        b.ptr.p_bool[i] = ae_false;
    }
    for(i=0; i<=npoints-1; i++)
    {
        if( (rep->p.ptr.p_int[i]<0||rep->p.ptr.p_int[i]>=npoints)||b.ptr.p_bool[rep->p.ptr.p_int[i]] )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        b.ptr.p_bool[rep->p.ptr.p_int[i]] = ae_true;
    }
    for(i=0; i<=npoints-2; i++)
    {
        if( (rep->z.ptr.pp_int[i][0]<0||rep->z.ptr.pp_int[i][0]>=rep->z.ptr.pp_int[i][1])||rep->z.ptr.pp_int[i][1]>=npoints+i )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        if( (rep->pz.ptr.pp_int[i][0]<0||rep->pz.ptr.pp_int[i][0]>=rep->pz.ptr.pp_int[i][1])||rep->pz.ptr.pp_int[i][1]>=npoints+i )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    for(i=0; i<=npoints-2; i++)
    {
        c0 = rep->z.ptr.pp_int[i][0];
        c1 = rep->z.ptr.pp_int[i][1];
        s0 = rep->pz.ptr.pp_int[i][0];
        s1 = rep->pz.ptr.pp_int[i][1];
        if( c0<npoints )
        {
            c0 = rep->p.ptr.p_int[c0];
        }
        if( c1<npoints )
        {
            c1 = rep->p.ptr.p_int[c1];
        }
        if( c0!=s0||c1!=s1 )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    clusterizergetkclusters(rep, npoints, &kidx, &kidxz, _state);
    if( kidx.cnt!=npoints||kidxz.cnt!=npoints )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    for(i=0; i<=npoints-1; i++)
    {
        if( kidxz.ptr.p_int[i]!=i||kidx.ptr.p_int[i]!=i )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * Test description:
     * * we generate (2*NPoints-1)x(2*NPoints-1) matrix of distances DM and
     *   (2*NPoints-1)xNPoints matrix of clusters CM (I-th row contains indexes
     *   of elements which belong to I-th cluster, negative indexes denote
     *   empty cells). Leading N*N square of DM is just a distance matrix,
     *   other elements are filled by some large number M (used to mark empty
     *   elements).
     * * we replay all merges
     * * every time we merge clusters I and J into K, we:
     *   * check that distance between I and J is equal to the smallest
     *     element of DM (note: we account for rounding errors when we
     *     decide on that)
     *   * check that distance is consistent with Rep.MergeDist
     *   * then, we enumerate all elements in clusters being merged,
     *     and check that after permutation their indexes fall into range
     *     prescribed by Rep.PM
     *   * fill K-th column/row of D by distances to cluster K
     *   * merge I-th and J-th rows of CM and store result into K-th row
     *   * clear DM and CM: fill I-th and J-th column/row of DM by large
     *     number M, fill I-th and J-th row of CM by -1.
     *
     * NOTE: DM is initialized by distance metric specific to AHC algorithm
     *       being used. CLINK, SLINK and average linkage use user-provided
     *       distance measure, say Euclidean one, without any modifications.
     *       Ward's method uses (and reports) squared and scaled Euclidean
     *       distances.
     */
    ae_matrix_set_length(&dm, 2*npoints-1, 2*npoints-1, _state);
    ae_matrix_set_length(&cm, 2*npoints-1, npoints, _state);
    ae_vector_set_length(&clustersizes, 2*npoints-1, _state);
    for(i=0; i<=2*npoints-2; i++)
    {
        for(j=0; j<=2*npoints-2; j++)
        {
            if( i<npoints&&j<npoints )
            {
                dm.ptr.pp_double[i][j] = d->ptr.pp_double[i][j];
                if( ahcalgo==4 )
                {
                    dm.ptr.pp_double[i][j] = 0.5*ae_sqr(dm.ptr.pp_double[i][j], _state);
                }
            }
            else
            {
                dm.ptr.pp_double[i][j] = ae_maxrealnumber;
            }
        }
    }
    for(i=0; i<=2*npoints-2; i++)
    {
        for(j=0; j<=npoints-1; j++)
        {
            cm.ptr.pp_int[i][j] = -1;
        }
    }
    for(i=0; i<=npoints-1; i++)
    {
        cm.ptr.pp_int[i][0] = i;
        clustersizes.ptr.p_int[i] = 1;
    }
    for(i=npoints; i<=2*npoints-2; i++)
    {
        clustersizes.ptr.p_int[i] = 0;
    }
    ae_vector_set_length(&clusterheights, 2*npoints-1, _state);
    for(i=0; i<=npoints-1; i++)
    {
        clusterheights.ptr.p_int[i] = 0;
    }
    for(mergeidx=0; mergeidx<=npoints-2; mergeidx++)
    {
        
        /*
         * Check that clusters with minimum distance are merged,
         * and that MergeDist is consistent with results.
         *
         * NOTE: we do not check for specific cluster indexes,
         *       because it is possible to have a tie. We just
         *       check that distance between clusters is a true
         *       minimum over all possible clusters.
         */
        v = ae_maxrealnumber;
        for(i=0; i<=2*npoints-2; i++)
        {
            for(j=0; j<=2*npoints-2; j++)
            {
                if( i!=j )
                {
                    v = ae_minreal(v, dm.ptr.pp_double[i][j], _state);
                }
            }
        }
        c0 = rep->z.ptr.pp_int[mergeidx][0];
        c1 = rep->z.ptr.pp_int[mergeidx][1];
        if( ae_fp_greater(dm.ptr.pp_double[c0][c1],v+10000*ae_machineepsilon) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        if( ae_fp_greater(rep->mergedist.ptr.p_double[mergeidx],v+10000*ae_machineepsilon) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Check that indexes of elements fall into range prescribed by Rep.PM,
         * and Rep.PM correctly described merge operation
         */
        s0 = clustersizes.ptr.p_int[c0];
        s1 = clustersizes.ptr.p_int[c1];
        for(j=0; j<=clustersizes.ptr.p_int[c0]-1; j++)
        {
            if( rep->p.ptr.p_int[cm.ptr.pp_int[c0][j]]<rep->pm.ptr.pp_int[mergeidx][0]||rep->p.ptr.p_int[cm.ptr.pp_int[c0][j]]>rep->pm.ptr.pp_int[mergeidx][1] )
            {
                
                /*
                 * Element falls outside of range described by PM
                 */
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
        for(j=0; j<=clustersizes.ptr.p_int[c1]-1; j++)
        {
            if( rep->p.ptr.p_int[cm.ptr.pp_int[c1][j]]<rep->pm.ptr.pp_int[mergeidx][2]||rep->p.ptr.p_int[cm.ptr.pp_int[c1][j]]>rep->pm.ptr.pp_int[mergeidx][3] )
            {
                
                /*
                 * Element falls outside of range described by PM
                 */
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
        if( (rep->pm.ptr.pp_int[mergeidx][1]-rep->pm.ptr.pp_int[mergeidx][0]!=s0-1||rep->pm.ptr.pp_int[mergeidx][3]-rep->pm.ptr.pp_int[mergeidx][2]!=s1-1)||rep->pm.ptr.pp_int[mergeidx][2]!=rep->pm.ptr.pp_int[mergeidx][1]+1 )
        {
            
            /*
             * Cluster size (as given by PM) is inconsistent with its actual size.
             */
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        if( rep->pm.ptr.pp_int[mergeidx][4]!=clusterheights.ptr.p_int[rep->z.ptr.pp_int[mergeidx][0]]||rep->pm.ptr.pp_int[mergeidx][5]!=clusterheights.ptr.p_int[rep->z.ptr.pp_int[mergeidx][1]] )
        {
            
            /*
             * Heights of subdendrograms as returned by PM are inconsistent with heights
             * calculated by us.
             */
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Update cluster heights
         */
        clusterheights.ptr.p_int[mergeidx+npoints] = ae_maxint(clusterheights.ptr.p_int[rep->z.ptr.pp_int[mergeidx][0]], clusterheights.ptr.p_int[rep->z.ptr.pp_int[mergeidx][1]], _state)+1;
        
        /*
         * Update CM
         */
        t = 0;
        for(j=0; j<=clustersizes.ptr.p_int[rep->z.ptr.pp_int[mergeidx][0]]-1; j++)
        {
            cm.ptr.pp_int[npoints+mergeidx][t] = cm.ptr.pp_int[rep->z.ptr.pp_int[mergeidx][0]][j];
            t = t+1;
        }
        for(j=0; j<=clustersizes.ptr.p_int[rep->z.ptr.pp_int[mergeidx][1]]-1; j++)
        {
            cm.ptr.pp_int[npoints+mergeidx][t] = cm.ptr.pp_int[rep->z.ptr.pp_int[mergeidx][1]][j];
            t = t+1;
        }
        clustersizes.ptr.p_int[npoints+mergeidx] = t;
        clustersizes.ptr.p_int[rep->z.ptr.pp_int[mergeidx][0]] = 0;
        clustersizes.ptr.p_int[rep->z.ptr.pp_int[mergeidx][1]] = 0;
        
        /*
         * Update distance matrix D
         */
        for(i=0; i<=2*npoints-2; i++)
        {
            
            /*
             * "Remove" columns/rows corresponding to clusters being merged
             */
            dm.ptr.pp_double[i][rep->z.ptr.pp_int[mergeidx][0]] = ae_maxrealnumber;
            dm.ptr.pp_double[i][rep->z.ptr.pp_int[mergeidx][1]] = ae_maxrealnumber;
            dm.ptr.pp_double[rep->z.ptr.pp_int[mergeidx][0]][i] = ae_maxrealnumber;
            dm.ptr.pp_double[rep->z.ptr.pp_int[mergeidx][1]][i] = ae_maxrealnumber;
        }
        for(i=0; i<=npoints+mergeidx-1; i++)
        {
            if( clustersizes.ptr.p_int[i]>0 )
            {
                
                /*
                 * Calculate column/row corresponding to new cluster
                 */
                if( ahcalgo==0 )
                {
                    
                    /*
                     * Calculate distance between clusters I and NPoints+MergeIdx for CLINK
                     */
                    v = 0.0;
                    for(i0=0; i0<=clustersizes.ptr.p_int[i]-1; i0++)
                    {
                        for(i1=0; i1<=clustersizes.ptr.p_int[npoints+mergeidx]-1; i1++)
                        {
                            v = ae_maxreal(v, d->ptr.pp_double[cm.ptr.pp_int[i][i0]][cm.ptr.pp_int[npoints+mergeidx][i1]], _state);
                        }
                    }
                }
                if( ahcalgo==1 )
                {
                    
                    /*
                     * Calculate distance between clusters I and NPoints+MergeIdx for SLINK
                     */
                    v = ae_maxrealnumber;
                    for(i0=0; i0<=clustersizes.ptr.p_int[i]-1; i0++)
                    {
                        for(i1=0; i1<=clustersizes.ptr.p_int[npoints+mergeidx]-1; i1++)
                        {
                            v = ae_minreal(v, d->ptr.pp_double[cm.ptr.pp_int[i][i0]][cm.ptr.pp_int[npoints+mergeidx][i1]], _state);
                        }
                    }
                }
                if( ahcalgo==2 )
                {
                    
                    /*
                     * Calculate distance between clusters I and NPoints+MergeIdx for unweighted average
                     */
                    v = 0.0;
                    t = 0;
                    for(i0=0; i0<=clustersizes.ptr.p_int[i]-1; i0++)
                    {
                        for(i1=0; i1<=clustersizes.ptr.p_int[npoints+mergeidx]-1; i1++)
                        {
                            v = v+d->ptr.pp_double[cm.ptr.pp_int[i][i0]][cm.ptr.pp_int[npoints+mergeidx][i1]];
                            t = t+1;
                        }
                    }
                    v = v/t;
                }
                if( ahcalgo==3 )
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
                if( ahcalgo==4 )
                {
                    
                    /*
                     * Calculate distance between clusters I and NPoints+MergeIdx for Ward's method:
                     * * X0 = center of mass for cluster I
                     * * X1 = center of mass for cluster NPoints+MergeIdx
                     * * S0 = size of cluster I
                     * * S1 = size of cluster NPoints+MergeIdx
                     * * distance between clusters is S0*S1/(S0+S1)*|X0-X1|^2
                     *
                     */
                    for(j=0; j<=nf-1; j++)
                    {
                        x0.ptr.p_double[j] = 0.0;
                        x1.ptr.p_double[j] = 0.0;
                    }
                    for(i0=0; i0<=clustersizes.ptr.p_int[i]-1; i0++)
                    {
                        for(j=0; j<=nf-1; j++)
                        {
                            x0.ptr.p_double[j] = x0.ptr.p_double[j]+xy->ptr.pp_double[cm.ptr.pp_int[i][i0]][j]/clustersizes.ptr.p_int[i];
                        }
                    }
                    for(i1=0; i1<=clustersizes.ptr.p_int[npoints+mergeidx]-1; i1++)
                    {
                        for(j=0; j<=nf-1; j++)
                        {
                            x1.ptr.p_double[j] = x1.ptr.p_double[j]+xy->ptr.pp_double[cm.ptr.pp_int[npoints+mergeidx][i1]][j]/clustersizes.ptr.p_int[npoints+mergeidx];
                        }
                    }
                    v = 0.0;
                    for(j=0; j<=nf-1; j++)
                    {
                        v = v+ae_sqr(x0.ptr.p_double[j]-x1.ptr.p_double[j], _state);
                    }
                    v = v*clustersizes.ptr.p_int[i]*clustersizes.ptr.p_int[npoints+mergeidx]/(clustersizes.ptr.p_int[i]+clustersizes.ptr.p_int[npoints+mergeidx]);
                }
                dm.ptr.pp_double[i][npoints+mergeidx] = v;
                dm.ptr.pp_double[npoints+mergeidx][i] = v;
            }
        }
        
        /*
         * Check that GetKClusters() correctly unpacks clusters for K=NPoints-(MergeIdx+1):
         * * check lengths of arays
         * * check consistency of CIdx/CZ parameters
         * * scan clusters (CZ parameter), for each cluster scan CM matrix which stores
         *   cluster elements (according to our replay of merges), for each element of
         *   the current cluster check that CIdx array correctly reflects its status.
         */
        k = npoints-(mergeidx+1);
        clusterizergetkclusters(rep, k, &kidx, &kidxz, _state);
        if( kidx.cnt!=npoints||kidxz.cnt!=k )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        for(i=0; i<=k-2; i++)
        {
            if( (kidxz.ptr.p_int[i]<0||kidxz.ptr.p_int[i]>=kidxz.ptr.p_int[i+1])||kidxz.ptr.p_int[i+1]>2*npoints-2 )
            {
                
                /*
                 * CZ is inconsistent
                 */
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
        for(i=0; i<=npoints-1; i++)
        {
            if( kidx.ptr.p_int[i]<0||kidx.ptr.p_int[i]>=k )
            {
                
                /*
                 * CIdx is inconsistent
                 */
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
        for(i=0; i<=k-1; i++)
        {
            for(j=0; j<=clustersizes.ptr.p_int[kidxz.ptr.p_int[i]]-1; j++)
            {
                currentelement = cm.ptr.pp_int[kidxz.ptr.p_int[i]][j];
                if( kidx.ptr.p_int[currentelement]!=i )
                {
                    
                    /*
                     * We've found element which belongs to I-th cluster (according to CM
                     * matrix, which reflects current status of agglomerative clustering),
                     * but this element does not belongs to I-th cluster according to
                     * results of ClusterizerGetKClusters()
                     */
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This procedure is a reference version of KMeansUpdateDistances().

INPUT PARAMETERS:
    XY          -   dataset, array [0..NPoints-1,0..NVars-1].
    NPoints     -   dataset size, NPoints>=K
    NVars       -   number of variables, NVars>=1
    CT          -   matrix of centers, centers are stored in rows
    K           -   number of centers, K>=1
    XYC         -   preallocated output buffer
    XYDist2     -   preallocated output buffer

OUTPUT PARAMETERS:
    XYC         -   new assignment of points to centers
    XYDist2     -   squared distances

  -- ALGLIB --
     Copyright 21.01.2015 by Bochkanov Sergey
*************************************************************************/
static void testclusteringunit_kmeansreferenceupdatedistances(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nvars,
     /* Real    */ ae_matrix* ct,
     ae_int_t k,
     /* Integer */ ae_vector* xyc,
     /* Real    */ ae_vector* xydist2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t cclosest;
    double dclosest;
    double v;
    ae_vector tmp;

    ae_frame_make(_state, &_frame_block);
    memset(&tmp, 0, sizeof(tmp));
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&tmp, nvars, _state);
    for(i=0; i<=npoints-1; i++)
    {
        cclosest = -1;
        dclosest = ae_maxrealnumber;
        for(j=0; j<=k-1; j++)
        {
            ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
            ae_v_sub(&tmp.ptr.p_double[0], 1, &ct->ptr.pp_double[j][0], 1, ae_v_len(0,nvars-1));
            v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
            if( ae_fp_less(v,dclosest) )
            {
                cclosest = j;
                dclosest = v;
            }
        }
        ae_assert(cclosest>=0, "KMeansUpdateDistances: internal error", _state);
        xyc->ptr.p_int[i] = cclosest;
        xydist2->ptr.p_double[i] = dclosest;
    }
    ae_frame_leave(_state);
}



static ae_int_t testdforestunit_algomax = 0;
static ae_int_t testdforestunit_splitmax = 2;
static void testdforestunit_testprocessing(ae_bool* err, ae_state *_state);
static void testdforestunit_basictest1(ae_bool* err, ae_state *_state);
static void testdforestunit_basictest2(ae_bool* err, ae_state *_state);
static void testdforestunit_basictest3(ae_bool* err, ae_state *_state);
static void testdforestunit_basictest4(ae_bool* err, ae_state *_state);
static void testdforestunit_basictest5(ae_bool* err, ae_state *_state);
static void testdforestunit_basictestrandom(ae_bool* err,
     ae_state *_state);
static void testdforestunit_basictestallsame(ae_bool* err,
     ae_state *_state);
static void testdforestunit_unsetdf(decisionforest* df, ae_state *_state);





ae_bool testdforest(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool basicerrors;
    ae_bool procerrors;
    ae_bool result;


    
    /*
     * Primary settings
     */
    basicerrors = ae_false;
    procerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Tests
     */
    testdforestunit_testprocessing(&procerrors, _state);
    testdforestunit_basictest1(&basicerrors, _state);
    testdforestunit_basictest2(&basicerrors, _state);
    testdforestunit_basictest3(&basicerrors, _state);
    testdforestunit_basictest4(&basicerrors, _state);
    testdforestunit_basictest5(&basicerrors, _state);
    testdforestunit_basictestrandom(&basicerrors, _state);
    testdforestunit_basictestallsame(&basicerrors, _state);
    
    /*
     * Final report
     */
    waserrors = basicerrors||procerrors;
    if( !silent )
    {
        printf("RANDOM FOREST TEST\n");
        printf("TOTAL RESULTS:                           ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* PROCESSING FUNCTIONS:                  ");
        if( !procerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* BASIC TESTS:                           ");
        if( !basicerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Processing functions test
*************************************************************************/
static void testdforestunit_testprocessing(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t nvars;
    ae_int_t nclasses;
    ae_int_t nsample;
    ae_int_t ntrees;
    decisionforest df1;
    decisionforest df2;
    decisionforestbuilder builder;
    dfreport rep;
    ae_int_t npoints;
    ae_matrix xy;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_int_t j;
    ae_bool allsame;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&df1, 0, sizeof(df1));
    memset(&df2, 0, sizeof(df2));
    memset(&builder, 0, sizeof(builder));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&y1, 0, sizeof(y1));
    memset(&y2, 0, sizeof(y2));
    _decisionforest_init(&df1, _state, ae_true);
    _decisionforest_init(&df2, _state, ae_true);
    _decisionforestbuilder_init(&builder, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);

    passcount = 100;
    
    /*
     * Main cycle
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * initialize parameters
         */
        nvars = 1+ae_randominteger(5, _state);
        nclasses = 1+ae_randominteger(3, _state);
        ntrees = 1+ae_randominteger(4, _state);
        
        /*
         * Initialize arrays and data
         */
        npoints = 10+ae_randominteger(50, _state);
        nsample = ae_maxint(10, ae_randominteger(npoints, _state), _state);
        ae_vector_set_length(&x1, nvars, _state);
        ae_vector_set_length(&x2, nvars, _state);
        ae_vector_set_length(&y1, nclasses, _state);
        ae_vector_set_length(&y2, nclasses, _state);
        ae_matrix_set_length(&xy, npoints, nvars+1, _state);
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nvars-1; j++)
            {
                if( j%2==0 )
                {
                    xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    xy.ptr.pp_double[i][j] = (double)(ae_randominteger(2, _state));
                }
            }
            if( nclasses==1 )
            {
                xy.ptr.pp_double[i][nvars] = 2*ae_randomreal(_state)-1;
            }
            else
            {
                xy.ptr.pp_double[i][nvars] = (double)(ae_randominteger(nclasses, _state));
            }
        }
        
        /*
         * create forest
         */
        dfbuildercreate(&builder, _state);
        dfbuildersetdataset(&builder, &xy, npoints, nvars, nclasses, _state);
        dfbuildersetrdfalgo(&builder, ae_randominteger(testdforestunit_algomax+1, _state), _state);
        dfbuildersetrdfsplitstrength(&builder, ae_randominteger(testdforestunit_splitmax+1, _state), _state);
        dfbuilderbuildrandomforest(&builder, ntrees, &df1, &rep, _state);
        
        /*
         * Same inputs leads to same outputs
         */
        for(i=0; i<=nvars-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nclasses-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        dfprocess(&df1, &x1, &y1, _state);
        dfprocess(&df1, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nclasses-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Same inputs on original forest leads to same outputs
         * on copy created using DFCopy
         */
        testdforestunit_unsetdf(&df2, _state);
        dfcopy(&df1, &df2, _state);
        for(i=0; i<=nvars-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nclasses-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        dfprocess(&df1, &x1, &y1, _state);
        dfprocess(&df2, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nclasses-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Same inputs on original forest leads to same outputs
         * on copy created using DFSerialize
         */
        testdforestunit_unsetdf(&df2, _state);
        {
            /*
             * This code passes data structure through serializers
             * (serializes it to string and loads back)
             */
            ae_serializer _local_serializer;
            ae_int_t _local_ssize;
            ae_frame _local_frame_block;
            ae_dyn_block _local_dynamic_block;
            
            ae_frame_make(_state, &_local_frame_block);
            
            ae_serializer_init(&_local_serializer);
            ae_serializer_alloc_start(&_local_serializer);
            dfalloc(&_local_serializer, &df1, _state);
            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
            memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
            ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
            dfserialize(&_local_serializer, &df1, _state);
            ae_serializer_stop(&_local_serializer, _state);
            ae_serializer_clear(&_local_serializer);
            
            ae_serializer_init(&_local_serializer);
            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
            dfunserialize(&_local_serializer, &df2, _state);
            ae_serializer_stop(&_local_serializer, _state);
            ae_serializer_clear(&_local_serializer);
            
            ae_frame_leave(_state);
        }
        for(i=0; i<=nvars-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nclasses-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        dfprocess(&df1, &x1, &y1, _state);
        dfprocess(&df2, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nclasses-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Normalization properties
         */
        if( nclasses>1 )
        {
            for(i=0; i<=nvars-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            dfprocess(&df1, &x1, &y1, _state);
            v = (double)(0);
            for(i=0; i<=nclasses-1; i++)
            {
                v = v+y1.ptr.p_double[i];
                *err = *err||ae_fp_less(y1.ptr.p_double[i],(double)(0));
            }
            *err = *err||ae_fp_greater(ae_fabs(v-1, _state),1000*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test:  one-tree forest built using full sample must remember all the
training cases
*************************************************************************/
static void testdforestunit_basictest1(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_matrix xy;
    ae_int_t npoints;
    ae_int_t ntrees;
    ae_int_t i;
    ae_int_t j;
    double s;
    ae_int_t info;
    decisionforestbuilder builder;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;
    ae_int_t nvars;
    ae_int_t nclasses;
    ae_int_t passcount;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&builder, 0, sizeof(builder));
    memset(&df, 0, sizeof(df));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforestbuilder_init(&builder, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    passcount = 10;
    for(nvars=1; nvars<=4; nvars++)
    {
        for(nclasses=1; nclasses<=3; nclasses++)
        {
            for(pass=0; pass<=passcount-1; pass++)
            {
                
                /*
                 * select number of points
                 */
                if( pass<3&&passcount>3 )
                {
                    npoints = 1+pass;
                }
                else
                {
                    npoints = 100+ae_randominteger(100, _state);
                }
                
                /*
                 * Prepare task
                 */
                ae_matrix_set_length(&xy, npoints, nvars+1, _state);
                ae_vector_set_length(&x, nvars, _state);
                ae_vector_set_length(&y, nclasses, _state);
                for(i=0; i<=npoints-1; i++)
                {
                    xy.ptr.pp_double[i][0] = (double)(i);
                    for(j=1; j<=nvars-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    }
                    if( nclasses>1 )
                    {
                        xy.ptr.pp_double[i][nvars] = (double)(ae_randominteger(nclasses, _state));
                    }
                    else
                    {
                        xy.ptr.pp_double[i][nvars] = ae_randomreal(_state)-0.5;
                    }
                }
                
                /*
                 * Train
                 *
                 * NOTE: we also test obsolete forest construction functions
                 */
                ae_assert(passcount>=2, "RDF: test integrity failed", _state);
                ntrees = 1;
                if( pass>0 )
                {
                    
                    /*
                     * Test new interface
                     */
                    dfbuildercreate(&builder, _state);
                    dfbuildersetdataset(&builder, &xy, npoints, nvars, nclasses, _state);
                    dfbuildersetsubsampleratio(&builder, 1.0, _state);
                    if( ae_fp_greater(ae_randomreal(_state),0.5) )
                    {
                        dfbuildersetrndvars(&builder, nvars, _state);
                    }
                    else
                    {
                        dfbuildersetrndvarsratio(&builder, 1.0, _state);
                    }
                    dfbuildersetrdfalgo(&builder, ae_randominteger(testdforestunit_algomax+1, _state), _state);
                    dfbuildersetrdfsplitstrength(&builder, ae_randominteger(testdforestunit_splitmax+1, _state), _state);
                    dfbuilderbuildrandomforest(&builder, ntrees, &df, &rep, _state);
                }
                else
                {
                    
                    /*
                     * Test old interface
                     */
                    if( hqrnduniformi(&rs, 2, _state)==0 )
                    {
                        dfbuildrandomdecisionforest(&xy, npoints, nvars, nclasses, ntrees, 1.0, &info, &df, &rep, _state);
                    }
                    else
                    {
                        dfbuildrandomdecisionforestx1(&xy, npoints, nvars, nclasses, ntrees, 1, 1.0, &info, &df, &rep, _state);
                    }
                    if( info<=0 )
                    {
                        ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testdforestunit.ap:324");
                        ae_frame_leave(_state);
                        return;
                    }
                }
                
                /*
                 * Test
                 */
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.rmserror, _state),1.0E-9), __FILE__, __LINE__, "testdforestunit.ap:332");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgerror, _state),1.0E-9), __FILE__, __LINE__, "testdforestunit.ap:333");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgrelerror, _state),1.0E-9), __FILE__, __LINE__, "testdforestunit.ap:334");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.relclserror, _state),(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:335");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgce, _state),(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:336");
                for(i=0; i<=npoints-1; i++)
                {
                    ae_v_move(&x.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
                    dfprocess(&df, &x, &y, _state);
                    if( nclasses>1 )
                    {
                        
                        /*
                         * Checks for classification tasks
                         */
                        s = (double)(0);
                        for(j=0; j<=nclasses-1; j++)
                        {
                            ae_set_error_flag(err, ae_fp_less(y.ptr.p_double[j],(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:349");
                            s = s+y.ptr.p_double[j];
                        }
                        ae_set_error_flag(err, ae_fp_greater(ae_fabs(s-1, _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testdforestunit.ap:352");
                        ae_set_error_flag(err, ae_fp_greater(ae_fabs(y.ptr.p_double[ae_round(xy.ptr.pp_double[i][nvars], _state)]-1, _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testdforestunit.ap:353");
                    }
                    else
                    {
                        
                        /*
                         * Regression problem
                         */
                        ae_set_error_flag(err, ae_fp_greater(ae_fabs(y.ptr.p_double[0]-xy.ptr.pp_double[i][nvars], _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testdforestunit.ap:360");
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test:  tests generalization ability on a simple noisy classification
task:
* 0<x<1 - P(class=0)=1
* 1<x<2 - P(class=0)=2-x
* 2<x<3 - P(class=0)=0
*************************************************************************/
static void testdforestunit_basictest2(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix xy;
    ae_int_t npoints;
    ae_int_t ntrees;
    ae_int_t i;
    decisionforest df;
    decisionforestbuilder builder;
    ae_vector x;
    ae_vector y;
    dfreport rep;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&df, 0, sizeof(df));
    memset(&builder, 0, sizeof(builder));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    _decisionforestbuilder_init(&builder, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    passcount = 1;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * select npoints and ntrees
         */
        npoints = 3000;
        ntrees = 100;
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&xy, npoints, 2, _state);
        ae_vector_set_length(&x, 1, _state);
        ae_vector_set_length(&y, 2, _state);
        for(i=0; i<=npoints-1; i++)
        {
            xy.ptr.pp_double[i][0] = 3*hqrnduniformr(&rs, _state);
            if( ae_fp_less_eq(xy.ptr.pp_double[i][0],(double)(1)) )
            {
                xy.ptr.pp_double[i][1] = (double)(0);
            }
            else
            {
                if( ae_fp_less_eq(xy.ptr.pp_double[i][0],(double)(2)) )
                {
                    if( ae_fp_less(hqrnduniformr(&rs, _state),xy.ptr.pp_double[i][0]-1) )
                    {
                        xy.ptr.pp_double[i][1] = (double)(1);
                    }
                    else
                    {
                        xy.ptr.pp_double[i][1] = (double)(0);
                    }
                }
                else
                {
                    xy.ptr.pp_double[i][1] = (double)(1);
                }
            }
        }
        
        /*
         * Build forest
         */
        dfbuildercreate(&builder, _state);
        dfbuildersetdataset(&builder, &xy, npoints, 1, 2, _state);
        dfbuildersetsubsampleratio(&builder, 0.05, _state);
        dfbuildersetrdfalgo(&builder, ae_randominteger(testdforestunit_algomax+1, _state), _state);
        dfbuilderbuildrandomforest(&builder, ntrees, &df, &rep, _state);
        
        /*
         * Check report fields
         */
        ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.oobrelclserror-(double)1/(double)12, _state),0.50*((double)1/(double)12)), __FILE__, __LINE__, "testdforestunit.ap:435");
        ae_set_error_flag(err, ae_fp_greater(rep.relclserror,rep.oobrelclserror), __FILE__, __LINE__, "testdforestunit.ap:436");
        ae_set_error_flag(err, ae_fp_greater_eq(rep.avgce,rep.oobavgce), __FILE__, __LINE__, "testdforestunit.ap:437");
        ae_set_error_flag(err, ae_fp_greater_eq(rep.rmserror,rep.oobrmserror), __FILE__, __LINE__, "testdforestunit.ap:438");
        ae_set_error_flag(err, ae_fp_greater_eq(rep.avgerror,rep.oobavgerror), __FILE__, __LINE__, "testdforestunit.ap:439");
        ae_set_error_flag(err, ae_fp_greater_eq(rep.avgrelerror,rep.oobavgrelerror), __FILE__, __LINE__, "testdforestunit.ap:440");
        
        /*
         * Check outputs
         */
        x.ptr.p_double[0] = 0.0;
        while(ae_fp_less_eq(x.ptr.p_double[0],3.0))
        {
            dfprocess(&df, &x, &y, _state);
            
            /*
             * Test for basic properties
             */
            ae_set_error_flag(err, ae_fp_less(y.ptr.p_double[0],(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:453");
            ae_set_error_flag(err, ae_fp_less(y.ptr.p_double[1],(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:454");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(y.ptr.p_double[0]+y.ptr.p_double[1]-1, _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testdforestunit.ap:455");
            
            /*
             * test for good correlation with results
             */
            if( ae_fp_less(x.ptr.p_double[0],(double)(1)) )
            {
                ae_set_error_flag(err, ae_fp_less(y.ptr.p_double[0],0.8), __FILE__, __LINE__, "testdforestunit.ap:461");
            }
            if( ae_fp_greater_eq(x.ptr.p_double[0],(double)(1))&&ae_fp_less_eq(x.ptr.p_double[0],(double)(2)) )
            {
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(y.ptr.p_double[1]-(x.ptr.p_double[0]-1), _state),0.5), __FILE__, __LINE__, "testdforestunit.ap:463");
            }
            if( ae_fp_greater(x.ptr.p_double[0],(double)(2)) )
            {
                ae_set_error_flag(err, ae_fp_less(y.ptr.p_double[1],0.8), __FILE__, __LINE__, "testdforestunit.ap:465");
            }
            
            /*
             * Advance
             */
            x.ptr.p_double[0] = x.ptr.p_double[0]+0.01;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test:  tests  generalization ability on a simple classification task
(no noise):
* |x|<1, |y|<1
* x^2+y^2<=0.25 - P(class=0)=1
* x^2+y^2>0.25  - P(class=0)=0
*************************************************************************/
static void testdforestunit_basictest3(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix xy;
    ae_int_t npoints;
    ae_int_t ntrees;
    ae_int_t i;
    ae_int_t j;
    ae_int_t info;
    decisionforestbuilder builder;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;
    ae_int_t testgridsize;
    double r;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&builder, 0, sizeof(builder));
    memset(&df, 0, sizeof(df));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforestbuilder_init(&builder, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    passcount = 2;
    testgridsize = 50;
    for(pass=0; pass<=passcount-1; pass++)
    {
        
        /*
         * select npoints and ntrees
         */
        npoints = 2000;
        ntrees = 100;
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&xy, npoints, 3, _state);
        ae_vector_set_length(&x, 2, _state);
        ae_vector_set_length(&y, 2, _state);
        for(i=0; i<=npoints-1; i++)
        {
            xy.ptr.pp_double[i][0] = 2*hqrnduniformr(&rs, _state)-1;
            xy.ptr.pp_double[i][1] = 2*hqrnduniformr(&rs, _state)-1;
            if( ae_fp_less_eq(ae_sqr(xy.ptr.pp_double[i][0], _state)+ae_sqr(xy.ptr.pp_double[i][1], _state),0.25) )
            {
                xy.ptr.pp_double[i][2] = (double)(0);
            }
            else
            {
                xy.ptr.pp_double[i][2] = (double)(1);
            }
        }
        
        /*
         * Build forest
         *
         * NOTE: we also test obsolete forest construction functions
         */
        ae_assert(passcount>=2, "RDF: test integrity failed", _state);
        if( pass>0 )
        {
            
            /*
             * Test new interface
             */
            dfbuildercreate(&builder, _state);
            dfbuildersetdataset(&builder, &xy, npoints, 2, 2, _state);
            dfbuildersetsubsampleratio(&builder, 0.10, _state);
            dfbuildersetrdfalgo(&builder, ae_randominteger(testdforestunit_algomax+1, _state), _state);
            dfbuilderbuildrandomforest(&builder, ntrees, &df, &rep, _state);
        }
        else
        {
            
            /*
             * Test old interface
             */
            if( hqrnduniformi(&rs, 2, _state)==0 )
            {
                dfbuildrandomdecisionforest(&xy, npoints, 2, 2, ntrees, 0.10, &info, &df, &rep, _state);
            }
            else
            {
                dfbuildrandomdecisionforestx1(&xy, npoints, 2, 2, ntrees, 1, 0.10, &info, &df, &rep, _state);
            }
            if( info<=0 )
            {
                ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testdforestunit.ap:559");
                ae_frame_leave(_state);
                return;
            }
        }
        
        /*
         * Test
         */
        for(i=-testgridsize/2; i<=testgridsize/2; i++)
        {
            for(j=-testgridsize/2; j<=testgridsize/2; j++)
            {
                x.ptr.p_double[0] = (double)i/(double)(testgridsize/2);
                x.ptr.p_double[1] = (double)j/(double)(testgridsize/2);
                dfprocess(&df, &x, &y, _state);
                
                /*
                 * Test for basic properties
                 */
                ae_set_error_flag(err, ae_fp_less(y.ptr.p_double[0],(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:577");
                ae_set_error_flag(err, ae_fp_less(y.ptr.p_double[1],(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:578");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(y.ptr.p_double[0]+y.ptr.p_double[1]-1, _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testdforestunit.ap:579");
                
                /*
                 * test for good correlation with results
                 */
                r = ae_sqrt(ae_sqr(x.ptr.p_double[0], _state)+ae_sqr(x.ptr.p_double[1], _state), _state);
                if( ae_fp_less(r,0.66*0.5) )
                {
                    ae_set_error_flag(err, ae_fp_less(y.ptr.p_double[0],0.66), __FILE__, __LINE__, "testdforestunit.ap:586");
                }
                if( ae_fp_greater(r,1.33*0.5) )
                {
                    ae_set_error_flag(err, ae_fp_less(y.ptr.p_double[1],0.66), __FILE__, __LINE__, "testdforestunit.ap:588");
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test: simple regression task without noise:
* |x|<1, |y|<1
* F(x,y) = x^2+y
*************************************************************************/
static void testdforestunit_basictest4(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix xy;
    ae_int_t npoints;
    ae_int_t ntrees;
    ae_int_t ns;
    ae_int_t i;
    ae_int_t j;
    ae_int_t info;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;
    ae_int_t testgridsize;
    decisionforestbuilder builder;
    double maxerr;
    double avgerr;
    ae_int_t cnt;
    double ey;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&df, 0, sizeof(df));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rep, 0, sizeof(rep));
    memset(&builder, 0, sizeof(builder));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);
    _decisionforestbuilder_init(&builder, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    passcount = 2;
    testgridsize = 50;
    for(pass=0; pass<=passcount-1; pass++)
    {
        
        /*
         * select npoints and ntrees
         */
        npoints = 5000;
        ntrees = 100;
        ns = ae_round(0.1*npoints, _state);
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&xy, npoints, 3, _state);
        ae_vector_set_length(&x, 2, _state);
        ae_vector_set_length(&y, 1, _state);
        for(i=0; i<=npoints-1; i++)
        {
            xy.ptr.pp_double[i][0] = 2*hqrnduniformr(&rs, _state)-1;
            xy.ptr.pp_double[i][1] = 2*hqrnduniformr(&rs, _state)-1;
            xy.ptr.pp_double[i][2] = ae_sqr(xy.ptr.pp_double[i][0], _state)+xy.ptr.pp_double[i][1];
        }
        
        /*
         * Build forest
         *
         * NOTE: we also test obsolete forest construction functions
         */
        ae_assert(passcount>=2, "RDF: test integrity failed", _state);
        if( pass>0 )
        {
            
            /*
             * Test new interface
             */
            dfbuildercreate(&builder, _state);
            dfbuildersetdataset(&builder, &xy, npoints, 2, 1, _state);
            dfbuildersetsubsampleratio(&builder, 0.10, _state);
            dfbuildersetrdfalgo(&builder, ae_randominteger(testdforestunit_algomax+1, _state), _state);
            dfbuilderbuildrandomforest(&builder, ntrees, &df, &rep, _state);
        }
        else
        {
            
            /*
             * Test old interface
             */
            if( hqrnduniformi(&rs, 2, _state)==0 )
            {
                dfbuildrandomdecisionforest(&xy, npoints, 2, 1, ntrees, 0.10, &info, &df, &rep, _state);
            }
            else
            {
                dfbuildrandomdecisionforestx1(&xy, npoints, 2, 1, ntrees, 1, 0.10, &info, &df, &rep, _state);
            }
            if( info<=0 )
            {
                ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testdforestunit.ap:679");
                ae_frame_leave(_state);
                return;
            }
        }
        
        /*
         * Check report fields
         */
        ae_set_error_flag(err, ae_fp_neq(rep.relclserror,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:687");
        ae_set_error_flag(err, ae_fp_neq(rep.avgce,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:688");
        ae_set_error_flag(err, ae_fp_neq(rep.oobrelclserror,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:689");
        ae_set_error_flag(err, ae_fp_neq(rep.oobavgce,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:690");
        ae_set_error_flag(err, ae_fp_greater_eq(rep.rmserror,rep.oobrmserror), __FILE__, __LINE__, "testdforestunit.ap:691");
        ae_set_error_flag(err, ae_fp_greater_eq(rep.avgerror,rep.oobavgerror), __FILE__, __LINE__, "testdforestunit.ap:692");
        ae_set_error_flag(err, ae_fp_greater_eq(rep.avgrelerror,rep.oobavgrelerror), __FILE__, __LINE__, "testdforestunit.ap:693");
        ae_set_error_flag(err, ae_fp_greater(rep.oobrmserror,0.05), __FILE__, __LINE__, "testdforestunit.ap:694");
        ae_set_error_flag(err, ae_fp_greater(rep.oobavgerror,0.05), __FILE__, __LINE__, "testdforestunit.ap:695");
        ae_set_error_flag(err, ae_fp_less(rep.oobrmserror,0.001), __FILE__, __LINE__, "testdforestunit.ap:696");
        ae_set_error_flag(err, ae_fp_less(rep.oobavgerror,0.001), __FILE__, __LINE__, "testdforestunit.ap:697");
        
        /*
         * Test
         */
        maxerr = (double)(0);
        avgerr = (double)(0);
        cnt = 0;
        for(i=ae_round(-0.7*testgridsize/2, _state); i<=ae_round(0.7*testgridsize/2, _state); i++)
        {
            for(j=ae_round(-0.7*testgridsize/2, _state); j<=ae_round(0.7*testgridsize/2, _state); j++)
            {
                x.ptr.p_double[0] = (double)i/(double)(testgridsize/2);
                x.ptr.p_double[1] = (double)j/(double)(testgridsize/2);
                ey = ae_sqr(x.ptr.p_double[0], _state)+x.ptr.p_double[1];
                dfprocess(&df, &x, &y, _state);
                maxerr = ae_maxreal(maxerr, ae_fabs(y.ptr.p_double[0]-ey, _state), _state);
                avgerr = avgerr+ae_fabs(y.ptr.p_double[0]-ey, _state);
                cnt = cnt+1;
            }
        }
        avgerr = avgerr/cnt;
        ae_set_error_flag(err, ae_fp_greater(maxerr,0.15), __FILE__, __LINE__, "testdforestunit.ap:717");
        ae_set_error_flag(err, ae_fp_greater(avgerr,0.05), __FILE__, __LINE__, "testdforestunit.ap:718");
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test: extended variable selection leads to better results.

Next task CAN be solved without EVS but it is very unlikely. With EVS
it can be easily and exactly solved.

Task matrix:
    1 0 0 0 ... 0   0
    0 1 0 0 ... 0   1
    0 0 1 0 ... 0   2
    0 0 0 1 ... 0   3
    0 0 0 0 ... 1   N-1
*************************************************************************/
static void testdforestunit_basictest5(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t nvars;
    ae_int_t npoints;
    ae_int_t nfeatures;
    ae_int_t ntrees;
    ae_int_t i;
    ae_int_t j;
    decisionforestbuilder builder;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&builder, 0, sizeof(builder));
    memset(&df, 0, sizeof(df));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforestbuilder_init(&builder, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);

    
    /*
     * select npoints and ntrees
     */
    npoints = 50;
    nvars = npoints;
    ntrees = 1;
    nfeatures = 1;
    
    /*
     * Prepare task
     */
    ae_matrix_set_length(&xy, npoints, nvars+1, _state);
    ae_vector_set_length(&x, nvars, _state);
    ae_vector_set_length(&y, 1, _state);
    for(i=0; i<=npoints-1; i++)
    {
        for(j=0; j<=nvars-1; j++)
        {
            xy.ptr.pp_double[i][j] = (double)(0);
        }
        xy.ptr.pp_double[i][i] = (double)(1);
        xy.ptr.pp_double[i][nvars] = (double)(i);
    }
    
    /*
     * Build forest
     */
    dfbuildercreate(&builder, _state);
    dfbuildersetdataset(&builder, &xy, npoints, nvars, 1, _state);
    dfbuildersetsubsampleratio(&builder, 1.00, _state);
    dfbuildersetrdfalgo(&builder, ae_randominteger(testdforestunit_algomax+1, _state), _state);
    dfbuildersetrdfsplitstrength(&builder, ae_randominteger(testdforestunit_splitmax+1, _state), _state);
    dfbuilderbuildrandomforest(&builder, ntrees, &df, &rep, _state);
    
    /*
     * Test
     */
    ae_set_error_flag(err, ae_fp_greater_eq(rep.rmserror,rep.oobrmserror), __FILE__, __LINE__, "testdforestunit.ap:789");
    ae_set_error_flag(err, ae_fp_greater_eq(rep.avgerror,rep.oobavgerror), __FILE__, __LINE__, "testdforestunit.ap:790");
    ae_set_error_flag(err, ae_fp_greater_eq(rep.avgrelerror,rep.oobavgrelerror), __FILE__, __LINE__, "testdforestunit.ap:791");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.rmserror, _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testdforestunit.ap:792");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgerror, _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testdforestunit.ap:793");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgrelerror, _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testdforestunit.ap:794");
    for(i=0; i<=npoints-1; i++)
    {
        ae_v_move(&x.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
        dfprocess(&df, &x, &y, _state);
        if( ae_fp_greater(ae_fabs(y.ptr.p_double[0]-xy.ptr.pp_double[i][nvars], _state),1000*ae_machineepsilon) )
        {
            ae_set_error_flag(err, ae_true, __FILE__, __LINE__, "testdforestunit.ap:800");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test: test correctness of error reports on completely random problem;
test that OOB errors are roughly unbiased.
*************************************************************************/
static void testdforestunit_basictestrandom(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t nvars;
    ae_int_t npoints;
    ae_int_t ntrees;
    ae_int_t nclasses;
    ae_int_t i;
    ae_int_t j;
    ae_int_t factor;
    decisionforestbuilder builder;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;
    hqrndstate rs;
    double meanlevel;
    double sigma;
    double v;
    double refrelcls;
    double refavgce;
    double refrms;
    double refavg;
    double refavgrel;
    ae_int_t relcnt;
    double oobtol;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&builder, 0, sizeof(builder));
    memset(&df, 0, sizeof(df));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforestbuilder_init(&builder, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    npoints = 200;
    nvars = 10;
    ntrees = 1000;
    oobtol = 0.20;
    for(nclasses=1; nclasses<=3; nclasses++)
    {
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&xy, npoints, nvars+1, _state);
        meanlevel = 10*hqrndnormal(&rs, _state);
        sigma = ae_pow((double)(10), hqrndnormal(&rs, _state), _state);
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nvars-1; j++)
            {
                xy.ptr.pp_double[i][j] = (double)(0);
                if( j%3==0 )
                {
                    xy.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
                if( j%3==1&&ae_fp_less(hqrnduniformr(&rs, _state),0.05) )
                {
                    xy.ptr.pp_double[i][j] = (double)(1);
                }
                if( j%3==2&&ae_fp_less(hqrnduniformr(&rs, _state),0.05) )
                {
                    xy.ptr.pp_double[i][j] = (double)(1+hqrnduniformi(&rs, 3, _state));
                }
            }
            if( nclasses==1 )
            {
                xy.ptr.pp_double[i][nvars] = meanlevel+sigma*hqrndnormal(&rs, _state);
                if( ae_fp_less(hqrnduniformr(&rs, _state),0.05) )
                {
                    xy.ptr.pp_double[i][nvars] = (double)(0);
                }
            }
            else
            {
                xy.ptr.pp_double[i][nvars] = (double)(hqrnduniformi(&rs, nclasses, _state));
            }
        }
        ae_vector_set_length(&x, nvars, _state);
        ae_vector_set_length(&y, nclasses, _state);
        
        /*
         * Build forest using 50% and 66% of the samples.
         * Check TRN and OOB errors (OOB should be 2x and 3x larger in these cases)
         */
        for(factor=2; factor<=3; factor++)
        {
            j = hqrnduniformi(&rs, 3, _state);
            dfbuildercreate(&builder, _state);
            dfbuildersetdataset(&builder, &xy, npoints, nvars, nclasses, _state);
            dfbuildersetsubsampleratio(&builder, 1-(double)1/(double)factor, _state);
            if( j>0 )
            {
                dfbuildersetrndvars(&builder, 1+hqrnduniformi(&rs, 5, _state), _state);
            }
            if( j==0 )
            {
                dfbuildersetrndvarsauto(&builder, _state);
            }
            if( j<0 )
            {
                dfbuildersetrndvarsratio(&builder, hqrnduniformr(&rs, _state), _state);
            }
            dfbuildersetrdfalgo(&builder, ae_randominteger(testdforestunit_algomax+1, _state), _state);
            dfbuildersetrdfsplitstrength(&builder, ae_randominteger(testdforestunit_splitmax+1, _state), _state);
            ae_set_error_flag(err, ae_fp_neq(dfbuildergetprogress(&builder, _state),0.0), __FILE__, __LINE__, "testdforestunit.ap:889");
            dfbuilderbuildrandomforest(&builder, ntrees, &df, &rep, _state);
            ae_set_error_flag(err, ae_fp_neq(dfbuildergetprogress(&builder, _state),1.0), __FILE__, __LINE__, "testdforestunit.ap:891");
            refrelcls = (double)(0);
            refavgce = (double)(0);
            refrms = (double)(0);
            refavg = (double)(0);
            refavgrel = (double)(0);
            relcnt = 0;
            if( nclasses==1 )
            {
                
                /*
                 * Check training set errors
                 */
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=0; j<=nvars-1; j++)
                    {
                        x.ptr.p_double[j] = xy.ptr.pp_double[i][j];
                    }
                    dfprocess(&df, &x, &y, _state);
                    v = y.ptr.p_double[0]-xy.ptr.pp_double[i][nvars];
                    refrms = refrms+ae_sqr(v, _state);
                    refavg = refavg+ae_fabs(v, _state);
                    if( ae_fp_neq(xy.ptr.pp_double[i][nvars],(double)(0)) )
                    {
                        refavgrel = refavgrel+ae_fabs(v/xy.ptr.pp_double[i][nvars], _state);
                        relcnt = relcnt+1;
                    }
                }
                refrms = ae_sqrt(refrms/npoints, _state);
                refavg = refavg/npoints;
                refavgrel = refavgrel/coalesce((double)(relcnt), (double)(1), _state);
                ae_set_error_flag(err, ae_fp_neq(rep.relclserror,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:920");
                ae_set_error_flag(err, ae_fp_neq(rep.avgce,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:921");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state)/refrms,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:922");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state)/refavg,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:923");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state)/refavgrel,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:924");
                ae_set_error_flag(err, ae_fp_neq(dfrelclserror(&df, &xy, npoints, _state),(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:925");
                ae_set_error_flag(err, ae_fp_neq(dfavgce(&df, &xy, npoints, _state),(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:926");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(dfrmserror(&df, &xy, npoints, _state)-refrms, _state)/refrms,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:927");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(dfavgerror(&df, &xy, npoints, _state)-refavg, _state)/refavg,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:928");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(dfavgrelerror(&df, &xy, npoints, _state)-refavgrel, _state)/refavgrel,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:929");
                
                /*
                 * Check OOB errors against training set errors:
                 * * RelCLS and AvgCE are not calculated for regression problems
                 * * RMS and AVG should be roughly FACTOR times larger
                 * * AVG-REL should be just larger (it is more prone to sampling noise than previous two)
                 */
                ae_set_error_flag(err, ae_fp_neq(rep.oobrelclserror,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:937");
                ae_set_error_flag(err, ae_fp_neq(rep.oobavgce,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:938");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.oobrmserror/rep.rmserror-factor, _state),oobtol), __FILE__, __LINE__, "testdforestunit.ap:939");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.oobavgerror/rep.avgerror-factor, _state),oobtol), __FILE__, __LINE__, "testdforestunit.ap:940");
                ae_set_error_flag(err, ae_fp_less(rep.oobavgrelerror,rep.avgrelerror), __FILE__, __LINE__, "testdforestunit.ap:941");
            }
            else
            {
                
                /*
                 * Check training set errors
                 */
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=0; j<=nvars-1; j++)
                    {
                        x.ptr.p_double[j] = xy.ptr.pp_double[i][j];
                    }
                    dfprocess(&df, &x, &y, _state);
                    for(j=0; j<=nclasses-1; j++)
                    {
                        v = y.ptr.p_double[j];
                        if( ae_fp_eq((double)(j),xy.ptr.pp_double[i][nvars]) )
                        {
                            v = v-1;
                        }
                        refrms = refrms+ae_sqr(v, _state);
                        refavg = refavg+ae_fabs(v, _state);
                        if( ae_fp_eq((double)(j),xy.ptr.pp_double[i][nvars]) )
                        {
                            refavgrel = refavgrel+ae_fabs(v, _state);
                            refavgce = refavgce-ae_log(v, _state);
                        }
                    }
                }
                refrms = ae_sqrt(refrms/(npoints*nclasses), _state);
                refavg = refavg/(npoints*nclasses);
                refavgrel = refavgrel/npoints;
                refavgce = refavgce/npoints;
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgce-refavgce, _state)/refavgce,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:971");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state)/refrms,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:972");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state)/refavg,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:973");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state)/refavgrel,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:974");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(dfrelclserror(&df, &xy, npoints, _state)-rep.relclserror, _state)*npoints,(double)(5)), __FILE__, __LINE__, "testdforestunit.ap:976");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(dfavgce(&df, &xy, npoints, _state)-refavgce, _state)/refavgce,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:977");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(dfrmserror(&df, &xy, npoints, _state)-refrms, _state)/refrms,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:978");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(dfavgerror(&df, &xy, npoints, _state)-refavg, _state)/refavg,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:979");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(dfavgrelerror(&df, &xy, npoints, _state)-refavgrel, _state)/refavgrel,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:980");
                
                /*
                 * Check OOB errors against training set errors
                 * * RelCLS, AvgCE and AvgRel should be just larger (too prone to sampling noise to perform more sensitive tests)
                 * * RMS and AVG should be roughly FACTOR times larger
                 *
                 */
                ae_set_error_flag(err, ae_fp_less(rep.oobrelclserror,rep.relclserror), __FILE__, __LINE__, "testdforestunit.ap:988");
                ae_set_error_flag(err, ae_fp_less(rep.oobavgce,rep.avgce), __FILE__, __LINE__, "testdforestunit.ap:989");
                ae_set_error_flag(err, ae_fp_less(rep.oobavgrelerror,rep.avgrelerror), __FILE__, __LINE__, "testdforestunit.ap:990");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.oobrmserror/rep.rmserror-factor, _state),oobtol), __FILE__, __LINE__, "testdforestunit.ap:991");
                ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.oobavgerror/rep.avgerror-factor, _state),oobtol), __FILE__, __LINE__, "testdforestunit.ap:992");
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test behavior of the RDF algorithm on problem where all values of variables
are same
*************************************************************************/
static void testdforestunit_basictestallsame(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t nvars;
    ae_int_t npoints;
    ae_int_t ntrees;
    ae_int_t nclasses;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    decisionforestbuilder builder;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;
    hqrndstate rs;
    double v;
    double refrelcls;
    double refavgce;
    double refrms;
    double refavg;
    double refavgrel;
    ae_int_t relcnt;
    double oobtol;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&builder, 0, sizeof(builder));
    memset(&df, 0, sizeof(df));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforestbuilder_init(&builder, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    npoints = 100;
    nvars = 10;
    ntrees = 100;
    oobtol = 0.25;
    for(nclasses=1; nclasses<=3; nclasses++)
    {
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&xy, npoints, nvars+1, _state);
        k = hqrnduniformi(&rs, 11, _state)-5;
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nvars-1; j++)
            {
                xy.ptr.pp_double[i][j] = (double)(k);
            }
            if( nclasses==1 )
            {
                xy.ptr.pp_double[i][nvars] = hqrndnormal(&rs, _state);
            }
            else
            {
                xy.ptr.pp_double[i][nvars] = (double)(hqrnduniformi(&rs, nclasses, _state));
            }
        }
        ae_vector_set_length(&x, nvars, _state);
        ae_vector_set_length(&y, nclasses, _state);
        
        /*
         * Build forest.
         * Check TRN and OOB errors.
         */
        j = hqrnduniformi(&rs, 3, _state);
        dfbuildercreate(&builder, _state);
        dfbuildersetdataset(&builder, &xy, npoints, nvars, nclasses, _state);
        dfbuildersetsubsampleratio(&builder, 0.5, _state);
        if( j>0 )
        {
            dfbuildersetrndvars(&builder, 1+hqrnduniformi(&rs, 5, _state), _state);
        }
        if( j==0 )
        {
            dfbuildersetrndvarsauto(&builder, _state);
        }
        if( j<0 )
        {
            dfbuildersetrndvarsratio(&builder, hqrnduniformr(&rs, _state), _state);
        }
        dfbuildersetrdfalgo(&builder, ae_randominteger(testdforestunit_algomax+1, _state), _state);
        dfbuildersetrdfsplitstrength(&builder, ae_randominteger(testdforestunit_splitmax+1, _state), _state);
        dfbuilderbuildrandomforest(&builder, ntrees, &df, &rep, _state);
        refrelcls = (double)(0);
        refavgce = (double)(0);
        refrms = (double)(0);
        refavg = (double)(0);
        refavgrel = (double)(0);
        relcnt = 0;
        if( nclasses==1 )
        {
            
            /*
             * Check training set errors
             */
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=nvars-1; j++)
                {
                    x.ptr.p_double[j] = xy.ptr.pp_double[i][j];
                }
                dfprocess(&df, &x, &y, _state);
                v = y.ptr.p_double[0]-xy.ptr.pp_double[i][nvars];
                refrms = refrms+ae_sqr(v, _state);
                refavg = refavg+ae_fabs(v, _state);
                if( ae_fp_neq(xy.ptr.pp_double[i][nvars],(double)(0)) )
                {
                    refavgrel = refavgrel+ae_fabs(v/xy.ptr.pp_double[i][nvars], _state);
                    relcnt = relcnt+1;
                }
            }
            refrms = ae_sqrt(refrms/npoints, _state);
            refavg = refavg/npoints;
            refavgrel = refavgrel/coalesce((double)(relcnt), (double)(1), _state);
            ae_set_error_flag(err, ae_fp_neq(rep.relclserror,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:1096");
            ae_set_error_flag(err, ae_fp_neq(rep.avgce,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:1097");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state)/refrms,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:1098");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state)/refavg,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:1099");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state)/refavgrel,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:1100");
            
            /*
             * Check OOB errors against training set errors:
             * * RelCLS and AvgCE are not calculated for regression problems
             * * RMS and AVG should be roughly same
             */
            ae_set_error_flag(err, ae_fp_neq(rep.oobrelclserror,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:1107");
            ae_set_error_flag(err, ae_fp_neq(rep.oobavgce,(double)(0)), __FILE__, __LINE__, "testdforestunit.ap:1108");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.oobrmserror/rep.rmserror-1, _state),oobtol), __FILE__, __LINE__, "testdforestunit.ap:1109");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.oobavgerror/rep.avgerror-1, _state),oobtol), __FILE__, __LINE__, "testdforestunit.ap:1110");
        }
        else
        {
            
            /*
             * Check training set errors
             */
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=nvars-1; j++)
                {
                    x.ptr.p_double[j] = xy.ptr.pp_double[i][j];
                }
                dfprocess(&df, &x, &y, _state);
                for(j=0; j<=nclasses-1; j++)
                {
                    v = y.ptr.p_double[j];
                    if( ae_fp_eq((double)(j),xy.ptr.pp_double[i][nvars]) )
                    {
                        v = v-1;
                    }
                    refrms = refrms+ae_sqr(v, _state);
                    refavg = refavg+ae_fabs(v, _state);
                    if( ae_fp_eq((double)(j),xy.ptr.pp_double[i][nvars]) )
                    {
                        refavgrel = refavgrel+ae_fabs(v, _state);
                        refavgce = refavgce-ae_log(v, _state);
                    }
                }
            }
            refrms = ae_sqrt(refrms/(npoints*nclasses), _state);
            refavg = refavg/(npoints*nclasses);
            refavgrel = refavgrel/npoints;
            refavgce = refavgce/npoints;
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgce-refavgce, _state)/refavgce,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:1140");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state)/refrms,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:1141");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state)/refavg,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:1142");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state)/refavgrel,1.0E-6), __FILE__, __LINE__, "testdforestunit.ap:1143");
            
            /*
             * Check OOB errors against training set errors
             * * RMS and AVG should be roughly same
             *
             */
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.oobrmserror/rep.rmserror-1, _state),oobtol), __FILE__, __LINE__, "testdforestunit.ap:1150");
            ae_set_error_flag(err, ae_fp_greater(ae_fabs(rep.oobavgerror/rep.avgerror-1, _state),oobtol), __FILE__, __LINE__, "testdforestunit.ap:1151");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Unsets DF
*************************************************************************/
static void testdforestunit_unsetdf(decisionforest* df, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t info;
    dfreport rep;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&rep, 0, sizeof(rep));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);

    ae_matrix_set_length(&xy, 0+1, 1+1, _state);
    xy.ptr.pp_double[0][0] = (double)(0);
    xy.ptr.pp_double[0][1] = (double)(0);
    dfbuildinternal(&xy, 1, 1, 1, 1, 1, 1, 0, &info, df, &rep, _state);
    ae_frame_leave(_state);
}



static double testgqunit_mapkind(ae_int_t k, ae_state *_state);
static void testgqunit_buildgausslegendrequadrature(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state);
static void testgqunit_buildgaussjacobiquadrature(ae_int_t n,
     double alpha,
     double beta,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state);
static void testgqunit_buildgausslaguerrequadrature(ae_int_t n,
     double alpha,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state);
static void testgqunit_buildgausshermitequadrature(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testgq(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector alpha;
    ae_vector beta;
    ae_vector x;
    ae_vector w;
    ae_vector x2;
    ae_vector w2;
    double err;
    ae_int_t n;
    ae_int_t i;
    ae_int_t info;
    ae_int_t akind;
    ae_int_t bkind;
    double alphac;
    double betac;
    double errtol;
    double nonstricterrtol;
    double stricterrtol;
    ae_bool recerrors;
    ae_bool specerrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&alpha, 0, sizeof(alpha));
    memset(&beta, 0, sizeof(beta));
    memset(&x, 0, sizeof(x));
    memset(&w, 0, sizeof(w));
    memset(&x2, 0, sizeof(x2));
    memset(&w2, 0, sizeof(w2));
    ae_vector_init(&alpha, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&beta, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);

    recerrors = ae_false;
    specerrors = ae_false;
    waserrors = ae_false;
    errtol = 1.0E-12;
    nonstricterrtol = 1.0E-6;
    stricterrtol = 1000*ae_machineepsilon;
    
    /*
     * Three tests for rec-based Gauss quadratures with known weights/nodes:
     * 1. Gauss-Legendre with N=2
     * 2. Gauss-Legendre with N=5
     * 3. Gauss-Chebyshev with N=1, 2, 4, 8, ..., 512
     */
    err = (double)(0);
    ae_vector_set_length(&alpha, 2, _state);
    ae_vector_set_length(&beta, 2, _state);
    alpha.ptr.p_double[0] = (double)(0);
    alpha.ptr.p_double[1] = (double)(0);
    beta.ptr.p_double[1] = (double)1/(double)(4*1*1-1);
    gqgeneraterec(&alpha, &beta, 2.0, 2, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+ae_sqrt((double)(3), _state)/3, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]-ae_sqrt((double)(3), _state)/3, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-1, _state), _state);
        for(i=0; i<=0; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 5, _state);
    ae_vector_set_length(&beta, 5, _state);
    alpha.ptr.p_double[0] = (double)(0);
    for(i=1; i<=4; i++)
    {
        alpha.ptr.p_double[i] = (double)(0);
        beta.ptr.p_double[i] = ae_sqr((double)(i), _state)/(4*ae_sqr((double)(i), _state)-1);
    }
    gqgeneraterec(&alpha, &beta, 2.0, 5, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+ae_sqrt(245+14*ae_sqrt((double)(70), _state), _state)/21, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+x.ptr.p_double[4], _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+ae_sqrt(245-14*ae_sqrt((double)(70), _state), _state)/21, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+x.ptr.p_double[3], _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2], _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(322-13*ae_sqrt((double)(70), _state))/900, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-w.ptr.p_double[4], _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(322+13*ae_sqrt((double)(70), _state))/900, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-w.ptr.p_double[3], _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(double)128/(double)225, _state), _state);
        for(i=0; i<=3; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    n = 1;
    while(n<=512)
    {
        ae_vector_set_length(&alpha, n, _state);
        ae_vector_set_length(&beta, n, _state);
        for(i=0; i<=n-1; i++)
        {
            alpha.ptr.p_double[i] = (double)(0);
            if( i==0 )
            {
                beta.ptr.p_double[i] = (double)(0);
            }
            if( i==1 )
            {
                beta.ptr.p_double[i] = (double)1/(double)2;
            }
            if( i>1 )
            {
                beta.ptr.p_double[i] = (double)1/(double)4;
            }
        }
        gqgeneraterec(&alpha, &beta, ae_pi, n, &info, &x, &w, _state);
        if( info>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-ae_cos(ae_pi*(n-i-0.5)/n, _state), _state), _state);
                err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-ae_pi/n, _state), _state);
            }
            for(i=0; i<=n-2; i++)
            {
                recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
            }
        }
        else
        {
            recerrors = ae_true;
        }
        n = n*2;
    }
    recerrors = recerrors||ae_fp_greater(err,errtol);
    
    /*
     * Three tests for rec-based Gauss-Lobatto quadratures with known weights/nodes:
     * 1. Gauss-Lobatto with N=3
     * 2. Gauss-Lobatto with N=4
     * 3. Gauss-Lobatto with N=6
     */
    err = (double)(0);
    ae_vector_set_length(&alpha, 2, _state);
    ae_vector_set_length(&beta, 2, _state);
    alpha.ptr.p_double[0] = (double)(0);
    alpha.ptr.p_double[1] = (double)(0);
    beta.ptr.p_double[0] = (double)(0);
    beta.ptr.p_double[1] = (double)(1*1)/(double)(4*1*1-1);
    gqgenerategausslobattorec(&alpha, &beta, 2.0, (double)(-1), (double)(1), 3, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1], _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(double)1/(double)3, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(double)4/(double)3, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(double)1/(double)3, _state), _state);
        for(i=0; i<=1; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 3, _state);
    ae_vector_set_length(&beta, 3, _state);
    alpha.ptr.p_double[0] = (double)(0);
    alpha.ptr.p_double[1] = (double)(0);
    alpha.ptr.p_double[2] = (double)(0);
    beta.ptr.p_double[0] = (double)(0);
    beta.ptr.p_double[1] = (double)(1*1)/(double)(4*1*1-1);
    beta.ptr.p_double[2] = (double)(2*2)/(double)(4*2*2-1);
    gqgenerategausslobattorec(&alpha, &beta, 2.0, (double)(-1), (double)(1), 4, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+ae_sqrt((double)(5), _state)/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]-ae_sqrt((double)(5), _state)/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[3]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(double)1/(double)6, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(double)5/(double)6, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(double)5/(double)6, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[3]-(double)1/(double)6, _state), _state);
        for(i=0; i<=2; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 5, _state);
    ae_vector_set_length(&beta, 5, _state);
    alpha.ptr.p_double[0] = (double)(0);
    alpha.ptr.p_double[1] = (double)(0);
    alpha.ptr.p_double[2] = (double)(0);
    alpha.ptr.p_double[3] = (double)(0);
    alpha.ptr.p_double[4] = (double)(0);
    beta.ptr.p_double[0] = (double)(0);
    beta.ptr.p_double[1] = (double)(1*1)/(double)(4*1*1-1);
    beta.ptr.p_double[2] = (double)(2*2)/(double)(4*2*2-1);
    beta.ptr.p_double[3] = (double)(3*3)/(double)(4*3*3-1);
    beta.ptr.p_double[4] = (double)(4*4)/(double)(4*4*4-1);
    gqgenerategausslobattorec(&alpha, &beta, 2.0, (double)(-1), (double)(1), 6, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+ae_sqrt((7+2*ae_sqrt((double)(7), _state))/21, _state), _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]+ae_sqrt((7-2*ae_sqrt((double)(7), _state))/21, _state), _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[3]-ae_sqrt((7-2*ae_sqrt((double)(7), _state))/21, _state), _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[4]-ae_sqrt((7+2*ae_sqrt((double)(7), _state))/21, _state), _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[5]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(double)1/(double)15, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(14-ae_sqrt((double)(7), _state))/30, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(14+ae_sqrt((double)(7), _state))/30, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[3]-(14+ae_sqrt((double)(7), _state))/30, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[4]-(14-ae_sqrt((double)(7), _state))/30, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[5]-(double)1/(double)15, _state), _state);
        for(i=0; i<=4; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    recerrors = recerrors||ae_fp_greater(err,errtol);
    
    /*
     * Three tests for rec-based Gauss-Radau quadratures with known weights/nodes:
     * 1. Gauss-Radau with N=2
     * 2. Gauss-Radau with N=3
     * 3. Gauss-Radau with N=3 (another case)
     */
    err = (double)(0);
    ae_vector_set_length(&alpha, 1, _state);
    ae_vector_set_length(&beta, 2, _state);
    alpha.ptr.p_double[0] = (double)(0);
    beta.ptr.p_double[0] = (double)(0);
    beta.ptr.p_double[1] = (double)(1*1)/(double)(4*1*1-1);
    gqgenerategaussradaurec(&alpha, &beta, 2.0, (double)(-1), 2, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]-(double)1/(double)3, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-0.5, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-1.5, _state), _state);
        for(i=0; i<=0; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 2, _state);
    ae_vector_set_length(&beta, 3, _state);
    alpha.ptr.p_double[0] = (double)(0);
    alpha.ptr.p_double[1] = (double)(0);
    for(i=0; i<=2; i++)
    {
        beta.ptr.p_double[i] = ae_sqr((double)(i), _state)/(4*ae_sqr((double)(i), _state)-1);
    }
    gqgenerategaussradaurec(&alpha, &beta, 2.0, (double)(-1), 3, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]-(1-ae_sqrt((double)(6), _state))/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]-(1+ae_sqrt((double)(6), _state))/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(double)2/(double)9, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(16+ae_sqrt((double)(6), _state))/18, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(16-ae_sqrt((double)(6), _state))/18, _state), _state);
        for(i=0; i<=1; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 2, _state);
    ae_vector_set_length(&beta, 3, _state);
    alpha.ptr.p_double[0] = (double)(0);
    alpha.ptr.p_double[1] = (double)(0);
    for(i=0; i<=2; i++)
    {
        beta.ptr.p_double[i] = ae_sqr((double)(i), _state)/(4*ae_sqr((double)(i), _state)-1);
    }
    gqgenerategaussradaurec(&alpha, &beta, 2.0, (double)(1), 3, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+(1-ae_sqrt((double)(6), _state))/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+(1+ae_sqrt((double)(6), _state))/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(double)2/(double)9, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(16+ae_sqrt((double)(6), _state))/18, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(16-ae_sqrt((double)(6), _state))/18, _state), _state);
        for(i=0; i<=1; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    recerrors = recerrors||ae_fp_greater(err,errtol);
    
    /*
     * test recurrence-based special cases (Legendre, Jacobi, Hermite, ...)
     * against another implementation (polynomial root-finder)
     */
    for(n=1; n<=20; n++)
    {
        
        /*
         * test gauss-legendre
         */
        err = (double)(0);
        gqgenerategausslegendre(n, &info, &x, &w, _state);
        if( info>0 )
        {
            testgqunit_buildgausslegendrequadrature(n, &x2, &w2, _state);
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-x2.ptr.p_double[i], _state), _state);
                err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
            }
        }
        else
        {
            specerrors = ae_true;
        }
        specerrors = specerrors||ae_fp_greater(err,errtol);
        
        /*
         * Test Gauss-Jacobi.
         * Since task is much more difficult we will use less strict
         * threshold.
         */
        err = (double)(0);
        for(akind=0; akind<=9; akind++)
        {
            for(bkind=0; bkind<=9; bkind++)
            {
                alphac = testgqunit_mapkind(akind, _state);
                betac = testgqunit_mapkind(bkind, _state);
                gqgenerategaussjacobi(n, alphac, betac, &info, &x, &w, _state);
                if( info>0 )
                {
                    testgqunit_buildgaussjacobiquadrature(n, alphac, betac, &x2, &w2, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-x2.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
                    }
                }
                else
                {
                    specerrors = ae_true;
                }
            }
        }
        specerrors = specerrors||ae_fp_greater(err,nonstricterrtol);
        
        /*
         * special test for Gauss-Jacobi (Chebyshev weight
         * function with analytically known nodes/weights)
         */
        err = (double)(0);
        gqgenerategaussjacobi(n, -0.5, -0.5, &info, &x, &w, _state);
        if( info>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]+ae_cos(ae_pi*(i+0.5)/n, _state), _state), _state);
                err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-ae_pi/n, _state), _state);
            }
        }
        else
        {
            specerrors = ae_true;
        }
        specerrors = specerrors||ae_fp_greater(err,stricterrtol);
        
        /*
         * Test Gauss-Laguerre
         */
        err = (double)(0);
        for(akind=0; akind<=9; akind++)
        {
            alphac = testgqunit_mapkind(akind, _state);
            gqgenerategausslaguerre(n, alphac, &info, &x, &w, _state);
            if( info>0 )
            {
                testgqunit_buildgausslaguerrequadrature(n, alphac, &x2, &w2, _state);
                for(i=0; i<=n-1; i++)
                {
                    err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-x2.ptr.p_double[i], _state), _state);
                    err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
                }
            }
            else
            {
                specerrors = ae_true;
            }
        }
        specerrors = specerrors||ae_fp_greater(err,nonstricterrtol);
        
        /*
         * Test Gauss-Hermite
         */
        err = (double)(0);
        gqgenerategausshermite(n, &info, &x, &w, _state);
        if( info>0 )
        {
            testgqunit_buildgausshermitequadrature(n, &x2, &w2, _state);
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-x2.ptr.p_double[i], _state), _state);
                err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
            }
        }
        else
        {
            specerrors = ae_true;
        }
        specerrors = specerrors||ae_fp_greater(err,nonstricterrtol);
    }
    
    /*
     * end
     */
    waserrors = recerrors||specerrors;
    if( !silent )
    {
        printf("TESTING GAUSS QUADRATURES\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SPECIAL CASES (LEGENDRE/JACOBI/..)      ");
        if( specerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* RECURRENCE-BASED:                       ");
        if( recerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Maps:
    0   =>  -0.9
    1   =>  -0.5
    2   =>  -0.1
    3   =>   0.0
    4   =>  +0.1
    5   =>  +0.5
    6   =>  +0.9
    7   =>  +1.0
    8   =>  +1.5
    9   =>  +2.0
*************************************************************************/
static double testgqunit_mapkind(ae_int_t k, ae_state *_state)
{
    double result;


    result = (double)(0);
    if( k==0 )
    {
        result = -0.9;
    }
    if( k==1 )
    {
        result = -0.5;
    }
    if( k==2 )
    {
        result = -0.1;
    }
    if( k==3 )
    {
        result = 0.0;
    }
    if( k==4 )
    {
        result = 0.1;
    }
    if( k==5 )
    {
        result = 0.5;
    }
    if( k==6 )
    {
        result = 0.9;
    }
    if( k==7 )
    {
        result = 1.0;
    }
    if( k==8 )
    {
        result = 1.5;
    }
    if( k==9 )
    {
        result = 2.0;
    }
    return result;
}


/*************************************************************************
Gauss-Legendre, another variant
*************************************************************************/
static void testgqunit_buildgausslegendrequadrature(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double r;
    double r1;
    double p1;
    double p2;
    double p3;
    double dp3;
    double tmp;

    ae_vector_clear(x);
    ae_vector_clear(w);

    ae_vector_set_length(x, n-1+1, _state);
    ae_vector_set_length(w, n-1+1, _state);
    for(i=0; i<=(n+1)/2-1; i++)
    {
        r = ae_cos(ae_pi*(4*i+3)/(4*n+2), _state);
        do
        {
            p2 = (double)(0);
            p3 = (double)(1);
            for(j=0; j<=n-1; j++)
            {
                p1 = p2;
                p2 = p3;
                p3 = ((2*j+1)*r*p2-j*p1)/(j+1);
            }
            dp3 = n*(r*p3-p2)/(r*r-1);
            r1 = r;
            r = r-p3/dp3;
        }
        while(ae_fp_greater_eq(ae_fabs(r-r1, _state),ae_machineepsilon*(1+ae_fabs(r, _state))*100));
        x->ptr.p_double[i] = r;
        x->ptr.p_double[n-1-i] = -r;
        w->ptr.p_double[i] = 2/((1-r*r)*dp3*dp3);
        w->ptr.p_double[n-1-i] = 2/((1-r*r)*dp3*dp3);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-2-i; j++)
        {
            if( ae_fp_greater_eq(x->ptr.p_double[j],x->ptr.p_double[j+1]) )
            {
                tmp = x->ptr.p_double[j];
                x->ptr.p_double[j] = x->ptr.p_double[j+1];
                x->ptr.p_double[j+1] = tmp;
                tmp = w->ptr.p_double[j];
                w->ptr.p_double[j] = w->ptr.p_double[j+1];
                w->ptr.p_double[j+1] = tmp;
            }
        }
    }
}


/*************************************************************************
Gauss-Jacobi, another variant
*************************************************************************/
static void testgqunit_buildgaussjacobiquadrature(ae_int_t n,
     double alpha,
     double beta,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double r;
    double r1;
    double t1;
    double t2;
    double t3;
    double p1;
    double p2;
    double p3;
    double pp;
    double an;
    double bn;
    double a;
    double b;
    double c;
    double tmpsgn;
    double tmp;
    double alfbet;
    double temp;

    ae_vector_clear(x);
    ae_vector_clear(w);

    ae_vector_set_length(x, n-1+1, _state);
    ae_vector_set_length(w, n-1+1, _state);
    r = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        if( i==0 )
        {
            an = alpha/n;
            bn = beta/n;
            t1 = (1+alpha)*(2.78/(4+n*n)+0.768*an/n);
            t2 = 1+1.48*an+0.96*bn+0.452*an*an+0.83*an*bn;
            r = (t2-t1)/t2;
        }
        else
        {
            if( i==1 )
            {
                t1 = (4.1+alpha)/((1+alpha)*(1+0.156*alpha));
                t2 = 1+0.06*(n-8)*(1+0.12*alpha)/n;
                t3 = 1+0.012*beta*(1+0.25*ae_fabs(alpha, _state))/n;
                r = r-t1*t2*t3*(1-r);
            }
            else
            {
                if( i==2 )
                {
                    t1 = (1.67+0.28*alpha)/(1+0.37*alpha);
                    t2 = 1+0.22*(n-8)/n;
                    t3 = 1+8*beta/((6.28+beta)*n*n);
                    r = r-t1*t2*t3*(x->ptr.p_double[0]-r);
                }
                else
                {
                    if( i<n-2 )
                    {
                        r = 3*x->ptr.p_double[i-1]-3*x->ptr.p_double[i-2]+x->ptr.p_double[i-3];
                    }
                    else
                    {
                        if( i==n-2 )
                        {
                            t1 = (1+0.235*beta)/(0.766+0.119*beta);
                            t2 = 1/(1+0.639*(n-4)/(1+0.71*(n-4)));
                            t3 = 1/(1+20*alpha/((7.5+alpha)*n*n));
                            r = r+t1*t2*t3*(r-x->ptr.p_double[i-2]);
                        }
                        else
                        {
                            if( i==n-1 )
                            {
                                t1 = (1+0.37*beta)/(1.67+0.28*beta);
                                t2 = 1/(1+0.22*(n-8)/n);
                                t3 = 1/(1+8*alpha/((6.28+alpha)*n*n));
                                r = r+t1*t2*t3*(r-x->ptr.p_double[i-2]);
                            }
                        }
                    }
                }
            }
        }
        alfbet = alpha+beta;
        do
        {
            temp = 2+alfbet;
            p1 = (alpha-beta+temp*r)*0.5;
            p2 = (double)(1);
            for(j=2; j<=n; j++)
            {
                p3 = p2;
                p2 = p1;
                temp = 2*j+alfbet;
                a = 2*j*(j+alfbet)*(temp-2);
                b = (temp-1)*(alpha*alpha-beta*beta+temp*(temp-2)*r);
                c = 2*(j-1+alpha)*(j-1+beta)*temp;
                p1 = (b*p2-c*p3)/a;
            }
            pp = (n*(alpha-beta-temp*r)*p1+2*(n+alpha)*(n+beta)*p2)/(temp*(1-r*r));
            r1 = r;
            r = r1-p1/pp;
        }
        while(ae_fp_greater_eq(ae_fabs(r-r1, _state),ae_machineepsilon*(1+ae_fabs(r, _state))*100));
        x->ptr.p_double[i] = r;
        w->ptr.p_double[i] = ae_exp(lngamma(alpha+n, &tmpsgn, _state)+lngamma(beta+n, &tmpsgn, _state)-lngamma((double)(n+1), &tmpsgn, _state)-lngamma(n+alfbet+1, &tmpsgn, _state), _state)*temp*ae_pow((double)(2), alfbet, _state)/(pp*p2);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-2-i; j++)
        {
            if( ae_fp_greater_eq(x->ptr.p_double[j],x->ptr.p_double[j+1]) )
            {
                tmp = x->ptr.p_double[j];
                x->ptr.p_double[j] = x->ptr.p_double[j+1];
                x->ptr.p_double[j+1] = tmp;
                tmp = w->ptr.p_double[j];
                w->ptr.p_double[j] = w->ptr.p_double[j+1];
                w->ptr.p_double[j+1] = tmp;
            }
        }
    }
}


/*************************************************************************
Gauss-Laguerre, another variant
*************************************************************************/
static void testgqunit_buildgausslaguerrequadrature(ae_int_t n,
     double alpha,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double r;
    double r1;
    double p1;
    double p2;
    double p3;
    double dp3;
    double tsg;
    double tmp;

    ae_vector_clear(x);
    ae_vector_clear(w);

    ae_vector_set_length(x, n-1+1, _state);
    ae_vector_set_length(w, n-1+1, _state);
    r = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        if( i==0 )
        {
            r = (1+alpha)*(3+0.92*alpha)/(1+2.4*n+1.8*alpha);
        }
        else
        {
            if( i==1 )
            {
                r = r+(15+6.25*alpha)/(1+0.9*alpha+2.5*n);
            }
            else
            {
                r = r+((1+2.55*(i-1))/(1.9*(i-1))+1.26*(i-1)*alpha/(1+3.5*(i-1)))/(1+0.3*alpha)*(r-x->ptr.p_double[i-2]);
            }
        }
        do
        {
            p2 = (double)(0);
            p3 = (double)(1);
            for(j=0; j<=n-1; j++)
            {
                p1 = p2;
                p2 = p3;
                p3 = ((-r+2*j+alpha+1)*p2-(j+alpha)*p1)/(j+1);
            }
            dp3 = (n*p3-(n+alpha)*p2)/r;
            r1 = r;
            r = r-p3/dp3;
        }
        while(ae_fp_greater_eq(ae_fabs(r-r1, _state),ae_machineepsilon*(1+ae_fabs(r, _state))*100));
        x->ptr.p_double[i] = r;
        w->ptr.p_double[i] = -ae_exp(lngamma(alpha+n, &tsg, _state)-lngamma((double)(n), &tsg, _state), _state)/(dp3*n*p2);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-2-i; j++)
        {
            if( ae_fp_greater_eq(x->ptr.p_double[j],x->ptr.p_double[j+1]) )
            {
                tmp = x->ptr.p_double[j];
                x->ptr.p_double[j] = x->ptr.p_double[j+1];
                x->ptr.p_double[j+1] = tmp;
                tmp = w->ptr.p_double[j];
                w->ptr.p_double[j] = w->ptr.p_double[j+1];
                w->ptr.p_double[j+1] = tmp;
            }
        }
    }
}


/*************************************************************************
Gauss-Hermite, another variant
*************************************************************************/
static void testgqunit_buildgausshermitequadrature(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double r;
    double r1;
    double p1;
    double p2;
    double p3;
    double dp3;
    double pipm4;
    double tmp;

    ae_vector_clear(x);
    ae_vector_clear(w);

    ae_vector_set_length(x, n-1+1, _state);
    ae_vector_set_length(w, n-1+1, _state);
    pipm4 = ae_pow(ae_pi, -0.25, _state);
    r = (double)(0);
    for(i=0; i<=(n+1)/2-1; i++)
    {
        if( i==0 )
        {
            r = ae_sqrt((double)(2*n+1), _state)-1.85575*ae_pow((double)(2*n+1), -(double)1/(double)6, _state);
        }
        else
        {
            if( i==1 )
            {
                r = r-1.14*ae_pow((double)(n), 0.426, _state)/r;
            }
            else
            {
                if( i==2 )
                {
                    r = 1.86*r-0.86*x->ptr.p_double[0];
                }
                else
                {
                    if( i==3 )
                    {
                        r = 1.91*r-0.91*x->ptr.p_double[1];
                    }
                    else
                    {
                        r = 2*r-x->ptr.p_double[i-2];
                    }
                }
            }
        }
        do
        {
            p2 = (double)(0);
            p3 = pipm4;
            for(j=0; j<=n-1; j++)
            {
                p1 = p2;
                p2 = p3;
                p3 = p2*r*ae_sqrt((double)2/(double)(j+1), _state)-p1*ae_sqrt((double)j/(double)(j+1), _state);
            }
            dp3 = ae_sqrt((double)(2*j), _state)*p2;
            r1 = r;
            r = r-p3/dp3;
        }
        while(ae_fp_greater_eq(ae_fabs(r-r1, _state),ae_machineepsilon*(1+ae_fabs(r, _state))*100));
        x->ptr.p_double[i] = r;
        w->ptr.p_double[i] = 2/(dp3*dp3);
        x->ptr.p_double[n-1-i] = -x->ptr.p_double[i];
        w->ptr.p_double[n-1-i] = w->ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-2-i; j++)
        {
            if( ae_fp_greater_eq(x->ptr.p_double[j],x->ptr.p_double[j+1]) )
            {
                tmp = x->ptr.p_double[j];
                x->ptr.p_double[j] = x->ptr.p_double[j+1];
                x->ptr.p_double[j+1] = tmp;
                tmp = w->ptr.p_double[j];
                w->ptr.p_double[j] = w->ptr.p_double[j+1];
                w->ptr.p_double[j+1] = tmp;
            }
        }
    }
}



static double testgkqunit_mapkind(ae_int_t k, ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testgkq(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pkind;
    double errtol;
    double eps;
    ae_int_t n;
    ae_int_t i;
    ae_int_t k;
    ae_int_t info;
    double err;
    ae_int_t akind;
    ae_int_t bkind;
    double alphac;
    double betac;
    ae_vector x1;
    ae_vector wg1;
    ae_vector wk1;
    ae_vector x2;
    ae_vector wg2;
    ae_vector wk2;
    ae_int_t info1;
    ae_int_t info2;
    ae_bool successatleastonce;
    ae_bool intblerrors;
    ae_bool vstblerrors;
    ae_bool generrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x1, 0, sizeof(x1));
    memset(&wg1, 0, sizeof(wg1));
    memset(&wk1, 0, sizeof(wk1));
    memset(&x2, 0, sizeof(x2));
    memset(&wg2, 0, sizeof(wg2));
    memset(&wk2, 0, sizeof(wk2));
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wg1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wk1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wg2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wk2, 0, DT_REAL, _state, ae_true);

    intblerrors = ae_false;
    vstblerrors = ae_false;
    generrors = ae_false;
    waserrors = ae_false;
    errtol = 10000*ae_machineepsilon;
    
    /*
     * test recurrence-based Legendre nodes against the precalculated table
     */
    for(pkind=0; pkind<=5; pkind++)
    {
        n = 0;
        if( pkind==0 )
        {
            n = 15;
        }
        if( pkind==1 )
        {
            n = 21;
        }
        if( pkind==2 )
        {
            n = 31;
        }
        if( pkind==3 )
        {
            n = 41;
        }
        if( pkind==4 )
        {
            n = 51;
        }
        if( pkind==5 )
        {
            n = 61;
        }
        gkqlegendrecalc(n, &info, &x1, &wk1, &wg1, _state);
        gkqlegendretbl(n, &x2, &wk2, &wg2, &eps, _state);
        if( info<=0 )
        {
            generrors = ae_true;
            break;
        }
        for(i=0; i<=n-1; i++)
        {
            vstblerrors = vstblerrors||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x2.ptr.p_double[i], _state),errtol);
            vstblerrors = vstblerrors||ae_fp_greater(ae_fabs(wk1.ptr.p_double[i]-wk2.ptr.p_double[i], _state),errtol);
            vstblerrors = vstblerrors||ae_fp_greater(ae_fabs(wg1.ptr.p_double[i]-wg2.ptr.p_double[i], _state),errtol);
        }
    }
    
    /*
     * Test recurrence-baced Gauss-Kronrod nodes against Gauss-only nodes
     * calculated with subroutines from GQ unit.
     */
    for(k=1; k<=30; k++)
    {
        n = 2*k+1;
        
        /*
         * Gauss-Legendre
         */
        err = (double)(0);
        gkqgenerategausslegendre(n, &info1, &x1, &wk1, &wg1, _state);
        gqgenerategausslegendre(k, &info2, &x2, &wg2, _state);
        if( info1>0&&info2>0 )
        {
            for(i=0; i<=k-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x1.ptr.p_double[2*i+1]-x2.ptr.p_double[i], _state), _state);
                err = ae_maxreal(err, ae_fabs(wg1.ptr.p_double[2*i+1]-wg2.ptr.p_double[i], _state), _state);
            }
        }
        else
        {
            generrors = ae_true;
        }
        generrors = generrors||ae_fp_greater(err,errtol);
    }
    for(k=1; k<=15; k++)
    {
        n = 2*k+1;
        
        /*
         * Gauss-Jacobi
         */
        successatleastonce = ae_false;
        err = (double)(0);
        for(akind=0; akind<=9; akind++)
        {
            for(bkind=0; bkind<=9; bkind++)
            {
                alphac = testgkqunit_mapkind(akind, _state);
                betac = testgkqunit_mapkind(bkind, _state);
                gkqgenerategaussjacobi(n, alphac, betac, &info1, &x1, &wk1, &wg1, _state);
                gqgenerategaussjacobi(k, alphac, betac, &info2, &x2, &wg2, _state);
                if( info1>0&&info2>0 )
                {
                    successatleastonce = ae_true;
                    for(i=0; i<=k-1; i++)
                    {
                        err = ae_maxreal(err, ae_fabs(x1.ptr.p_double[2*i+1]-x2.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(wg1.ptr.p_double[2*i+1]-wg2.ptr.p_double[i], _state), _state);
                    }
                }
                else
                {
                    generrors = generrors||info1!=-5;
                }
            }
        }
        generrors = (generrors||ae_fp_greater(err,errtol))||!successatleastonce;
    }
    
    /*
     * end
     */
    waserrors = (intblerrors||vstblerrors)||generrors;
    if( !silent )
    {
        printf("TESTING GAUSS-KRONROD QUADRATURES\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* PRE-CALCULATED TABLE:                   ");
        if( intblerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* CALCULATED AGAINST THE TABLE:           ");
        if( vstblerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* GENERAL PROPERTIES:                     ");
        if( generrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Maps:
    0   =>  -0.9
    1   =>  -0.5
    2   =>  -0.1
    3   =>   0.0
    4   =>  +0.1
    5   =>  +0.5
    6   =>  +0.9
    7   =>  +1.0
    8   =>  +1.5
    9   =>  +2.0
*************************************************************************/
static double testgkqunit_mapkind(ae_int_t k, ae_state *_state)
{
    double result;


    result = (double)(0);
    if( k==0 )
    {
        result = -0.9;
    }
    if( k==1 )
    {
        result = -0.5;
    }
    if( k==2 )
    {
        result = -0.1;
    }
    if( k==3 )
    {
        result = 0.0;
    }
    if( k==4 )
    {
        result = 0.1;
    }
    if( k==5 )
    {
        result = 0.5;
    }
    if( k==6 )
    {
        result = 0.9;
    }
    if( k==7 )
    {
        result = 1.0;
    }
    if( k==8 )
    {
        result = 1.5;
    }
    if( k==9 )
    {
        result = 2.0;
    }
    return result;
}








/*************************************************************************
Test
*************************************************************************/
ae_bool testautogk(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double a;
    double b;
    autogkstate state;
    autogkreport rep;
    double v;
    double exact;
    double eabs;
    double alpha;
    ae_int_t pkind;
    double errtol;
    ae_bool simpleerrors;
    ae_bool sngenderrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    _autogkstate_init(&state, _state, ae_true);
    _autogkreport_init(&rep, _state, ae_true);

    simpleerrors = ae_false;
    sngenderrors = ae_false;
    waserrors = ae_false;
    errtol = 10000*ae_machineepsilon;
    
    /*
     * Simple test: integral(exp(x),+-1,+-2), no maximum width requirements
     */
    a = (2*ae_randominteger(2, _state)-1)*1.0;
    b = (2*ae_randominteger(2, _state)-1)*2.0;
    autogksmooth(a, b, &state, _state);
    while(autogkiteration(&state, _state))
    {
        state.f = ae_exp(state.x, _state);
    }
    autogkresults(&state, &v, &rep, _state);
    exact = ae_exp(b, _state)-ae_exp(a, _state);
    eabs = ae_fabs(ae_exp(b, _state)-ae_exp(a, _state), _state);
    if( rep.terminationtype<=0 )
    {
        simpleerrors = ae_true;
    }
    else
    {
        simpleerrors = simpleerrors||ae_fp_greater(ae_fabs(exact-v, _state),errtol*eabs);
    }
    
    /*
     * Simple test: integral(exp(x),+-1,+-2), XWidth=0.1
     */
    a = (2*ae_randominteger(2, _state)-1)*1.0;
    b = (2*ae_randominteger(2, _state)-1)*2.0;
    autogksmoothw(a, b, 0.1, &state, _state);
    while(autogkiteration(&state, _state))
    {
        state.f = ae_exp(state.x, _state);
    }
    autogkresults(&state, &v, &rep, _state);
    exact = ae_exp(b, _state)-ae_exp(a, _state);
    eabs = ae_fabs(ae_exp(b, _state)-ae_exp(a, _state), _state);
    if( rep.terminationtype<=0 )
    {
        simpleerrors = ae_true;
    }
    else
    {
        simpleerrors = simpleerrors||ae_fp_greater(ae_fabs(exact-v, _state),errtol*eabs);
    }
    
    /*
     * Simple test: integral(cos(100*x),0,2*pi), no maximum width requirements
     */
    a = (double)(0);
    b = 2*ae_pi;
    autogksmooth(a, b, &state, _state);
    while(autogkiteration(&state, _state))
    {
        state.f = ae_cos(100*state.x, _state);
    }
    autogkresults(&state, &v, &rep, _state);
    exact = (double)(0);
    eabs = (double)(4);
    if( rep.terminationtype<=0 )
    {
        simpleerrors = ae_true;
    }
    else
    {
        simpleerrors = simpleerrors||ae_fp_greater(ae_fabs(exact-v, _state),errtol*eabs);
    }
    
    /*
     * Simple test: integral(cos(100*x),0,2*pi), XWidth=0.3
     */
    a = (double)(0);
    b = 2*ae_pi;
    autogksmoothw(a, b, 0.3, &state, _state);
    while(autogkiteration(&state, _state))
    {
        state.f = ae_cos(100*state.x, _state);
    }
    autogkresults(&state, &v, &rep, _state);
    exact = (double)(0);
    eabs = (double)(4);
    if( rep.terminationtype<=0 )
    {
        simpleerrors = ae_true;
    }
    else
    {
        simpleerrors = simpleerrors||ae_fp_greater(ae_fabs(exact-v, _state),errtol*eabs);
    }
    
    /*
     * singular problem on [a,b] = [0.1, 0.5]
     *     f2(x) = (1+x)*(b-x)^alpha, -1 < alpha < 1
     */
    for(pkind=0; pkind<=6; pkind++)
    {
        a = 0.1;
        b = 0.5;
        alpha = 0.0;
        if( pkind==0 )
        {
            alpha = -0.9;
        }
        if( pkind==1 )
        {
            alpha = -0.5;
        }
        if( pkind==2 )
        {
            alpha = -0.1;
        }
        if( pkind==3 )
        {
            alpha = 0.0;
        }
        if( pkind==4 )
        {
            alpha = 0.1;
        }
        if( pkind==5 )
        {
            alpha = 0.5;
        }
        if( pkind==6 )
        {
            alpha = 0.9;
        }
        
        /*
         * f1(x) = (1+x)*(x-a)^alpha, -1 < alpha < 1
         * 1. use singular integrator for [a,b]
         * 2. use singular integrator for [b,a]
         */
        exact = ae_pow(b-a, alpha+2, _state)/(alpha+2)+(1+a)*ae_pow(b-a, alpha+1, _state)/(alpha+1);
        eabs = ae_fabs(exact, _state);
        autogksingular(a, b, alpha, 0.0, &state, _state);
        while(autogkiteration(&state, _state))
        {
            if( ae_fp_less(state.xminusa,0.01) )
            {
                state.f = ae_pow(state.xminusa, alpha, _state)*(1+state.x);
            }
            else
            {
                state.f = ae_pow(state.x-a, alpha, _state)*(1+state.x);
            }
        }
        autogkresults(&state, &v, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            sngenderrors = ae_true;
        }
        else
        {
            sngenderrors = sngenderrors||ae_fp_greater(ae_fabs(v-exact, _state),errtol*eabs);
        }
        autogksingular(b, a, 0.0, alpha, &state, _state);
        while(autogkiteration(&state, _state))
        {
            if( ae_fp_greater(state.bminusx,-0.01) )
            {
                state.f = ae_pow(-state.bminusx, alpha, _state)*(1+state.x);
            }
            else
            {
                state.f = ae_pow(state.x-a, alpha, _state)*(1+state.x);
            }
        }
        autogkresults(&state, &v, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            sngenderrors = ae_true;
        }
        else
        {
            sngenderrors = sngenderrors||ae_fp_greater(ae_fabs(-v-exact, _state),errtol*eabs);
        }
        
        /*
         * f1(x) = (1+x)*(b-x)^alpha, -1 < alpha < 1
         * 1. use singular integrator for [a,b]
         * 2. use singular integrator for [b,a]
         */
        exact = (1+b)*ae_pow(b-a, alpha+1, _state)/(alpha+1)-ae_pow(b-a, alpha+2, _state)/(alpha+2);
        eabs = ae_fabs(exact, _state);
        autogksingular(a, b, 0.0, alpha, &state, _state);
        while(autogkiteration(&state, _state))
        {
            if( ae_fp_less(state.bminusx,0.01) )
            {
                state.f = ae_pow(state.bminusx, alpha, _state)*(1+state.x);
            }
            else
            {
                state.f = ae_pow(b-state.x, alpha, _state)*(1+state.x);
            }
        }
        autogkresults(&state, &v, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            sngenderrors = ae_true;
        }
        else
        {
            sngenderrors = sngenderrors||ae_fp_greater(ae_fabs(v-exact, _state),errtol*eabs);
        }
        autogksingular(b, a, alpha, 0.0, &state, _state);
        while(autogkiteration(&state, _state))
        {
            if( ae_fp_greater(state.xminusa,-0.01) )
            {
                state.f = ae_pow(-state.xminusa, alpha, _state)*(1+state.x);
            }
            else
            {
                state.f = ae_pow(b-state.x, alpha, _state)*(1+state.x);
            }
        }
        autogkresults(&state, &v, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            sngenderrors = ae_true;
        }
        else
        {
            sngenderrors = sngenderrors||ae_fp_greater(ae_fabs(-v-exact, _state),errtol*eabs);
        }
    }
    
    /*
     * end
     */
    waserrors = simpleerrors||sngenderrors;
    if( !silent )
    {
        printf("TESTING AUTOGK\n");
        printf("INTEGRATION WITH GIVEN ACCURACY:          ");
        if( simpleerrors||sngenderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SIMPLE PROBLEMS:                        ");
        if( simpleerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SINGULAR PROBLEMS (ENDS OF INTERVAL):   ");
        if( sngenderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testfftunit_reffftc1d(/* Complex */ ae_vector* a,
     ae_int_t n,
     ae_state *_state);
static void testfftunit_reffftc1dinv(/* Complex */ ae_vector* a,
     ae_int_t n,
     ae_state *_state);
static void testfftunit_refinternalcfft(/* Real    */ ae_vector* a,
     ae_int_t nn,
     ae_bool inversefft,
     ae_state *_state);
static void testfftunit_refinternalrfft(/* Real    */ ae_vector* a,
     ae_int_t nn,
     /* Complex */ ae_vector* f,
     ae_state *_state);
static void testfftunit_quicktest(ae_int_t n,
     double* referr,
     double* refrerr,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testfft(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t k;
    ae_vector a1;
    ae_vector a2;
    ae_vector a3;
    ae_vector r1;
    ae_vector r2;
    ae_vector buf;
    fasttransformplan plan;
    ae_int_t maxsmalln;
    double bidierr;
    double bidirerr;
    double referr;
    double refrerr;
    double reinterr;
    double errtol;
    ae_bool referrors;
    ae_bool bidierrors;
    ae_bool refrerrors;
    ae_bool bidirerrors;
    ae_bool reinterrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a1, 0, sizeof(a1));
    memset(&a2, 0, sizeof(a2));
    memset(&a3, 0, sizeof(a3));
    memset(&r1, 0, sizeof(r1));
    memset(&r2, 0, sizeof(r2));
    memset(&buf, 0, sizeof(buf));
    memset(&plan, 0, sizeof(plan));
    ae_vector_init(&a1, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&a2, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&a3, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&r1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);
    _fasttransformplan_init(&plan, _state, ae_true);

    maxsmalln = 128;
    errtol = 100000*ae_pow((double)(maxsmalln), (double)3/(double)2, _state)*ae_machineepsilon;
    bidierrors = ae_false;
    referrors = ae_false;
    bidirerrors = ae_false;
    refrerrors = ae_false;
    reinterrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Test bi-directional error: norm(x-invFFT(FFT(x)))
     */
    bidierr = (double)(0);
    bidirerr = (double)(0);
    for(n=1; n<=maxsmalln; n++)
    {
        
        /*
         * Complex FFT/invFFT
         */
        ae_vector_set_length(&a1, n, _state);
        ae_vector_set_length(&a2, n, _state);
        ae_vector_set_length(&a3, n, _state);
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            a1.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            a2.ptr.p_complex[i] = a1.ptr.p_complex[i];
            a3.ptr.p_complex[i] = a1.ptr.p_complex[i];
        }
        fftc1d(&a2, n, _state);
        fftc1dinv(&a2, n, _state);
        fftc1dinv(&a3, n, _state);
        fftc1d(&a3, n, _state);
        for(i=0; i<=n-1; i++)
        {
            bidierr = ae_maxreal(bidierr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a2.ptr.p_complex[i]), _state), _state);
            bidierr = ae_maxreal(bidierr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a3.ptr.p_complex[i]), _state), _state);
        }
        
        /*
         * Real
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fftr1d(&r2, n, &a1, _state);
        ae_v_muld(&r2.ptr.p_double[0], 1, ae_v_len(0,n-1), 0);
        fftr1dinv(&a1, n, &r2, _state);
        for(i=0; i<=n-1; i++)
        {
            bidirerr = ae_maxreal(bidirerr, ae_c_abs(ae_complex_from_d(r1.ptr.p_double[i]-r2.ptr.p_double[i]), _state), _state);
        }
    }
    bidierrors = bidierrors||ae_fp_greater(bidierr,errtol);
    bidirerrors = bidirerrors||ae_fp_greater(bidirerr,errtol);
    
    /*
     * Test against reference O(N^2) implementation for small N's
     * (we do not test large N's because reference implementation will be too slow).
     */
    referr = (double)(0);
    refrerr = (double)(0);
    for(n=1; n<=maxsmalln; n++)
    {
        
        /*
         * Complex FFT
         */
        ae_vector_set_length(&a1, n, _state);
        ae_vector_set_length(&a2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            a1.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            a2.ptr.p_complex[i] = a1.ptr.p_complex[i];
        }
        fftc1d(&a1, n, _state);
        testfftunit_reffftc1d(&a2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a2.ptr.p_complex[i]), _state), _state);
        }
        
        /*
         * Complex inverse FFT
         */
        ae_vector_set_length(&a1, n, _state);
        ae_vector_set_length(&a2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            a1.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            a2.ptr.p_complex[i] = a1.ptr.p_complex[i];
        }
        fftc1dinv(&a1, n, _state);
        testfftunit_reffftc1dinv(&a2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a2.ptr.p_complex[i]), _state), _state);
        }
        
        /*
         * Real forward/inverse FFT:
         * * calculate and check forward FFT
         * * use precalculated FFT to check backward FFT
         *   fill unused parts of frequencies array with random numbers
         *   to ensure that they are not really used
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fftr1d(&r1, n, &a1, _state);
        testfftunit_refinternalrfft(&r2, n, &a2, _state);
        for(i=0; i<=n-1; i++)
        {
            refrerr = ae_maxreal(refrerr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a2.ptr.p_complex[i]), _state), _state);
        }
        ae_vector_set_length(&a3, ae_ifloor((double)n/(double)2, _state)+1, _state);
        for(i=0; i<=ae_ifloor((double)n/(double)2, _state); i++)
        {
            a3.ptr.p_complex[i] = a2.ptr.p_complex[i];
        }
        a3.ptr.p_complex[0].y = 2*ae_randomreal(_state)-1;
        if( n%2==0 )
        {
            a3.ptr.p_complex[ae_ifloor((double)n/(double)2, _state)].y = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = (double)(0);
        }
        fftr1dinv(&a3, n, &r1, _state);
        for(i=0; i<=n-1; i++)
        {
            refrerr = ae_maxreal(refrerr, ae_fabs(r2.ptr.p_double[i]-r1.ptr.p_double[i], _state), _state);
        }
    }
    referrors = referrors||ae_fp_greater(referr,errtol);
    refrerrors = refrerrors||ae_fp_greater(refrerr,errtol);
    
    /*
     * Test for large N's:
     * * we perform FFT
     * * we selectively calculate K (small number) of DFT components (using reference formula)
     *   and compare them with ones calculated by fast implementation
     * * K components to test are chosen at random (random sampling with possible repetitions)
     * * overall complexity of the test is O(N*logN+K*N)
     * Several N's are tested, with different kinds of factorizations
     */
    referr = (double)(0);
    refrerr = (double)(0);
    testfftunit_quicktest(1000, &referr, &refrerr, _state);
    testfftunit_quicktest(1024, &referr, &refrerr, _state);
    testfftunit_quicktest(1025, &referr, &refrerr, _state);
    testfftunit_quicktest(2000, &referr, &refrerr, _state);
    testfftunit_quicktest(2048, &referr, &refrerr, _state);
    testfftunit_quicktest(6535, &referr, &refrerr, _state);
    testfftunit_quicktest(65536, &referr, &refrerr, _state);
    testfftunit_quicktest(104729, &referr, &refrerr, _state);
    testfftunit_quicktest(139129, &referr, &refrerr, _state);
    testfftunit_quicktest(141740, &referr, &refrerr, _state);
    referrors = referrors||ae_fp_greater(referr,errtol);
    refrerrors = refrerrors||ae_fp_greater(refrerr,errtol);
    
    /*
     * test internal real even FFT
     */
    reinterr = (double)(0);
    for(k=1; k<=maxsmalln/2; k++)
    {
        n = 2*k;
        
        /*
         * Real forward FFT
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        ftcomplexfftplan(n/2, 1, &plan, _state);
        ae_vector_set_length(&buf, n, _state);
        fftr1dinternaleven(&r1, n, &buf, &plan, _state);
        testfftunit_refinternalrfft(&r2, n, &a2, _state);
        reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[0]-a2.ptr.p_complex[0].x, _state), _state);
        reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[1]-a2.ptr.p_complex[n/2].x, _state), _state);
        for(i=1; i<=n/2-1; i++)
        {
            reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[2*i+0]-a2.ptr.p_complex[i].x, _state), _state);
            reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[2*i+1]-a2.ptr.p_complex[i].y, _state), _state);
        }
        
        /*
         * Real backward FFT
         */
        ae_vector_set_length(&r1, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        ae_vector_set_length(&a2, ae_ifloor((double)n/(double)2, _state)+1, _state);
        a2.ptr.p_complex[0] = ae_complex_from_d(r1.ptr.p_double[0]);
        for(i=1; i<=ae_ifloor((double)n/(double)2, _state)-1; i++)
        {
            a2.ptr.p_complex[i].x = r1.ptr.p_double[2*i+0];
            a2.ptr.p_complex[i].y = r1.ptr.p_double[2*i+1];
        }
        a2.ptr.p_complex[ae_ifloor((double)n/(double)2, _state)] = ae_complex_from_d(r1.ptr.p_double[1]);
        ftcomplexfftplan(n/2, 1, &plan, _state);
        ae_vector_set_length(&buf, n, _state);
        fftr1dinvinternaleven(&r1, n, &buf, &plan, _state);
        fftr1dinv(&a2, n, &r2, _state);
        for(i=0; i<=n-1; i++)
        {
            reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[i]-r2.ptr.p_double[i], _state), _state);
        }
    }
    reinterrors = reinterrors||ae_fp_greater(reinterr,errtol);
    
    /*
     * end
     */
    waserrors = (((bidierrors||bidirerrors)||referrors)||refrerrors)||reinterrors;
    if( !silent )
    {
        printf("TESTING FFT\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* BI-DIRECTIONAL COMPLEX TEST:            ");
        if( bidierrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE COMPLEX FFT:          ");
        if( referrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* BI-DIRECTIONAL REAL TEST:               ");
        if( bidirerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE REAL FFT:             ");
        if( refrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* INTERNAL EVEN FFT:                      ");
        if( reinterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference FFT
*************************************************************************/
static void testfftunit_reffftc1d(/* Complex */ ae_vector* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector buf;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&buf, 0, sizeof(buf));
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);

    ae_assert(n>0, "FFTC1D: incorrect N!", _state);
    ae_vector_set_length(&buf, 2*n, _state);
    for(i=0; i<=n-1; i++)
    {
        buf.ptr.p_double[2*i+0] = a->ptr.p_complex[i].x;
        buf.ptr.p_double[2*i+1] = a->ptr.p_complex[i].y;
    }
    testfftunit_refinternalcfft(&buf, n, ae_false, _state);
    for(i=0; i<=n-1; i++)
    {
        a->ptr.p_complex[i].x = buf.ptr.p_double[2*i+0];
        a->ptr.p_complex[i].y = buf.ptr.p_double[2*i+1];
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference inverse FFT
*************************************************************************/
static void testfftunit_reffftc1dinv(/* Complex */ ae_vector* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector buf;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&buf, 0, sizeof(buf));
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);

    ae_assert(n>0, "FFTC1DInv: incorrect N!", _state);
    ae_vector_set_length(&buf, 2*n, _state);
    for(i=0; i<=n-1; i++)
    {
        buf.ptr.p_double[2*i+0] = a->ptr.p_complex[i].x;
        buf.ptr.p_double[2*i+1] = a->ptr.p_complex[i].y;
    }
    testfftunit_refinternalcfft(&buf, n, ae_true, _state);
    for(i=0; i<=n-1; i++)
    {
        a->ptr.p_complex[i].x = buf.ptr.p_double[2*i+0];
        a->ptr.p_complex[i].y = buf.ptr.p_double[2*i+1];
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Internal complex FFT stub.
Uses straightforward formula with O(N^2) complexity.
*************************************************************************/
static void testfftunit_refinternalcfft(/* Real    */ ae_vector* a,
     ae_int_t nn,
     ae_bool inversefft,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector tmp;
    ae_int_t i;
    ae_int_t k;
    double hre;
    double him;
    double c;
    double s;
    double re;
    double im;

    ae_frame_make(_state, &_frame_block);
    memset(&tmp, 0, sizeof(tmp));
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&tmp, 2*nn-1+1, _state);
    if( !inversefft )
    {
        for(i=0; i<=nn-1; i++)
        {
            hre = (double)(0);
            him = (double)(0);
            for(k=0; k<=nn-1; k++)
            {
                re = a->ptr.p_double[2*k];
                im = a->ptr.p_double[2*k+1];
                c = ae_cos(-2*ae_pi*k*i/nn, _state);
                s = ae_sin(-2*ae_pi*k*i/nn, _state);
                hre = hre+c*re-s*im;
                him = him+c*im+s*re;
            }
            tmp.ptr.p_double[2*i] = hre;
            tmp.ptr.p_double[2*i+1] = him;
        }
        for(i=0; i<=2*nn-1; i++)
        {
            a->ptr.p_double[i] = tmp.ptr.p_double[i];
        }
    }
    else
    {
        for(k=0; k<=nn-1; k++)
        {
            hre = (double)(0);
            him = (double)(0);
            for(i=0; i<=nn-1; i++)
            {
                re = a->ptr.p_double[2*i];
                im = a->ptr.p_double[2*i+1];
                c = ae_cos(2*ae_pi*k*i/nn, _state);
                s = ae_sin(2*ae_pi*k*i/nn, _state);
                hre = hre+c*re-s*im;
                him = him+c*im+s*re;
            }
            tmp.ptr.p_double[2*k] = hre/nn;
            tmp.ptr.p_double[2*k+1] = him/nn;
        }
        for(i=0; i<=2*nn-1; i++)
        {
            a->ptr.p_double[i] = tmp.ptr.p_double[i];
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Internal real FFT stub.
Uses straightforward formula with O(N^2) complexity.
*************************************************************************/
static void testfftunit_refinternalrfft(/* Real    */ ae_vector* a,
     ae_int_t nn,
     /* Complex */ ae_vector* f,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector tmp;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    memset(&tmp, 0, sizeof(tmp));
    ae_vector_clear(f);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&tmp, 2*nn-1+1, _state);
    for(i=0; i<=nn-1; i++)
    {
        tmp.ptr.p_double[2*i] = a->ptr.p_double[i];
        tmp.ptr.p_double[2*i+1] = (double)(0);
    }
    testfftunit_refinternalcfft(&tmp, nn, ae_false, _state);
    ae_vector_set_length(f, nn, _state);
    for(i=0; i<=nn-1; i++)
    {
        f->ptr.p_complex[i].x = tmp.ptr.p_double[2*i+0];
        f->ptr.p_complex[i].y = tmp.ptr.p_double[2*i+1];
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function performs real/complex FFT of given length on random data,
selects K random components and compares them with values calculated by
DFT definition.

It updates RefErr and RefRErr as follows:
    RefErr:=  max(RefErr, error_of_complex_FFT)
    RefRErr:= max(RefRErr,error_of_real_FFT)

*************************************************************************/
static void testfftunit_quicktest(ae_int_t n,
     double* referr,
     double* refrerr,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector a0;
    ae_vector a1;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t idx;
    ae_complex v;
    double c;
    double s;
    double re;
    double im;

    ae_frame_make(_state, &_frame_block);
    memset(&a0, 0, sizeof(a0));
    memset(&a1, 0, sizeof(a1));
    ae_vector_init(&a0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&a1, 0, DT_COMPLEX, _state, ae_true);

    k = 10;
    
    /*
     * Complex FFT - forward and inverse
     */
    ae_vector_set_length(&a0, n, _state);
    ae_vector_set_length(&a1, n, _state);
    for(i=0; i<=n-1; i++)
    {
        a0.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
        a0.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        a1.ptr.p_complex[i] = a0.ptr.p_complex[i];
    }
    fftc1d(&a0, n, _state);
    for(i=0; i<=k-1; i++)
    {
        idx = ae_randominteger(n, _state);
        v = ae_complex_from_i(0);
        for(j=0; j<=n-1; j++)
        {
            re = a1.ptr.p_complex[j].x;
            im = a1.ptr.p_complex[j].y;
            c = ae_cos(-2*ae_pi*j*idx/n, _state);
            s = ae_sin(-2*ae_pi*j*idx/n, _state);
            v.x = v.x+c*re-s*im;
            v.y = v.y+c*im+s*re;
        }
        *referr = ae_maxreal(*referr, ae_c_abs(ae_c_sub(v,a0.ptr.p_complex[idx]), _state), _state);
    }
    fftc1dinv(&a0, n, _state);
    for(i=0; i<=n-1; i++)
    {
        *referr = ae_maxreal(*referr, ae_c_abs(ae_c_sub(a0.ptr.p_complex[i],a1.ptr.p_complex[i]), _state), _state);
    }
    ae_frame_leave(_state);
}



static void testfhtunit_reffhtr1d(/* Real    */ ae_vector* a,
     ae_int_t n,
     ae_state *_state);
static void testfhtunit_reffhtr1dinv(/* Real    */ ae_vector* a,
     ae_int_t n,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testfht(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_vector r1;
    ae_vector r2;
    ae_vector r3;
    ae_int_t maxn;
    double bidierr;
    double referr;
    double errtol;
    ae_bool referrors;
    ae_bool bidierrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&r1, 0, sizeof(r1));
    memset(&r2, 0, sizeof(r2));
    memset(&r3, 0, sizeof(r3));
    ae_vector_init(&r1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r3, 0, DT_REAL, _state, ae_true);

    maxn = 128;
    errtol = 100000*ae_pow((double)(maxn), (double)3/(double)2, _state)*ae_machineepsilon;
    bidierrors = ae_false;
    referrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Test bi-directional error: norm(x-invFHT(FHT(x)))
     */
    bidierr = (double)(0);
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * FHT/invFHT
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        ae_vector_set_length(&r3, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
            r3.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fhtr1d(&r2, n, _state);
        fhtr1dinv(&r2, n, _state);
        fhtr1dinv(&r3, n, _state);
        fhtr1d(&r3, n, _state);
        for(i=0; i<=n-1; i++)
        {
            bidierr = ae_maxreal(bidierr, ae_fabs(r1.ptr.p_double[i]-r2.ptr.p_double[i], _state), _state);
            bidierr = ae_maxreal(bidierr, ae_fabs(r1.ptr.p_double[i]-r3.ptr.p_double[i], _state), _state);
        }
    }
    bidierrors = bidierrors||ae_fp_greater(bidierr,errtol);
    
    /*
     * Test against reference O(N^2) implementation
     */
    referr = (double)(0);
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * FHT
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fhtr1d(&r1, n, _state);
        testfhtunit_reffhtr1d(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            referr = ae_maxreal(referr, ae_fabs(r1.ptr.p_double[i]-r2.ptr.p_double[i], _state), _state);
        }
        
        /*
         * inverse FHT
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fhtr1dinv(&r1, n, _state);
        testfhtunit_reffhtr1dinv(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            referr = ae_maxreal(referr, ae_fabs(r1.ptr.p_double[i]-r2.ptr.p_double[i], _state), _state);
        }
    }
    referrors = referrors||ae_fp_greater(referr,errtol);
    
    /*
     * end
     */
    waserrors = bidierrors||referrors;
    if( !silent )
    {
        printf("TESTING FHT\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* BI-DIRECTIONAL TEST:                    ");
        if( bidierrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE FHT:                  ");
        if( referrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference FHT
*************************************************************************/
static void testfhtunit_reffhtr1d(/* Real    */ ae_vector* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector buf;
    ae_int_t i;
    ae_int_t j;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&buf, 0, sizeof(buf));
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);

    ae_assert(n>0, "RefFHTR1D: incorrect N!", _state);
    ae_vector_set_length(&buf, n, _state);
    for(i=0; i<=n-1; i++)
    {
        v = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            v = v+a->ptr.p_double[j]*(ae_cos(2*ae_pi*i*j/n, _state)+ae_sin(2*ae_pi*i*j/n, _state));
        }
        buf.ptr.p_double[i] = v;
    }
    for(i=0; i<=n-1; i++)
    {
        a->ptr.p_double[i] = buf.ptr.p_double[i];
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference inverse FHT
*************************************************************************/
static void testfhtunit_reffhtr1dinv(/* Real    */ ae_vector* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(n>0, "RefFHTR1DInv: incorrect N!", _state);
    testfhtunit_reffhtr1d(a, n, _state);
    for(i=0; i<=n-1; i++)
    {
        a->ptr.p_double[i] = a->ptr.p_double[i]/n;
    }
}



static void testconvunit_refconvc1d(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testconvunit_refconvc1dcircular(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testconvunit_refconvr1d(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state);
static void testconvunit_refconvr1dcircular(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testconv(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t i;
    ae_int_t rkind;
    ae_int_t circkind;
    ae_vector ra;
    ae_vector rb;
    ae_vector rr1;
    ae_vector rr2;
    ae_vector ca;
    ae_vector cb;
    ae_vector cr1;
    ae_vector cr2;
    ae_int_t maxn;
    double referr;
    double refrerr;
    double inverr;
    double invrerr;
    double errtol;
    ae_bool referrors;
    ae_bool refrerrors;
    ae_bool inverrors;
    ae_bool invrerrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ra, 0, sizeof(ra));
    memset(&rb, 0, sizeof(rb));
    memset(&rr1, 0, sizeof(rr1));
    memset(&rr2, 0, sizeof(rr2));
    memset(&ca, 0, sizeof(ca));
    memset(&cb, 0, sizeof(cb));
    memset(&cr1, 0, sizeof(cr1));
    memset(&cr2, 0, sizeof(cr2));
    ae_vector_init(&ra, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rr1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rr2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ca, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cb, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cr1, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cr2, 0, DT_COMPLEX, _state, ae_true);

    maxn = 32;
    errtol = 100000*ae_pow((double)(maxn), (double)3/(double)2, _state)*ae_machineepsilon;
    referrors = ae_false;
    refrerrors = ae_false;
    inverrors = ae_false;
    invrerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Test against reference O(N^2) implementation.
     *
     * Automatic ConvC1D() and different algorithms of ConvC1DX() are tested.
     */
    referr = (double)(0);
    refrerr = (double)(0);
    for(m=1; m<=maxn; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(circkind=0; circkind<=1; circkind++)
            {
                for(rkind=-3; rkind<=1; rkind++)
                {
                    
                    /*
                     * skip impossible combinations of parameters:
                     * * circular convolution, M<N, RKind<>-3 - internal subroutine does not support M<N.
                     */
                    if( (circkind!=0&&m<n)&&rkind!=-3 )
                    {
                        continue;
                    }
                    
                    /*
                     * Complex convolution
                     */
                    ae_vector_set_length(&ca, m, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        ca.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                        ca.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                    }
                    ae_vector_set_length(&cb, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        cb.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                        cb.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                    }
                    ae_vector_set_length(&cr1, 1, _state);
                    if( rkind==-3 )
                    {
                        
                        /*
                         * test wrapper subroutine:
                         * * circular/non-circular
                         */
                        if( circkind==0 )
                        {
                            convc1d(&ca, m, &cb, n, &cr1, _state);
                        }
                        else
                        {
                            convc1dcircular(&ca, m, &cb, n, &cr1, _state);
                        }
                    }
                    else
                    {
                        
                        /*
                         * test internal subroutine
                         */
                        if( m>=n )
                        {
                            
                            /*
                             * test internal subroutine:
                             * * circular/non-circular mode
                             */
                            convc1dx(&ca, m, &cb, n, circkind!=0, rkind, 0, &cr1, _state);
                        }
                        else
                        {
                            
                            /*
                             * test internal subroutine - circular mode only
                             */
                            ae_assert(circkind==0, "Convolution test: internal error!", _state);
                            convc1dx(&cb, n, &ca, m, ae_false, rkind, 0, &cr1, _state);
                        }
                    }
                    if( circkind==0 )
                    {
                        testconvunit_refconvc1d(&ca, m, &cb, n, &cr2, _state);
                    }
                    else
                    {
                        testconvunit_refconvc1dcircular(&ca, m, &cb, n, &cr2, _state);
                    }
                    if( circkind==0 )
                    {
                        for(i=0; i<=m+n-2; i++)
                        {
                            referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],cr2.ptr.p_complex[i]), _state), _state);
                        }
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],cr2.ptr.p_complex[i]), _state), _state);
                        }
                    }
                    
                    /*
                     * Real convolution
                     */
                    ae_vector_set_length(&ra, m, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        ra.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    ae_vector_set_length(&rb, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        rb.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    ae_vector_set_length(&rr1, 1, _state);
                    if( rkind==-3 )
                    {
                        
                        /*
                         * test wrapper subroutine:
                         * * circular/non-circular
                         */
                        if( circkind==0 )
                        {
                            convr1d(&ra, m, &rb, n, &rr1, _state);
                        }
                        else
                        {
                            convr1dcircular(&ra, m, &rb, n, &rr1, _state);
                        }
                    }
                    else
                    {
                        if( m>=n )
                        {
                            
                            /*
                             * test internal subroutine:
                             * * circular/non-circular mode
                             */
                            convr1dx(&ra, m, &rb, n, circkind!=0, rkind, 0, &rr1, _state);
                        }
                        else
                        {
                            
                            /*
                             * test internal subroutine - non-circular mode only
                             */
                            convr1dx(&rb, n, &ra, m, circkind!=0, rkind, 0, &rr1, _state);
                        }
                    }
                    if( circkind==0 )
                    {
                        testconvunit_refconvr1d(&ra, m, &rb, n, &rr2, _state);
                    }
                    else
                    {
                        testconvunit_refconvr1dcircular(&ra, m, &rb, n, &rr2, _state);
                    }
                    if( circkind==0 )
                    {
                        for(i=0; i<=m+n-2; i++)
                        {
                            refrerr = ae_maxreal(refrerr, ae_fabs(rr1.ptr.p_double[i]-rr2.ptr.p_double[i], _state), _state);
                        }
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            refrerr = ae_maxreal(refrerr, ae_fabs(rr1.ptr.p_double[i]-rr2.ptr.p_double[i], _state), _state);
                        }
                    }
                }
            }
        }
    }
    referrors = referrors||ae_fp_greater(referr,errtol);
    refrerrors = refrerrors||ae_fp_greater(refrerr,errtol);
    
    /*
     * Test inverse convolution
     */
    inverr = (double)(0);
    invrerr = (double)(0);
    for(m=1; m<=maxn; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * Complex circilar and non-circular
             */
            ae_vector_set_length(&ca, m, _state);
            for(i=0; i<=m-1; i++)
            {
                ca.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                ca.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&cb, n, _state);
            for(i=0; i<=n-1; i++)
            {
                cb.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                cb.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&cr1, 1, _state);
            ae_vector_set_length(&cr2, 1, _state);
            convc1d(&ca, m, &cb, n, &cr2, _state);
            convc1dinv(&cr2, m+n-1, &cb, n, &cr1, _state);
            for(i=0; i<=m-1; i++)
            {
                inverr = ae_maxreal(inverr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],ca.ptr.p_complex[i]), _state), _state);
            }
            ae_vector_set_length(&cr1, 1, _state);
            ae_vector_set_length(&cr2, 1, _state);
            convc1dcircular(&ca, m, &cb, n, &cr2, _state);
            convc1dcircularinv(&cr2, m, &cb, n, &cr1, _state);
            for(i=0; i<=m-1; i++)
            {
                inverr = ae_maxreal(inverr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],ca.ptr.p_complex[i]), _state), _state);
            }
            
            /*
             * Real circilar and non-circular
             */
            ae_vector_set_length(&ra, m, _state);
            for(i=0; i<=m-1; i++)
            {
                ra.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&rb, n, _state);
            for(i=0; i<=n-1; i++)
            {
                rb.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&rr1, 1, _state);
            ae_vector_set_length(&rr2, 1, _state);
            convr1d(&ra, m, &rb, n, &rr2, _state);
            convr1dinv(&rr2, m+n-1, &rb, n, &rr1, _state);
            for(i=0; i<=m-1; i++)
            {
                invrerr = ae_maxreal(invrerr, ae_fabs(rr1.ptr.p_double[i]-ra.ptr.p_double[i], _state), _state);
            }
            ae_vector_set_length(&rr1, 1, _state);
            ae_vector_set_length(&rr2, 1, _state);
            convr1dcircular(&ra, m, &rb, n, &rr2, _state);
            convr1dcircularinv(&rr2, m, &rb, n, &rr1, _state);
            for(i=0; i<=m-1; i++)
            {
                invrerr = ae_maxreal(invrerr, ae_fabs(rr1.ptr.p_double[i]-ra.ptr.p_double[i], _state), _state);
            }
        }
    }
    inverrors = inverrors||ae_fp_greater(inverr,errtol);
    invrerrors = invrerrors||ae_fp_greater(invrerr,errtol);
    
    /*
     * end
     */
    waserrors = ((referrors||refrerrors)||inverrors)||invrerrors;
    if( !silent )
    {
        printf("TESTING CONVOLUTION\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE COMPLEX CONV:         ");
        if( referrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE REAL CONV:            ");
        if( refrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX INVERSE:                        ");
        if( inverrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* REAL INVERSE:                           ");
        if( invrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testconvunit_refconvc1d(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    ae_complex v;

    ae_vector_clear(r);

    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=m+n-2; i++)
    {
        r->ptr.p_complex[i] = ae_complex_from_i(0);
    }
    for(i=0; i<=m-1; i++)
    {
        v = a->ptr.p_complex[i];
        ae_v_caddc(&r->ptr.p_complex[i], 1, &b->ptr.p_complex[0], 1, "N", ae_v_len(i,i+n-1), v);
    }
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testconvunit_refconvc1dcircular(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i1;
    ae_int_t i2;
    ae_int_t j2;
    ae_vector buf;

    ae_frame_make(_state, &_frame_block);
    memset(&buf, 0, sizeof(buf));
    ae_vector_clear(r);
    ae_vector_init(&buf, 0, DT_COMPLEX, _state, ae_true);

    testconvunit_refconvc1d(a, m, b, n, &buf, _state);
    ae_vector_set_length(r, m, _state);
    ae_v_cmove(&r->ptr.p_complex[0], 1, &buf.ptr.p_complex[0], 1, "N", ae_v_len(0,m-1));
    i1 = m;
    while(i1<=m+n-2)
    {
        i2 = ae_minint(i1+m-1, m+n-2, _state);
        j2 = i2-i1;
        ae_v_cadd(&r->ptr.p_complex[0], 1, &buf.ptr.p_complex[i1], 1, "N", ae_v_len(0,j2));
        i1 = i1+m;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference FFT
*************************************************************************/
static void testconvunit_refconvr1d(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    double v;

    ae_vector_clear(r);

    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=m+n-2; i++)
    {
        r->ptr.p_double[i] = (double)(0);
    }
    for(i=0; i<=m-1; i++)
    {
        v = a->ptr.p_double[i];
        ae_v_addd(&r->ptr.p_double[i], 1, &b->ptr.p_double[0], 1, ae_v_len(i,i+n-1), v);
    }
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testconvunit_refconvr1dcircular(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i1;
    ae_int_t i2;
    ae_int_t j2;
    ae_vector buf;

    ae_frame_make(_state, &_frame_block);
    memset(&buf, 0, sizeof(buf));
    ae_vector_clear(r);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);

    testconvunit_refconvr1d(a, m, b, n, &buf, _state);
    ae_vector_set_length(r, m, _state);
    ae_v_move(&r->ptr.p_double[0], 1, &buf.ptr.p_double[0], 1, ae_v_len(0,m-1));
    i1 = m;
    while(i1<=m+n-2)
    {
        i2 = ae_minint(i1+m-1, m+n-2, _state);
        j2 = i2-i1;
        ae_v_add(&r->ptr.p_double[0], 1, &buf.ptr.p_double[i1], 1, ae_v_len(0,j2));
        i1 = i1+m;
    }
    ae_frame_leave(_state);
}



static void testcorrunit_refcorrc1d(/* Complex */ ae_vector* signal,
     ae_int_t n,
     /* Complex */ ae_vector* pattern,
     ae_int_t m,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refcorrc1dcircular(/* Complex */ ae_vector* signal,
     ae_int_t n,
     /* Complex */ ae_vector* pattern,
     ae_int_t m,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refcorrr1d(/* Real    */ ae_vector* signal,
     ae_int_t n,
     /* Real    */ ae_vector* pattern,
     ae_int_t m,
     /* Real    */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refcorrr1dcircular(/* Real    */ ae_vector* signal,
     ae_int_t n,
     /* Real    */ ae_vector* pattern,
     ae_int_t m,
     /* Real    */ ae_vector* r,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testcorr(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t i;
    ae_vector ra;
    ae_vector rb;
    ae_vector rr1;
    ae_vector rr2;
    ae_vector ca;
    ae_vector cb;
    ae_vector cr1;
    ae_vector cr2;
    ae_int_t maxn;
    double referr;
    double refrerr;
    double errtol;
    ae_bool referrors;
    ae_bool refrerrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ra, 0, sizeof(ra));
    memset(&rb, 0, sizeof(rb));
    memset(&rr1, 0, sizeof(rr1));
    memset(&rr2, 0, sizeof(rr2));
    memset(&ca, 0, sizeof(ca));
    memset(&cb, 0, sizeof(cb));
    memset(&cr1, 0, sizeof(cr1));
    memset(&cr2, 0, sizeof(cr2));
    ae_vector_init(&ra, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rr1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rr2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ca, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cb, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cr1, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cr2, 0, DT_COMPLEX, _state, ae_true);

    maxn = 32;
    errtol = 100000*ae_pow((double)(maxn), (double)3/(double)2, _state)*ae_machineepsilon;
    referrors = ae_false;
    refrerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Test against reference O(N^2) implementation.
     */
    referr = (double)(0);
    refrerr = (double)(0);
    for(m=1; m<=maxn; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * Complex correlation
             */
            ae_vector_set_length(&ca, m, _state);
            for(i=0; i<=m-1; i++)
            {
                ca.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                ca.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&cb, n, _state);
            for(i=0; i<=n-1; i++)
            {
                cb.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                cb.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&cr1, 1, _state);
            corrc1d(&ca, m, &cb, n, &cr1, _state);
            testcorrunit_refcorrc1d(&ca, m, &cb, n, &cr2, _state);
            for(i=0; i<=m+n-2; i++)
            {
                referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],cr2.ptr.p_complex[i]), _state), _state);
            }
            ae_vector_set_length(&cr1, 1, _state);
            corrc1dcircular(&ca, m, &cb, n, &cr1, _state);
            testcorrunit_refcorrc1dcircular(&ca, m, &cb, n, &cr2, _state);
            for(i=0; i<=m-1; i++)
            {
                referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],cr2.ptr.p_complex[i]), _state), _state);
            }
            
            /*
             * Real correlation
             */
            ae_vector_set_length(&ra, m, _state);
            for(i=0; i<=m-1; i++)
            {
                ra.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&rb, n, _state);
            for(i=0; i<=n-1; i++)
            {
                rb.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&rr1, 1, _state);
            corrr1d(&ra, m, &rb, n, &rr1, _state);
            testcorrunit_refcorrr1d(&ra, m, &rb, n, &rr2, _state);
            for(i=0; i<=m+n-2; i++)
            {
                refrerr = ae_maxreal(refrerr, ae_fabs(rr1.ptr.p_double[i]-rr2.ptr.p_double[i], _state), _state);
            }
            ae_vector_set_length(&rr1, 1, _state);
            corrr1dcircular(&ra, m, &rb, n, &rr1, _state);
            testcorrunit_refcorrr1dcircular(&ra, m, &rb, n, &rr2, _state);
            for(i=0; i<=m-1; i++)
            {
                refrerr = ae_maxreal(refrerr, ae_fabs(rr1.ptr.p_double[i]-rr2.ptr.p_double[i], _state), _state);
            }
        }
    }
    referrors = referrors||ae_fp_greater(referr,errtol);
    refrerrors = refrerrors||ae_fp_greater(refrerr,errtol);
    
    /*
     * end
     */
    waserrors = referrors||refrerrors;
    if( !silent )
    {
        printf("TESTING CORRELATION\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE COMPLEX CORR:         ");
        if( referrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE REAL CORR:            ");
        if( refrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refcorrc1d(/* Complex */ ae_vector* signal,
     ae_int_t n,
     /* Complex */ ae_vector* pattern,
     ae_int_t m,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_vector s;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    ae_vector_clear(r);
    ae_vector_init(&s, 0, DT_COMPLEX, _state, ae_true);

    ae_vector_set_length(&s, m+n-1, _state);
    ae_v_cmove(&s.ptr.p_complex[0], 1, &signal->ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
    for(i=n; i<=m+n-2; i++)
    {
        s.ptr.p_complex[i] = ae_complex_from_i(0);
    }
    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=n-1; i++)
    {
        v = ae_complex_from_i(0);
        for(j=0; j<=m-1; j++)
        {
            if( i+j>=n )
            {
                break;
            }
            v = ae_c_add(v,ae_c_mul(ae_c_conj(pattern->ptr.p_complex[j], _state),s.ptr.p_complex[i+j]));
        }
        r->ptr.p_complex[i] = v;
    }
    for(i=1; i<=m-1; i++)
    {
        v = ae_complex_from_i(0);
        for(j=i; j<=m-1; j++)
        {
            v = ae_c_add(v,ae_c_mul(ae_c_conj(pattern->ptr.p_complex[j], _state),s.ptr.p_complex[j-i]));
        }
        r->ptr.p_complex[m+n-1-i] = v;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refcorrc1dcircular(/* Complex */ ae_vector* signal,
     ae_int_t n,
     /* Complex */ ae_vector* pattern,
     ae_int_t m,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_complex v;

    ae_vector_clear(r);

    ae_vector_set_length(r, n, _state);
    for(i=0; i<=n-1; i++)
    {
        v = ae_complex_from_i(0);
        for(j=0; j<=m-1; j++)
        {
            v = ae_c_add(v,ae_c_mul(ae_c_conj(pattern->ptr.p_complex[j], _state),signal->ptr.p_complex[(i+j)%n]));
        }
        r->ptr.p_complex[i] = v;
    }
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refcorrr1d(/* Real    */ ae_vector* signal,
     ae_int_t n,
     /* Real    */ ae_vector* pattern,
     ae_int_t m,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_vector s;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    ae_vector_clear(r);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&s, m+n-1, _state);
    ae_v_move(&s.ptr.p_double[0], 1, &signal->ptr.p_double[0], 1, ae_v_len(0,n-1));
    for(i=n; i<=m+n-2; i++)
    {
        s.ptr.p_double[i] = (double)(0);
    }
    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=n-1; i++)
    {
        v = (double)(0);
        for(j=0; j<=m-1; j++)
        {
            if( i+j>=n )
            {
                break;
            }
            v = v+pattern->ptr.p_double[j]*s.ptr.p_double[i+j];
        }
        r->ptr.p_double[i] = v;
    }
    for(i=1; i<=m-1; i++)
    {
        v = (double)(0);
        for(j=i; j<=m-1; j++)
        {
            v = v+pattern->ptr.p_double[j]*s.ptr.p_double[-i+j];
        }
        r->ptr.p_double[m+n-1-i] = v;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refcorrr1dcircular(/* Real    */ ae_vector* signal,
     ae_int_t n,
     /* Real    */ ae_vector* pattern,
     ae_int_t m,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;

    ae_vector_clear(r);

    ae_vector_set_length(r, n, _state);
    for(i=0; i<=n-1; i++)
    {
        v = (double)(0);
        for(j=0; j<=m-1; j++)
        {
            v = v+pattern->ptr.p_double[j]*signal->ptr.p_double[(i+j)%n];
        }
        r->ptr.p_double[i] = v;
    }
}



static void testidwintunit_testxy(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t d,
     ae_int_t nq,
     ae_int_t nw,
     ae_bool* idwerrors,
     ae_state *_state);
static void testidwintunit_testdegree(ae_int_t n,
     ae_int_t nx,
     ae_int_t d,
     ae_int_t dtask,
     ae_bool* idwerrors,
     ae_state *_state);
static void testidwintunit_testnoisy(ae_bool* idwerrors, ae_state *_state);





/*************************************************************************
Testing IDW interpolation
*************************************************************************/
ae_bool testidwint(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t i;
    ae_int_t j;
    double vx;
    double vy;
    double vz;
    ae_int_t d;
    ae_int_t dtask;
    ae_int_t nx;
    ae_int_t nq;
    ae_int_t nw;
    ae_int_t smalln;
    ae_int_t largen;
    ae_bool waserrors;
    ae_bool idwerrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    idwerrors = ae_false;
    smalln = 256;
    largen = 1024;
    nq = 10;
    nw = 18;
    
    /*
     * Simple test:
     * * F = x^3 + sin(pi*y)*z^2 - (x+y)^2
     * * space is either R1=[-1,+1] (other dimensions are
     *   fixed at 0), R1^2 or R1^3.
     ** D = -1, 0, 1, 2
     */
    for(nx=1; nx<=2; nx++)
    {
        ae_matrix_set_length(&xy, largen, nx+1, _state);
        for(i=0; i<=largen-1; i++)
        {
            for(j=0; j<=nx-1; j++)
            {
                xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            if( nx>=1 )
            {
                vx = xy.ptr.pp_double[i][0];
            }
            else
            {
                vx = (double)(0);
            }
            if( nx>=2 )
            {
                vy = xy.ptr.pp_double[i][1];
            }
            else
            {
                vy = (double)(0);
            }
            if( nx>=3 )
            {
                vz = xy.ptr.pp_double[i][2];
            }
            else
            {
                vz = (double)(0);
            }
            xy.ptr.pp_double[i][nx] = vx*vx*vx+ae_sin(ae_pi*vy, _state)*ae_sqr(vz, _state)-ae_sqr(vx+vy, _state);
        }
        for(d=-1; d<=2; d++)
        {
            testidwintunit_testxy(&xy, largen, nx, d, nq, nw, &idwerrors, _state);
        }
    }
    
    /*
     * Another simple test:
     * * five points in 2D - (0,0), (0,1), (1,0), (-1,0) (0,-1)
     * * F is random
     * * D = -1, 0, 1, 2
     */
    nx = 2;
    ae_matrix_set_length(&xy, 5, nx+1, _state);
    xy.ptr.pp_double[0][0] = (double)(0);
    xy.ptr.pp_double[0][1] = (double)(0);
    xy.ptr.pp_double[0][2] = 2*ae_randomreal(_state)-1;
    xy.ptr.pp_double[1][0] = (double)(1);
    xy.ptr.pp_double[1][1] = (double)(0);
    xy.ptr.pp_double[1][2] = 2*ae_randomreal(_state)-1;
    xy.ptr.pp_double[2][0] = (double)(0);
    xy.ptr.pp_double[2][1] = (double)(1);
    xy.ptr.pp_double[2][2] = 2*ae_randomreal(_state)-1;
    xy.ptr.pp_double[3][0] = (double)(-1);
    xy.ptr.pp_double[3][1] = (double)(0);
    xy.ptr.pp_double[3][2] = 2*ae_randomreal(_state)-1;
    xy.ptr.pp_double[4][0] = (double)(0);
    xy.ptr.pp_double[4][1] = (double)(-1);
    xy.ptr.pp_double[4][2] = 2*ae_randomreal(_state)-1;
    for(d=-1; d<=2; d++)
    {
        testidwintunit_testxy(&xy, 5, nx, d, nq, nw, &idwerrors, _state);
    }
    
    /*
     * Degree test.
     *
     * F is either:
     * * constant (DTask=0)
     * * linear (DTask=1)
     * * quadratic (DTask=2)
     *
     * Nodal functions are either
     * * constant (D=0)
     * * linear (D=1)
     * * quadratic (D=2)
     *
     * When DTask<=D, we can interpolate without errors.
     * When DTask>D, we MUST have errors.
     */
    for(nx=1; nx<=3; nx++)
    {
        for(d=0; d<=2; d++)
        {
            for(dtask=0; dtask<=2; dtask++)
            {
                testidwintunit_testdegree(smalln, nx, d, dtask, &idwerrors, _state);
            }
        }
    }
    
    /*
     * Noisy test
     */
    testidwintunit_testnoisy(&idwerrors, _state);
    
    /*
     * report
     */
    waserrors = idwerrors;
    if( !silent )
    {
        printf("TESTING INVERSE DISTANCE WEIGHTING\n");
        printf("* IDW:                                   ");
        if( !idwerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Testing IDW:
* generate model using N/NX/D/NQ/NW
* test basic properties
*************************************************************************/
static void testidwintunit_testxy(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t d,
     ae_int_t nq,
     ae_int_t nw,
     ae_bool* idwerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double lipschitzstep;
    ae_int_t i;
    ae_int_t i1;
    ae_int_t i2;
    double v;
    double v1;
    double v2;
    double t;
    double l1;
    double l2;
    idwinterpolant z1;
    ae_vector x;

    ae_frame_make(_state, &_frame_block);
    memset(&z1, 0, sizeof(z1));
    memset(&x, 0, sizeof(x));
    _idwinterpolant_init(&z1, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    lipschitzstep = 0.001;
    ae_vector_set_length(&x, nx, _state);
    
    /*
     * build
     */
    idwbuildmodifiedshepard(xy, n, nx, d, nq, nw, &z1, _state);
    
    /*
     * first, test interpolation properties at nodes
     */
    for(i=0; i<=n-1; i++)
    {
        ae_v_move(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
        *idwerrors = *idwerrors||ae_fp_neq(idwcalc(&z1, &x, _state),xy->ptr.pp_double[i][nx]);
    }
    
    /*
     * test Lipschitz continuity
     */
    i1 = ae_randominteger(n, _state);
    do
    {
        i2 = ae_randominteger(n, _state);
    }
    while(i2==i1);
    l1 = (double)(0);
    t = (double)(0);
    while(ae_fp_less(t,(double)(1)))
    {
        v = 1-t;
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v1 = idwcalc(&z1, &x, _state);
        v = 1-(t+lipschitzstep);
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t+lipschitzstep;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v2 = idwcalc(&z1, &x, _state);
        l1 = ae_maxreal(l1, ae_fabs(v2-v1, _state)/lipschitzstep, _state);
        t = t+lipschitzstep;
    }
    l2 = (double)(0);
    t = (double)(0);
    while(ae_fp_less(t,(double)(1)))
    {
        v = 1-t;
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v1 = idwcalc(&z1, &x, _state);
        v = 1-(t+lipschitzstep/3);
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t+lipschitzstep/3;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v2 = idwcalc(&z1, &x, _state);
        l2 = ae_maxreal(l2, ae_fabs(v2-v1, _state)/(lipschitzstep/3), _state);
        t = t+lipschitzstep/3;
    }
    *idwerrors = *idwerrors||ae_fp_greater(l2,2.0*l1);
    ae_frame_leave(_state);
}


/*************************************************************************
Testing degree properties

F is either:
* constant (DTask=0)
* linear (DTask=1)
* quadratic (DTask=2)

Nodal functions are either
* constant (D=0)
* linear (D=1)
* quadratic (D=2)

When DTask<=D, we can interpolate without errors.
When DTask>D, we MUST have errors.
*************************************************************************/
static void testidwintunit_testdegree(ae_int_t n,
     ae_int_t nx,
     ae_int_t d,
     ae_int_t dtask,
     ae_bool* idwerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    ae_int_t nq;
    ae_int_t nw;
    ae_int_t i;
    ae_int_t j;
    double v;
    double c0;
    ae_vector c1;
    ae_matrix c2;
    ae_vector x;
    ae_matrix xy;
    idwinterpolant z1;
    double v1;
    double v2;

    ae_frame_make(_state, &_frame_block);
    memset(&c1, 0, sizeof(c1));
    memset(&c2, 0, sizeof(c2));
    memset(&x, 0, sizeof(x));
    memset(&xy, 0, sizeof(xy));
    memset(&z1, 0, sizeof(z1));
    ae_vector_init(&c1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _idwinterpolant_init(&z1, _state, ae_true);

    threshold = 1.0E6*ae_machineepsilon;
    nq = 2*(nx*nx+nx+1);
    nw = 10;
    ae_assert(nq<=n, "TestDegree: internal error", _state);
    
    /*
     * prepare model
     */
    c0 = 2*ae_randomreal(_state)-1;
    ae_vector_set_length(&c1, nx, _state);
    for(i=0; i<=nx-1; i++)
    {
        c1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
    }
    ae_matrix_set_length(&c2, nx, nx, _state);
    for(i=0; i<=nx-1; i++)
    {
        for(j=i+1; j<=nx-1; j++)
        {
            c2.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            c2.ptr.pp_double[j][i] = c2.ptr.pp_double[i][j];
        }
        do
        {
            c2.ptr.pp_double[i][i] = 2*ae_randomreal(_state)-1;
        }
        while(ae_fp_less_eq(ae_fabs(c2.ptr.pp_double[i][i], _state),0.3));
    }
    
    /*
     * prepare points
     */
    ae_matrix_set_length(&xy, n, nx+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=nx-1; j++)
        {
            xy.ptr.pp_double[i][j] = 4*ae_randomreal(_state)-2;
        }
        xy.ptr.pp_double[i][nx] = c0;
        if( dtask>=1 )
        {
            v = ae_v_dotproduct(&c1.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            xy.ptr.pp_double[i][nx] = xy.ptr.pp_double[i][nx]+v;
        }
        if( dtask==2 )
        {
            for(j=0; j<=nx-1; j++)
            {
                v = ae_v_dotproduct(&c2.ptr.pp_double[j][0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                xy.ptr.pp_double[i][nx] = xy.ptr.pp_double[i][nx]+xy.ptr.pp_double[i][j]*v;
            }
        }
    }
    
    /*
     * build interpolant, calculate value at random point
     */
    idwbuildmodifiedshepard(&xy, n, nx, d, nq, nw, &z1, _state);
    ae_vector_set_length(&x, nx, _state);
    for(i=0; i<=nx-1; i++)
    {
        x.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
    }
    v1 = idwcalc(&z1, &x, _state);
    
    /*
     * calculate model value at the same point
     */
    v2 = c0;
    if( dtask>=1 )
    {
        v = ae_v_dotproduct(&c1.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,nx-1));
        v2 = v2+v;
    }
    if( dtask==2 )
    {
        for(j=0; j<=nx-1; j++)
        {
            v = ae_v_dotproduct(&c2.ptr.pp_double[j][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            v2 = v2+x.ptr.p_double[j]*v;
        }
    }
    
    /*
     * Compare
     */
    if( dtask<=d )
    {
        *idwerrors = *idwerrors||ae_fp_greater(ae_fabs(v2-v1, _state),threshold);
    }
    else
    {
        *idwerrors = *idwerrors||ae_fp_less(ae_fabs(v2-v1, _state),threshold);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Noisy test:
 * F = x^2 + y^2 + z^2 + noise on [-1,+1]^3
 * space is either R1=[-1,+1] (other dimensions are
   fixed at 0), R1^2 or R1^3.
 * D = 1, 2
 * 4096 points is used for function generation,
   4096 points - for testing
 * RMS error of "noisy" model on test set must be
   lower than RMS error of interpolation model.
*************************************************************************/
static void testidwintunit_testnoisy(ae_bool* idwerrors, ae_state *_state)
{
    ae_frame _frame_block;
    double noiselevel;
    ae_int_t nq;
    ae_int_t nw;
    ae_int_t d;
    ae_int_t nx;
    ae_int_t ntrn;
    ae_int_t ntst;
    ae_int_t i;
    ae_int_t j;
    double v;
    double t;
    double v1;
    double v2;
    double ve;
    ae_matrix xy;
    ae_vector x;
    idwinterpolant z1;
    idwinterpolant z2;
    double rms1;
    double rms2;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&x, 0, sizeof(x));
    memset(&z1, 0, sizeof(z1));
    memset(&z2, 0, sizeof(z2));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    _idwinterpolant_init(&z1, _state, ae_true);
    _idwinterpolant_init(&z2, _state, ae_true);

    nq = 20;
    nw = 40;
    noiselevel = 0.2;
    ntrn = 256;
    ntst = 1024;
    for(d=1; d<=2; d++)
    {
        for(nx=1; nx<=2; nx++)
        {
            
            /*
             * prepare dataset
             */
            ae_matrix_set_length(&xy, ntrn, nx+1, _state);
            for(i=0; i<=ntrn-1; i++)
            {
                v = noiselevel*(2*ae_randomreal(_state)-1);
                for(j=0; j<=nx-1; j++)
                {
                    t = 2*ae_randomreal(_state)-1;
                    v = v+ae_sqr(t, _state);
                    xy.ptr.pp_double[i][j] = t;
                }
                xy.ptr.pp_double[i][nx] = v;
            }
            
            /*
             * build interpolants
             */
            idwbuildmodifiedshepard(&xy, ntrn, nx, d, nq, nw, &z1, _state);
            idwbuildnoisy(&xy, ntrn, nx, d, nq, nw, &z2, _state);
            
            /*
             * calculate RMS errors
             */
            ae_vector_set_length(&x, nx, _state);
            rms1 = (double)(0);
            rms2 = (double)(0);
            for(i=0; i<=ntst-1; i++)
            {
                ve = (double)(0);
                for(j=0; j<=nx-1; j++)
                {
                    t = 2*ae_randomreal(_state)-1;
                    x.ptr.p_double[j] = t;
                    ve = ve+ae_sqr(t, _state);
                }
                v1 = idwcalc(&z1, &x, _state);
                v2 = idwcalc(&z2, &x, _state);
                rms1 = rms1+ae_sqr(v1-ve, _state);
                rms2 = rms2+ae_sqr(v2-ve, _state);
            }
            *idwerrors = *idwerrors||ae_fp_greater(rms2,rms1);
        }
    }
    ae_frame_leave(_state);
}



static void testratintunit_poldiff2(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_int_t n,
     double t,
     double* p,
     double* dp,
     double* d2p,
     ae_state *_state);
static void testratintunit_brcunset(barycentricinterpolant* b,
     ae_state *_state);





ae_bool testratint(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool bcerrors;
    ae_bool nperrors;
    double threshold;
    double lipschitztol;
    ae_int_t passcount;
    barycentricinterpolant b1;
    barycentricinterpolant b2;
    ae_vector x;
    ae_vector x2;
    ae_vector y;
    ae_vector y2;
    ae_vector w;
    ae_vector w2;
    ae_vector xc;
    ae_vector yc;
    ae_vector dc;
    double h;
    double s1;
    double s2;
    ae_int_t n;
    ae_int_t n2;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t d;
    ae_int_t pass;
    double maxerr;
    double t;
    double a;
    double b;
    double s;
    double v0;
    double v1;
    double v2;
    double v3;
    double d0;
    double d1;
    double d2;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&b1, 0, sizeof(b1));
    memset(&b2, 0, sizeof(b2));
    memset(&x, 0, sizeof(x));
    memset(&x2, 0, sizeof(x2));
    memset(&y, 0, sizeof(y));
    memset(&y2, 0, sizeof(y2));
    memset(&w, 0, sizeof(w));
    memset(&w2, 0, sizeof(w2));
    memset(&xc, 0, sizeof(xc));
    memset(&yc, 0, sizeof(yc));
    memset(&dc, 0, sizeof(dc));
    _barycentricinterpolant_init(&b1, _state, ae_true);
    _barycentricinterpolant_init(&b2, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);

    nperrors = ae_false;
    bcerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * PassCount        number of repeated passes
     * Threshold        error tolerance
     * LipschitzTol     Lipschitz constant increase allowed
     *                  when calculating constant on a twice denser grid
     */
    passcount = 5;
    threshold = 1000000*ae_machineepsilon;
    lipschitztol = 1.3;
    
    /*
     * Basic barycentric functions
     */
    for(n=1; n<=10; n++)
    {
        
        /*
         * randomized tests
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * generate weights from polynomial interpolation
             */
            v0 = 1+0.4*ae_randomreal(_state)-0.2;
            v1 = 2*ae_randomreal(_state)-1;
            v2 = 2*ae_randomreal(_state)-1;
            v3 = 2*ae_randomreal(_state)-1;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( n==1 )
                {
                    x.ptr.p_double[i] = (double)(0);
                }
                else
                {
                    x.ptr.p_double[i] = v0*ae_cos(i*ae_pi/(n-1), _state);
                }
                y.ptr.p_double[i] = ae_sin(v1*x.ptr.p_double[i], _state)+ae_cos(v2*x.ptr.p_double[i], _state)+ae_exp(v3*x.ptr.p_double[i], _state);
            }
            for(j=0; j<=n-1; j++)
            {
                w.ptr.p_double[j] = (double)(1);
                for(k=0; k<=n-1; k++)
                {
                    if( k!=j )
                    {
                        w.ptr.p_double[j] = w.ptr.p_double[j]/(x.ptr.p_double[j]-x.ptr.p_double[k]);
                    }
                }
            }
            barycentricbuildxyw(&x, &y, &w, n, &b1, _state);
            
            /*
             * unpack, then pack again and compare
             */
            testratintunit_brcunset(&b2, _state);
            barycentricunpack(&b1, &n2, &x2, &y2, &w2, _state);
            bcerrors = bcerrors||n2!=n;
            barycentricbuildxyw(&x2, &y2, &w2, n2, &b2, _state);
            t = 2*ae_randomreal(_state)-1;
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, t, _state)-barycentriccalc(&b2, t, _state), _state),threshold);
            
            /*
             * copy, compare
             */
            testratintunit_brcunset(&b2, _state);
            barycentriccopy(&b1, &b2, _state);
            t = 2*ae_randomreal(_state)-1;
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, t, _state)-barycentriccalc(&b2, t, _state), _state),threshold);
            
            /*
             * test interpolation properties
             */
            for(i=0; i<=n-1; i++)
            {
                
                /*
                 * test interpolation at nodes
                 */
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, x.ptr.p_double[i], _state)-y.ptr.p_double[i], _state),threshold*ae_fabs(y.ptr.p_double[i], _state));
                
                /*
                 * compare with polynomial interpolation
                 */
                t = 2*ae_randomreal(_state)-1;
                testratintunit_poldiff2(&x, &y, n, t, &v0, &v1, &v2, _state);
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, t, _state)-v0, _state),threshold*ae_maxreal(ae_fabs(v0, _state), (double)(1), _state));
                
                /*
                 * test continuity between nodes
                 * calculate Lipschitz constant on two grids -
                 * dense and even more dense. If Lipschitz constant
                 * on a denser grid is significantly increased,
                 * continuity test is failed
                 */
                t = 3.0;
                k = 100;
                s1 = (double)(0);
                for(j=0; j<=k-1; j++)
                {
                    v1 = x.ptr.p_double[i]+(t-x.ptr.p_double[i])*j/k;
                    v2 = x.ptr.p_double[i]+(t-x.ptr.p_double[i])*(j+1)/k;
                    s1 = ae_maxreal(s1, ae_fabs(barycentriccalc(&b1, v2, _state)-barycentriccalc(&b1, v1, _state), _state)/ae_fabs(v2-v1, _state), _state);
                }
                k = 2*k;
                s2 = (double)(0);
                for(j=0; j<=k-1; j++)
                {
                    v1 = x.ptr.p_double[i]+(t-x.ptr.p_double[i])*j/k;
                    v2 = x.ptr.p_double[i]+(t-x.ptr.p_double[i])*(j+1)/k;
                    s2 = ae_maxreal(s2, ae_fabs(barycentriccalc(&b1, v2, _state)-barycentriccalc(&b1, v1, _state), _state)/ae_fabs(v2-v1, _state), _state);
                }
                bcerrors = bcerrors||(ae_fp_greater(s2,lipschitztol*s1)&&ae_fp_greater(s1,threshold*k));
            }
            
            /*
             * test differentiation properties
             */
            for(i=0; i<=n-1; i++)
            {
                t = 2*ae_randomreal(_state)-1;
                testratintunit_poldiff2(&x, &y, n, t, &v0, &v1, &v2, _state);
                d0 = (double)(0);
                d1 = (double)(0);
                d2 = (double)(0);
                barycentricdiff1(&b1, t, &d0, &d1, _state);
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v0-d0, _state),threshold*ae_maxreal(ae_fabs(v0, _state), (double)(1), _state));
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v1-d1, _state),threshold*ae_maxreal(ae_fabs(v1, _state), (double)(1), _state));
                d0 = (double)(0);
                d1 = (double)(0);
                d2 = (double)(0);
                barycentricdiff2(&b1, t, &d0, &d1, &d2, _state);
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v0-d0, _state),threshold*ae_maxreal(ae_fabs(v0, _state), (double)(1), _state));
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v1-d1, _state),threshold*ae_maxreal(ae_fabs(v1, _state), (double)(1), _state));
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v2-d2, _state),ae_sqrt(threshold, _state)*ae_maxreal(ae_fabs(v2, _state), (double)(1), _state));
            }
            
            /*
             * test linear translation
             */
            t = 2*ae_randomreal(_state)-1;
            a = 2*ae_randomreal(_state)-1;
            b = 2*ae_randomreal(_state)-1;
            testratintunit_brcunset(&b2, _state);
            barycentriccopy(&b1, &b2, _state);
            barycentriclintransx(&b2, a, b, _state);
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, a*t+b, _state)-barycentriccalc(&b2, t, _state), _state),threshold);
            a = (double)(0);
            b = 2*ae_randomreal(_state)-1;
            testratintunit_brcunset(&b2, _state);
            barycentriccopy(&b1, &b2, _state);
            barycentriclintransx(&b2, a, b, _state);
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, a*t+b, _state)-barycentriccalc(&b2, t, _state), _state),threshold);
            a = 2*ae_randomreal(_state)-1;
            b = 2*ae_randomreal(_state)-1;
            testratintunit_brcunset(&b2, _state);
            barycentriccopy(&b1, &b2, _state);
            barycentriclintransy(&b2, a, b, _state);
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(a*barycentriccalc(&b1, t, _state)+b-barycentriccalc(&b2, t, _state), _state),threshold);
        }
    }
    for(pass=0; pass<=3; pass++)
    {
        
        /*
         * Crash-test: small numbers, large numbers
         */
        ae_vector_set_length(&x, 4, _state);
        ae_vector_set_length(&y, 4, _state);
        ae_vector_set_length(&w, 4, _state);
        h = (double)(1);
        if( pass%2==0 )
        {
            h = 100*ae_minrealnumber;
        }
        if( pass%2==1 )
        {
            h = 0.01*ae_maxrealnumber;
        }
        x.ptr.p_double[0] = 0*h;
        x.ptr.p_double[1] = 1*h;
        x.ptr.p_double[2] = 2*h;
        x.ptr.p_double[3] = 3*h;
        y.ptr.p_double[0] = 0*h;
        y.ptr.p_double[1] = 1*h;
        y.ptr.p_double[2] = 2*h;
        y.ptr.p_double[3] = 3*h;
        w.ptr.p_double[0] = -1/(x.ptr.p_double[1]-x.ptr.p_double[0]);
        w.ptr.p_double[1] = 1*(1/(x.ptr.p_double[1]-x.ptr.p_double[0])+1/(x.ptr.p_double[2]-x.ptr.p_double[1]));
        w.ptr.p_double[2] = -1*(1/(x.ptr.p_double[2]-x.ptr.p_double[1])+1/(x.ptr.p_double[3]-x.ptr.p_double[2]));
        w.ptr.p_double[3] = 1/(x.ptr.p_double[3]-x.ptr.p_double[2]);
        v0 = (double)(0);
        if( pass/2==0 )
        {
            v0 = (double)(0);
        }
        if( pass/2==1 )
        {
            v0 = 0.6*h;
        }
        barycentricbuildxyw(&x, &y, &w, 4, &b1, _state);
        t = barycentriccalc(&b1, v0, _state);
        d0 = (double)(0);
        d1 = (double)(0);
        d2 = (double)(0);
        barycentricdiff1(&b1, v0, &d0, &d1, _state);
        bcerrors = bcerrors||ae_fp_greater(ae_fabs(t-v0, _state),threshold*v0);
        bcerrors = bcerrors||ae_fp_greater(ae_fabs(d0-v0, _state),threshold*v0);
        bcerrors = bcerrors||ae_fp_greater(ae_fabs(d1-1, _state),1000*threshold);
    }
    
    /*
     * crash test: large abscissas, small argument
     *
     * test for errors in D0 is not very strict
     * because renormalization used in Diff1()
     * destroys part of precision.
     */
    ae_vector_set_length(&x, 4, _state);
    ae_vector_set_length(&y, 4, _state);
    ae_vector_set_length(&w, 4, _state);
    h = 0.01*ae_maxrealnumber;
    x.ptr.p_double[0] = 0*h;
    x.ptr.p_double[1] = 1*h;
    x.ptr.p_double[2] = 2*h;
    x.ptr.p_double[3] = 3*h;
    y.ptr.p_double[0] = 0*h;
    y.ptr.p_double[1] = 1*h;
    y.ptr.p_double[2] = 2*h;
    y.ptr.p_double[3] = 3*h;
    w.ptr.p_double[0] = -1/(x.ptr.p_double[1]-x.ptr.p_double[0]);
    w.ptr.p_double[1] = 1*(1/(x.ptr.p_double[1]-x.ptr.p_double[0])+1/(x.ptr.p_double[2]-x.ptr.p_double[1]));
    w.ptr.p_double[2] = -1*(1/(x.ptr.p_double[2]-x.ptr.p_double[1])+1/(x.ptr.p_double[3]-x.ptr.p_double[2]));
    w.ptr.p_double[3] = 1/(x.ptr.p_double[3]-x.ptr.p_double[2]);
    v0 = 100*ae_minrealnumber;
    barycentricbuildxyw(&x, &y, &w, 4, &b1, _state);
    t = barycentriccalc(&b1, v0, _state);
    d0 = (double)(0);
    d1 = (double)(0);
    d2 = (double)(0);
    barycentricdiff1(&b1, v0, &d0, &d1, _state);
    bcerrors = bcerrors||ae_fp_greater(ae_fabs(t, _state),v0*(1+threshold));
    bcerrors = bcerrors||ae_fp_greater(ae_fabs(d0, _state),v0*(1+threshold));
    bcerrors = bcerrors||ae_fp_greater(ae_fabs(d1-1, _state),1000*threshold);
    
    /*
     * crash test: test safe barycentric formula
     */
    ae_vector_set_length(&x, 4, _state);
    ae_vector_set_length(&y, 4, _state);
    ae_vector_set_length(&w, 4, _state);
    h = 2*ae_minrealnumber;
    x.ptr.p_double[0] = 0*h;
    x.ptr.p_double[1] = 1*h;
    x.ptr.p_double[2] = 2*h;
    x.ptr.p_double[3] = 3*h;
    y.ptr.p_double[0] = 0*h;
    y.ptr.p_double[1] = 1*h;
    y.ptr.p_double[2] = 2*h;
    y.ptr.p_double[3] = 3*h;
    w.ptr.p_double[0] = -1/(x.ptr.p_double[1]-x.ptr.p_double[0]);
    w.ptr.p_double[1] = 1*(1/(x.ptr.p_double[1]-x.ptr.p_double[0])+1/(x.ptr.p_double[2]-x.ptr.p_double[1]));
    w.ptr.p_double[2] = -1*(1/(x.ptr.p_double[2]-x.ptr.p_double[1])+1/(x.ptr.p_double[3]-x.ptr.p_double[2]));
    w.ptr.p_double[3] = 1/(x.ptr.p_double[3]-x.ptr.p_double[2]);
    v0 = ae_minrealnumber;
    barycentricbuildxyw(&x, &y, &w, 4, &b1, _state);
    t = barycentriccalc(&b1, v0, _state);
    bcerrors = bcerrors||ae_fp_greater(ae_fabs(t-v0, _state)/v0,threshold);
    
    /*
     * Testing "No Poles" interpolation
     */
    maxerr = (double)(0);
    for(pass=1; pass<=passcount-1; pass++)
    {
        ae_vector_set_length(&x, 1, _state);
        ae_vector_set_length(&y, 1, _state);
        x.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
        y.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
        barycentricbuildfloaterhormann(&x, &y, 1, 1, &b1, _state);
        maxerr = ae_maxreal(maxerr, ae_fabs(barycentriccalc(&b1, 2*ae_randomreal(_state)-1, _state)-y.ptr.p_double[0], _state), _state);
    }
    for(n=2; n<=10; n++)
    {
        
        /*
         * compare interpolant built by subroutine
         * with interpolant built by hands
         */
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&y, n, _state);
        ae_vector_set_length(&w, n, _state);
        ae_vector_set_length(&w2, n, _state);
        
        /*
         * D=1, non-equidistant nodes
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Initialize X, Y, W
             */
            a = -1-1*ae_randomreal(_state);
            b = 1+1*ae_randomreal(_state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = ae_atan((b-a)*i/(n-1)+a, _state);
            }
            for(i=0; i<=n-1; i++)
            {
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            w.ptr.p_double[0] = -1/(x.ptr.p_double[1]-x.ptr.p_double[0]);
            s = (double)(1);
            for(i=1; i<=n-2; i++)
            {
                w.ptr.p_double[i] = s*(1/(x.ptr.p_double[i]-x.ptr.p_double[i-1])+1/(x.ptr.p_double[i+1]-x.ptr.p_double[i]));
                s = -s;
            }
            w.ptr.p_double[n-1] = s/(x.ptr.p_double[n-1]-x.ptr.p_double[n-2]);
            for(i=0; i<=n-1; i++)
            {
                k = ae_randominteger(n, _state);
                if( k!=i )
                {
                    t = x.ptr.p_double[i];
                    x.ptr.p_double[i] = x.ptr.p_double[k];
                    x.ptr.p_double[k] = t;
                    t = y.ptr.p_double[i];
                    y.ptr.p_double[i] = y.ptr.p_double[k];
                    y.ptr.p_double[k] = t;
                    t = w.ptr.p_double[i];
                    w.ptr.p_double[i] = w.ptr.p_double[k];
                    w.ptr.p_double[k] = t;
                }
            }
            
            /*
             * Build and test
             */
            barycentricbuildfloaterhormann(&x, &y, n, 1, &b1, _state);
            barycentricbuildxyw(&x, &y, &w, n, &b2, _state);
            for(i=1; i<=2*n; i++)
            {
                t = a+(b-a)*ae_randomreal(_state);
                maxerr = ae_maxreal(maxerr, ae_fabs(barycentriccalc(&b1, t, _state)-barycentriccalc(&b2, t, _state), _state), _state);
            }
        }
        
        /*
         * D = 0, 1, 2. Equidistant nodes.
         */
        for(d=0; d<=2; d++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Skip incorrect (N,D) pairs
                 */
                if( n<2*d )
                {
                    continue;
                }
                
                /*
                 * Initialize X, Y, W
                 */
                a = -1-1*ae_randomreal(_state);
                b = 1+1*ae_randomreal(_state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = (b-a)*i/(n-1)+a;
                }
                for(i=0; i<=n-1; i++)
                {
                    y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                s = (double)(1);
                if( d==0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        w.ptr.p_double[i] = s;
                        s = -s;
                    }
                }
                if( d==1 )
                {
                    w.ptr.p_double[0] = -s;
                    for(i=1; i<=n-2; i++)
                    {
                        w.ptr.p_double[i] = 2*s;
                        s = -s;
                    }
                    w.ptr.p_double[n-1] = s;
                }
                if( d==2 )
                {
                    w.ptr.p_double[0] = s;
                    w.ptr.p_double[1] = -3*s;
                    for(i=2; i<=n-3; i++)
                    {
                        w.ptr.p_double[i] = 4*s;
                        s = -s;
                    }
                    w.ptr.p_double[n-2] = 3*s;
                    w.ptr.p_double[n-1] = -s;
                }
                
                /*
                 * Mix
                 */
                for(i=0; i<=n-1; i++)
                {
                    k = ae_randominteger(n, _state);
                    if( k!=i )
                    {
                        t = x.ptr.p_double[i];
                        x.ptr.p_double[i] = x.ptr.p_double[k];
                        x.ptr.p_double[k] = t;
                        t = y.ptr.p_double[i];
                        y.ptr.p_double[i] = y.ptr.p_double[k];
                        y.ptr.p_double[k] = t;
                        t = w.ptr.p_double[i];
                        w.ptr.p_double[i] = w.ptr.p_double[k];
                        w.ptr.p_double[k] = t;
                    }
                }
                
                /*
                 * Build and test
                 */
                barycentricbuildfloaterhormann(&x, &y, n, d, &b1, _state);
                barycentricbuildxyw(&x, &y, &w, n, &b2, _state);
                for(i=1; i<=2*n; i++)
                {
                    t = a+(b-a)*ae_randomreal(_state);
                    maxerr = ae_maxreal(maxerr, ae_fabs(barycentriccalc(&b1, t, _state)-barycentriccalc(&b2, t, _state), _state), _state);
                }
            }
        }
    }
    if( ae_fp_greater(maxerr,threshold) )
    {
        nperrors = ae_true;
    }
    
    /*
     * report
     */
    waserrors = bcerrors||nperrors;
    if( !silent )
    {
        printf("TESTING RATIONAL INTERPOLATION\n");
        printf("BASIC BARYCENTRIC FUNCTIONS:             ");
        if( bcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("FLOATER-HORMANN:                         ");
        if( nperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testratintunit_poldiff2(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_int_t n,
     double t,
     double* p,
     double* dp,
     double* d2p,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector _f;
    ae_int_t m;
    ae_int_t i;
    ae_vector df;
    ae_vector d2f;

    ae_frame_make(_state, &_frame_block);
    memset(&_f, 0, sizeof(_f));
    memset(&df, 0, sizeof(df));
    memset(&d2f, 0, sizeof(d2f));
    ae_vector_init_copy(&_f, f, _state, ae_true);
    f = &_f;
    *p = 0;
    *dp = 0;
    *d2p = 0;
    ae_vector_init(&df, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d2f, 0, DT_REAL, _state, ae_true);

    n = n-1;
    ae_vector_set_length(&df, n+1, _state);
    ae_vector_set_length(&d2f, n+1, _state);
    for(i=0; i<=n; i++)
    {
        d2f.ptr.p_double[i] = (double)(0);
        df.ptr.p_double[i] = (double)(0);
    }
    for(m=1; m<=n; m++)
    {
        for(i=0; i<=n-m; i++)
        {
            d2f.ptr.p_double[i] = ((t-x->ptr.p_double[i+m])*d2f.ptr.p_double[i]+(x->ptr.p_double[i]-t)*d2f.ptr.p_double[i+1]+2*df.ptr.p_double[i]-2*df.ptr.p_double[i+1])/(x->ptr.p_double[i]-x->ptr.p_double[i+m]);
            df.ptr.p_double[i] = ((t-x->ptr.p_double[i+m])*df.ptr.p_double[i]+f->ptr.p_double[i]+(x->ptr.p_double[i]-t)*df.ptr.p_double[i+1]-f->ptr.p_double[i+1])/(x->ptr.p_double[i]-x->ptr.p_double[i+m]);
            f->ptr.p_double[i] = ((t-x->ptr.p_double[i+m])*f->ptr.p_double[i]+(x->ptr.p_double[i]-t)*f->ptr.p_double[i+1])/(x->ptr.p_double[i]-x->ptr.p_double[i+m]);
        }
    }
    *p = f->ptr.p_double[0];
    *dp = df.ptr.p_double[0];
    *d2p = d2f.ptr.p_double[0];
    ae_frame_leave(_state);
}


static void testratintunit_brcunset(barycentricinterpolant* b,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_vector y;
    ae_vector w;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&w, 0, sizeof(w));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&x, 1, _state);
    ae_vector_set_length(&y, 1, _state);
    ae_vector_set_length(&w, 1, _state);
    x.ptr.p_double[0] = (double)(0);
    y.ptr.p_double[0] = (double)(0);
    w.ptr.p_double[0] = (double)(1);
    barycentricbuildxyw(&x, &y, &w, 1, b, _state);
    ae_frame_leave(_state);
}



static void testfitsphereunit_testspherefittingls(ae_bool* err,
     ae_state *_state);
static void testfitsphereunit_testspherefittingns(ae_bool* err,
     ae_state *_state);
static void testfitsphereunit_testspherefittingvosswinkel2(ae_bool* err,
     ae_state *_state);
static void testfitsphereunit_calcradii(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nx,
     /* Real    */ ae_vector* cx,
     double* rlo,
     double* rhi,
     ae_state *_state);
static void testfitsphereunit_calclserror(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nx,
     /* Real    */ ae_vector* cx,
     double* err,
     ae_state *_state);
static void testfitsphereunit_addvalue(/* Real    */ ae_matrix* xy,
     ae_int_t* cnt,
     double v,
     ae_state *_state);





ae_bool testfitsphere(ae_bool silent, ae_state *_state)
{
    ae_bool nserrors;
    ae_bool lserrors;
    ae_bool wereerrors;
    ae_bool result;


    nserrors = ae_false;
    lserrors = ae_false;
    
    /*
     * Sphere fitting, several different test suites
     */
    testfitsphereunit_testspherefittingls(&lserrors, _state);
    testfitsphereunit_testspherefittingns(&nserrors, _state);
    testfitsphereunit_testspherefittingvosswinkel2(&nserrors, _state);
    
    /*
     * report
     */
    wereerrors = nserrors||lserrors;
    if( !silent )
    {
        printf("TESTING FITSPHERE\n");
        printf("* LEAST SQUARES CIRCLE FITTING            ");
        if( lserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* NON-SMOOTH FITTING (MC, MI, MZ)         ");
        if( nserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( wereerrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !wereerrors;
    return result;
}


/*************************************************************************
This function tests least squares (LS) sphere fitting using generic
synthetic datasets

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testfitsphereunit_testspherefittingls(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    ae_matrix xy;
    double xtol;
    ae_int_t npoints;
    ae_int_t nx;
    ae_vector cx;
    ae_vector cy;
    double rlo;
    double rhi;
    double ftol;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double vv;
    double v0;
    double v1;
    ae_int_t problemtype;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&xy, 0, sizeof(xy));
    memset(&cx, 0, sizeof(cx));
    memset(&cy, 0, sizeof(cy));
    _hqrndstate_init(&rs, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cy, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    xtol = 1.0E-5;
    
    /*
     * Generate random problem
     */
    for(nx=1; nx<=4; nx++)
    {
        
        /*
         * Generate synthetic dataset, at least 5 points
         */
        npoints = 5+2*nx+hqrnduniformi(&rs, 50+ae_round(ae_pow((double)(4), (double)(nx), _state), _state), _state);
        ae_matrix_set_length(&xy, npoints, nx, _state);
        for(i=0; i<=npoints-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=nx-1; j++)
            {
                vv = hqrndnormal(&rs, _state);
                v = v+ae_sqr(vv, _state);
                xy.ptr.pp_double[i][j] = vv;
            }
            ae_assert(ae_fp_greater(v,(double)(0)), "Assertion failed", _state);
            v = (1+0.1*hqrnduniformr(&rs, _state))/ae_sqrt(v, _state);
            for(j=0; j<=nx-1; j++)
            {
                xy.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]*v;
            }
        }
        
        /*
         * Solve with generic solver, check
         */
        unsetrealarray(&cx, _state);
        rlo = (double)(0);
        rhi = (double)(0);
        problemtype = 0;
        fitspherex(&xy, npoints, nx, problemtype, 0.0, 0, 0.0, &cx, &rlo, &rhi, _state);
        ae_set_error_flag(err, ae_fp_neq(rlo,rhi), __FILE__, __LINE__, "testfitsphereunit.ap:109");
        vv = 0.0;
        for(i=0; i<=npoints-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=nx-1; j++)
            {
                v = v+ae_sqr(cx.ptr.p_double[j]-xy.ptr.pp_double[i][j], _state);
            }
            vv = vv+ae_sqrt(v, _state)/npoints;
        }
        ae_set_error_flag(err, ae_fp_greater(ae_fabs(vv-rlo, _state),xtol), __FILE__, __LINE__, "testfitsphereunit.ap:118");
        testfitsphereunit_calclserror(&xy, npoints, nx, &cx, &v0, _state);
        
        /*
         * Check that small perturbations to center position increase target function
         *
         * NOTE: in fact, we do allow small increase in target function - but no more
         *       than FTol=1E-6*XTol. It helps to avoid spurious error reports in
         *       degenerate cases.
         */
        ftol = 1.0E-6*xtol;
        ae_vector_set_length(&cy, nx, _state);
        for(j=0; j<=nx-1; j++)
        {
            for(k=0; k<=nx-1; k++)
            {
                cy.ptr.p_double[k] = cx.ptr.p_double[k];
            }
            cy.ptr.p_double[j] = cx.ptr.p_double[j]+xtol;
            testfitsphereunit_calclserror(&xy, npoints, nx, &cy, &v1, _state);
            ae_set_error_flag(err, ae_fp_less(v1,v0-ftol), __FILE__, __LINE__, "testfitsphereunit.ap:137");
            for(k=0; k<=nx-1; k++)
            {
                cy.ptr.p_double[k] = cx.ptr.p_double[k];
            }
            cy.ptr.p_double[j] = cx.ptr.p_double[j]-xtol;
            testfitsphereunit_calclserror(&xy, npoints, nx, &cy, &v1, _state);
            ae_set_error_flag(err, ae_fp_less(v1,v0-ftol), __FILE__, __LINE__, "testfitsphereunit.ap:144");
        }
        
        /*
         * Compare against results returned by specific solver
         */
        fitspherels(&xy, npoints, nx, &cy, &v, _state);
        ae_set_error_flag(err, ae_fp_neq(v,rlo), __FILE__, __LINE__, "testfitsphereunit.ap:151");
        for(j=0; j<=nx-1; j++)
        {
            ae_set_error_flag(err, ae_fp_neq(cy.ptr.p_double[j],cx.ptr.p_double[j]), __FILE__, __LINE__, "testfitsphereunit.ap:153");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests sphere fitting using generic synthetic datasets and
non-smooth target functions (MC, MI, MZ fitting)

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testfitsphereunit_testspherefittingns(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    ae_matrix xy;
    ae_int_t npoints;
    ae_int_t nx;
    ae_vector cx;
    ae_vector cy;
    double rlo;
    double rhi;
    double rlo2;
    double rhi2;
    double xtol;
    double ftol;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double vv;
    ae_int_t problemtype;
    double vlo;
    double vhi;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&xy, 0, sizeof(xy));
    memset(&cx, 0, sizeof(cx));
    memset(&cy, 0, sizeof(cy));
    _hqrndstate_init(&rs, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cy, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    xtol = 1.0E-5;
    
    /*
     * Generate random problem
     */
    for(nx=1; nx<=4; nx++)
    {
        
        /*
         * Generate synthetic dataset
         */
        npoints = 50+ae_round(ae_pow((double)(4), (double)(nx), _state), _state);
        ae_matrix_set_length(&xy, npoints, nx, _state);
        for(i=0; i<=npoints-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=nx-1; j++)
            {
                vv = hqrndnormal(&rs, _state);
                v = v+ae_sqr(vv, _state);
                xy.ptr.pp_double[i][j] = vv;
            }
            ae_assert(ae_fp_greater(v,(double)(0)), "Assertion failed", _state);
            v = (1+0.1*hqrnduniformr(&rs, _state))/ae_sqrt(v, _state);
            for(j=0; j<=nx-1; j++)
            {
                xy.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]*v;
            }
        }
        
        /*
         * Perform various kinds of fit, NLC solver is used
         */
        for(problemtype=1; problemtype<=3; problemtype++)
        {
            
            /*
             * Solve with generic solver
             */
            unsetrealarray(&cx, _state);
            rlo = (double)(0);
            rhi = (double)(0);
            fitspherex(&xy, npoints, nx, problemtype, 0.0, 0, 0.0, &cx, &rlo, &rhi, _state);
            
            /*
             * Check that small perturbations to center position increase target function
             *
             * NOTE: in fact, we do allow small increase in target function - but no more
             *       than FTol=1E-6*XTol. It helps to avoid spurious error reports in
             *       degenerate cases.
             */
            ftol = 1.0E-6*xtol;
            ae_vector_set_length(&cy, nx, _state);
            if( problemtype==2||problemtype==3 )
            {
                vlo = (double)(1);
            }
            else
            {
                vlo = (double)(0);
            }
            if( problemtype==1||problemtype==3 )
            {
                vhi = (double)(1);
            }
            else
            {
                vhi = (double)(0);
            }
            for(j=0; j<=nx-1; j++)
            {
                for(k=0; k<=nx-1; k++)
                {
                    cy.ptr.p_double[k] = cx.ptr.p_double[k];
                }
                cy.ptr.p_double[j] = cx.ptr.p_double[j]+xtol;
                testfitsphereunit_calcradii(&xy, npoints, nx, &cy, &rlo2, &rhi2, _state);
                ae_set_error_flag(err, ae_fp_less(rhi2*vhi-rlo2*vlo,rhi*vhi-rlo*vlo-ftol), __FILE__, __LINE__, "testfitsphereunit.ap:245");
                for(k=0; k<=nx-1; k++)
                {
                    cy.ptr.p_double[k] = cx.ptr.p_double[k];
                }
                cy.ptr.p_double[j] = cx.ptr.p_double[j]-xtol;
                testfitsphereunit_calcradii(&xy, npoints, nx, &cy, &rlo2, &rhi2, _state);
                ae_set_error_flag(err, ae_fp_less(rhi2*vhi-rlo2*vlo,rhi*vhi-rlo*vlo-ftol), __FILE__, __LINE__, "testfitsphereunit.ap:252");
            }
            
            /*
             * Compare against results returned by specific solver
             */
            if( problemtype==1 )
            {
                fitspheremc(&xy, npoints, nx, &cy, &rhi2, _state);
                ae_set_error_flag(err, ae_fp_neq(rhi2,rhi), __FILE__, __LINE__, "testfitsphereunit.ap:261");
                for(j=0; j<=nx-1; j++)
                {
                    ae_set_error_flag(err, ae_fp_neq(cy.ptr.p_double[j],cx.ptr.p_double[j]), __FILE__, __LINE__, "testfitsphereunit.ap:263");
                }
            }
            if( problemtype==2 )
            {
                fitspheremi(&xy, npoints, nx, &cy, &rlo2, _state);
                ae_set_error_flag(err, ae_fp_neq(rlo2,rlo), __FILE__, __LINE__, "testfitsphereunit.ap:268");
                for(j=0; j<=nx-1; j++)
                {
                    ae_set_error_flag(err, ae_fp_neq(cy.ptr.p_double[j],cx.ptr.p_double[j]), __FILE__, __LINE__, "testfitsphereunit.ap:270");
                }
            }
            if( problemtype==3 )
            {
                fitspheremz(&xy, npoints, nx, &cy, &rlo2, &rhi2, _state);
                ae_set_error_flag(err, ae_fp_neq(rlo2,rlo), __FILE__, __LINE__, "testfitsphereunit.ap:275");
                ae_set_error_flag(err, ae_fp_neq(rhi2,rhi), __FILE__, __LINE__, "testfitsphereunit.ap:276");
                for(j=0; j<=nx-1; j++)
                {
                    ae_set_error_flag(err, ae_fp_neq(cy.ptr.p_double[j],cx.ptr.p_double[j]), __FILE__, __LINE__, "testfitsphereunit.ap:278");
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests sphere fitting

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testfitsphereunit_testspherefittingvosswinkel2(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    ae_matrix xy;
    ae_int_t cnt;
    ae_vector cx;
    double rlo;
    double rhi;
    double tol;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&xy, 0, sizeof(xy));
    memset(&cx, 0, sizeof(cx));
    _hqrndstate_init(&rs, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cx, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Test problem #2 by Vosswinkel GmbH
     */
    ae_matrix_set_length(&xy, 40, 2, _state);
    cnt = 0;
    testfitsphereunit_addvalue(&xy, &cnt, 0.1026, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.000036, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.101119, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.016144, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.096754, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.031654, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.088981, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.045634, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.082056, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.06008, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.074966, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.075647, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.065, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.090471, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.052411, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.104381, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.036436, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.114859, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.019034, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.126577, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.001191, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.139295, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.024689, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.147143, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.049729, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.147861, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.076402, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.145907, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.103928, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.139553, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.133726, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.130429, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.159051, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.112298, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.179496, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.08821, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.194562, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.059989, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.204838, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.029135, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.206971, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.00349, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.206207, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.036427, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.197079, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.06806, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.180492, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.096353, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.158203, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.119891, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.132669, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.138375, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.105652, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.152229, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.078587, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.16316, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.049984, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.167084, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.022067, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.165233, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.004002, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.16075, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.028058, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.151829, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.050088, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.141178, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.067646, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.124169, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.081421, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.10567, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.087305, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.082618, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.094189, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.064399, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.099445, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.047018, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.09936, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.028981, _state);
    testfitsphereunit_addvalue(&xy, &cnt, 0.101784, _state);
    testfitsphereunit_addvalue(&xy, &cnt, -0.012918, _state);
    tol = 1.0E-7;
    
    /*
     * MZ problem, NLC solver
     */
    unsetrealarray(&cx, _state);
    rlo = (double)(0);
    rhi = (double)(0);
    fitspheremz(&xy, xy.rows, xy.cols, &cx, &rlo, &rhi, _state);
    ae_set_error_flag(err, cx.cnt!=2, __FILE__, __LINE__, "testfitsphereunit.ap:395");
    if( *err )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[0]+0.050884688, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:398");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[1]+0.011472328, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:399");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rlo-0.150973382, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:400");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rhi-0.164374709, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:401");
    unsetrealarray(&cx, _state);
    rlo = (double)(0);
    rhi = (double)(0);
    fitspherex(&xy, xy.rows, xy.cols, 3, 0.0, 0, 0.0, &cx, &rlo, &rhi, _state);
    ae_set_error_flag(err, cx.cnt!=2, __FILE__, __LINE__, "testfitsphereunit.ap:406");
    if( *err )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[0]+0.050884688, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:409");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[1]+0.011472328, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:410");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rlo-0.150973382, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:411");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rhi-0.164374709, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:412");
    
    /*
     * MC problem, NLC solver
     */
    unsetrealarray(&cx, _state);
    rlo = (double)(0);
    rhi = (double)(0);
    fitspheremc(&xy, xy.rows, xy.cols, &cx, &rhi, _state);
    ae_set_error_flag(err, cx.cnt!=2, __FILE__, __LINE__, "testfitsphereunit.ap:421");
    if( *err )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[0]+0.051137580, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:424");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[1]+0.011680985, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:425");
    ae_set_error_flag(err, ae_fp_neq(rlo,(double)(0)), __FILE__, __LINE__, "testfitsphereunit.ap:426");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rhi-0.164365735, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:427");
    unsetrealarray(&cx, _state);
    rlo = (double)(0);
    rhi = (double)(0);
    fitspherex(&xy, xy.rows, xy.cols, 1, 0.0, 0, 0.0, &cx, &rlo, &rhi, _state);
    ae_set_error_flag(err, cx.cnt!=2, __FILE__, __LINE__, "testfitsphereunit.ap:432");
    if( *err )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[0]+0.051137580, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:435");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[1]+0.011680985, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:436");
    ae_set_error_flag(err, ae_fp_neq(rlo,(double)(0)), __FILE__, __LINE__, "testfitsphereunit.ap:437");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rhi-0.164365735, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:438");
    
    /*
     * MI problem, NLC solver
     */
    unsetrealarray(&cx, _state);
    rlo = (double)(0);
    rhi = (double)(0);
    fitspheremi(&xy, xy.rows, xy.cols, &cx, &rlo, _state);
    ae_set_error_flag(err, cx.cnt!=2, __FILE__, __LINE__, "testfitsphereunit.ap:447");
    if( *err )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[0]+0.054593489, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:450");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[1]+0.007459466, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:451");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rlo-0.152429205, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:452");
    ae_set_error_flag(err, ae_fp_neq(rhi,(double)(0)), __FILE__, __LINE__, "testfitsphereunit.ap:453");
    unsetrealarray(&cx, _state);
    rlo = (double)(0);
    rhi = (double)(0);
    fitspherex(&xy, xy.rows, xy.cols, 2, 0.0, 0, 0.0, &cx, &rlo, &rhi, _state);
    ae_set_error_flag(err, cx.cnt!=2, __FILE__, __LINE__, "testfitsphereunit.ap:458");
    if( *err )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[0]+0.054593489, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:461");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(cx.ptr.p_double[1]+0.007459466, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:462");
    ae_set_error_flag(err, ae_fp_greater(ae_fabs(rlo-0.152429205, _state),tol), __FILE__, __LINE__, "testfitsphereunit.ap:463");
    ae_set_error_flag(err, ae_fp_neq(rhi,(double)(0)), __FILE__, __LINE__, "testfitsphereunit.ap:464");
    ae_frame_leave(_state);
}


/*************************************************************************
Used to calculate RLo/Rhi given XY and center position
*************************************************************************/
static void testfitsphereunit_calcradii(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nx,
     /* Real    */ ae_vector* cx,
     double* rlo,
     double* rhi,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;

    *rlo = 0;
    *rhi = 0;

    *rlo = ae_maxrealnumber;
    *rhi = (double)(0);
    for(i=0; i<=npoints-1; i++)
    {
        v = (double)(0);
        for(j=0; j<=nx-1; j++)
        {
            v = v+ae_sqr(xy->ptr.pp_double[i][j]-cx->ptr.p_double[j], _state);
        }
        v = ae_sqrt(v, _state);
        *rhi = ae_maxreal(*rhi, v, _state);
        *rlo = ae_minreal(*rlo, v, _state);
    }
}


/*************************************************************************
Used to calculate least squares error given XY and center position
*************************************************************************/
static void testfitsphereunit_calclserror(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nx,
     /* Real    */ ae_vector* cx,
     double* err,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    double rad;

    *err = 0;

    rad = 0.0;
    for(i=0; i<=npoints-1; i++)
    {
        v = (double)(0);
        for(j=0; j<=nx-1; j++)
        {
            v = v+ae_sqr(cx->ptr.p_double[j]-xy->ptr.pp_double[i][j], _state);
        }
        rad = rad+ae_sqrt(v, _state)/npoints;
    }
    *err = 0.0;
    for(i=0; i<=npoints-1; i++)
    {
        v = (double)(0);
        for(j=0; j<=nx-1; j++)
        {
            v = v+ae_sqr(cx->ptr.p_double[j]-xy->ptr.pp_double[i][j], _state);
        }
        *err = *err+ae_sqr(rad-ae_sqrt(v, _state), _state);
    }
}


/*************************************************************************
Used to initialize dynamic array with constant values
*************************************************************************/
static void testfitsphereunit_addvalue(/* Real    */ ae_matrix* xy,
     ae_int_t* cnt,
     double v,
     ae_state *_state)
{


    xy->ptr.pp_double[*cnt/xy->cols][*cnt%xy->cols] = v;
    *cnt = *cnt+1;
}



static void testspline1dunit_lconst(double a,
     double b,
     spline1dinterpolant* c,
     double lstep,
     double* l0,
     double* l1,
     double* l2,
     ae_state *_state);
static ae_bool testspline1dunit_enumerateallsplines(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* y,
     ae_int_t n,
     ae_int_t* splineindex,
     spline1dinterpolant* s,
     ae_state *_state);
static ae_bool testspline1dunit_testunpack(spline1dinterpolant* c,
     /* Real    */ ae_vector* x,
     ae_state *_state);
static void testspline1dunit_unsetspline1d(spline1dinterpolant* c,
     ae_state *_state);
static ae_bool testspline1dunit_testmonotonespline(ae_state *_state);
static void testspline1dunit_testsplinefitting(ae_bool* fiterrors,
     ae_state *_state);





ae_bool testspline1d(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool crserrors;
    ae_bool cserrors;
    ae_bool hserrors;
    ae_bool aserrors;
    ae_bool lserrors;
    ae_bool dserrors;
    ae_bool uperrors;
    ae_bool cperrors;
    ae_bool lterrors;
    ae_bool ierrors;
    ae_bool monotoneerr;
    ae_bool fiterr;
    double threshold;
    ae_int_t passcount;
    double lstep;
    double h;
    ae_int_t maxn;
    ae_int_t bltype;
    ae_int_t brtype;
    ae_bool periodiccond;
    ae_int_t n;
    ae_int_t i;
    ae_int_t k;
    ae_int_t pass;
    ae_vector x;
    ae_vector y;
    ae_vector yp;
    ae_vector w;
    ae_vector w2;
    ae_vector y2;
    ae_vector d;
    ae_vector xc;
    ae_vector yc;
    ae_vector xtest;
    ae_int_t n2;
    ae_vector tmp0;
    ae_vector tmp1;
    ae_vector tmp2;
    ae_vector tmpx;
    ae_vector dc;
    spline1dinterpolant c;
    spline1dinterpolant c2;
    double a;
    double b;
    double bl;
    double br;
    double t;
    double sa;
    double sb;
    double v;
    double l10;
    double l11;
    double l12;
    double l20;
    double l21;
    double l22;
    double p0;
    double p1;
    double p2;
    double s;
    double ds;
    double d2s;
    double s2;
    double ds2;
    double d2s2;
    double vl;
    double vm;
    double vr;
    double err;
    double tension;
    double intab;
    ae_int_t splineindex;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&yp, 0, sizeof(yp));
    memset(&w, 0, sizeof(w));
    memset(&w2, 0, sizeof(w2));
    memset(&y2, 0, sizeof(y2));
    memset(&d, 0, sizeof(d));
    memset(&xc, 0, sizeof(xc));
    memset(&yc, 0, sizeof(yc));
    memset(&xtest, 0, sizeof(xtest));
    memset(&tmp0, 0, sizeof(tmp0));
    memset(&tmp1, 0, sizeof(tmp1));
    memset(&tmp2, 0, sizeof(tmp2));
    memset(&tmpx, 0, sizeof(tmpx));
    memset(&dc, 0, sizeof(dc));
    memset(&c, 0, sizeof(c));
    memset(&c2, 0, sizeof(c2));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xtest, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _spline1dinterpolant_init(&c, _state, ae_true);
    _spline1dinterpolant_init(&c2, _state, ae_true);

    waserrors = ae_false;
    passcount = 20;
    lstep = 0.005;
    h = 0.00001;
    maxn = 10;
    threshold = 10000*ae_machineepsilon;
    lserrors = ae_false;
    cserrors = ae_false;
    crserrors = ae_false;
    hserrors = ae_false;
    aserrors = ae_false;
    dserrors = ae_false;
    cperrors = ae_false;
    uperrors = ae_false;
    lterrors = ae_false;
    ierrors = ae_false;
    fiterr = ae_false;
    
    /*
     * General test: linear, cubic, Hermite, Akima
     */
    for(n=2; n<=maxn; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        ae_vector_set_length(&yp, n-1+1, _state);
        ae_vector_set_length(&d, n-1+1, _state);
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Prepare task:
             * * X contains abscissas from [A,B]
             * * Y contains function values
             * * YP contains periodic function values
             */
            a = -1-ae_randomreal(_state);
            b = 1+ae_randomreal(_state);
            bl = 2*ae_randomreal(_state)-1;
            br = 2*ae_randomreal(_state)-1;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 0.5*(b+a)+0.5*(b-a)*ae_cos(ae_pi*(2*i+1)/(2*n), _state);
                if( i==0 )
                {
                    x.ptr.p_double[i] = a;
                }
                if( i==n-1 )
                {
                    x.ptr.p_double[i] = b;
                }
                y.ptr.p_double[i] = ae_cos(1.3*ae_pi*x.ptr.p_double[i]+0.4, _state);
                yp.ptr.p_double[i] = y.ptr.p_double[i];
                d.ptr.p_double[i] = -1.3*ae_pi*ae_sin(1.3*ae_pi*x.ptr.p_double[i]+0.4, _state);
            }
            yp.ptr.p_double[n-1] = yp.ptr.p_double[0];
            for(i=0; i<=n-1; i++)
            {
                k = ae_randominteger(n, _state);
                if( k!=i )
                {
                    t = x.ptr.p_double[i];
                    x.ptr.p_double[i] = x.ptr.p_double[k];
                    x.ptr.p_double[k] = t;
                    t = y.ptr.p_double[i];
                    y.ptr.p_double[i] = y.ptr.p_double[k];
                    y.ptr.p_double[k] = t;
                    t = yp.ptr.p_double[i];
                    yp.ptr.p_double[i] = yp.ptr.p_double[k];
                    yp.ptr.p_double[k] = t;
                    t = d.ptr.p_double[i];
                    d.ptr.p_double[i] = d.ptr.p_double[k];
                    d.ptr.p_double[k] = t;
                }
            }
            
            /*
             * Build linear spline
             * Test for general interpolation scheme properties:
             * * values at nodes
             * * continuous function
             * Test for specific properties is implemented below.
             */
            spline1dbuildlinear(&x, &y, n, &c, _state);
            err = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
            }
            lserrors = lserrors||ae_fp_greater(err,threshold);
            testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
            testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
            lserrors = lserrors||ae_fp_greater(l20/l10,1.2);
            
            /*
             * Build cubic spline.
             * Test for interpolation scheme properties:
             * * values at nodes
             * * boundary conditions
             * * continuous function
             * * continuous first derivative
             * * continuous second derivative
             * * periodicity properties
             * * Spline1DGridDiff(), Spline1DGridDiff2() and Spline1DDiff()
             *   calls must return same results
             */
            for(bltype=-1; bltype<=2; bltype++)
            {
                for(brtype=-1; brtype<=2; brtype++)
                {
                    
                    /*
                     * skip meaningless combination of boundary conditions
                     * (one condition is periodic, another is not)
                     */
                    periodiccond = bltype==-1||brtype==-1;
                    if( periodiccond&&bltype!=brtype )
                    {
                        continue;
                    }
                    
                    /*
                     * build
                     */
                    if( periodiccond )
                    {
                        spline1dbuildcubic(&x, &yp, n, bltype, bl, brtype, br, &c, _state);
                    }
                    else
                    {
                        spline1dbuildcubic(&x, &y, n, bltype, bl, brtype, br, &c, _state);
                    }
                    
                    /*
                     * interpolation properties
                     */
                    err = (double)(0);
                    if( periodiccond )
                    {
                        
                        /*
                         * * check values at nodes; spline is periodic so
                         *   we add random number of periods to nodes
                         * * we also test for periodicity of derivatives
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            v = x.ptr.p_double[i];
                            vm = v+(b-a)*(ae_randominteger(5, _state)-2);
                            t = yp.ptr.p_double[i]-spline1dcalc(&c, vm, _state);
                            err = ae_maxreal(err, ae_fabs(t, _state), _state);
                            spline1ddiff(&c, v, &s, &ds, &d2s, _state);
                            spline1ddiff(&c, vm, &s2, &ds2, &d2s2, _state);
                            err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                            err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                            err = ae_maxreal(err, ae_fabs(d2s-d2s2, _state), _state);
                        }
                        
                        /*
                         * periodicity between nodes
                         */
                        v = a+(b-a)*ae_randomreal(_state);
                        vm = v+(b-a)*(ae_randominteger(5, _state)-2);
                        err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, v, _state)-spline1dcalc(&c, vm, _state), _state), _state);
                        spline1ddiff(&c, v, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, vm, &s2, &ds2, &d2s2, _state);
                        err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(d2s-d2s2, _state), _state);
                    }
                    else
                    {
                        
                        /*
                         * * check values at nodes
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
                        }
                    }
                    cserrors = cserrors||ae_fp_greater(err,threshold);
                    
                    /*
                     * check boundary conditions
                     */
                    err = (double)(0);
                    if( bltype==0 )
                    {
                        spline1ddiff(&c, a-h, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, a+h, &s2, &ds2, &d2s2, _state);
                        t = (d2s2-d2s)/(2*h);
                        err = ae_maxreal(err, ae_fabs(t, _state), _state);
                    }
                    if( bltype==1 )
                    {
                        t = (spline1dcalc(&c, a+h, _state)-spline1dcalc(&c, a-h, _state))/(2*h);
                        err = ae_maxreal(err, ae_fabs(bl-t, _state), _state);
                    }
                    if( bltype==2 )
                    {
                        t = (spline1dcalc(&c, a+h, _state)-2*spline1dcalc(&c, a, _state)+spline1dcalc(&c, a-h, _state))/ae_sqr(h, _state);
                        err = ae_maxreal(err, ae_fabs(bl-t, _state), _state);
                    }
                    if( brtype==0 )
                    {
                        spline1ddiff(&c, b-h, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, b+h, &s2, &ds2, &d2s2, _state);
                        t = (d2s2-d2s)/(2*h);
                        err = ae_maxreal(err, ae_fabs(t, _state), _state);
                    }
                    if( brtype==1 )
                    {
                        t = (spline1dcalc(&c, b+h, _state)-spline1dcalc(&c, b-h, _state))/(2*h);
                        err = ae_maxreal(err, ae_fabs(br-t, _state), _state);
                    }
                    if( brtype==2 )
                    {
                        t = (spline1dcalc(&c, b+h, _state)-2*spline1dcalc(&c, b, _state)+spline1dcalc(&c, b-h, _state))/ae_sqr(h, _state);
                        err = ae_maxreal(err, ae_fabs(br-t, _state), _state);
                    }
                    if( bltype==-1||brtype==-1 )
                    {
                        spline1ddiff(&c, a+100*ae_machineepsilon, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, b-100*ae_machineepsilon, &s2, &ds2, &d2s2, _state);
                        err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(d2s-d2s2, _state), _state);
                    }
                    cserrors = cserrors||ae_fp_greater(err,1.0E-3);
                    
                    /*
                     * Check Lipschitz continuity
                     */
                    testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
                    testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
                    if( ae_fp_greater(l10,1.0E-6) )
                    {
                        cserrors = cserrors||ae_fp_greater(l20/l10,1.2);
                    }
                    if( ae_fp_greater(l11,1.0E-6) )
                    {
                        cserrors = cserrors||ae_fp_greater(l21/l11,1.2);
                    }
                    if( ae_fp_greater(l12,1.0E-6) )
                    {
                        cserrors = cserrors||ae_fp_greater(l22/l12,1.2);
                    }
                    
                    /*
                     * compare spline1dgriddiff() and spline1ddiff() results
                     */
                    err = (double)(0);
                    if( periodiccond )
                    {
                        spline1dgriddiffcubic(&x, &yp, n, bltype, bl, brtype, br, &tmp1, _state);
                    }
                    else
                    {
                        spline1dgriddiffcubic(&x, &y, n, bltype, bl, brtype, br, &tmp1, _state);
                    }
                    ae_assert(tmp1.cnt>=n, "Assertion failed", _state);
                    for(i=0; i<=n-1; i++)
                    {
                        spline1ddiff(&c, x.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(ds-tmp1.ptr.p_double[i], _state), _state);
                    }
                    if( periodiccond )
                    {
                        spline1dgriddiff2cubic(&x, &yp, n, bltype, bl, brtype, br, &tmp1, &tmp2, _state);
                    }
                    else
                    {
                        spline1dgriddiff2cubic(&x, &y, n, bltype, bl, brtype, br, &tmp1, &tmp2, _state);
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        spline1ddiff(&c, x.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(ds-tmp1.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(d2s-tmp2.ptr.p_double[i], _state), _state);
                    }
                    cserrors = cserrors||ae_fp_greater(err,threshold);
                    
                    /*
                     * compare spline1dconv()/convdiff()/convdiff2() and spline1ddiff() results
                     */
                    n2 = 2+ae_randominteger(2*n, _state);
                    ae_vector_set_length(&tmpx, n2, _state);
                    for(i=0; i<=n2-1; i++)
                    {
                        tmpx.ptr.p_double[i] = 0.5*(a+b)+(a-b)*(2*ae_randomreal(_state)-1);
                    }
                    err = (double)(0);
                    if( periodiccond )
                    {
                        spline1dconvcubic(&x, &yp, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, _state);
                    }
                    else
                    {
                        spline1dconvcubic(&x, &y, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, _state);
                    }
                    for(i=0; i<=n2-1; i++)
                    {
                        spline1ddiff(&c, tmpx.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(s-tmp0.ptr.p_double[i], _state), _state);
                    }
                    if( periodiccond )
                    {
                        spline1dconvdiffcubic(&x, &yp, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, &tmp1, _state);
                    }
                    else
                    {
                        spline1dconvdiffcubic(&x, &y, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, &tmp1, _state);
                    }
                    for(i=0; i<=n2-1; i++)
                    {
                        spline1ddiff(&c, tmpx.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(s-tmp0.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-tmp1.ptr.p_double[i], _state), _state);
                    }
                    if( periodiccond )
                    {
                        spline1dconvdiff2cubic(&x, &yp, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, &tmp1, &tmp2, _state);
                    }
                    else
                    {
                        spline1dconvdiff2cubic(&x, &y, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, &tmp1, &tmp2, _state);
                    }
                    for(i=0; i<=n2-1; i++)
                    {
                        spline1ddiff(&c, tmpx.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(s-tmp0.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-tmp1.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(d2s-tmp2.ptr.p_double[i], _state), _state);
                    }
                    cserrors = cserrors||ae_fp_greater(err,threshold);
                }
            }
            
            /*
             * Build Catmull-Rom spline.
             * Test for interpolation scheme properties:
             * * values at nodes
             * * boundary conditions
             * * continuous function
             * * continuous first derivative
             * * periodicity properties
             */
            for(bltype=-1; bltype<=0; bltype++)
            {
                periodiccond = bltype==-1;
                
                /*
                 * select random tension value, then build
                 */
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    if( ae_fp_greater(ae_randomreal(_state),0.5) )
                    {
                        tension = (double)(0);
                    }
                    else
                    {
                        tension = (double)(1);
                    }
                }
                else
                {
                    tension = ae_randomreal(_state);
                }
                if( periodiccond )
                {
                    spline1dbuildcatmullrom(&x, &yp, n, bltype, tension, &c, _state);
                }
                else
                {
                    spline1dbuildcatmullrom(&x, &y, n, bltype, tension, &c, _state);
                }
                
                /*
                 * interpolation properties
                 */
                err = (double)(0);
                if( periodiccond )
                {
                    
                    /*
                     * * check values at nodes; spline is periodic so
                     *   we add random number of periods to nodes
                     * * we also test for periodicity of first derivative
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        v = x.ptr.p_double[i];
                        vm = v+(b-a)*(ae_randominteger(5, _state)-2);
                        t = yp.ptr.p_double[i]-spline1dcalc(&c, vm, _state);
                        err = ae_maxreal(err, ae_fabs(t, _state), _state);
                        spline1ddiff(&c, v, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, vm, &s2, &ds2, &d2s2, _state);
                        err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                    }
                    
                    /*
                     * periodicity between nodes
                     */
                    v = a+(b-a)*ae_randomreal(_state);
                    vm = v+(b-a)*(ae_randominteger(5, _state)-2);
                    err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, v, _state)-spline1dcalc(&c, vm, _state), _state), _state);
                    spline1ddiff(&c, v, &s, &ds, &d2s, _state);
                    spline1ddiff(&c, vm, &s2, &ds2, &d2s2, _state);
                    err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                    err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                }
                else
                {
                    
                    /*
                     * * check values at nodes
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
                    }
                }
                crserrors = crserrors||ae_fp_greater(err,threshold);
                
                /*
                 * check boundary conditions
                 */
                err = (double)(0);
                if( bltype==0 )
                {
                    spline1ddiff(&c, a-h, &s, &ds, &d2s, _state);
                    spline1ddiff(&c, a+h, &s2, &ds2, &d2s2, _state);
                    t = (d2s2-d2s)/(2*h);
                    err = ae_maxreal(err, ae_fabs(t, _state), _state);
                    spline1ddiff(&c, b-h, &s, &ds, &d2s, _state);
                    spline1ddiff(&c, b+h, &s2, &ds2, &d2s2, _state);
                    t = (d2s2-d2s)/(2*h);
                    err = ae_maxreal(err, ae_fabs(t, _state), _state);
                }
                if( bltype==-1 )
                {
                    spline1ddiff(&c, a+100*ae_machineepsilon, &s, &ds, &d2s, _state);
                    spline1ddiff(&c, b-100*ae_machineepsilon, &s2, &ds2, &d2s2, _state);
                    err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                    err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                }
                crserrors = crserrors||ae_fp_greater(err,1.0E-3);
                
                /*
                 * Check Lipschitz continuity
                 */
                testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
                testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
                if( ae_fp_greater(l10,1.0E-6) )
                {
                    crserrors = crserrors||ae_fp_greater(l20/l10,1.2);
                }
                if( ae_fp_greater(l11,1.0E-6) )
                {
                    crserrors = crserrors||ae_fp_greater(l21/l11,1.2);
                }
            }
            
            /*
             * Build Hermite spline.
             * Test for interpolation scheme properties:
             * * values and derivatives at nodes
             * * continuous function
             * * continuous first derivative
             */
            spline1dbuildhermite(&x, &y, &d, n, &c, _state);
            err = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
            }
            hserrors = hserrors||ae_fp_greater(err,threshold);
            err = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                t = (spline1dcalc(&c, x.ptr.p_double[i]+h, _state)-spline1dcalc(&c, x.ptr.p_double[i]-h, _state))/(2*h);
                err = ae_maxreal(err, ae_fabs(d.ptr.p_double[i]-t, _state), _state);
            }
            hserrors = hserrors||ae_fp_greater(err,1.0E-3);
            testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
            testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
            hserrors = hserrors||ae_fp_greater(l20/l10,1.2);
            hserrors = hserrors||ae_fp_greater(l21/l11,1.2);
            
            /*
             * Build Akima spline
             * Test for general interpolation scheme properties:
             * * values at nodes
             * * continuous function
             * * continuous first derivative
             * Test for Akima-specific properties is implemented below.
             */
            spline1dbuildakima(&x, &y, n, &c, _state);
            err = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
            }
            aserrors = aserrors||ae_fp_greater(err,threshold);
            testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
            testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
            hserrors = hserrors||(ae_fp_greater(l10,1.0E-10)&&ae_fp_greater(l20/l10,1.2));
            hserrors = hserrors||(ae_fp_greater(l11,1.0E-10)&&ae_fp_greater(l21/l11,1.2));
        }
    }
    
    /*
     * Special linear spline test:
     * test for linearity between x[i] and x[i+1]
     */
    for(n=2; n<=maxn; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        
        /*
         * Prepare task
         */
        a = (double)(-1);
        b = (double)(1);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = a+(b-a)*i/(n-1);
            y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        spline1dbuildlinear(&x, &y, n, &c, _state);
        
        /*
         * Test
         */
        err = (double)(0);
        for(k=0; k<=n-2; k++)
        {
            a = x.ptr.p_double[k];
            b = x.ptr.p_double[k+1];
            for(pass=1; pass<=passcount; pass++)
            {
                t = a+(b-a)*ae_randomreal(_state);
                v = y.ptr.p_double[k]+(t-a)/(b-a)*(y.ptr.p_double[k+1]-y.ptr.p_double[k]);
                err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, t, _state)-v, _state), _state);
            }
        }
        lserrors = lserrors||ae_fp_greater(err,threshold);
    }
    
    /*
     * Special Akima test: test outlier sensitivity
     * Spline value at (x[i], x[i+1]) should depend from
     * f[i-2], f[i-1], f[i], f[i+1], f[i+2], f[i+3] only.
     */
    for(n=5; n<=maxn; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        ae_vector_set_length(&y2, n-1+1, _state);
        
        /*
         * Prepare unperturbed Akima spline
         */
        a = (double)(-1);
        b = (double)(1);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = a+(b-a)*i/(n-1);
            y.ptr.p_double[i] = ae_cos(1.3*ae_pi*x.ptr.p_double[i]+0.4, _state);
        }
        spline1dbuildakima(&x, &y, n, &c, _state);
        
        /*
         * Process perturbed tasks
         */
        err = (double)(0);
        for(k=0; k<=n-1; k++)
        {
            ae_v_move(&y2.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
            y2.ptr.p_double[k] = (double)(5);
            spline1dbuildakima(&x, &y2, n, &c2, _state);
            
            /*
             * Test left part independence
             */
            if( k-3>=1 )
            {
                a = (double)(-1);
                b = x.ptr.p_double[k-3];
                for(pass=1; pass<=passcount; pass++)
                {
                    t = a+(b-a)*ae_randomreal(_state);
                    err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, t, _state)-spline1dcalc(&c2, t, _state), _state), _state);
                }
            }
            
            /*
             * Test right part independence
             */
            if( k+3<=n-2 )
            {
                a = x.ptr.p_double[k+3];
                b = (double)(1);
                for(pass=1; pass<=passcount; pass++)
                {
                    t = a+(b-a)*ae_randomreal(_state);
                    err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, t, _state)-spline1dcalc(&c2, t, _state), _state), _state);
                }
            }
        }
        aserrors = aserrors||ae_fp_greater(err,threshold);
    }
    
    /*
     * Differentiation, copy/unpack test
     */
    for(n=2; n<=maxn; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        
        /*
         * Prepare cubic spline
         */
        a = -1-ae_randomreal(_state);
        b = 1+ae_randomreal(_state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = a+(b-a)*i/(n-1);
            y.ptr.p_double[i] = ae_cos(1.3*ae_pi*x.ptr.p_double[i]+0.4, _state);
        }
        spline1dbuildcubic(&x, &y, n, 2, 0.0, 2, 0.0, &c, _state);
        
        /*
         * Test diff
         */
        err = (double)(0);
        for(pass=1; pass<=passcount; pass++)
        {
            t = a+(b-a)*ae_randomreal(_state);
            spline1ddiff(&c, t, &s, &ds, &d2s, _state);
            vl = spline1dcalc(&c, t-h, _state);
            vm = spline1dcalc(&c, t, _state);
            vr = spline1dcalc(&c, t+h, _state);
            err = ae_maxreal(err, ae_fabs(s-vm, _state), _state);
            err = ae_maxreal(err, ae_fabs(ds-(vr-vl)/(2*h), _state), _state);
            err = ae_maxreal(err, ae_fabs(d2s-(vr-2*vm+vl)/ae_sqr(h, _state), _state), _state);
        }
        dserrors = dserrors||ae_fp_greater(err,0.001);
        
        /*
         * Test copy
         */
        testspline1dunit_unsetspline1d(&c2, _state);
        spline1dcopy(&c, &c2, _state);
        err = (double)(0);
        for(pass=1; pass<=passcount; pass++)
        {
            t = a+(b-a)*ae_randomreal(_state);
            err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, t, _state)-spline1dcalc(&c2, t, _state), _state), _state);
        }
        cperrors = cperrors||ae_fp_greater(err,threshold);
        
        /*
         * Test unpack
         */
        uperrors = uperrors||!testspline1dunit_testunpack(&c, &x, _state);
    }
    
    /*
     * Linear translation errors
     */
    for(n=2; n<=maxn; n++)
    {
        
        /*
         * Prepare:
         * * X, Y - grid points
         * * XTest - test points
         */
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&y, n, _state);
        a = -1-ae_randomreal(_state);
        b = 1+ae_randomreal(_state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = a+(b-a)*(i+0.2*ae_randomreal(_state)-0.1)/(n-1);
            y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        ae_vector_set_length(&xtest, 5*n+2, _state);
        for(i=0; i<=xtest.cnt-1; i++)
        {
            xtest.ptr.p_double[i] = a+(b-a)*(i-1)/(xtest.cnt-3);
        }
        splineindex = 0;
        while(testspline1dunit_enumerateallsplines(&x, &y, n, &splineindex, &c, _state))
        {
            
            /*
             * LinTransX, general A
             */
            sa = 4*ae_randomreal(_state)-2;
            sb = 2*ae_randomreal(_state)-1;
            spline1dcopy(&c, &c2, _state);
            spline1dlintransx(&c2, sa, sb, _state);
            for(i=0; i<=xtest.cnt-1; i++)
            {
                lterrors = lterrors||ae_fp_greater(ae_fabs(spline1dcalc(&c, xtest.ptr.p_double[i], _state)-spline1dcalc(&c2, (xtest.ptr.p_double[i]-sb)/sa, _state), _state),threshold);
            }
            
            /*
             * LinTransX, special case: A=0
             */
            sb = 2*ae_randomreal(_state)-1;
            spline1dcopy(&c, &c2, _state);
            spline1dlintransx(&c2, (double)(0), sb, _state);
            for(i=0; i<=xtest.cnt-1; i++)
            {
                lterrors = lterrors||ae_fp_greater(ae_fabs(spline1dcalc(&c, sb, _state)-spline1dcalc(&c2, xtest.ptr.p_double[i], _state), _state),threshold);
            }
            
            /*
             * LinTransY
             */
            sa = 2*ae_randomreal(_state)-1;
            sb = 2*ae_randomreal(_state)-1;
            spline1dcopy(&c, &c2, _state);
            spline1dlintransy(&c2, sa, sb, _state);
            for(i=0; i<=xtest.cnt-1; i++)
            {
                lterrors = lterrors||ae_fp_greater(ae_fabs(sa*spline1dcalc(&c, xtest.ptr.p_double[i], _state)+sb-spline1dcalc(&c2, xtest.ptr.p_double[i], _state), _state),threshold);
            }
        }
    }
    
    /*
     * Testing integration.
     * Three tests are performed:
     *
     * * approximate test (well behaved smooth function, many points,
     *   integration inside [a,b]), non-periodic spline
     *
     * * exact test (integration of parabola, outside of [a,b], non-periodic spline
     *
     * * approximate test for periodic splines. F(x)=cos(2*pi*x)+1.
     *   Period length is equals to 1.0, so all operations with
     *   multiples of period are done exactly. For each value of PERIOD
     *   we calculate and test integral at four points:
     *   -   0 < t0 < PERIOD
     *   -   t1 = PERIOD-eps
     *   -   t2 = PERIOD
     *   -   t3 = PERIOD+eps
     */
    err = (double)(0);
    for(n=20; n<=35; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Prepare cubic spline
             */
            a = -1-0.2*ae_randomreal(_state);
            b = 1+0.2*ae_randomreal(_state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = a+(b-a)*i/(n-1);
                y.ptr.p_double[i] = ae_sin(ae_pi*x.ptr.p_double[i]+0.4, _state)+ae_exp(x.ptr.p_double[i], _state);
            }
            bl = ae_pi*ae_cos(ae_pi*a+0.4, _state)+ae_exp(a, _state);
            br = ae_pi*ae_cos(ae_pi*b+0.4, _state)+ae_exp(b, _state);
            spline1dbuildcubic(&x, &y, n, 1, bl, 1, br, &c, _state);
            
            /*
             * Test
             */
            t = a+(b-a)*ae_randomreal(_state);
            v = -ae_cos(ae_pi*a+0.4, _state)/ae_pi+ae_exp(a, _state);
            v = -ae_cos(ae_pi*t+0.4, _state)/ae_pi+ae_exp(t, _state)-v;
            v = v-spline1dintegrate(&c, t, _state);
            err = ae_maxreal(err, ae_fabs(v, _state), _state);
        }
    }
    ierrors = ierrors||ae_fp_greater(err,0.001);
    p0 = 2*ae_randomreal(_state)-1;
    p1 = 2*ae_randomreal(_state)-1;
    p2 = 2*ae_randomreal(_state)-1;
    a = -ae_randomreal(_state)-0.5;
    b = ae_randomreal(_state)+0.5;
    n = 2;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&y, n, _state);
    ae_vector_set_length(&d, n, _state);
    x.ptr.p_double[0] = a;
    y.ptr.p_double[0] = p0+p1*a+p2*ae_sqr(a, _state);
    d.ptr.p_double[0] = p1+2*p2*a;
    x.ptr.p_double[1] = b;
    y.ptr.p_double[1] = p0+p1*b+p2*ae_sqr(b, _state);
    d.ptr.p_double[1] = p1+2*p2*b;
    spline1dbuildhermite(&x, &y, &d, n, &c, _state);
    bl = ae_minreal(a, b, _state)-ae_fabs(b-a, _state);
    br = ae_minreal(a, b, _state)+ae_fabs(b-a, _state);
    err = (double)(0);
    for(pass=1; pass<=100; pass++)
    {
        t = bl+(br-bl)*ae_randomreal(_state);
        v = p0*t+p1*ae_sqr(t, _state)/2+p2*ae_sqr(t, _state)*t/3-(p0*a+p1*ae_sqr(a, _state)/2+p2*ae_sqr(a, _state)*a/3);
        v = v-spline1dintegrate(&c, t, _state);
        err = ae_maxreal(err, ae_fabs(v, _state), _state);
    }
    ierrors = ierrors||ae_fp_greater(err,threshold);
    n = 100;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&y, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = (double)i/(double)(n-1);
        y.ptr.p_double[i] = ae_cos(2*ae_pi*x.ptr.p_double[i], _state)+1;
    }
    y.ptr.p_double[0] = (double)(2);
    y.ptr.p_double[n-1] = (double)(2);
    spline1dbuildcubic(&x, &y, n, -1, 0.0, -1, 0.0, &c, _state);
    intab = spline1dintegrate(&c, 1.0, _state);
    v = ae_randomreal(_state);
    vr = spline1dintegrate(&c, v, _state);
    ierrors = ierrors||ae_fp_greater(ae_fabs(intab-1, _state),0.001);
    for(i=-10; i<=10; i++)
    {
        ierrors = ierrors||ae_fp_greater(ae_fabs(spline1dintegrate(&c, i+v, _state)-(i*intab+vr), _state),0.001);
        ierrors = ierrors||ae_fp_greater(ae_fabs(spline1dintegrate(&c, i-1000*ae_machineepsilon, _state)-i*intab, _state),0.001);
        ierrors = ierrors||ae_fp_greater(ae_fabs(spline1dintegrate(&c, (double)(i), _state)-i*intab, _state),0.001);
        ierrors = ierrors||ae_fp_greater(ae_fabs(spline1dintegrate(&c, i+1000*ae_machineepsilon, _state)-i*intab, _state),0.001);
    }
    
    /*
     * Test monotone cubic Hermit interpolation
     */
    monotoneerr = testspline1dunit_testmonotonespline(_state);
    
    /*
     * Test fitting errors
     */
    testspline1dunit_testsplinefitting(&fiterr, _state);
    
    /*
     * report
     */
    waserrors = ((((((((((lserrors||cserrors)||crserrors)||hserrors)||aserrors)||dserrors)||cperrors)||uperrors)||lterrors)||ierrors)||monotoneerr)||fiterr;
    if( !silent )
    {
        printf("TESTING SPLINE INTERPOLATION\n");
        
        /*
         * Normal tests
         */
        printf("INTERPOLATION:\n");
        printf("* LINEAR SPLINE                          ");
        if( lserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* CUBIC SPLINE                           ");
        if( cserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* CATMULL-ROM SPLINE                     ");
        if( crserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* HERMITE SPLINE                         ");
        if( hserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AKIMA SPLINE                           ");
        if( aserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* MONOTONE SPLINE                        ");
        if( monotoneerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("FITTING:                                 ");
        if( fiterr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("DIFFERENTIATION TEST:                    ");
        if( dserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("COPY/SERIALIZATION TEST:                 ");
        if( cperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("UNPACK TEST:                             ");
        if( uperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LIN.TRANS. TEST:                         ");
        if( lterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("INTEGRATION TEST:                        ");
        if( ierrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Lipschitz constants for spline inself, first and second derivatives.
*************************************************************************/
static void testspline1dunit_lconst(double a,
     double b,
     spline1dinterpolant* c,
     double lstep,
     double* l0,
     double* l1,
     double* l2,
     ae_state *_state)
{
    double t;
    double vl;
    double vm;
    double vr;
    double prevf;
    double prevd;
    double prevd2;
    double f;
    double d;
    double d2;

    *l0 = 0;
    *l1 = 0;
    *l2 = 0;

    *l0 = (double)(0);
    *l1 = (double)(0);
    *l2 = (double)(0);
    t = a-0.1;
    vl = spline1dcalc(c, t-2*lstep, _state);
    vm = spline1dcalc(c, t-lstep, _state);
    vr = spline1dcalc(c, t, _state);
    f = vm;
    d = (vr-vl)/(2*lstep);
    d2 = (vr-2*vm+vl)/ae_sqr(lstep, _state);
    while(ae_fp_less_eq(t,b+0.1))
    {
        prevf = f;
        prevd = d;
        prevd2 = d2;
        vl = vm;
        vm = vr;
        vr = spline1dcalc(c, t+lstep, _state);
        f = vm;
        d = (vr-vl)/(2*lstep);
        d2 = (vr-2*vm+vl)/ae_sqr(lstep, _state);
        *l0 = ae_maxreal(*l0, ae_fabs((f-prevf)/lstep, _state), _state);
        *l1 = ae_maxreal(*l1, ae_fabs((d-prevd)/lstep, _state), _state);
        *l2 = ae_maxreal(*l2, ae_fabs((d2-prevd2)/lstep, _state), _state);
        t = t+lstep;
    }
}


/*************************************************************************
This function is used to enumerate all spline types  which  can  be  built
from given dataset. It should be used as follows:

>
> init X, Y, N
> SplineIndex:=0;
> while EnumerateAllSplines(X, Y, N, SplineIndex, S) do
> begin
>     do something with S
> end;
>

On initial call EnumerateAllSplines accepts:
* dataset X, Y, number of points N (N>=2)
* SplineIndex, equal to 0

It returns:
* True, in case there is a spline type which corresponds to SplineIndex.
  In this case S contains spline which was built using X/Y and spline type,
  as specified by input value of SplineIndex. SplineIndex is advanced to
  the next value.
* False, in case SplineIndex contains past-the-end value, spline is not built.

This function tries different variants of linear/cubic, periodic/nonperiodic
splines.
*************************************************************************/
static ae_bool testspline1dunit_enumerateallsplines(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* y,
     ae_int_t n,
     ae_int_t* splineindex,
     spline1dinterpolant* s,
     ae_state *_state)
{
    ae_int_t idxoffs;
    ae_bool result;

    _spline1dinterpolant_clear(s);

    ae_assert(*splineindex>=0, "Assertion failed", _state);
    result = ae_false;
    if( *splineindex==0 )
    {
        
        /*
         * Linear spline
         */
        spline1dbuildlinear(x, y, n, s, _state);
        *splineindex = *splineindex+1;
        result = ae_true;
        return result;
    }
    else
    {
        if( *splineindex>=1&&*splineindex<11 )
        {
            
            /*
             * Cubic spline, either periodic or non-periodic
             */
            idxoffs = *splineindex-1;
            if( idxoffs==9 )
            {
                
                /*
                 * Periodic spline
                 */
                spline1dbuildcubic(x, y, n, -1, 0.0, -1, 0.0, s, _state);
            }
            else
            {
                
                /*
                 * Non-periodic spline
                 */
                spline1dbuildcubic(x, y, n, idxoffs/3, 2*ae_randomreal(_state)-1, idxoffs%3, 2*ae_randomreal(_state)-1, s, _state);
            }
            *splineindex = *splineindex+1;
            result = ae_true;
            return result;
        }
    }
    return result;
}


/*************************************************************************
Unpack testing
*************************************************************************/
static ae_bool testspline1dunit_testunpack(spline1dinterpolant* c,
     /* Real    */ ae_vector* x,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t n;
    double err;
    double t;
    double v1;
    double v2;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix tbl;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&tbl, 0, sizeof(tbl));
    ae_matrix_init(&tbl, 0, 0, DT_REAL, _state, ae_true);

    passcount = 20;
    err = (double)(0);
    spline1dunpack(c, &n, &tbl, _state);
    for(i=0; i<=n-2; i++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            t = ae_randomreal(_state)*(tbl.ptr.pp_double[i][1]-tbl.ptr.pp_double[i][0]);
            v1 = tbl.ptr.pp_double[i][2]+t*tbl.ptr.pp_double[i][3]+ae_sqr(t, _state)*tbl.ptr.pp_double[i][4]+t*ae_sqr(t, _state)*tbl.ptr.pp_double[i][5];
            v2 = spline1dcalc(c, tbl.ptr.pp_double[i][0]+t, _state);
            err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
        }
    }
    for(i=0; i<=n-2; i++)
    {
        err = ae_maxreal(err, ae_fabs(x->ptr.p_double[i]-tbl.ptr.pp_double[i][0], _state), _state);
    }
    for(i=0; i<=n-2; i++)
    {
        err = ae_maxreal(err, ae_fabs(x->ptr.p_double[i+1]-tbl.ptr.pp_double[i][1], _state), _state);
    }
    result = ae_fp_less(err,100*ae_machineepsilon);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unset spline, i.e. initialize it with random garbage
*************************************************************************/
static void testspline1dunit_unsetspline1d(spline1dinterpolant* c,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_vector y;
    ae_vector d;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&d, 0, sizeof(d));
    _spline1dinterpolant_clear(c);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&y, 2, _state);
    ae_vector_set_length(&d, 2, _state);
    x.ptr.p_double[0] = (double)(-1);
    y.ptr.p_double[0] = ae_randomreal(_state);
    d.ptr.p_double[0] = ae_randomreal(_state);
    x.ptr.p_double[1] = (double)(1);
    y.ptr.p_double[1] = ae_randomreal(_state);
    d.ptr.p_double[1] = ae_randomreal(_state);
    spline1dbuildhermite(&x, &y, &d, 2, c, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Tests that built spline is monotone.
*************************************************************************/
static ae_bool testspline1dunit_testmonotonespline(ae_state *_state)
{
    ae_frame _frame_block;
    spline1dinterpolant c;
    spline1dinterpolant s2;
    double c0;
    double c1;
    ae_vector x;
    ae_vector y;
    ae_vector d;
    ae_int_t m;
    ae_vector n;
    ae_int_t alln;
    ae_int_t shift;
    double sign0;
    double sign1;
    double r;
    double st;
    double eps;
    double delta;
    double v;
    double dv;
    double d2v;
    ae_int_t nseg;
    ae_int_t npoints;
    ae_int_t tp;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t kmax;
    ae_int_t l;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&s2, 0, sizeof(s2));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&d, 0, sizeof(d));
    memset(&n, 0, sizeof(n));
    _spline1dinterpolant_init(&c, _state, ae_true);
    _spline1dinterpolant_init(&s2, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&n, 0, DT_INT, _state, ae_true);

    eps = 100*ae_machineepsilon;
    
    /*
     * Special test - N=2.
     *
     * Following properties are tested:
     * * monotone spline must be equal to the Hermite spline with
     *   zero derivative at the ends
     * * monotone spline is constant beyond left/right boundaries
     */
    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&y, 2, _state);
    ae_vector_set_length(&d, 2, _state);
    x.ptr.p_double[0] = -0.1-ae_randomreal(_state);
    y.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
    d.ptr.p_double[0] = 0.0;
    x.ptr.p_double[1] = 0.1+ae_randomreal(_state);
    y.ptr.p_double[1] = y.ptr.p_double[0];
    d.ptr.p_double[1] = 0.0;
    spline1dbuildmonotone(&x, &y, 2, &c, _state);
    spline1dbuildhermite(&x, &y, &d, 2, &s2, _state);
    v = 2*ae_randomreal(_state)-1;
    if( ae_fp_greater(ae_fabs(spline1dcalc(&c, v, _state)-spline1dcalc(&s2, v, _state), _state),eps) )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    if( ae_fp_neq(spline1dcalc(&c, (double)(-5), _state),y.ptr.p_double[0]) )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    if( ae_fp_neq(spline1dcalc(&c, (double)(5), _state),y.ptr.p_double[0]) )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Special test - N=3, x=[0,1,2], y=[0,1,0].
     * Monotone spline must be equal to the Hermite spline with
     * zero derivative at all points.
     */
    ae_vector_set_length(&x, 3, _state);
    ae_vector_set_length(&y, 3, _state);
    ae_vector_set_length(&d, 3, _state);
    x.ptr.p_double[0] = 0.0;
    y.ptr.p_double[0] = 0.0;
    d.ptr.p_double[0] = 0.0;
    x.ptr.p_double[1] = 1.0;
    y.ptr.p_double[1] = 1.0;
    d.ptr.p_double[1] = 0.0;
    x.ptr.p_double[2] = 2.0;
    y.ptr.p_double[2] = 0.0;
    d.ptr.p_double[2] = 0.0;
    spline1dbuildmonotone(&x, &y, 3, &c, _state);
    spline1dbuildhermite(&x, &y, &d, 3, &s2, _state);
    for(i=0; i<=10; i++)
    {
        v = x.ptr.p_double[0]+(double)i/(double)10*(x.ptr.p_double[2]-x.ptr.p_double[0]);
        if( ae_fp_greater(ae_fabs(spline1dcalc(&c, v, _state)-spline1dcalc(&s2, v, _state), _state),eps) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * Special test - N=5, x=[0,1,2,3,4], y=[0,1,1,2,3].
     *
     * 1) spline passes through all prescribed points
     * 2) spline derivative at all points except x=3 is exactly zero
     * 3) spline derivative at x=3 is 1.0 (within machine epsilon)
     */
    ae_vector_set_length(&x, 5, _state);
    ae_vector_set_length(&y, 5, _state);
    x.ptr.p_double[0] = 0.0;
    y.ptr.p_double[0] = 0.0;
    x.ptr.p_double[1] = 1.0;
    y.ptr.p_double[1] = 1.0;
    x.ptr.p_double[2] = 2.0;
    y.ptr.p_double[2] = 1.0;
    x.ptr.p_double[3] = 3.0;
    y.ptr.p_double[3] = 2.0;
    x.ptr.p_double[4] = 4.0;
    y.ptr.p_double[4] = 3.0;
    spline1dbuildmonotone(&x, &y, 5, &c, _state);
    for(i=0; i<=4; i++)
    {
        spline1ddiff(&c, x.ptr.p_double[i], &v, &dv, &d2v, _state);
        if( ae_fp_greater(ae_fabs(v-y.ptr.p_double[i], _state),eps) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        if( (ae_fp_eq(x.ptr.p_double[i],3.0)&&ae_fp_greater(ae_fabs(dv-1.0, _state),eps))||(ae_fp_neq(x.ptr.p_double[i],3.0)&&ae_fp_neq(dv,(double)(0))) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * Special test:
     * * N=4
     * * three fixed points - (0,0), (1,1), (2,0)
     * * one special point (x,y) with x in [0.1,0.9], y in [0.1,0.9]
     * * monotonicity of the interpolant at [0,1] is checked with very small step 1/KMax
     */
    ae_vector_set_length(&x, 4, _state);
    ae_vector_set_length(&y, 4, _state);
    x.ptr.p_double[0] = 0.0;
    y.ptr.p_double[0] = 0.0;
    x.ptr.p_double[2] = 1.0;
    y.ptr.p_double[2] = 1.0;
    x.ptr.p_double[3] = 2.0;
    y.ptr.p_double[3] = 0.0;
    for(i=1; i<=9; i++)
    {
        for(j=1; j<=9; j++)
        {
            x.ptr.p_double[1] = (double)i/(double)10;
            y.ptr.p_double[1] = (double)j/(double)10;
            spline1dbuildmonotone(&x, &y, 4, &c, _state);
            kmax = 1000;
            for(k=0; k<=kmax-1; k++)
            {
                if( ae_fp_greater(spline1dcalc(&c, (double)k/(double)kmax, _state),spline1dcalc(&c, (double)(k+1)/(double)kmax, _state)) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    
    /*
     * General case
     */
    delta = (double)(0);
    nseg = 10;
    npoints = 15;
    passcount = 30;
    for(pass=1; pass<=passcount; pass++)
    {
        tp = ae_randominteger(6, _state)+4;
        r = (double)(ae_randominteger(76, _state)+25);
        m = ae_randominteger(nseg, _state)+1;
        ae_vector_set_length(&n, m, _state);
        alln = 0;
        for(i=0; i<=m-1; i++)
        {
            n.ptr.p_int[i] = ae_randominteger(npoints, _state)+2;
            alln = alln+n.ptr.p_int[i];
        }
        ae_vector_set_length(&x, alln, _state);
        ae_vector_set_length(&y, alln, _state);
        x.ptr.p_double[0] = r*(2*ae_randomreal(_state)-1);
        y.ptr.p_double[0] = r*(2*ae_randomreal(_state)-1);
        
        /*
         * Builds monotone function
         */
        st = 0.1+0.7*ae_randomreal(_state);
        shift = 0;
        sign0 = ae_pow((double)(-1), (double)(0), _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=1; j<=n.ptr.p_int[i]-1; j++)
            {
                x.ptr.p_double[shift+j] = x.ptr.p_double[shift+j-1]+st+ae_randomreal(_state);
                delta = ae_maxreal(delta, x.ptr.p_double[shift+j]-x.ptr.p_double[shift+j-1], _state);
                y.ptr.p_double[shift+j] = y.ptr.p_double[shift+j-1]+sign0*(st+ae_randomreal(_state));
            }
            shift = shift+n.ptr.p_int[i];
            if( i!=m-1 )
            {
                sign0 = ae_pow((double)(-1), (double)(i+1), _state);
                x.ptr.p_double[shift] = x.ptr.p_double[shift-1]+st+ae_randomreal(_state);
                y.ptr.p_double[shift] = y.ptr.p_double[shift-1]+sign0*ae_randomreal(_state);
            }
        }
        delta = 3*delta;
        spline1dbuildmonotone(&x, &y, alln, &c, _state);
        
        /*
         * Check that built function is monotone
         */
        shift = 0;
        for(i=0; i<=m-1; i++)
        {
            for(j=1; j<=n.ptr.p_int[i]-1; j++)
            {
                st = (x.ptr.p_double[shift+j]-x.ptr.p_double[shift+j-1])/tp;
                sign0 = y.ptr.p_double[shift+j]-y.ptr.p_double[shift+j-1];
                if( ae_fp_neq(sign0,(double)(0)) )
                {
                    sign0 = sign0/ae_fabs(sign0, _state);
                }
                for(l=0; l<=tp-1; l++)
                {
                    c0 = spline1dcalc(&c, x.ptr.p_double[shift+j-1]+l*st, _state);
                    c1 = spline1dcalc(&c, x.ptr.p_double[shift+j-1]+(l+1)*st, _state);
                    sign1 = c1-c0;
                    if( ae_fp_neq(sign1,(double)(0)) )
                    {
                        sign1 = sign1/ae_fabs(sign1, _state);
                    }
                    if( ae_fp_less(sign0*sign1,(double)(0)) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
        c0 = spline1dcalc(&c, x.ptr.p_double[0]-delta, _state);
        c1 = spline1dcalc(&c, x.ptr.p_double[0], _state);
        if( ae_fp_greater(ae_fabs(c0-c1, _state),eps) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        c0 = spline1dcalc(&c, x.ptr.p_double[alln-1], _state);
        c1 = spline1dcalc(&c, x.ptr.p_double[alln-1]+delta, _state);
        if( ae_fp_greater(ae_fabs(c0-c1, _state),eps) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Builds constant function
         */
        y.ptr.p_double[0] = r*(2*ae_randomreal(_state)-1);
        for(i=1; i<=alln-1; i++)
        {
            y.ptr.p_double[i] = y.ptr.p_double[0];
        }
        spline1dbuildmonotone(&x, &y, alln, &c, _state);
        shift = 0;
        for(i=0; i<=m-1; i++)
        {
            for(j=1; j<=n.ptr.p_int[i]-1; j++)
            {
                st = (x.ptr.p_double[shift+j]-x.ptr.p_double[shift+j-1])/tp;
                sign0 = y.ptr.p_double[shift+j]-y.ptr.p_double[shift+j-1];
                for(l=0; l<=tp-1; l++)
                {
                    c0 = spline1dcalc(&c, x.ptr.p_double[shift+j-1]+l*st, _state);
                    c1 = spline1dcalc(&c, x.ptr.p_double[shift+j-1]+(l+1)*st, _state);
                    sign1 = c1-c0;
                    if( ae_fp_greater(sign0,eps)||ae_fp_greater(sign1,eps) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


static void testspline1dunit_testsplinefitting(ae_bool* fiterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    double nonstrictthreshold;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t k;
    ae_int_t pass;
    ae_vector x;
    ae_vector y;
    ae_vector w;
    ae_vector w2;
    ae_vector xc;
    ae_vector yc;
    ae_vector d;
    ae_vector dc;
    ae_int_t info;
    ae_int_t info2;
    spline1dinterpolant c;
    spline1dinterpolant c2;
    spline1dfitreport rep;
    spline1dfitreport rep2;
    double s;
    double v;
    double v1;
    double v2;
    double refrms;
    double refavg;
    double refavgrel;
    double refmax;
    double rho;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&w, 0, sizeof(w));
    memset(&w2, 0, sizeof(w2));
    memset(&xc, 0, sizeof(xc));
    memset(&yc, 0, sizeof(yc));
    memset(&d, 0, sizeof(d));
    memset(&dc, 0, sizeof(dc));
    memset(&c, 0, sizeof(c));
    memset(&c2, 0, sizeof(c2));
    memset(&rep, 0, sizeof(rep));
    memset(&rep2, 0, sizeof(rep2));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _spline1dinterpolant_init(&c, _state, ae_true);
    _spline1dinterpolant_init(&c2, _state, ae_true);
    _spline1dfitreport_init(&rep, _state, ae_true);
    _spline1dfitreport_init(&rep2, _state, ae_true);

    
    /*
     * Valyes:
     * * pass count
     * * threshold - for tests which must be satisfied exactly
     * * nonstrictthreshold - for approximate tests
     */
    passcount = 20;
    threshold = 10000*ae_machineepsilon;
    nonstrictthreshold = 1.0E-4;
    *fiterrors = ae_false;
    
    /*
     * check basic properties of penalized splines which are
     * preserved independently of Rho parameter.
     */
    for(m=4; m<=10; m++)
    {
        for(k=-5; k<=5; k++)
        {
            rho = (double)(k);
            
            /*
             * when we have two points (even with different weights),
             * resulting spline must be equal to the straight line
             */
            ae_vector_set_length(&x, 2, _state);
            ae_vector_set_length(&y, 2, _state);
            ae_vector_set_length(&w, 2, _state);
            x.ptr.p_double[0] = -0.5-ae_randomreal(_state);
            y.ptr.p_double[0] = 0.5+ae_randomreal(_state);
            w.ptr.p_double[0] = 1+ae_randomreal(_state);
            x.ptr.p_double[1] = 0.5+ae_randomreal(_state);
            y.ptr.p_double[1] = 0.5+ae_randomreal(_state);
            w.ptr.p_double[1] = 1+ae_randomreal(_state);
            spline1dfitpenalized(&x, &y, 2, m, rho, &info, &c, &rep, _state);
            if( info>0 )
            {
                v = 2*ae_randomreal(_state)-1;
                v1 = (v-x.ptr.p_double[0])/(x.ptr.p_double[1]-x.ptr.p_double[0])*y.ptr.p_double[1]+(v-x.ptr.p_double[1])/(x.ptr.p_double[0]-x.ptr.p_double[1])*y.ptr.p_double[0];
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v1-spline1dcalc(&c, v, _state), _state),nonstrictthreshold);
            }
            else
            {
                *fiterrors = ae_true;
            }
            spline1dfitpenalizedw(&x, &y, &w, 2, m, rho, &info, &c, &rep, _state);
            if( info>0 )
            {
                v = 2*ae_randomreal(_state)-1;
                v1 = (v-x.ptr.p_double[0])/(x.ptr.p_double[1]-x.ptr.p_double[0])*y.ptr.p_double[1]+(v-x.ptr.p_double[1])/(x.ptr.p_double[0]-x.ptr.p_double[1])*y.ptr.p_double[0];
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v1-spline1dcalc(&c, v, _state), _state),nonstrictthreshold);
            }
            else
            {
                *fiterrors = ae_true;
            }
            
            /*
             * spline fitting is invariant with respect to
             * scaling of weights (of course, ANY fitting algorithm
             * must be invariant, but we want to test this property
             * just to be sure that it is correctly implemented)
             */
            for(n=2; n<=2*m; n++)
            {
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&y, n, _state);
                ae_vector_set_length(&w, n, _state);
                ae_vector_set_length(&w2, n, _state);
                s = 1+ae_exp(10*ae_randomreal(_state), _state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = (double)i/(double)(n-1);
                    y.ptr.p_double[i] = ae_randomreal(_state);
                    w.ptr.p_double[i] = 0.1+ae_randomreal(_state);
                    w2.ptr.p_double[i] = w.ptr.p_double[i]*s;
                }
                spline1dfitpenalizedw(&x, &y, &w, n, m, rho, &info, &c, &rep, _state);
                spline1dfitpenalizedw(&x, &y, &w2, n, m, rho, &info2, &c2, &rep2, _state);
                if( info>0&&info2>0 )
                {
                    v = ae_randomreal(_state);
                    v1 = spline1dcalc(&c, v, _state);
                    v2 = spline1dcalc(&c2, v, _state);
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v1-v2, _state),nonstrictthreshold);
                }
                else
                {
                    *fiterrors = ae_true;
                }
            }
        }
    }
    
    /*
     * Advanced proprties:
     * * penalized spline with M about 5*N and sufficiently small Rho
     *   must pass through all points on equidistant grid
     */
    for(n=2; n<=10; n++)
    {
        m = 5*n;
        rho = (double)(-5);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&y, n, _state);
        ae_vector_set_length(&w, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)i/(double)(n-1);
            y.ptr.p_double[i] = ae_randomreal(_state);
            w.ptr.p_double[i] = 0.1+ae_randomreal(_state);
        }
        spline1dfitpenalized(&x, &y, n, m, rho, &info, &c, &rep, _state);
        if( info>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state),nonstrictthreshold);
            }
        }
        else
        {
            *fiterrors = ae_true;
        }
        spline1dfitpenalizedw(&x, &y, &w, n, m, rho, &info, &c, &rep, _state);
        if( info>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state),nonstrictthreshold);
            }
        }
        else
        {
            *fiterrors = ae_true;
        }
    }
    
    /*
     * Check correctness of error reports
     */
    for(pass=1; pass<=passcount; pass++)
    {
        ae_assert(passcount>=2, "PassCount should be 2 or greater!", _state);
        
        /*
         * solve simple task (all X[] are the same, Y[] are specially
         * calculated to ensure simple form of all types of errors)
         * and check correctness of the errors calculated by subroutines
         *
         * First pass is done with zero Y[], other passes - with random Y[].
         * It should test both ability to correctly calculate errors and
         * ability to not fail while working with zeros :)
         */
        n = 4;
        if( pass==1 )
        {
            v1 = (double)(0);
            v2 = (double)(0);
            v = (double)(0);
        }
        else
        {
            v1 = ae_randomreal(_state);
            v2 = ae_randomreal(_state);
            v = 1+ae_randomreal(_state);
        }
        ae_vector_set_length(&x, 4, _state);
        ae_vector_set_length(&y, 4, _state);
        ae_vector_set_length(&w, 4, _state);
        x.ptr.p_double[0] = (double)(0);
        y.ptr.p_double[0] = v-v2;
        w.ptr.p_double[0] = (double)(1);
        x.ptr.p_double[1] = (double)(0);
        y.ptr.p_double[1] = v-v1;
        w.ptr.p_double[1] = (double)(1);
        x.ptr.p_double[2] = (double)(0);
        y.ptr.p_double[2] = v+v1;
        w.ptr.p_double[2] = (double)(1);
        x.ptr.p_double[3] = (double)(0);
        y.ptr.p_double[3] = v+v2;
        w.ptr.p_double[3] = (double)(1);
        refrms = ae_sqrt((ae_sqr(v1, _state)+ae_sqr(v2, _state))/2, _state);
        refavg = (ae_fabs(v1, _state)+ae_fabs(v2, _state))/2;
        if( pass==1 )
        {
            refavgrel = (double)(0);
        }
        else
        {
            refavgrel = 0.25*(ae_fabs(v2, _state)/ae_fabs(v-v2, _state)+ae_fabs(v1, _state)/ae_fabs(v-v1, _state)+ae_fabs(v1, _state)/ae_fabs(v+v1, _state)+ae_fabs(v2, _state)/ae_fabs(v+v2, _state));
        }
        refmax = ae_maxreal(v1, v2, _state);
        
        /*
         * Test penalized spline
         */
        spline1dfitpenalizedw(&x, &y, &w, 4, 4, 0.0, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *fiterrors = ae_true;
        }
        else
        {
            s = spline1dcalc(&c, (double)(0), _state);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}



static void testparametricunit_testrdp(ae_bool* errorflag,
     ae_state *_state);
static void testparametricunit_unsetp2(pspline2interpolant* p,
     ae_state *_state);
static void testparametricunit_unsetp3(pspline3interpolant* p,
     ae_state *_state);





ae_bool testparametric(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool p2errors;
    ae_bool p3errors;
    ae_bool rdperrors;
    double nonstrictthreshold;
    double threshold;
    ae_int_t maxn;
    ae_int_t periodicity;
    ae_int_t skind;
    ae_int_t pkind;
    ae_bool periodic;
    double a;
    double b;
    ae_int_t n;
    ae_int_t tmpn;
    ae_int_t i;
    double vx;
    double vy;
    double vz;
    double vx2;
    double vy2;
    double vz2;
    double vdx;
    double vdy;
    double vdz;
    double vdx2;
    double vdy2;
    double vdz2;
    double vd2x;
    double vd2y;
    double vd2z;
    double vd2x2;
    double vd2y2;
    double vd2z2;
    double v0;
    double v1;
    ae_vector x;
    ae_vector y;
    ae_vector z;
    ae_vector t;
    ae_vector t2;
    ae_vector t3;
    ae_matrix xy;
    ae_matrix xyz;
    pspline2interpolant p2;
    pspline3interpolant p3;
    spline1dinterpolant s;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&z, 0, sizeof(z));
    memset(&t, 0, sizeof(t));
    memset(&t2, 0, sizeof(t2));
    memset(&t3, 0, sizeof(t3));
    memset(&xy, 0, sizeof(xy));
    memset(&xyz, 0, sizeof(xyz));
    memset(&p2, 0, sizeof(p2));
    memset(&p3, 0, sizeof(p3));
    memset(&s, 0, sizeof(s));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t3, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xyz, 0, 0, DT_REAL, _state, ae_true);
    _pspline2interpolant_init(&p2, _state, ae_true);
    _pspline3interpolant_init(&p3, _state, ae_true);
    _spline1dinterpolant_init(&s, _state, ae_true);

    waserrors = ae_false;
    maxn = 10;
    threshold = 10000*ae_machineepsilon;
    nonstrictthreshold = 0.00001;
    p2errors = ae_false;
    p3errors = ae_false;
    rdperrors = ae_false;
    testparametricunit_testrdp(&rdperrors, _state);
    
    /*
     * Test basic properties of 2- and 3-dimensional splines:
     * * PSpline2ParameterValues() properties
     * * values at nodes
     * * for periodic splines - periodicity properties
     *
     * Variables used:
     * * N              points count
     * * SKind          spline
     * * PKind          parameterization
     * * Periodicity    whether we have periodic spline or not
     */
    for(n=2; n<=maxn; n++)
    {
        for(skind=0; skind<=2; skind++)
        {
            for(pkind=0; pkind<=2; pkind++)
            {
                for(periodicity=0; periodicity<=1; periodicity++)
                {
                    periodic = periodicity==1;
                    
                    /*
                     * skip unsupported combinations of parameters
                     */
                    if( periodic&&n<3 )
                    {
                        continue;
                    }
                    if( periodic&&skind==0 )
                    {
                        continue;
                    }
                    if( n<5&&skind==0 )
                    {
                        continue;
                    }
                    
                    /*
                     * init
                     */
                    ae_matrix_set_length(&xy, n, 2, _state);
                    ae_matrix_set_length(&xyz, n, 3, _state);
                    taskgenint1dequidist((double)(-1), (double)(1), n, &t2, &x, _state);
                    ae_v_move(&xy.ptr.pp_double[0][0], xy.stride, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_v_move(&xyz.ptr.pp_double[0][0], xyz.stride, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    taskgenint1dequidist((double)(-1), (double)(1), n, &t2, &y, _state);
                    ae_v_move(&xy.ptr.pp_double[0][1], xy.stride, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_v_move(&xyz.ptr.pp_double[0][1], xyz.stride, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    taskgenint1dequidist((double)(-1), (double)(1), n, &t2, &z, _state);
                    ae_v_move(&xyz.ptr.pp_double[0][2], xyz.stride, &z.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    testparametricunit_unsetp2(&p2, _state);
                    testparametricunit_unsetp3(&p3, _state);
                    if( periodic )
                    {
                        pspline2buildperiodic(&xy, n, skind, pkind, &p2, _state);
                        pspline3buildperiodic(&xyz, n, skind, pkind, &p3, _state);
                    }
                    else
                    {
                        pspline2build(&xy, n, skind, pkind, &p2, _state);
                        pspline3build(&xyz, n, skind, pkind, &p3, _state);
                    }
                    
                    /*
                     * PSpline2ParameterValues() properties
                     */
                    pspline2parametervalues(&p2, &tmpn, &t2, _state);
                    if( tmpn!=n )
                    {
                        p2errors = ae_true;
                        continue;
                    }
                    pspline3parametervalues(&p3, &tmpn, &t3, _state);
                    if( tmpn!=n )
                    {
                        p3errors = ae_true;
                        continue;
                    }
                    p2errors = p2errors||ae_fp_neq(t2.ptr.p_double[0],(double)(0));
                    p3errors = p3errors||ae_fp_neq(t3.ptr.p_double[0],(double)(0));
                    for(i=1; i<=n-1; i++)
                    {
                        p2errors = p2errors||ae_fp_less_eq(t2.ptr.p_double[i],t2.ptr.p_double[i-1]);
                        p3errors = p3errors||ae_fp_less_eq(t3.ptr.p_double[i],t3.ptr.p_double[i-1]);
                    }
                    if( periodic )
                    {
                        p2errors = p2errors||ae_fp_greater_eq(t2.ptr.p_double[n-1],(double)(1));
                        p3errors = p3errors||ae_fp_greater_eq(t3.ptr.p_double[n-1],(double)(1));
                    }
                    else
                    {
                        p2errors = p2errors||ae_fp_neq(t2.ptr.p_double[n-1],(double)(1));
                        p3errors = p3errors||ae_fp_neq(t3.ptr.p_double[n-1],(double)(1));
                    }
                    
                    /*
                     * Now we have parameter values stored at T,
                     * and want to test whether the actully correspond to
                     * points
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        
                        /*
                         * 2-dimensional test
                         */
                        pspline2calc(&p2, t2.ptr.p_double[i], &vx, &vy, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                        
                        /*
                         * 3-dimensional test
                         */
                        pspline3calc(&p3, t3.ptr.p_double[i], &vx, &vy, &vz, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-z.ptr.p_double[i], _state),threshold);
                    }
                    
                    /*
                     * Test periodicity (if needed)
                     */
                    if( periodic )
                    {
                        
                        /*
                         * periodicity at nodes
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            
                            /*
                             * 2-dimensional test
                             */
                            pspline2calc(&p2, t2.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vy, _state);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            pspline2diff(&p2, t2.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vdx, &vy, &vdy, _state);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            pspline2diff2(&p2, t2.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, _state);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            
                            /*
                             * 3-dimensional test
                             */
                            pspline3calc(&p3, t3.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vy, &vz, _state);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vz-z.ptr.p_double[i], _state),threshold);
                            pspline3diff(&p3, t3.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vdx, &vy, &vdy, &vz, &vdz, _state);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vz-z.ptr.p_double[i], _state),threshold);
                            pspline3diff2(&p3, t3.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, &vz, &vdz, &vd2z, _state);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vz-z.ptr.p_double[i], _state),threshold);
                        }
                        
                        /*
                         * periodicity between nodes
                         */
                        v0 = ae_randomreal(_state);
                        pspline2calc(&p2, v0, &vx, &vy, _state);
                        pspline2calc(&p2, v0+ae_randominteger(10, _state)-5, &vx2, &vy2, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        pspline3calc(&p3, v0, &vx, &vy, &vz, _state);
                        pspline3calc(&p3, v0+ae_randominteger(10, _state)-5, &vx2, &vy2, &vz2, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
                        
                        /*
                         * near-boundary test for continuity of function values and derivatives:
                         * 2-dimensional curve
                         */
                        ae_assert(skind==1||skind==2, "TEST: unexpected spline type!", _state);
                        v0 = 100*ae_machineepsilon;
                        v1 = 1-v0;
                        pspline2calc(&p2, v0, &vx, &vy, _state);
                        pspline2calc(&p2, v1, &vx2, &vy2, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        pspline2diff(&p2, v0, &vx, &vdx, &vy, &vdy, _state);
                        pspline2diff(&p2, v1, &vx2, &vdx2, &vy2, &vdy2, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),nonstrictthreshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),nonstrictthreshold);
                        pspline2diff2(&p2, v0, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, _state);
                        pspline2diff2(&p2, v1, &vx2, &vdx2, &vd2x2, &vy2, &vdy2, &vd2y2, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),nonstrictthreshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),nonstrictthreshold);
                        if( skind==2 )
                        {
                            
                            /*
                             * second derivative test only for cubic splines
                             */
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vd2x-vd2x2, _state),nonstrictthreshold);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vd2y-vd2y2, _state),nonstrictthreshold);
                        }
                        
                        /*
                         * near-boundary test for continuity of function values and derivatives:
                         * 3-dimensional curve
                         */
                        ae_assert(skind==1||skind==2, "TEST: unexpected spline type!", _state);
                        v0 = 100*ae_machineepsilon;
                        v1 = 1-v0;
                        pspline3calc(&p3, v0, &vx, &vy, &vz, _state);
                        pspline3calc(&p3, v1, &vx2, &vy2, &vz2, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
                        pspline3diff(&p3, v0, &vx, &vdx, &vy, &vdy, &vz, &vdz, _state);
                        pspline3diff(&p3, v1, &vx2, &vdx2, &vy2, &vdy2, &vz2, &vdz2, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),nonstrictthreshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),nonstrictthreshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdz-vdz2, _state),nonstrictthreshold);
                        pspline3diff2(&p3, v0, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, &vz, &vdz, &vd2z, _state);
                        pspline3diff2(&p3, v1, &vx2, &vdx2, &vd2x2, &vy2, &vdy2, &vd2y2, &vz2, &vdz2, &vd2z2, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),nonstrictthreshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),nonstrictthreshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdz-vdz2, _state),nonstrictthreshold);
                        if( skind==2 )
                        {
                            
                            /*
                             * second derivative test only for cubic splines
                             */
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vd2x-vd2x2, _state),nonstrictthreshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vd2y-vd2y2, _state),nonstrictthreshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vd2z-vd2z2, _state),nonstrictthreshold);
                        }
                    }
                }
            }
        }
    }
    
    /*
     * Test differentiation, tangents, calculation between nodes.
     *
     * Because differentiation is done in parameterization/spline/periodicity
     * oblivious manner, we don't have to test all possible combinations
     * of spline types and parameterizations.
     *
     * Actually we test special combination with properties which allow us
     * to easily solve this problem:
     * * 2 (3) variables
     * * first variable is sampled from equidistant grid on [0,1]
     * * other variables are random
     * * uniform parameterization is used
     * * periodicity - none
     * * spline type - any (we use cubic splines)
     * Same problem allows us to test calculation BETWEEN nodes.
     */
    for(n=2; n<=maxn; n++)
    {
        
        /*
         * init
         */
        ae_matrix_set_length(&xy, n, 2, _state);
        ae_matrix_set_length(&xyz, n, 3, _state);
        taskgenint1dequidist((double)(0), (double)(1), n, &t, &x, _state);
        ae_v_move(&xy.ptr.pp_double[0][0], xy.stride, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        ae_v_move(&xyz.ptr.pp_double[0][0], xyz.stride, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        taskgenint1dequidist((double)(0), (double)(1), n, &t, &y, _state);
        ae_v_move(&xy.ptr.pp_double[0][1], xy.stride, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
        ae_v_move(&xyz.ptr.pp_double[0][1], xyz.stride, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
        taskgenint1dequidist((double)(0), (double)(1), n, &t, &z, _state);
        ae_v_move(&xyz.ptr.pp_double[0][2], xyz.stride, &z.ptr.p_double[0], 1, ae_v_len(0,n-1));
        testparametricunit_unsetp2(&p2, _state);
        testparametricunit_unsetp3(&p3, _state);
        pspline2build(&xy, n, 2, 0, &p2, _state);
        pspline3build(&xyz, n, 2, 0, &p3, _state);
        
        /*
         * Test 2D/3D spline:
         * * build non-parametric cubic spline from T and X/Y
         * * calculate its value and derivatives at V0
         * * compare with Spline2Calc/Spline2Diff/Spline2Diff2
         * Because of task properties both variants should
         * return same answer.
         */
        v0 = ae_randomreal(_state);
        spline1dbuildcubic(&t, &x, n, 0, 0.0, 0, 0.0, &s, _state);
        spline1ddiff(&s, v0, &vx2, &vdx2, &vd2x2, _state);
        spline1dbuildcubic(&t, &y, n, 0, 0.0, 0, 0.0, &s, _state);
        spline1ddiff(&s, v0, &vy2, &vdy2, &vd2y2, _state);
        spline1dbuildcubic(&t, &z, n, 0, 0.0, 0, 0.0, &s, _state);
        spline1ddiff(&s, v0, &vz2, &vdz2, &vd2z2, _state);
        
        /*
         * 2D test
         */
        pspline2calc(&p2, v0, &vx, &vy, _state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        pspline2diff(&p2, v0, &vx, &vdx, &vy, &vdy, _state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),threshold);
        pspline2diff2(&p2, v0, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, _state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vd2x-vd2x2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vd2y-vd2y2, _state),threshold);
        
        /*
         * 3D test
         */
        pspline3calc(&p3, v0, &vx, &vy, &vz, _state);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
        pspline3diff(&p3, v0, &vx, &vdx, &vy, &vdy, &vz, &vdz, _state);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdz-vdz2, _state),threshold);
        pspline3diff2(&p3, v0, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, &vz, &vdz, &vd2z, _state);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdz-vdz2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vd2x-vd2x2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vd2y-vd2y2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vd2z-vd2z2, _state),threshold);
        
        /*
         * Test tangents for 2D/3D
         */
        pspline2tangent(&p2, v0, &vx, &vy, _state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vdx2/safepythag2(vdx2, vdy2, _state), _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vdy2/safepythag2(vdx2, vdy2, _state), _state),threshold);
        pspline3tangent(&p3, v0, &vx, &vy, &vz, _state);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vdx2/safepythag3(vdx2, vdy2, vdz2, _state), _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vdy2/safepythag3(vdx2, vdy2, vdz2, _state), _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vdz2/safepythag3(vdx2, vdy2, vdz2, _state), _state),threshold);
    }
    
    /*
     * Arc length test.
     *
     * Simple problem with easy solution (points on a straight line with
     * uniform parameterization).
     */
    for(n=2; n<=maxn; n++)
    {
        ae_matrix_set_length(&xy, n, 2, _state);
        ae_matrix_set_length(&xyz, n, 3, _state);
        for(i=0; i<=n-1; i++)
        {
            xy.ptr.pp_double[i][0] = (double)(i);
            xy.ptr.pp_double[i][1] = (double)(i);
            xyz.ptr.pp_double[i][0] = (double)(i);
            xyz.ptr.pp_double[i][1] = (double)(i);
            xyz.ptr.pp_double[i][2] = (double)(i);
        }
        pspline2build(&xy, n, 1, 0, &p2, _state);
        pspline3build(&xyz, n, 1, 0, &p3, _state);
        a = ae_randomreal(_state);
        b = ae_randomreal(_state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(pspline2arclength(&p2, a, b, _state)-(b-a)*ae_sqrt((double)(2), _state)*(n-1), _state),nonstrictthreshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(pspline3arclength(&p3, a, b, _state)-(b-a)*ae_sqrt((double)(3), _state)*(n-1), _state),nonstrictthreshold);
    }
    
    /*
     * report
     */
    waserrors = (p2errors||p3errors)||rdperrors;
    if( !silent )
    {
        printf("TESTING PARAMETRIC INTERPOLATION\n");
        
        /*
         * Normal tests
         */
        printf("2D SPLINES:                              ");
        if( p2errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("3D SPLINES:                              ");
        if( p3errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RDP:                                     ");
        if( rdperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests 4PL/5PL fitting. On error sets FitErrors flag variable;
on success - flag is not changed.
*************************************************************************/
static void testparametricunit_testrdp(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    ae_vector x;
    ae_vector y;
    ae_vector e;
    ae_vector x2;
    ae_vector y2;
    ae_vector x3;
    ae_vector y3;
    ae_matrix xy;
    ae_matrix xy2;
    ae_matrix xy3;
    ae_vector idx2;
    ae_vector idx3;
    ae_int_t nsections;
    ae_int_t nsections3;
    double eps;
    double v;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t d;
    spline1dinterpolant s;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&e, 0, sizeof(e));
    memset(&x2, 0, sizeof(x2));
    memset(&y2, 0, sizeof(y2));
    memset(&x3, 0, sizeof(x3));
    memset(&y3, 0, sizeof(y3));
    memset(&xy, 0, sizeof(xy));
    memset(&xy2, 0, sizeof(xy2));
    memset(&xy3, 0, sizeof(xy3));
    memset(&idx2, 0, sizeof(idx2));
    memset(&idx3, 0, sizeof(idx3));
    memset(&s, 0, sizeof(s));
    _hqrndstate_init(&rs, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x3, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y3, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy3, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&idx2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&idx3, 0, DT_INT, _state, ae_true);
    _spline1dinterpolant_init(&s, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Parametric test 1: non-closed curve
     */
    ae_matrix_set_length(&xy, 4, 2, _state);
    xy.ptr.pp_double[0][0] = (double)(0);
    xy.ptr.pp_double[0][1] = (double)(0);
    xy.ptr.pp_double[1][0] = (double)(1);
    xy.ptr.pp_double[1][1] = (double)(2);
    xy.ptr.pp_double[2][0] = (double)(3);
    xy.ptr.pp_double[2][1] = (double)(1);
    xy.ptr.pp_double[3][0] = (double)(3);
    xy.ptr.pp_double[3][1] = (double)(3);
    parametricrdpfixed(&xy, 4, 2, 0, ae_sqrt((double)(2), _state)+0.001, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=1, __FILE__, __LINE__, "testparametricunit.ap:41");
    if( nsections==1 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:44");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:45");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:46");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:47");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:48");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=3, __FILE__, __LINE__, "testparametricunit.ap:49");
    }
    parametricrdpfixed(&xy, 4, 2, 0, ae_sqrt((double)(2), _state)-0.001, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=3, __FILE__, __LINE__, "testparametricunit.ap:52");
    if( nsections==3 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:55");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:56");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:57");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:58");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(2)), __FILE__, __LINE__, "testparametricunit.ap:59");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=1, __FILE__, __LINE__, "testparametricunit.ap:60");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:61");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][1],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:62");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[2]!=2, __FILE__, __LINE__, "testparametricunit.ap:63");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[3][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:64");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[3][1],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:65");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[3]!=3, __FILE__, __LINE__, "testparametricunit.ap:66");
    }
    parametricrdpfixed(&xy, 4, 2, 1, 0.0, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=1, __FILE__, __LINE__, "testparametricunit.ap:69");
    if( nsections==1 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:72");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:73");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:74");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:75");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:76");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=3, __FILE__, __LINE__, "testparametricunit.ap:77");
    }
    parametricrdpfixed(&xy, 4, 2, 2, 0.0, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=2, __FILE__, __LINE__, "testparametricunit.ap:80");
    if( nsections==2 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:83");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:84");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:85");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:86");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:87");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=2, __FILE__, __LINE__, "testparametricunit.ap:88");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:89");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][1],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:90");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[2]!=3, __FILE__, __LINE__, "testparametricunit.ap:91");
    }
    parametricrdpfixed(&xy, 4, 2, 3, 0.0, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=3, __FILE__, __LINE__, "testparametricunit.ap:94");
    if( nsections==3 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:97");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:98");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:99");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:100");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(2)), __FILE__, __LINE__, "testparametricunit.ap:101");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=1, __FILE__, __LINE__, "testparametricunit.ap:102");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:103");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][1],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:104");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[2]!=2, __FILE__, __LINE__, "testparametricunit.ap:105");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[3][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:106");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[3][1],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:107");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[3]!=3, __FILE__, __LINE__, "testparametricunit.ap:108");
    }
    parametricrdpfixed(&xy, 4, 2, 4, 0.0, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=3, __FILE__, __LINE__, "testparametricunit.ap:111");
    if( nsections==3 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:114");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:115");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:116");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:117");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(2)), __FILE__, __LINE__, "testparametricunit.ap:118");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=1, __FILE__, __LINE__, "testparametricunit.ap:119");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:120");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][1],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:121");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[2]!=2, __FILE__, __LINE__, "testparametricunit.ap:122");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[3][0],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:123");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[3][1],(double)(3)), __FILE__, __LINE__, "testparametricunit.ap:124");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[3]!=3, __FILE__, __LINE__, "testparametricunit.ap:125");
    }
    
    /*
     * Parametric test 2: closed curve
     */
    ae_matrix_set_length(&xy, 5, 2, _state);
    xy.ptr.pp_double[0][0] = (double)(0);
    xy.ptr.pp_double[0][1] = (double)(0);
    xy.ptr.pp_double[1][0] = (double)(1);
    xy.ptr.pp_double[1][1] = (double)(0);
    xy.ptr.pp_double[2][0] = (double)(1);
    xy.ptr.pp_double[2][1] = (double)(1);
    xy.ptr.pp_double[3][0] = (double)(0);
    xy.ptr.pp_double[3][1] = (double)(1);
    xy.ptr.pp_double[4][0] = (double)(0);
    xy.ptr.pp_double[4][1] = (double)(0);
    parametricrdpfixed(&xy, 5, 2, 0, ae_sqrt((double)(2), _state)+0.001, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=1, __FILE__, __LINE__, "testparametricunit.ap:144");
    if( nsections==1 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:147");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:148");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:149");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:150");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:151");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=4, __FILE__, __LINE__, "testparametricunit.ap:152");
    }
    parametricrdpfixed(&xy, 5, 2, 0, ae_sqrt((double)(2), _state)-0.001, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=2, __FILE__, __LINE__, "testparametricunit.ap:155");
    if( nsections==2 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:158");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:159");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:160");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:161");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:162");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=2, __FILE__, __LINE__, "testparametricunit.ap:163");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:164");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:165");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[2]!=4, __FILE__, __LINE__, "testparametricunit.ap:166");
    }
    parametricrdpfixed(&xy, 5, 2, 0, ae_sqrt((double)(2), _state)/2+0.001, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=2, __FILE__, __LINE__, "testparametricunit.ap:169");
    if( nsections==2 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:172");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:173");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:174");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:175");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:176");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=2, __FILE__, __LINE__, "testparametricunit.ap:177");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:178");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:179");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[2]!=4, __FILE__, __LINE__, "testparametricunit.ap:180");
    }
    parametricrdpfixed(&xy, 5, 2, 0, ae_sqrt((double)(2), _state)/2-0.001, &xy2, &idx2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=4, __FILE__, __LINE__, "testparametricunit.ap:183");
    if( nsections==4 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:186");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[0][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:187");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:188");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][0],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:189");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[1][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:190");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[1]!=1, __FILE__, __LINE__, "testparametricunit.ap:191");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][0],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:192");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[2][1],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:193");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[2]!=2, __FILE__, __LINE__, "testparametricunit.ap:194");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[3][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:195");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[3][1],(double)(1)), __FILE__, __LINE__, "testparametricunit.ap:196");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[3]!=3, __FILE__, __LINE__, "testparametricunit.ap:197");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[4][0],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:198");
        ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[4][1],(double)(0)), __FILE__, __LINE__, "testparametricunit.ap:199");
        ae_set_error_flag(errorflag, idx2.ptr.p_int[4]!=4, __FILE__, __LINE__, "testparametricunit.ap:200");
    }
    
    /*
     * Parametric, variable precision test (non-fixed), results are compared against fixed-section test
     */
    eps = 10.0;
    n = 100;
    while(ae_fp_greater_eq(eps,0.0001))
    {
        
        /*
         * Try different dimension counts
         */
        for(d=1; d<=5; d++)
        {
            
            /*
             * Generate dataset
             */
            ae_matrix_set_length(&xy, n, d, _state);
            for(i=0; i<=n-1; i++)
            {
                v = ae_pi*i/(n-1);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][j] = ae_pow(ae_sin(v, _state), (double)(j+1), _state)+0.01*(hqrnduniformr(&rs, _state)-0.5);
                }
            }
            
            /*
             * Perform run of eps-based RDP algorithm
             */
            parametricrdpfixed(&xy, n, d, 0, eps, &xy2, &idx2, &nsections, _state);
            ae_set_error_flag(errorflag, nsections==0, __FILE__, __LINE__, "testparametricunit.ap:231");
            if( nsections==0 )
            {
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * Check properties
             */
            ae_set_error_flag(errorflag, idx2.ptr.p_int[0]!=0, __FILE__, __LINE__, "testparametricunit.ap:238");
            for(i=0; i<=nsections-1; i++)
            {
                ae_set_error_flag(errorflag, idx2.ptr.p_int[i]>=idx2.ptr.p_int[i+1], __FILE__, __LINE__, "testparametricunit.ap:240");
            }
            ae_set_error_flag(errorflag, idx2.ptr.p_int[nsections]!=n-1, __FILE__, __LINE__, "testparametricunit.ap:241");
            for(i=0; i<=nsections; i++)
            {
                for(j=0; j<=d-1; j++)
                {
                    ae_set_error_flag(errorflag, ae_fp_neq(xy2.ptr.pp_double[i][j],xy.ptr.pp_double[idx2.ptr.p_int[i]][j]), __FILE__, __LINE__, "testparametricunit.ap:244");
                }
            }
            ae_vector_set_length(&x, nsections+1, _state);
            ae_vector_set_length(&y, nsections+1, _state);
            ae_vector_set_length(&e, n, _state);
            for(i=0; i<=n-1; i++)
            {
                e.ptr.p_double[i] = (double)(0);
            }
            for(j=0; j<=d-1; j++)
            {
                for(i=0; i<=nsections; i++)
                {
                    x.ptr.p_double[i] = (double)(idx2.ptr.p_int[i]);
                    y.ptr.p_double[i] = xy2.ptr.pp_double[i][j];
                }
                spline1dbuildlinear(&x, &y, nsections+1, &s, _state);
                for(i=0; i<=n-1; i++)
                {
                    e.ptr.p_double[i] = e.ptr.p_double[i]+ae_sqr(spline1dcalc(&s, (double)(i), _state)-xy.ptr.pp_double[i][j], _state);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_sqrt(e.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testparametricunit.ap:262");
            }
            
            /*
             * compare results with values returned by section-based algorithm
             */
            parametricrdpfixed(&xy, n, d, nsections, 0.0, &xy3, &idx3, &nsections3, _state);
            ae_set_error_flag(errorflag, nsections3!=nsections, __FILE__, __LINE__, "testparametricunit.ap:268");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nsections; i++)
            {
                ae_set_error_flag(errorflag, idx2.ptr.p_int[i]!=idx3.ptr.p_int[i], __FILE__, __LINE__, "testparametricunit.ap:273");
                for(j=0; j<=d-1; j++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xy2.ptr.pp_double[i][j]-xy3.ptr.pp_double[i][j], _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testparametricunit.ap:275");
                }
            }
        }
        
        /*
         * Next epsilon
         */
        eps = eps*0.5;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Unset spline, i.e. initialize it with random garbage
*************************************************************************/
static void testparametricunit_unsetp2(pspline2interpolant* p,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    _pspline2interpolant_clear(p);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&xy, 2, 2, _state);
    xy.ptr.pp_double[0][0] = (double)(-1);
    xy.ptr.pp_double[0][1] = (double)(-1);
    xy.ptr.pp_double[1][0] = (double)(1);
    xy.ptr.pp_double[1][1] = (double)(1);
    pspline2build(&xy, 2, 1, 0, p, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Unset spline, i.e. initialize it with random garbage
*************************************************************************/
static void testparametricunit_unsetp3(pspline3interpolant* p,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    _pspline3interpolant_clear(p);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&xy, 2, 3, _state);
    xy.ptr.pp_double[0][0] = (double)(-1);
    xy.ptr.pp_double[0][1] = (double)(-1);
    xy.ptr.pp_double[0][2] = (double)(-1);
    xy.ptr.pp_double[1][0] = (double)(1);
    xy.ptr.pp_double[1][1] = (double)(1);
    xy.ptr.pp_double[1][2] = (double)(1);
    pspline3build(&xy, 2, 1, 0, p, _state);
    ae_frame_leave(_state);
}



static ae_bool testspline3dunit_basictest(ae_state *_state);
static ae_bool testspline3dunit_testunpack(ae_state *_state);
static ae_bool testspline3dunit_testlintrans(ae_state *_state);
static ae_bool testspline3dunit_testtrilinearresample(ae_state *_state);
static void testspline3dunit_buildrndgrid(ae_bool isvect,
     ae_bool reorder,
     ae_int_t* n,
     ae_int_t* m,
     ae_int_t* l,
     ae_int_t* d,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* z,
     /* Real    */ ae_vector* f,
     ae_state *_state);





ae_bool testspline3d(ae_bool silence, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool basicerr;
    ae_bool unpackerr;
    ae_bool lintransferr;
    ae_bool trilinreserr;
    ae_bool result;


    basicerr = testspline3dunit_basictest(_state);
    unpackerr = testspline3dunit_testunpack(_state);
    lintransferr = testspline3dunit_testlintrans(_state);
    trilinreserr = testspline3dunit_testtrilinearresample(_state);
    waserrors = ((basicerr||unpackerr)||lintransferr)||trilinreserr;
    if( !silence )
    {
        printf("TESTING 3D SPLINE\n");
        printf("BASIC TEST:                              ");
        if( basicerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("UNPACK TEST:                             ");
        if( unpackerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LIN_TRANSF TEST:                         ");
        if( lintransferr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TRILINEAR RESAMPLING TEST:               ");
        if( trilinreserr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        
        /*
         * Summary
         */
        if( waserrors )
        {
            printf("TEST FAILED");
        }
        else
        {
            printf("TEST PASSED");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
The function does test basic functionality.
*************************************************************************/
static ae_bool testspline3dunit_basictest(ae_state *_state)
{
    ae_frame _frame_block;
    spline3dinterpolant c;
    spline3dinterpolant cc;
    ae_vector vvf;
    double vsf;
    ae_int_t d;
    ae_int_t m;
    ae_int_t n;
    ae_int_t l;
    ae_vector x;
    ae_vector y;
    ae_vector z;
    ae_vector sf;
    ae_vector vf;
    double eps;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t offs;
    ae_int_t di;
    double ax;
    double ay;
    double az;
    double axy;
    double ayz;
    double vx;
    double vy;
    double vz;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&cc, 0, sizeof(cc));
    memset(&vvf, 0, sizeof(vvf));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&z, 0, sizeof(z));
    memset(&sf, 0, sizeof(sf));
    memset(&vf, 0, sizeof(vf));
    _spline3dinterpolant_init(&c, _state, ae_true);
    _spline3dinterpolant_init(&cc, _state, ae_true);
    ae_vector_init(&vvf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vf, 0, DT_REAL, _state, ae_true);

    eps = 1000*ae_machineepsilon;
    
    /*
     * Test spline ability to reproduce D-dimensional vector function
     *     f[idx](x,y,z) = idx+AX*x + AY*y + AZ*z + AXY*x*y + AYZ*y*z
     * with random AX/AY/...
     *
     * We generate random test function, build spline, then evaluate
     * it in the random test point.
     */
    for(d=1; d<=3; d++)
    {
        n = 2+ae_randominteger(4, _state);
        m = 2+ae_randominteger(4, _state);
        l = 2+ae_randominteger(4, _state);
        ae_vector_set_length(&x, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)(i);
        }
        ae_vector_set_length(&y, m, _state);
        for(i=0; i<=m-1; i++)
        {
            y.ptr.p_double[i] = (double)(i);
        }
        ae_vector_set_length(&z, l, _state);
        for(i=0; i<=l-1; i++)
        {
            z.ptr.p_double[i] = (double)(i);
        }
        ae_vector_set_length(&vf, l*m*n*d, _state);
        offs = 0;
        ax = 2*ae_randomreal(_state)-1;
        ay = 2*ae_randomreal(_state)-1;
        az = 2*ae_randomreal(_state)-1;
        axy = 2*ae_randomreal(_state)-1;
        ayz = 2*ae_randomreal(_state)-1;
        for(k=0; k<=l-1; k++)
        {
            for(j=0; j<=m-1; j++)
            {
                for(i=0; i<=n-1; i++)
                {
                    for(di=0; di<=d-1; di++)
                    {
                        vf.ptr.p_double[offs] = di+ax*i+ay*j+az*k+axy*i*j+ayz*j*k;
                        offs = offs+1;
                    }
                }
            }
        }
        spline3dbuildtrilinearv(&x, n, &y, m, &z, l, &vf, d, &c, _state);
        vx = ae_randomreal(_state)*n;
        vy = ae_randomreal(_state)*m;
        vz = ae_randomreal(_state)*l;
        spline3dcalcv(&c, vx, vy, vz, &vf, _state);
        for(di=0; di<=d-1; di++)
        {
            if( ae_fp_greater(ae_fabs(di+ax*vx+ay*vy+az*vz+axy*vx*vy+ayz*vy*vz-vf.ptr.p_double[di], _state),eps) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
        if( d==1 )
        {
            vsf = spline3dcalc(&c, vx, vy, vz, _state);
            if( ae_fp_greater(ae_fabs(ax*vx+ay*vy+az*vz+axy*vx*vy+ayz*vy*vz-vsf, _state),eps) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    
    /*
     * Generate random grid and test function.
     * Test spline ability to reproduce function values at grid nodes.
     */
    passcount = 20;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Prepare a model and check that functions (Spline3DBuildTrilinear,
         * Spline3DCalc,Spline3DCalcV) work correctly and
         */
        testspline3dunit_buildrndgrid(ae_true, ae_true, &n, &m, &l, &d, &x, &y, &z, &vf, _state);
        rvectorsetlengthatleast(&sf, n*m*l, _state);
        
        /*
         * Check that the model's values are equal to the function's values
         * in grid points
         */
        spline3dbuildtrilinearv(&x, n, &y, m, &z, l, &vf, d, &c, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                for(k=0; k<=l-1; k++)
                {
                    spline3dcalcv(&c, x.ptr.p_double[i], y.ptr.p_double[j], z.ptr.p_double[k], &vvf, _state);
                    for(di=0; di<=d-1; di++)
                    {
                        if( ae_fp_greater(ae_fabs(vf.ptr.p_double[d*(n*(m*k+j)+i)+di]-vvf.ptr.p_double[di], _state),eps) )
                        {
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unpack/UnpackV test
*************************************************************************/
static ae_bool testspline3dunit_testunpack(ae_state *_state)
{
    ae_frame _frame_block;
    spline3dinterpolant c;
    ae_matrix tbl0;
    ae_matrix tbl1;
    ae_int_t n;
    ae_int_t m;
    ae_int_t l;
    ae_int_t d;
    ae_int_t sz;
    ae_int_t un;
    ae_int_t um;
    ae_int_t ul;
    ae_int_t ud;
    ae_int_t ust;
    ae_int_t uvn;
    ae_int_t uvm;
    ae_int_t uvl;
    ae_int_t uvd;
    ae_int_t uvst;
    ae_int_t ci;
    ae_int_t cj;
    ae_int_t ck;
    ae_vector x;
    ae_vector y;
    ae_vector z;
    ae_vector sf;
    ae_vector vf;
    ae_int_t p0;
    ae_int_t p1;
    double tx;
    double ty;
    double tz;
    double v1;
    double v2;
    double err;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool bperr;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t di;
    ae_int_t i0;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&tbl0, 0, sizeof(tbl0));
    memset(&tbl1, 0, sizeof(tbl1));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&z, 0, sizeof(z));
    memset(&sf, 0, sizeof(sf));
    memset(&vf, 0, sizeof(vf));
    _spline3dinterpolant_init(&c, _state, ae_true);
    ae_matrix_init(&tbl0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&tbl1, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vf, 0, DT_REAL, _state, ae_true);

    passcount = 20;
    err = (double)(0);
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * generate random grid.
         * NOTE: for this test we need ordered grid, i.e. grid
         *       with nodes in ascending order
         */
        testspline3dunit_buildrndgrid(ae_true, ae_false, &n, &m, &l, &d, &x, &y, &z, &vf, _state);
        sz = n*m*l;
        rvectorsetlengthatleast(&sf, sz, _state);
        spline3dbuildtrilinearv(&x, n, &y, m, &z, l, &vf, d, &c, _state);
        spline3dunpackv(&c, &uvn, &uvm, &uvl, &uvd, &uvst, &tbl0, _state);
        for(di=0; di<=d-1; di++)
        {
            
            /*
             * DI-th component copy of a vector-function to
             * a scalar function
             */
            for(i=0; i<=sz-1; i++)
            {
                sf.ptr.p_double[i] = vf.ptr.p_double[d*i+di];
            }
            spline3dbuildtrilinearv(&x, n, &y, m, &z, l, &sf, 1, &c, _state);
            spline3dunpackv(&c, &un, &um, &ul, &ud, &ust, &tbl1, _state);
            for(i=0; i<=n-2; i++)
            {
                for(j=0; j<=m-2; j++)
                {
                    for(k=0; k<=l-2; k++)
                    {
                        p1 = (n-1)*((m-1)*k+j)+i;
                        p0 = d*p1+di;
                        
                        /*
                         * Check that all components are correct:
                         *  *first check, that unpacked componets are equal
                         *   to packed components;
                         */
                        bperr = (((((((((((((((((un!=n||um!=m)||ul!=l)||ae_fp_neq(tbl1.ptr.pp_double[p1][0],x.ptr.p_double[i]))||ae_fp_neq(tbl1.ptr.pp_double[p1][1],x.ptr.p_double[i+1]))||ae_fp_neq(tbl1.ptr.pp_double[p1][2],y.ptr.p_double[j]))||ae_fp_neq(tbl1.ptr.pp_double[p1][3],y.ptr.p_double[j+1]))||ae_fp_neq(tbl1.ptr.pp_double[p1][4],z.ptr.p_double[k]))||ae_fp_neq(tbl1.ptr.pp_double[p1][5],z.ptr.p_double[k+1]))||uvn!=n)||uvm!=m)||uvl!=l)||uvd!=d)||ae_fp_neq(tbl0.ptr.pp_double[p0][0],x.ptr.p_double[i]))||ae_fp_neq(tbl0.ptr.pp_double[p0][1],x.ptr.p_double[i+1]))||ae_fp_neq(tbl0.ptr.pp_double[p0][2],y.ptr.p_double[j]))||ae_fp_neq(tbl0.ptr.pp_double[p0][3],y.ptr.p_double[j+1]))||ae_fp_neq(tbl0.ptr.pp_double[p0][4],z.ptr.p_double[k]))||ae_fp_neq(tbl0.ptr.pp_double[p0][5],z.ptr.p_double[k+1]);
                        
                        /*
                         *  *check, that all components unpacked by Unpack
                         *   function are equal to all components unpacked
                         *   by UnpackV function.
                         */
                        for(i0=0; i0<=13; i0++)
                        {
                            bperr = bperr||ae_fp_neq(tbl0.ptr.pp_double[p0][i0],tbl1.ptr.pp_double[p1][i0]);
                        }
                        if( bperr )
                        {
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                        tx = (0.001+0.999*ae_randomreal(_state))*(tbl1.ptr.pp_double[p1][1]-tbl1.ptr.pp_double[p1][0]);
                        ty = (0.001+0.999*ae_randomreal(_state))*(tbl1.ptr.pp_double[p1][3]-tbl1.ptr.pp_double[p1][2]);
                        tz = (0.001+0.999*ae_randomreal(_state))*(tbl1.ptr.pp_double[p1][5]-tbl1.ptr.pp_double[p1][4]);
                        
                        /*
                         * Interpolation properties for:
                         *  *scalar function;
                         */
                        v1 = (double)(0);
                        for(ci=0; ci<=1; ci++)
                        {
                            for(cj=0; cj<=1; cj++)
                            {
                                for(ck=0; ck<=1; ck++)
                                {
                                    v1 = v1+tbl1.ptr.pp_double[p1][6+2*(2*ck+cj)+ci]*ae_pow(tx, (double)(ci), _state)*ae_pow(ty, (double)(cj), _state)*ae_pow(tz, (double)(ck), _state);
                                }
                            }
                        }
                        v2 = spline3dcalc(&c, tbl1.ptr.pp_double[p1][0]+tx, tbl1.ptr.pp_double[p1][2]+ty, tbl1.ptr.pp_double[p1][4]+tz, _state);
                        err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                        
                        /*
                         *  *component of vector function.
                         */
                        v1 = (double)(0);
                        for(ci=0; ci<=1; ci++)
                        {
                            for(cj=0; cj<=1; cj++)
                            {
                                for(ck=0; ck<=1; ck++)
                                {
                                    v1 = v1+tbl0.ptr.pp_double[p0][6+2*(2*ck+cj)+ci]*ae_pow(tx, (double)(ci), _state)*ae_pow(ty, (double)(cj), _state)*ae_pow(tz, (double)(ck), _state);
                                }
                            }
                        }
                        v2 = spline3dcalc(&c, tbl0.ptr.pp_double[p0][0]+tx, tbl0.ptr.pp_double[p0][2]+ty, tbl0.ptr.pp_double[p0][4]+tz, _state);
                        err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                    }
                }
            }
        }
    }
    result = ae_fp_greater(err,1.0E+5*ae_machineepsilon);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
LinTrans test
*************************************************************************/
static ae_bool testspline3dunit_testlintrans(ae_state *_state)
{
    ae_frame _frame_block;
    spline3dinterpolant c;
    spline3dinterpolant c2;
    ae_int_t m;
    ae_int_t n;
    ae_int_t l;
    ae_int_t d;
    ae_vector x;
    ae_vector y;
    ae_vector z;
    ae_vector f;
    double a1;
    double a2;
    double a3;
    double b1;
    double b2;
    double b3;
    double tx;
    double ty;
    double tz;
    double vx;
    double vy;
    double vz;
    ae_vector v1;
    ae_vector v2;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t xjob;
    ae_int_t yjob;
    ae_int_t zjob;
    double err;
    ae_int_t i;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&c2, 0, sizeof(c2));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&z, 0, sizeof(z));
    memset(&f, 0, sizeof(f));
    memset(&v1, 0, sizeof(v1));
    memset(&v2, 0, sizeof(v2));
    _spline3dinterpolant_init(&c, _state, ae_true);
    _spline3dinterpolant_init(&c2, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&f, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&v1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&v2, 0, DT_REAL, _state, ae_true);

    err = (double)(0);
    passcount = 15;
    for(pass=1; pass<=passcount; pass++)
    {
        testspline3dunit_buildrndgrid(ae_true, ae_false, &n, &m, &l, &d, &x, &y, &z, &f, _state);
        spline3dbuildtrilinearv(&x, n, &y, m, &z, l, &f, d, &c, _state);
        for(xjob=0; xjob<=1; xjob++)
        {
            for(yjob=0; yjob<=1; yjob++)
            {
                for(zjob=0; zjob<=1; zjob++)
                {
                    
                    /*
                     * Prepare
                     */
                    do
                    {
                        a1 = 2.0*ae_randomreal(_state)-1.0;
                    }
                    while(ae_fp_eq(a1,(double)(0)));
                    a1 = a1*xjob;
                    b1 = x.ptr.p_double[0]+ae_randomreal(_state)*(x.ptr.p_double[n-1]-x.ptr.p_double[0]+2.0)-1.0;
                    do
                    {
                        a2 = 2.0*ae_randomreal(_state)-1.0;
                    }
                    while(ae_fp_eq(a2,(double)(0)));
                    a2 = a2*yjob;
                    b2 = y.ptr.p_double[0]+ae_randomreal(_state)*(y.ptr.p_double[m-1]-y.ptr.p_double[0]+2.0)-1.0;
                    do
                    {
                        a3 = 2.0*ae_randomreal(_state)-1.0;
                    }
                    while(ae_fp_eq(a3,(double)(0)));
                    a3 = a3*zjob;
                    b3 = z.ptr.p_double[0]+ae_randomreal(_state)*(z.ptr.p_double[l-1]-z.ptr.p_double[0]+2.0)-1.0;
                    
                    /*
                     * Test XYZ
                     */
                    spline3dcopy(&c, &c2, _state);
                    spline3dlintransxyz(&c2, a1, b1, a2, b2, a3, b3, _state);
                    tx = x.ptr.p_double[0]+ae_randomreal(_state)*(x.ptr.p_double[n-1]-x.ptr.p_double[0]);
                    ty = y.ptr.p_double[0]+ae_randomreal(_state)*(y.ptr.p_double[m-1]-y.ptr.p_double[0]);
                    tz = z.ptr.p_double[0]+ae_randomreal(_state)*(z.ptr.p_double[l-1]-z.ptr.p_double[0]);
                    if( xjob==0 )
                    {
                        tx = b1;
                        vx = x.ptr.p_double[0]+ae_randomreal(_state)*(x.ptr.p_double[n-1]-x.ptr.p_double[0]);
                    }
                    else
                    {
                        vx = (tx-b1)/a1;
                    }
                    if( yjob==0 )
                    {
                        ty = b2;
                        vy = y.ptr.p_double[0]+ae_randomreal(_state)*(y.ptr.p_double[m-1]-y.ptr.p_double[0]);
                    }
                    else
                    {
                        vy = (ty-b2)/a2;
                    }
                    if( zjob==0 )
                    {
                        tz = b3;
                        vz = z.ptr.p_double[0]+ae_randomreal(_state)*(z.ptr.p_double[l-1]-z.ptr.p_double[0]);
                    }
                    else
                    {
                        vz = (tz-b3)/a3;
                    }
                    spline3dcalcv(&c, tx, ty, tz, &v1, _state);
                    spline3dcalcv(&c2, vx, vy, vz, &v2, _state);
                    for(i=0; i<=d-1; i++)
                    {
                        err = ae_maxreal(err, ae_fabs(v1.ptr.p_double[i]-v2.ptr.p_double[i], _state), _state);
                    }
                    if( ae_fp_greater(err,1.0E+4*ae_machineepsilon) )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                    
                    /*
                     * Test F
                     */
                    spline3dcopy(&c, &c2, _state);
                    spline3dlintransf(&c2, a1, b1, _state);
                    tx = x.ptr.p_double[0]+ae_randomreal(_state)*(x.ptr.p_double[n-1]-x.ptr.p_double[0]);
                    ty = y.ptr.p_double[0]+ae_randomreal(_state)*(y.ptr.p_double[m-1]-y.ptr.p_double[0]);
                    tz = z.ptr.p_double[0]+ae_randomreal(_state)*(z.ptr.p_double[l-1]-z.ptr.p_double[0]);
                    spline3dcalcv(&c, tx, ty, tz, &v1, _state);
                    spline3dcalcv(&c2, tx, ty, tz, &v2, _state);
                    for(i=0; i<=d-1; i++)
                    {
                        err = ae_maxreal(err, ae_fabs(a1*v1.ptr.p_double[i]+b1-v2.ptr.p_double[i], _state), _state);
                    }
                }
            }
        }
    }
    result = ae_fp_greater(err,1.0E+4*ae_machineepsilon);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Resample test
*************************************************************************/
static ae_bool testspline3dunit_testtrilinearresample(ae_state *_state)
{
    ae_frame _frame_block;
    spline3dinterpolant c;
    ae_int_t n;
    ae_int_t m;
    ae_int_t l;
    ae_int_t n2;
    ae_int_t m2;
    ae_int_t l2;
    ae_vector x;
    ae_vector y;
    ae_vector z;
    ae_vector f;
    ae_vector fr;
    double v1;
    double v2;
    double err;
    double mf;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&z, 0, sizeof(z));
    memset(&f, 0, sizeof(f));
    memset(&fr, 0, sizeof(fr));
    _spline3dinterpolant_init(&c, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&f, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&fr, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    passcount = 20;
    for(pass=1; pass<=passcount; pass++)
    {
        n = ae_randominteger(4, _state)+2;
        m = ae_randominteger(4, _state)+2;
        l = ae_randominteger(4, _state)+2;
        n2 = ae_randominteger(4, _state)+2;
        m2 = ae_randominteger(4, _state)+2;
        l2 = ae_randominteger(4, _state)+2;
        rvectorsetlengthatleast(&x, n, _state);
        rvectorsetlengthatleast(&y, m, _state);
        rvectorsetlengthatleast(&z, l, _state);
        rvectorsetlengthatleast(&f, n*m*l, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)i/(double)(n-1);
        }
        for(i=0; i<=m-1; i++)
        {
            y.ptr.p_double[i] = (double)i/(double)(m-1);
        }
        for(i=0; i<=l-1; i++)
        {
            z.ptr.p_double[i] = (double)i/(double)(l-1);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                for(k=0; k<=l-1; k++)
                {
                    f.ptr.p_double[n*(m*k+j)+i] = 2*ae_randomreal(_state)-1;
                }
            }
        }
        spline3dresampletrilinear(&f, l, m, n, l2, m2, n2, &fr, _state);
        spline3dbuildtrilinearv(&x, n, &y, m, &z, l, &f, 1, &c, _state);
        err = (double)(0);
        mf = (double)(0);
        for(i=0; i<=n2-1; i++)
        {
            for(j=0; j<=m2-1; j++)
            {
                for(k=0; k<=l2-1; k++)
                {
                    v1 = spline3dcalc(&c, (double)i/(double)(n2-1), (double)j/(double)(m2-1), (double)k/(double)(l2-1), _state);
                    v2 = fr.ptr.p_double[n2*(m2*k+j)+i];
                    err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                    mf = ae_maxreal(mf, ae_fabs(v1, _state), _state);
                }
            }
        }
        result = result||ae_fp_greater(err/mf,1.0E+4*ae_machineepsilon);
        if( result )
        {
            ae_frame_leave(_state);
            return result;
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The function does build random function on random grid with random number
of points:
* N, M, K   -   random from 2 to 5
* D         -   1 in case IsVect=False, 1..3 in case IsVect=True
* X, Y, Z   -   each variable spans from MinV to MaxV, with MinV is random
                number from [-1.5,0.5] and MaxV is random number from
                [0.5,1.5]. All nodes are well separated. All nodes are
                randomly reordered in case Reorder=False. When Reorder=True,
                nodes are returned in ascending order.
* F         -   random values from [-1,+1]
*************************************************************************/
static void testspline3dunit_buildrndgrid(ae_bool isvect,
     ae_bool reorder,
     ae_int_t* n,
     ae_int_t* m,
     ae_int_t* l,
     ae_int_t* d,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* z,
     /* Real    */ ae_vector* f,
     ae_state *_state)
{
    double st;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t di;
    double v;
    double mx;
    double maxv;
    double minv;

    *n = 0;
    *m = 0;
    *l = 0;
    *d = 0;
    ae_vector_clear(x);
    ae_vector_clear(y);
    ae_vector_clear(z);
    ae_vector_clear(f);

    st = 0.3;
    *m = ae_randominteger(4, _state)+2;
    *n = ae_randominteger(4, _state)+2;
    *l = ae_randominteger(4, _state)+2;
    if( isvect )
    {
        *d = ae_randominteger(3, _state)+1;
    }
    else
    {
        *d = 1;
    }
    rvectorsetlengthatleast(x, *n, _state);
    rvectorsetlengthatleast(y, *m, _state);
    rvectorsetlengthatleast(z, *l, _state);
    rvectorsetlengthatleast(f, *n*(*m)*(*l)*(*d), _state);
    
    /*
     * Fill X
     */
    x->ptr.p_double[0] = (double)(0);
    for(i=1; i<=*n-1; i++)
    {
        x->ptr.p_double[i] = x->ptr.p_double[i-1]+st+ae_randomreal(_state);
    }
    minv = -0.5-ae_randomreal(_state);
    maxv = 0.5+ae_randomreal(_state);
    mx = x->ptr.p_double[*n-1];
    for(i=0; i<=*n-1; i++)
    {
        x->ptr.p_double[i] = x->ptr.p_double[i]/mx*(maxv-minv)+minv;
    }
    if( reorder )
    {
        for(i=0; i<=*n-1; i++)
        {
            k = ae_randominteger(*n, _state);
            v = x->ptr.p_double[i];
            x->ptr.p_double[i] = x->ptr.p_double[k];
            x->ptr.p_double[k] = v;
        }
    }
    
    /*
     * Fill Y
     */
    y->ptr.p_double[0] = (double)(0);
    for(i=1; i<=*m-1; i++)
    {
        y->ptr.p_double[i] = y->ptr.p_double[i-1]+st+ae_randomreal(_state);
    }
    minv = -0.5-ae_randomreal(_state);
    maxv = 0.5+ae_randomreal(_state);
    mx = y->ptr.p_double[*m-1];
    for(i=0; i<=*m-1; i++)
    {
        y->ptr.p_double[i] = y->ptr.p_double[i]/mx*(maxv-minv)+minv;
    }
    if( reorder )
    {
        for(i=0; i<=*m-1; i++)
        {
            k = ae_randominteger(*m, _state);
            v = y->ptr.p_double[i];
            y->ptr.p_double[i] = y->ptr.p_double[k];
            y->ptr.p_double[k] = v;
        }
    }
    
    /*
     * Fill Z
     */
    z->ptr.p_double[0] = (double)(0);
    for(i=1; i<=*l-1; i++)
    {
        z->ptr.p_double[i] = z->ptr.p_double[i-1]+st+ae_randomreal(_state);
    }
    minv = -0.5-ae_randomreal(_state);
    maxv = 0.5+ae_randomreal(_state);
    mx = z->ptr.p_double[*l-1];
    for(i=0; i<=*l-1; i++)
    {
        z->ptr.p_double[i] = z->ptr.p_double[i]/mx*(maxv-minv)+minv;
    }
    if( reorder )
    {
        for(i=0; i<=*l-1; i++)
        {
            k = ae_randominteger(*l, _state);
            v = z->ptr.p_double[i];
            z->ptr.p_double[i] = z->ptr.p_double[k];
            z->ptr.p_double[k] = v;
        }
    }
    
    /*
     * Fill F
     */
    for(i=0; i<=*n-1; i++)
    {
        for(j=0; j<=*m-1; j++)
        {
            for(k=0; k<=*l-1; k++)
            {
                for(di=0; di<=*d-1; di++)
                {
                    f->ptr.p_double[*d*(*n*(*m*k+j)+i)+di] = 2*ae_randomreal(_state)-1;
                }
            }
        }
    }
}



static double testpolintunit_internalpolint(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_int_t n,
     double t,
     ae_state *_state);
static void testpolintunit_brcunset(barycentricinterpolant* b,
     ae_state *_state);





/*************************************************************************
Unit test
*************************************************************************/
ae_bool testpolint(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool interrors;
    double threshold;
    ae_vector x;
    ae_vector y;
    ae_vector w;
    ae_vector c;
    ae_vector c0;
    ae_vector c1;
    ae_vector c2;
    ae_vector x2;
    ae_vector y2;
    ae_vector w2;
    ae_vector xfull;
    ae_vector yfull;
    double a;
    double b;
    double t;
    ae_int_t i;
    ae_int_t k;
    ae_vector xc;
    ae_vector yc;
    ae_vector dc;
    double v;
    double v0;
    double v1;
    double v2;
    double v3;
    double v4;
    double pscale;
    double poffset;
    double eps;
    barycentricinterpolant p;
    barycentricinterpolant p1;
    barycentricinterpolant p2;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&w, 0, sizeof(w));
    memset(&c, 0, sizeof(c));
    memset(&c0, 0, sizeof(c0));
    memset(&c1, 0, sizeof(c1));
    memset(&c2, 0, sizeof(c2));
    memset(&x2, 0, sizeof(x2));
    memset(&y2, 0, sizeof(y2));
    memset(&w2, 0, sizeof(w2));
    memset(&xfull, 0, sizeof(xfull));
    memset(&yfull, 0, sizeof(yfull));
    memset(&xc, 0, sizeof(xc));
    memset(&yc, 0, sizeof(yc));
    memset(&dc, 0, sizeof(dc));
    memset(&p, 0, sizeof(p));
    memset(&p1, 0, sizeof(p1));
    memset(&p2, 0, sizeof(p2));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xfull, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yfull, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _barycentricinterpolant_init(&p, _state, ae_true);
    _barycentricinterpolant_init(&p1, _state, ae_true);
    _barycentricinterpolant_init(&p2, _state, ae_true);

    waserrors = ae_false;
    interrors = ae_false;
    maxn = 5;
    passcount = 20;
    threshold = 1.0E8*ae_machineepsilon;
    
    /*
     * Test equidistant interpolation
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * prepare task:
             * * equidistant points
             * * random Y
             * * T in [A,B] or near (within 10% of its width)
             */
            do
            {
                a = 2*ae_randomreal(_state)-1;
                b = 2*ae_randomreal(_state)-1;
            }
            while(ae_fp_less_eq(ae_fabs(a-b, _state),0.2));
            t = a+(1.2*ae_randomreal(_state)-0.1)*(b-a);
            taskgenint1dequidist(a, b, n, &x, &y, _state);
            
            /*
             * test "fast" equidistant interpolation (no barycentric model)
             */
            interrors = interrors||ae_fp_greater(ae_fabs(polynomialcalceqdist(a, b, &y, n, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "slow" equidistant interpolation (create barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuild(&x, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "fast" interpolation (create "fast" barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuildeqdist(a, b, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
        }
    }
    
    /*
     * Test Chebyshev-1 interpolation
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * prepare task:
             * * equidistant points
             * * random Y
             * * T in [A,B] or near (within 10% of its width)
             */
            do
            {
                a = 2*ae_randomreal(_state)-1;
                b = 2*ae_randomreal(_state)-1;
            }
            while(ae_fp_less_eq(ae_fabs(a-b, _state),0.2));
            t = a+(1.2*ae_randomreal(_state)-0.1)*(b-a);
            taskgenint1dcheb1(a, b, n, &x, &y, _state);
            
            /*
             * test "fast" interpolation (no barycentric model)
             */
            interrors = interrors||ae_fp_greater(ae_fabs(polynomialcalccheb1(a, b, &y, n, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "slow" interpolation (create barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuild(&x, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "fast" interpolation (create "fast" barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuildcheb1(a, b, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
        }
    }
    
    /*
     * Test Chebyshev-2 interpolation
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * prepare task:
             * * equidistant points
             * * random Y
             * * T in [A,B] or near (within 10% of its width)
             */
            do
            {
                a = 2*ae_randomreal(_state)-1;
                b = 2*ae_randomreal(_state)-1;
            }
            while(ae_fp_less_eq(ae_fabs(a-b, _state),0.2));
            t = a+(1.2*ae_randomreal(_state)-0.1)*(b-a);
            taskgenint1dcheb2(a, b, n, &x, &y, _state);
            
            /*
             * test "fast" interpolation (no barycentric model)
             */
            interrors = interrors||ae_fp_greater(ae_fabs(polynomialcalccheb2(a, b, &y, n, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "slow" interpolation (create barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuild(&x, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "fast" interpolation (create "fast" barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuildcheb2(a, b, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
        }
    }
    
    /*
     * Testing conversion Barycentric<->Chebyshev
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(k=1; k<=3; k++)
        {
            
            /*
             * Allocate
             */
            ae_vector_set_length(&x, k, _state);
            ae_vector_set_length(&y, k, _state);
            
            /*
             * Generate problem
             */
            a = 2*ae_randomreal(_state)-1;
            b = a+(0.1+ae_randomreal(_state))*(2*ae_randominteger(2, _state)-1);
            v0 = 2*ae_randomreal(_state)-1;
            v1 = 2*ae_randomreal(_state)-1;
            v2 = 2*ae_randomreal(_state)-1;
            if( k==1 )
            {
                x.ptr.p_double[0] = 0.5*(a+b);
                y.ptr.p_double[0] = v0;
            }
            if( k==2 )
            {
                x.ptr.p_double[0] = a;
                y.ptr.p_double[0] = v0-v1;
                x.ptr.p_double[1] = b;
                y.ptr.p_double[1] = v0+v1;
            }
            if( k==3 )
            {
                x.ptr.p_double[0] = a;
                y.ptr.p_double[0] = v0-v1+v2;
                x.ptr.p_double[1] = 0.5*(a+b);
                y.ptr.p_double[1] = v0-v2;
                x.ptr.p_double[2] = b;
                y.ptr.p_double[2] = v0+v1+v2;
            }
            
            /*
             * Test forward conversion
             */
            polynomialbuild(&x, &y, k, &p, _state);
            ae_vector_set_length(&c, 1, _state);
            polynomialbar2cheb(&p, a, b, &c, _state);
            interrors = interrors||c.cnt!=k;
            if( k>=1 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[0]-v0, _state),threshold);
            }
            if( k>=2 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[1]-v1, _state),threshold);
            }
            if( k>=3 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[2]-v2, _state),threshold);
            }
            
            /*
             * Test backward conversion
             */
            polynomialcheb2bar(&c, k, a, b, &p2, _state);
            v = a+ae_randomreal(_state)*(b-a);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, v, _state)-barycentriccalc(&p2, v, _state), _state),threshold);
        }
    }
    
    /*
     * Testing conversion Barycentric<->Power
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(k=1; k<=5; k++)
        {
            
            /*
             * Allocate
             */
            ae_vector_set_length(&x, k, _state);
            ae_vector_set_length(&y, k, _state);
            
            /*
             * Generate problem
             */
            poffset = 2*ae_randomreal(_state)-1;
            pscale = (0.1+ae_randomreal(_state))*(2*ae_randominteger(2, _state)-1);
            v0 = 2*ae_randomreal(_state)-1;
            v1 = 2*ae_randomreal(_state)-1;
            v2 = 2*ae_randomreal(_state)-1;
            v3 = 2*ae_randomreal(_state)-1;
            v4 = 2*ae_randomreal(_state)-1;
            if( k==1 )
            {
                x.ptr.p_double[0] = poffset;
                y.ptr.p_double[0] = v0;
            }
            if( k==2 )
            {
                x.ptr.p_double[0] = poffset-pscale;
                y.ptr.p_double[0] = v0-v1;
                x.ptr.p_double[1] = poffset+pscale;
                y.ptr.p_double[1] = v0+v1;
            }
            if( k==3 )
            {
                x.ptr.p_double[0] = poffset-pscale;
                y.ptr.p_double[0] = v0-v1+v2;
                x.ptr.p_double[1] = poffset;
                y.ptr.p_double[1] = v0;
                x.ptr.p_double[2] = poffset+pscale;
                y.ptr.p_double[2] = v0+v1+v2;
            }
            if( k==4 )
            {
                x.ptr.p_double[0] = poffset-pscale;
                y.ptr.p_double[0] = v0-v1+v2-v3;
                x.ptr.p_double[1] = poffset-0.5*pscale;
                y.ptr.p_double[1] = v0-0.5*v1+0.25*v2-0.125*v3;
                x.ptr.p_double[2] = poffset+0.5*pscale;
                y.ptr.p_double[2] = v0+0.5*v1+0.25*v2+0.125*v3;
                x.ptr.p_double[3] = poffset+pscale;
                y.ptr.p_double[3] = v0+v1+v2+v3;
            }
            if( k==5 )
            {
                x.ptr.p_double[0] = poffset-pscale;
                y.ptr.p_double[0] = v0-v1+v2-v3+v4;
                x.ptr.p_double[1] = poffset-0.5*pscale;
                y.ptr.p_double[1] = v0-0.5*v1+0.25*v2-0.125*v3+0.0625*v4;
                x.ptr.p_double[2] = poffset;
                y.ptr.p_double[2] = v0;
                x.ptr.p_double[3] = poffset+0.5*pscale;
                y.ptr.p_double[3] = v0+0.5*v1+0.25*v2+0.125*v3+0.0625*v4;
                x.ptr.p_double[4] = poffset+pscale;
                y.ptr.p_double[4] = v0+v1+v2+v3+v4;
            }
            
            /*
             * Test forward conversion
             */
            polynomialbuild(&x, &y, k, &p, _state);
            ae_vector_set_length(&c, 1, _state);
            polynomialbar2pow(&p, poffset, pscale, &c, _state);
            interrors = interrors||c.cnt!=k;
            if( k>=1 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[0]-v0, _state),threshold);
            }
            if( k>=2 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[1]-v1, _state),threshold);
            }
            if( k>=3 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[2]-v2, _state),threshold);
            }
            if( k>=4 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[3]-v3, _state),threshold);
            }
            if( k>=5 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[4]-v4, _state),threshold);
            }
            
            /*
             * Test backward conversion
             */
            polynomialpow2bar(&c, k, poffset, pscale, &p2, _state);
            v = poffset+(2*ae_randomreal(_state)-1)*pscale;
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, v, _state)-barycentriccalc(&p2, v, _state), _state),threshold);
        }
    }
    
    /*
     * crash-test: ability to solve tasks which will overflow/underflow
     * weights with straightforward implementation
     */
    for(n=1; n<=20; n++)
    {
        a = -0.1*ae_maxrealnumber;
        b = 0.1*ae_maxrealnumber;
        taskgenint1dequidist(a, b, n, &x, &y, _state);
        polynomialbuild(&x, &y, n, &p, _state);
        for(i=0; i<=n-1; i++)
        {
            interrors = interrors||ae_fp_eq(p.w.ptr.p_double[i],(double)(0));
        }
    }
    
    /*
     * Test issue #634: instability in PolynomialBar2Pow().
     *
     * Function returns incorrect coefficients when called with
     * approximately-unit scale for data which have significantly
     * non-unit scale.
     */
    n = 7;
    eps = 1.0E-8;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&x2, n, _state);
    ae_vector_set_length(&y, n, _state);
    x.ptr.p_double[0] = ae_randomreal(_state)-0.5;
    y.ptr.p_double[0] = ae_randomreal(_state)-0.5;
    for(i=1; i<=n-1; i++)
    {
        x.ptr.p_double[i] = x.ptr.p_double[i-1]+ae_randomreal(_state)+0.1;
        y.ptr.p_double[i] = ae_randomreal(_state)-0.5;
    }
    polynomialbuild(&x, &y, n, &p, _state);
    polynomialbar2pow(&p, 0.0, 1.0, &c0, _state);
    pscale = 1.0E-10;
    for(i=0; i<=n-1; i++)
    {
        x2.ptr.p_double[i] = x.ptr.p_double[i]*pscale;
    }
    polynomialbuild(&x2, &y, n, &p, _state);
    polynomialbar2pow(&p, 0.0, 1.0, &c1, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_set_error_flag(&interrors, ae_fp_greater(ae_fabs(c0.ptr.p_double[i]-c1.ptr.p_double[i]*ae_pow(pscale, (double)(i), _state), _state),eps), __FILE__, __LINE__, "testpolintunit.ap:395");
    }
    pscale = 1.0E10;
    for(i=0; i<=n-1; i++)
    {
        x2.ptr.p_double[i] = x.ptr.p_double[i]*pscale;
    }
    polynomialbuild(&x2, &y, n, &p, _state);
    polynomialbar2pow(&p, 0.0, 1.0, &c2, _state);
    for(i=0; i<=n-1; i++)
    {
        ae_set_error_flag(&interrors, ae_fp_greater(ae_fabs(c0.ptr.p_double[i]-c2.ptr.p_double[i]*ae_pow(pscale, (double)(i), _state), _state),eps), __FILE__, __LINE__, "testpolintunit.ap:402");
    }
    
    /*
     * report
     */
    waserrors = interrors;
    if( !silent )
    {
        printf("TESTING POLYNOMIAL INTERPOLATION\n");
        
        /*
         * Normal tests
         */
        printf("INTERPOLATION TEST:                      ");
        if( interrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static double testpolintunit_internalpolint(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_int_t n,
     double t,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector _f;
    ae_int_t i;
    ae_int_t j;
    double result;

    ae_frame_make(_state, &_frame_block);
    memset(&_f, 0, sizeof(_f));
    ae_vector_init_copy(&_f, f, _state, ae_true);
    f = &_f;

    n = n-1;
    for(j=0; j<=n-1; j++)
    {
        for(i=j+1; i<=n; i++)
        {
            f->ptr.p_double[i] = ((t-x->ptr.p_double[j])*f->ptr.p_double[i]-(t-x->ptr.p_double[i])*f->ptr.p_double[j])/(x->ptr.p_double[i]-x->ptr.p_double[j]);
        }
    }
    result = f->ptr.p_double[n];
    ae_frame_leave(_state);
    return result;
}


static void testpolintunit_brcunset(barycentricinterpolant* b,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_vector y;
    ae_vector w;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&w, 0, sizeof(w));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&x, 1, _state);
    ae_vector_set_length(&y, 1, _state);
    ae_vector_set_length(&w, 1, _state);
    x.ptr.p_double[0] = (double)(0);
    y.ptr.p_double[0] = (double)(0);
    w.ptr.p_double[0] = (double)(1);
    barycentricbuildxyw(&x, &y, &w, 1, b, _state);
    ae_frame_leave(_state);
}



static void testlsfitunit_testpolynomialfitting(ae_bool* fiterrors,
     ae_state *_state);
static void testlsfitunit_testrationalfitting(ae_bool* fiterrors,
     ae_state *_state);
static void testlsfitunit_testsplinefitting(ae_bool* fiterrors,
     ae_state *_state);
static void testlsfitunit_testgeneralfitting(ae_bool* llserrors,
     ae_bool* nlserrors,
     ae_state *_state);
static void testlsfitunit_testrdp(ae_bool* errorflag, ae_state *_state);
static void testlsfitunit_testlogisticfitting(ae_bool* fiterrors,
     ae_state *_state);
static void testlsfitunit_testbcnls(ae_bool* errorflag, ae_state *_state);
static void testlsfitunit_testlcnls(ae_bool* errorflag, ae_state *_state);
static ae_bool testlsfitunit_isglssolution(ae_int_t n,
     ae_int_t m,
     ae_int_t k,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* fmatrix,
     /* Real    */ ae_matrix* cmatrix,
     /* Real    */ ae_vector* c,
     ae_state *_state);
static double testlsfitunit_getglserror(ae_int_t n,
     ae_int_t m,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* fmatrix,
     /* Real    */ ae_vector* c,
     ae_state *_state);
static void testlsfitunit_fitlinearnonlinear(ae_int_t m,
     ae_int_t deravailable,
     /* Real    */ ae_matrix* xy,
     lsfitstate* state,
     ae_bool* nlserrors,
     ae_state *_state);
static void testlsfitunit_testgradientcheck(ae_bool* testg,
     ae_state *_state);
static void testlsfitunit_funcderiv(/* Real    */ ae_vector* c,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* x0,
     ae_int_t k,
     ae_int_t m,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state);
static void testlsfitunit_testfunc1(ae_int_t k,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* z,
     double* f,
     ae_bool needf,
     /* Real    */ ae_vector* g,
     ae_bool needg,
     ae_state *_state);
static void testlsfitunit_testfunc2(/* Real    */ ae_vector* x,
     ae_int_t nx,
     /* Real    */ ae_vector* c,
     ae_int_t nc,
     double* f,
     ae_bool needf,
     /* Real    */ ae_vector* g,
     ae_bool needg,
     ae_state *_state);
static void testlsfitunit_testfunc3(/* Real    */ ae_vector* x,
     ae_int_t nx,
     /* Real    */ ae_vector* c,
     ae_int_t nc,
     double* f,
     ae_bool needf,
     /* Real    */ ae_vector* g,
     ae_bool needg,
     ae_state *_state);





ae_bool testlsfit(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool llserrors;
    ae_bool nlserrors;
    ae_bool polfiterrors;
    ae_bool ratfiterrors;
    ae_bool splfiterrors;
    ae_bool graderrors;
    ae_bool logisticerrors;
    ae_bool rdperrors;
    ae_bool result;


    waserrors = ae_false;
    polfiterrors = ae_false;
    ratfiterrors = ae_false;
    splfiterrors = ae_false;
    llserrors = ae_false;
    nlserrors = ae_false;
    graderrors = ae_false;
    logisticerrors = ae_false;
    rdperrors = ae_false;
    testlsfitunit_testrdp(&rdperrors, _state);
    testlsfitunit_testlogisticfitting(&logisticerrors, _state);
    testlsfitunit_testpolynomialfitting(&polfiterrors, _state);
    testlsfitunit_testrationalfitting(&ratfiterrors, _state);
    testlsfitunit_testsplinefitting(&splfiterrors, _state);
    testlsfitunit_testgeneralfitting(&llserrors, &nlserrors, _state);
    testlsfitunit_testgradientcheck(&graderrors, _state);
    
    /*
     * report
     */
    waserrors = ((((((llserrors||nlserrors)||polfiterrors)||ratfiterrors)||splfiterrors)||graderrors)||logisticerrors)||rdperrors;
    if( !silent )
    {
        printf("TESTING LEAST SQUARES\n");
        printf("POLYNOMIAL LEAST SQUARES:                ");
        if( polfiterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RATIONAL LEAST SQUARES:                  ");
        if( ratfiterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("SPLINE LEAST SQUARES:                    ");
        if( splfiterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LINEAR LEAST SQUARES:                    ");
        if( llserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("NON-LINEAR LEAST SQUARES:                ");
        if( nlserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("TEST FOR VERIFICATION OF THE GRADIENT:   ");
        if( graderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LOGISTIC FITTING (4PL/5PL):              ");
        if( logisticerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RDP ALGORITHM:                           ");
        if( rdperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    return result;
}


/*************************************************************************
Unit test
*************************************************************************/
static void testlsfitunit_testpolynomialfitting(ae_bool* fiterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    ae_vector x;
    ae_vector y;
    ae_vector w;
    ae_vector x2;
    ae_vector y2;
    ae_vector w2;
    ae_vector xfull;
    ae_vector yfull;
    double t;
    ae_int_t i;
    ae_int_t k;
    ae_vector xc;
    ae_vector yc;
    ae_vector dc;
    ae_int_t info;
    ae_int_t info2;
    double v;
    double v0;
    double v1;
    double v2;
    double s;
    double xmin;
    double xmax;
    double refrms;
    double refavg;
    double refavgrel;
    double refmax;
    barycentricinterpolant p;
    barycentricinterpolant p1;
    barycentricinterpolant p2;
    polynomialfitreport rep;
    polynomialfitreport rep2;
    ae_int_t n;
    ae_int_t m;
    ae_int_t maxn;
    ae_int_t pass;
    ae_int_t passcount;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&w, 0, sizeof(w));
    memset(&x2, 0, sizeof(x2));
    memset(&y2, 0, sizeof(y2));
    memset(&w2, 0, sizeof(w2));
    memset(&xfull, 0, sizeof(xfull));
    memset(&yfull, 0, sizeof(yfull));
    memset(&xc, 0, sizeof(xc));
    memset(&yc, 0, sizeof(yc));
    memset(&dc, 0, sizeof(dc));
    memset(&p, 0, sizeof(p));
    memset(&p1, 0, sizeof(p1));
    memset(&p2, 0, sizeof(p2));
    memset(&rep, 0, sizeof(rep));
    memset(&rep2, 0, sizeof(rep2));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xfull, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yfull, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _barycentricinterpolant_init(&p, _state, ae_true);
    _barycentricinterpolant_init(&p1, _state, ae_true);
    _barycentricinterpolant_init(&p2, _state, ae_true);
    _polynomialfitreport_init(&rep, _state, ae_true);
    _polynomialfitreport_init(&rep2, _state, ae_true);

    *fiterrors = ae_false;
    maxn = 5;
    passcount = 20;
    threshold = 1.0E8*ae_machineepsilon;
    
    /*
     * Test polunomial fitting
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * N=M+K fitting (i.e. interpolation)
             */
            for(k=0; k<=n-1; k++)
            {
                taskgenint1d((double)(-1), (double)(1), n, &xfull, &yfull, _state);
                ae_vector_set_length(&x, n-k, _state);
                ae_vector_set_length(&y, n-k, _state);
                ae_vector_set_length(&w, n-k, _state);
                if( k>0 )
                {
                    ae_vector_set_length(&xc, k, _state);
                    ae_vector_set_length(&yc, k, _state);
                    ae_vector_set_length(&dc, k, _state);
                }
                for(i=0; i<=n-k-1; i++)
                {
                    x.ptr.p_double[i] = xfull.ptr.p_double[i];
                    y.ptr.p_double[i] = yfull.ptr.p_double[i];
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                for(i=0; i<=k-1; i++)
                {
                    xc.ptr.p_double[i] = xfull.ptr.p_double[n-k+i];
                    yc.ptr.p_double[i] = yfull.ptr.p_double[n-k+i];
                    dc.ptr.p_int[i] = 0;
                }
                polynomialfitwc(&x, &y, &w, n-k, &xc, &yc, &dc, k, n, &info, &p1, &rep, _state);
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-k-1; i++)
                    {
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(barycentriccalc(&p1, x.ptr.p_double[i], _state)-y.ptr.p_double[i], _state),threshold);
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(barycentriccalc(&p1, xc.ptr.p_double[i], _state)-yc.ptr.p_double[i], _state),threshold);
                    }
                }
            }
            
            /*
             * Testing constraints on derivatives.
             * Special tasks which will always have solution:
             * 1. P(0)=YC[0]
             * 2. P(0)=YC[0], P'(0)=YC[1]
             */
            if( n>1 )
            {
                for(m=3; m<=5; m++)
                {
                    for(k=1; k<=2; k++)
                    {
                        taskgenint1d((double)(-1), (double)(1), n, &x, &y, _state);
                        ae_vector_set_length(&w, n, _state);
                        ae_vector_set_length(&xc, 2, _state);
                        ae_vector_set_length(&yc, 2, _state);
                        ae_vector_set_length(&dc, 2, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            w.ptr.p_double[i] = 1+ae_randomreal(_state);
                        }
                        xc.ptr.p_double[0] = (double)(0);
                        yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
                        dc.ptr.p_int[0] = 0;
                        xc.ptr.p_double[1] = (double)(0);
                        yc.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                        dc.ptr.p_int[1] = 1;
                        polynomialfitwc(&x, &y, &w, n, &xc, &yc, &dc, k, m, &info, &p1, &rep, _state);
                        if( info<=0 )
                        {
                            *fiterrors = ae_true;
                        }
                        else
                        {
                            barycentricdiff1(&p1, 0.0, &v0, &v1, _state);
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v0-yc.ptr.p_double[0], _state),threshold);
                            if( k==2 )
                            {
                                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v1-yc.ptr.p_double[1], _state),threshold);
                            }
                        }
                    }
                }
            }
        }
    }
    for(m=2; m<=8; m++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * General fitting
             *
             * interpolating function through M nodes should have
             * greater RMS error than fitting it through the same M nodes
             */
            n = 100;
            ae_vector_set_length(&x2, n, _state);
            ae_vector_set_length(&y2, n, _state);
            ae_vector_set_length(&w2, n, _state);
            xmin = (double)(0);
            xmax = 2*ae_pi;
            for(i=0; i<=n-1; i++)
            {
                x2.ptr.p_double[i] = 2*ae_pi*ae_randomreal(_state);
                y2.ptr.p_double[i] = ae_sin(x2.ptr.p_double[i], _state);
                w2.ptr.p_double[i] = (double)(1);
            }
            ae_vector_set_length(&x, m, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                x.ptr.p_double[i] = xmin+(xmax-xmin)*i/(m-1);
                y.ptr.p_double[i] = ae_sin(x.ptr.p_double[i], _state);
            }
            polynomialbuild(&x, &y, m, &p1, _state);
            polynomialfitwc(&x2, &y2, &w2, n, &xc, &yc, &dc, 0, m, &info, &p2, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * calculate P1 (interpolant) RMS error, compare with P2 error
                 */
                v1 = (double)(0);
                v2 = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    v1 = v1+ae_sqr(barycentriccalc(&p1, x2.ptr.p_double[i], _state)-y2.ptr.p_double[i], _state);
                    v2 = v2+ae_sqr(barycentriccalc(&p2, x2.ptr.p_double[i], _state)-y2.ptr.p_double[i], _state);
                }
                v1 = ae_sqrt(v1/n, _state);
                v2 = ae_sqrt(v2/n, _state);
                *fiterrors = *fiterrors||ae_fp_greater(v2,v1);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v2-rep.rmserror, _state),threshold);
            }
            
            /*
             * compare weighted and non-weighted
             */
            n = 20;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = i+(ae_randomreal(_state)-0.5);
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = (double)(1);
            }
            polynomialfitwc(&x, &y, &w, n, &xc, &yc, &dc, 0, m, &info, &p1, &rep, _state);
            polynomialfit(&x, &y, n, m, &info2, &p2, &rep2, _state);
            if( info<=0||info2<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * calculate P1 (interpolant), compare with P2 error
                 * compare RMS errors
                 */
                t = 2*ae_randomreal(_state)-1;
                v1 = barycentriccalc(&p1, t, _state);
                v2 = barycentriccalc(&p2, t, _state);
                *fiterrors = *fiterrors||!approxequal(v2, v1, 1.0E-12, _state);
                *fiterrors = *fiterrors||!approxequal(rep.rmserror, rep2.rmserror, 1.0E-12*maxreal3((double)(1), rep.rmserror, rep2.rmserror, _state), _state);
                *fiterrors = *fiterrors||!approxequal(rep.avgerror, rep2.avgerror, 1.0E-12*maxreal3((double)(1), rep.avgerror, rep2.avgerror, _state), _state);
                *fiterrors = *fiterrors||!approxequal(rep.avgrelerror, rep2.avgrelerror, 1.0E-12*maxreal3((double)(1), rep.avgrelerror, rep2.avgrelerror, _state), _state);
                *fiterrors = *fiterrors||!approxequal(rep.maxerror, rep2.maxerror, 1.0E-12*maxreal3((double)(1), rep.maxerror, rep2.maxerror, _state), _state);
            }
        }
    }
    for(m=1; m<=maxn; m++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            ae_assert(passcount>=2, "PassCount should be 2 or greater!", _state);
            
            /*
             * solve simple task (all X[] are the same, Y[] are specially
             * calculated to ensure simple form of all types of errors)
             * and check correctness of the errors calculated by subroutines
             *
             * First pass is done with zero Y[], other passes - with random Y[].
             * It should test both ability to correctly calculate errors and
             * ability to not fail while working with zeros :)
             */
            n = 4*maxn;
            if( pass==1 )
            {
                v1 = (double)(0);
                v2 = (double)(0);
                v = (double)(0);
            }
            else
            {
                v1 = ae_randomreal(_state);
                v2 = ae_randomreal(_state);
                v = 1+ae_randomreal(_state);
            }
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=maxn-1; i++)
            {
                x.ptr.p_double[4*i+0] = (double)(i);
                y.ptr.p_double[4*i+0] = v-v2;
                w.ptr.p_double[4*i+0] = (double)(1);
                x.ptr.p_double[4*i+1] = (double)(i);
                y.ptr.p_double[4*i+1] = v-v1;
                w.ptr.p_double[4*i+1] = (double)(1);
                x.ptr.p_double[4*i+2] = (double)(i);
                y.ptr.p_double[4*i+2] = v+v1;
                w.ptr.p_double[4*i+2] = (double)(1);
                x.ptr.p_double[4*i+3] = (double)(i);
                y.ptr.p_double[4*i+3] = v+v2;
                w.ptr.p_double[4*i+3] = (double)(1);
            }
            refrms = ae_sqrt((ae_sqr(v1, _state)+ae_sqr(v2, _state))/2, _state);
            refavg = (ae_fabs(v1, _state)+ae_fabs(v2, _state))/2;
            if( pass==1 )
            {
                refavgrel = (double)(0);
            }
            else
            {
                refavgrel = 0.25*(ae_fabs(v2, _state)/ae_fabs(v-v2, _state)+ae_fabs(v1, _state)/ae_fabs(v-v1, _state)+ae_fabs(v1, _state)/ae_fabs(v+v1, _state)+ae_fabs(v2, _state)/ae_fabs(v+v2, _state));
            }
            refmax = ae_maxreal(v1, v2, _state);
            
            /*
             * Test errors correctness
             */
            polynomialfit(&x, &y, n, m, &info, &p, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                s = barycentriccalc(&p, (double)(0), _state);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
}


static void testlsfitunit_testrationalfitting(ae_bool* fiterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    ae_int_t maxn;
    ae_int_t passcount;
    barycentricinterpolant b1;
    barycentricinterpolant b2;
    ae_vector x;
    ae_vector x2;
    ae_vector y;
    ae_vector y2;
    ae_vector w;
    ae_vector w2;
    ae_vector xc;
    ae_vector yc;
    ae_vector dc;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t k;
    ae_int_t pass;
    double t;
    double s;
    double v;
    double v0;
    double v1;
    double v2;
    ae_int_t info;
    ae_int_t info2;
    double xmin;
    double xmax;
    double refrms;
    double refavg;
    double refavgrel;
    double refmax;
    barycentricfitreport rep;
    barycentricfitreport rep2;

    ae_frame_make(_state, &_frame_block);
    memset(&b1, 0, sizeof(b1));
    memset(&b2, 0, sizeof(b2));
    memset(&x, 0, sizeof(x));
    memset(&x2, 0, sizeof(x2));
    memset(&y, 0, sizeof(y));
    memset(&y2, 0, sizeof(y2));
    memset(&w, 0, sizeof(w));
    memset(&w2, 0, sizeof(w2));
    memset(&xc, 0, sizeof(xc));
    memset(&yc, 0, sizeof(yc));
    memset(&dc, 0, sizeof(dc));
    memset(&rep, 0, sizeof(rep));
    memset(&rep2, 0, sizeof(rep2));
    _barycentricinterpolant_init(&b1, _state, ae_true);
    _barycentricinterpolant_init(&b2, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _barycentricfitreport_init(&rep, _state, ae_true);
    _barycentricfitreport_init(&rep2, _state, ae_true);

    *fiterrors = ae_false;
    
    /*
     * PassCount        number of repeated passes
     * Threshold        error tolerance
     * LipschitzTol     Lipschitz constant increase allowed
     *                  when calculating constant on a twice denser grid
     */
    passcount = 5;
    maxn = 15;
    threshold = 1000000*ae_machineepsilon;
    
    /*
     * Test rational fitting:
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=2; n<=maxn; n++)
        {
            
            /*
             * N=M+K fitting (i.e. interpolation)
             */
            for(k=0; k<=n-1; k++)
            {
                ae_vector_set_length(&x, n-k, _state);
                ae_vector_set_length(&y, n-k, _state);
                ae_vector_set_length(&w, n-k, _state);
                if( k>0 )
                {
                    ae_vector_set_length(&xc, k, _state);
                    ae_vector_set_length(&yc, k, _state);
                    ae_vector_set_length(&dc, k, _state);
                }
                for(i=0; i<=n-k-1; i++)
                {
                    x.ptr.p_double[i] = (double)i/(double)(n-1);
                    y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                for(i=0; i<=k-1; i++)
                {
                    xc.ptr.p_double[i] = (double)(n-k+i)/(double)(n-1);
                    yc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    dc.ptr.p_int[i] = 0;
                }
                barycentricfitfloaterhormannwc(&x, &y, &w, n-k, &xc, &yc, &dc, k, n, &info, &b1, &rep, _state);
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-k-1; i++)
                    {
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, x.ptr.p_double[i], _state)-y.ptr.p_double[i], _state),threshold);
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, xc.ptr.p_double[i], _state)-yc.ptr.p_double[i], _state),threshold);
                    }
                }
            }
            
            /*
             * Testing constraints on derivatives:
             * * several M's are tried
             * * several K's are tried - 1, 2.
             * * constraints at the ends of the interval
             */
            for(m=3; m<=5; m++)
            {
                for(k=1; k<=2; k++)
                {
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&y, n, _state);
                    ae_vector_set_length(&w, n, _state);
                    ae_vector_set_length(&xc, 2, _state);
                    ae_vector_set_length(&yc, 2, _state);
                    ae_vector_set_length(&dc, 2, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        w.ptr.p_double[i] = 1+ae_randomreal(_state);
                    }
                    xc.ptr.p_double[0] = (double)(-1);
                    yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
                    dc.ptr.p_int[0] = 0;
                    xc.ptr.p_double[1] = (double)(1);
                    yc.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                    dc.ptr.p_int[1] = 0;
                    barycentricfitfloaterhormannwc(&x, &y, &w, n, &xc, &yc, &dc, k, m, &info, &b1, &rep, _state);
                    if( info<=0 )
                    {
                        *fiterrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=k-1; i++)
                        {
                            barycentricdiff1(&b1, xc.ptr.p_double[i], &v0, &v1, _state);
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v0-yc.ptr.p_double[i], _state),threshold);
                        }
                    }
                }
            }
        }
    }
    for(m=2; m<=8; m++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * General fitting
             *
             * interpolating function through M nodes should have
             * greater RMS error than fitting it through the same M nodes
             */
            n = 100;
            ae_vector_set_length(&x2, n, _state);
            ae_vector_set_length(&y2, n, _state);
            ae_vector_set_length(&w2, n, _state);
            xmin = ae_maxrealnumber;
            xmax = -ae_maxrealnumber;
            for(i=0; i<=n-1; i++)
            {
                x2.ptr.p_double[i] = 2*ae_pi*ae_randomreal(_state);
                y2.ptr.p_double[i] = ae_sin(x2.ptr.p_double[i], _state);
                w2.ptr.p_double[i] = (double)(1);
                xmin = ae_minreal(xmin, x2.ptr.p_double[i], _state);
                xmax = ae_maxreal(xmax, x2.ptr.p_double[i], _state);
            }
            ae_vector_set_length(&x, m, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                x.ptr.p_double[i] = xmin+(xmax-xmin)*i/(m-1);
                y.ptr.p_double[i] = ae_sin(x.ptr.p_double[i], _state);
            }
            barycentricbuildfloaterhormann(&x, &y, m, 3, &b1, _state);
            barycentricfitfloaterhormannwc(&x2, &y2, &w2, n, &xc, &yc, &dc, 0, m, &info, &b2, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * calculate B1 (interpolant) RMS error, compare with B2 error
                 */
                v1 = (double)(0);
                v2 = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    v1 = v1+ae_sqr(barycentriccalc(&b1, x2.ptr.p_double[i], _state)-y2.ptr.p_double[i], _state);
                    v2 = v2+ae_sqr(barycentriccalc(&b2, x2.ptr.p_double[i], _state)-y2.ptr.p_double[i], _state);
                }
                v1 = ae_sqrt(v1/n, _state);
                v2 = ae_sqrt(v2/n, _state);
                *fiterrors = *fiterrors||ae_fp_greater(v2,v1);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v2-rep.rmserror, _state),threshold);
            }
            
            /*
             * compare weighted and non-weighted
             */
            n = 20;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = i+(ae_randomreal(_state)-0.5);
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = (double)(1);
            }
            barycentricfitfloaterhormannwc(&x, &y, &w, n, &xc, &yc, &dc, 0, m, &info, &b1, &rep, _state);
            barycentricfitfloaterhormann(&x, &y, n, m, &info2, &b2, &rep2, _state);
            if( info<=0||info2<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * calculate B1 (interpolant), compare with B2
                 * compare RMS errors
                 */
                t = 2*ae_randomreal(_state)-1;
                v1 = barycentriccalc(&b1, t, _state);
                v2 = barycentriccalc(&b2, t, _state);
                *fiterrors = *fiterrors||!approxequal(v2, v1, 1.0E-12, _state);
                *fiterrors = *fiterrors||!approxequal(rep.rmserror, rep2.rmserror, 1.0E-12*maxreal3((double)(1), rep.rmserror, rep2.rmserror, _state), _state);
                *fiterrors = *fiterrors||!approxequal(rep.avgerror, rep2.avgerror, 1.0E-12*maxreal3((double)(1), rep.avgerror, rep2.avgerror, _state), _state);
                *fiterrors = *fiterrors||!approxequal(rep.avgrelerror, rep2.avgrelerror, 1.0E-12*maxreal3((double)(1), rep.avgrelerror, rep2.avgrelerror, _state), _state);
                *fiterrors = *fiterrors||!approxequal(rep.maxerror, rep2.maxerror, 1.0E-12*maxreal3((double)(1), rep.maxerror, rep2.maxerror, _state), _state);
            }
        }
    }
    for(pass=1; pass<=passcount; pass++)
    {
        ae_assert(passcount>=2, "PassCount should be 2 or greater!", _state);
        
        /*
         * solve simple task (all X[] are the same, Y[] are specially
         * calculated to ensure simple form of all types of errors)
         * and check correctness of the errors calculated by subroutines
         *
         * First pass is done with zero Y[], other passes - with random Y[].
         * It should test both ability to correctly calculate errors and
         * ability to not fail while working with zeros :)
         */
        n = 4;
        if( pass==1 )
        {
            v1 = (double)(0);
            v2 = (double)(0);
            v = (double)(0);
        }
        else
        {
            v1 = ae_randomreal(_state);
            v2 = ae_randomreal(_state);
            v = 1+ae_randomreal(_state);
        }
        ae_vector_set_length(&x, 4, _state);
        ae_vector_set_length(&y, 4, _state);
        ae_vector_set_length(&w, 4, _state);
        x.ptr.p_double[0] = (double)(0);
        y.ptr.p_double[0] = v-v2;
        w.ptr.p_double[0] = (double)(1);
        x.ptr.p_double[1] = (double)(0);
        y.ptr.p_double[1] = v-v1;
        w.ptr.p_double[1] = (double)(1);
        x.ptr.p_double[2] = (double)(0);
        y.ptr.p_double[2] = v+v1;
        w.ptr.p_double[2] = (double)(1);
        x.ptr.p_double[3] = (double)(0);
        y.ptr.p_double[3] = v+v2;
        w.ptr.p_double[3] = (double)(1);
        refrms = ae_sqrt((ae_sqr(v1, _state)+ae_sqr(v2, _state))/2, _state);
        refavg = (ae_fabs(v1, _state)+ae_fabs(v2, _state))/2;
        if( pass==1 )
        {
            refavgrel = (double)(0);
        }
        else
        {
            refavgrel = 0.25*(ae_fabs(v2, _state)/ae_fabs(v-v2, _state)+ae_fabs(v1, _state)/ae_fabs(v-v1, _state)+ae_fabs(v1, _state)/ae_fabs(v+v1, _state)+ae_fabs(v2, _state)/ae_fabs(v+v2, _state));
        }
        refmax = ae_maxreal(v1, v2, _state);
        
        /*
         * Test errors correctness
         */
        barycentricfitfloaterhormann(&x, &y, 4, 2, &info, &b1, &rep, _state);
        if( info<=0 )
        {
            *fiterrors = ae_true;
        }
        else
        {
            s = barycentriccalc(&b1, (double)(0), _state);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


static void testlsfitunit_testsplinefitting(ae_bool* fiterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t k;
    ae_int_t pass;
    ae_vector x;
    ae_vector y;
    ae_vector w;
    ae_vector w2;
    ae_vector xc;
    ae_vector yc;
    ae_vector d;
    ae_vector dc;
    double sa;
    double sb;
    ae_int_t info;
    ae_int_t info1;
    ae_int_t info2;
    spline1dinterpolant c;
    spline1dinterpolant c2;
    spline1dfitreport rep;
    spline1dfitreport rep2;
    double s;
    double ds;
    double d2s;
    ae_int_t stype;
    double t;
    double v;
    double v1;
    double v2;
    double refrms;
    double refavg;
    double refavgrel;
    double refmax;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&w, 0, sizeof(w));
    memset(&w2, 0, sizeof(w2));
    memset(&xc, 0, sizeof(xc));
    memset(&yc, 0, sizeof(yc));
    memset(&d, 0, sizeof(d));
    memset(&dc, 0, sizeof(dc));
    memset(&c, 0, sizeof(c));
    memset(&c2, 0, sizeof(c2));
    memset(&rep, 0, sizeof(rep));
    memset(&rep2, 0, sizeof(rep2));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _spline1dinterpolant_init(&c, _state, ae_true);
    _spline1dinterpolant_init(&c2, _state, ae_true);
    _spline1dfitreport_init(&rep, _state, ae_true);
    _spline1dfitreport_init(&rep2, _state, ae_true);

    
    /*
     * Valyes:
     * * pass count
     * * threshold - for tests which must be satisfied exactly
     */
    passcount = 20;
    threshold = 10000*ae_machineepsilon;
    *fiterrors = ae_false;
    
    /*
     * Test fitting by Cubic and Hermite splines (obsolete, but still supported)
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Cubic splines
         * Ability to handle boundary constraints (1-4 constraints on F, dF/dx).
         */
        for(m=4; m<=8; m++)
        {
            for(k=1; k<=4; k++)
            {
                if( k>=m )
                {
                    continue;
                }
                n = 100;
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&y, n, _state);
                ae_vector_set_length(&w, n, _state);
                ae_vector_set_length(&xc, 4, _state);
                ae_vector_set_length(&yc, 4, _state);
                ae_vector_set_length(&dc, 4, _state);
                sa = 1+ae_randomreal(_state);
                sb = 2*ae_randomreal(_state)-1;
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                    y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                xc.ptr.p_double[0] = sb;
                yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[0] = 0;
                xc.ptr.p_double[1] = sb;
                yc.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[1] = 1;
                xc.ptr.p_double[2] = sa+sb;
                yc.ptr.p_double[2] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[2] = 0;
                xc.ptr.p_double[3] = sa+sb;
                yc.ptr.p_double[3] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[3] = 1;
                spline1dfitcubicwc(&x, &y, &w, n, &xc, &yc, &dc, k, m, &info, &c, &rep, _state);
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    
                    /*
                     * Check that constraints are satisfied
                     */
                    for(i=0; i<=k-1; i++)
                    {
                        spline1ddiff(&c, xc.ptr.p_double[i], &s, &ds, &d2s, _state);
                        if( dc.ptr.p_int[i]==0 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-yc.ptr.p_double[i], _state),threshold);
                        }
                        if( dc.ptr.p_int[i]==1 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(ds-yc.ptr.p_double[i], _state),threshold);
                        }
                        if( dc.ptr.p_int[i]==2 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(d2s-yc.ptr.p_double[i], _state),threshold);
                        }
                    }
                }
            }
        }
        
        /*
         * Cubic splines
         * Ability to handle one internal constraint
         */
        for(m=4; m<=8; m++)
        {
            n = 100;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            ae_vector_set_length(&xc, 1, _state);
            ae_vector_set_length(&yc, 1, _state);
            ae_vector_set_length(&dc, 1, _state);
            sa = 1+ae_randomreal(_state);
            sb = 2*ae_randomreal(_state)-1;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = 1+ae_randomreal(_state);
            }
            xc.ptr.p_double[0] = sa*ae_randomreal(_state)+sb;
            yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
            dc.ptr.p_int[0] = ae_randominteger(2, _state);
            spline1dfitcubicwc(&x, &y, &w, n, &xc, &yc, &dc, 1, m, &info, &c, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * Check that constraints are satisfied
                 */
                spline1ddiff(&c, xc.ptr.p_double[0], &s, &ds, &d2s, _state);
                if( dc.ptr.p_int[0]==0 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-yc.ptr.p_double[0], _state),threshold);
                }
                if( dc.ptr.p_int[0]==1 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(ds-yc.ptr.p_double[0], _state),threshold);
                }
                if( dc.ptr.p_int[0]==2 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(d2s-yc.ptr.p_double[0], _state),threshold);
                }
            }
        }
        
        /*
         * Hermite splines
         * Ability to handle boundary constraints (1-4 constraints on F, dF/dx).
         */
        for(m=4; m<=8; m++)
        {
            for(k=1; k<=4; k++)
            {
                if( k>=m )
                {
                    continue;
                }
                if( m%2!=0 )
                {
                    continue;
                }
                n = 100;
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&y, n, _state);
                ae_vector_set_length(&w, n, _state);
                ae_vector_set_length(&xc, 4, _state);
                ae_vector_set_length(&yc, 4, _state);
                ae_vector_set_length(&dc, 4, _state);
                sa = 1+ae_randomreal(_state);
                sb = 2*ae_randomreal(_state)-1;
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                    y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                xc.ptr.p_double[0] = sb;
                yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[0] = 0;
                xc.ptr.p_double[1] = sb;
                yc.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[1] = 1;
                xc.ptr.p_double[2] = sa+sb;
                yc.ptr.p_double[2] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[2] = 0;
                xc.ptr.p_double[3] = sa+sb;
                yc.ptr.p_double[3] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[3] = 1;
                spline1dfithermitewc(&x, &y, &w, n, &xc, &yc, &dc, k, m, &info, &c, &rep, _state);
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    
                    /*
                     * Check that constraints are satisfied
                     */
                    for(i=0; i<=k-1; i++)
                    {
                        spline1ddiff(&c, xc.ptr.p_double[i], &s, &ds, &d2s, _state);
                        if( dc.ptr.p_int[i]==0 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-yc.ptr.p_double[i], _state),threshold);
                        }
                        if( dc.ptr.p_int[i]==1 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(ds-yc.ptr.p_double[i], _state),threshold);
                        }
                        if( dc.ptr.p_int[i]==2 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(d2s-yc.ptr.p_double[i], _state),threshold);
                        }
                    }
                }
            }
        }
        
        /*
         * Hermite splines
         * Ability to handle one internal constraint
         */
        for(m=4; m<=8; m++)
        {
            if( m%2!=0 )
            {
                continue;
            }
            n = 100;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            ae_vector_set_length(&xc, 1, _state);
            ae_vector_set_length(&yc, 1, _state);
            ae_vector_set_length(&dc, 1, _state);
            sa = 1+ae_randomreal(_state);
            sb = 2*ae_randomreal(_state)-1;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = 1+ae_randomreal(_state);
            }
            xc.ptr.p_double[0] = sa*ae_randomreal(_state)+sb;
            yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
            dc.ptr.p_int[0] = ae_randominteger(2, _state);
            spline1dfithermitewc(&x, &y, &w, n, &xc, &yc, &dc, 1, m, &info, &c, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * Check that constraints are satisfied
                 */
                spline1ddiff(&c, xc.ptr.p_double[0], &s, &ds, &d2s, _state);
                if( dc.ptr.p_int[0]==0 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-yc.ptr.p_double[0], _state),threshold);
                }
                if( dc.ptr.p_int[0]==1 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(ds-yc.ptr.p_double[0], _state),threshold);
                }
                if( dc.ptr.p_int[0]==2 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(d2s-yc.ptr.p_double[0], _state),threshold);
                }
            }
        }
    }
    for(m=4; m<=8; m++)
    {
        for(stype=0; stype<=1; stype++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                if( stype==1&&m%2!=0 )
                {
                    continue;
                }
                
                /*
                 * cubic/Hermite spline fitting:
                 * * generate "template spline" C2
                 * * generate 2*N points from C2, such that result of
                 *   ideal fit should be equal to C2
                 * * fit, store in C
                 * * compare C and C2
                 */
                sa = 1+ae_randomreal(_state);
                sb = 2*ae_randomreal(_state)-1;
                if( stype==0 )
                {
                    ae_vector_set_length(&x, m-2, _state);
                    ae_vector_set_length(&y, m-2, _state);
                    for(i=0; i<=m-2-1; i++)
                    {
                        x.ptr.p_double[i] = sa*i/(m-2-1)+sb;
                        y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    spline1dbuildcubic(&x, &y, m-2, 1, 2*ae_randomreal(_state)-1, 1, 2*ae_randomreal(_state)-1, &c2, _state);
                }
                if( stype==1 )
                {
                    ae_vector_set_length(&x, m/2, _state);
                    ae_vector_set_length(&y, m/2, _state);
                    ae_vector_set_length(&d, m/2, _state);
                    for(i=0; i<=m/2-1; i++)
                    {
                        x.ptr.p_double[i] = sa*i/(m/2-1)+sb;
                        y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    spline1dbuildhermite(&x, &y, &d, m/2, &c2, _state);
                }
                n = 50;
                ae_vector_set_length(&x, 2*n, _state);
                ae_vector_set_length(&y, 2*n, _state);
                ae_vector_set_length(&w, 2*n, _state);
                for(i=0; i<=n-1; i++)
                {
                    
                    /*
                     * "if i=0" and "if i=1" are needed to
                     * synchronize interval size for C2 and
                     * spline being fitted (i.e. C).
                     */
                    t = ae_randomreal(_state);
                    x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                    if( i==0 )
                    {
                        x.ptr.p_double[i] = sb;
                    }
                    if( i==1 )
                    {
                        x.ptr.p_double[i] = sa+sb;
                    }
                    v = spline1dcalc(&c2, x.ptr.p_double[i], _state);
                    y.ptr.p_double[i] = v+t;
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                    x.ptr.p_double[n+i] = x.ptr.p_double[i];
                    y.ptr.p_double[n+i] = v-t;
                    w.ptr.p_double[n+i] = w.ptr.p_double[i];
                }
                info = -1;
                if( stype==0 )
                {
                    spline1dfitcubicwc(&x, &y, &w, 2*n, &xc, &yc, &dc, 0, m, &info, &c, &rep, _state);
                }
                if( stype==1 )
                {
                    spline1dfithermitewc(&x, &y, &w, 2*n, &xc, &yc, &dc, 0, m, &info, &c, &rep, _state);
                }
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        v = sa*ae_randomreal(_state)+sb;
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(spline1dcalc(&c, v, _state)-spline1dcalc(&c2, v, _state), _state),threshold);
                    }
                }
            }
        }
    }
    for(m=4; m<=8; m++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * prepare points/weights
             */
            n = 10+ae_randominteger(10, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = i+(ae_randomreal(_state)-0.5);
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = (double)(1);
            }
            
            /*
             * Fit cubic with unity weights, without weights, then compare
             */
            if( m>=4 )
            {
                spline1dfitcubicwc(&x, &y, &w, n, &xc, &yc, &dc, 0, m, &info1, &c, &rep, _state);
                spline1dfitcubic(&x, &y, n, m, &info2, &c2, &rep2, _state);
                if( info1<=0||info2<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_randomreal(_state)*(n-1);
                        *fiterrors = *fiterrors||!approxequal(spline1dcalc(&c, v, _state), spline1dcalc(&c2, v, _state), 1.0E-12, _state);
                        *fiterrors = *fiterrors||!approxequal(rep.taskrcond, rep2.taskrcond, 1.0E-12*maxreal3((double)(1), rep.taskrcond, rep2.taskrcond, _state), _state);
                        *fiterrors = *fiterrors||!approxequal(rep.rmserror, rep2.rmserror, 1.0E-12*maxreal3((double)(1), rep.rmserror, rep2.rmserror, _state), _state);
                        *fiterrors = *fiterrors||!approxequal(rep.avgerror, rep2.avgerror, 1.0E-12*maxreal3((double)(1), rep.avgerror, rep2.avgerror, _state), _state);
                        *fiterrors = *fiterrors||!approxequal(rep.avgrelerror, rep2.avgrelerror, 1.0E-12*maxreal3((double)(1), rep.avgrelerror, rep2.avgrelerror, _state), _state);
                        *fiterrors = *fiterrors||!approxequal(rep.maxerror, rep2.maxerror, 1.0E-12*maxreal3((double)(1), rep.maxerror, rep2.maxerror, _state), _state);
                    }
                }
            }
            
            /*
             * Fit Hermite with unity weights, without weights, then compare
             */
            if( m>=4&&m%2==0 )
            {
                spline1dfithermitewc(&x, &y, &w, n, &xc, &yc, &dc, 0, m, &info1, &c, &rep, _state);
                spline1dfithermite(&x, &y, n, m, &info2, &c2, &rep2, _state);
                if( info1<=0||info2<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_randomreal(_state)*(n-1);
                        *fiterrors = *fiterrors||!approxequal(spline1dcalc(&c, v, _state), spline1dcalc(&c2, v, _state), 1.0E-12, _state);
                        *fiterrors = *fiterrors||!approxequal(rep.taskrcond, rep2.taskrcond, 1.0E-12*maxreal3((double)(1), rep.taskrcond, rep2.taskrcond, _state), _state);
                        *fiterrors = *fiterrors||!approxequal(rep.rmserror, rep2.rmserror, 1.0E-12*maxreal3((double)(1), rep.rmserror, rep2.rmserror, _state), _state);
                        *fiterrors = *fiterrors||!approxequal(rep.avgerror, rep2.avgerror, 1.0E-12*maxreal3((double)(1), rep.avgerror, rep2.avgerror, _state), _state);
                        *fiterrors = *fiterrors||!approxequal(rep.avgrelerror, rep2.avgrelerror, 1.0E-12*maxreal3((double)(1), rep.avgrelerror, rep2.avgrelerror, _state), _state);
                        *fiterrors = *fiterrors||!approxequal(rep.maxerror, rep2.maxerror, 1.0E-12*maxreal3((double)(1), rep.maxerror, rep2.maxerror, _state), _state);
                    }
                }
            }
        }
    }
    
    /*
     * Check correctness of error reports
     */
    for(pass=1; pass<=passcount; pass++)
    {
        ae_assert(passcount>=2, "PassCount should be 2 or greater!", _state);
        
        /*
         * solve simple task (all X[] are the same, Y[] are specially
         * calculated to ensure simple form of all types of errors)
         * and check correctness of the errors calculated by subroutines
         *
         * First pass is done with zero Y[], other passes - with random Y[].
         * It should test both ability to correctly calculate errors and
         * ability to not fail while working with zeros :)
         */
        n = 4;
        if( pass==1 )
        {
            v1 = (double)(0);
            v2 = (double)(0);
            v = (double)(0);
        }
        else
        {
            v1 = ae_randomreal(_state);
            v2 = ae_randomreal(_state);
            v = 1+ae_randomreal(_state);
        }
        ae_vector_set_length(&x, 4, _state);
        ae_vector_set_length(&y, 4, _state);
        ae_vector_set_length(&w, 4, _state);
        x.ptr.p_double[0] = (double)(0);
        y.ptr.p_double[0] = v-v2;
        w.ptr.p_double[0] = (double)(1);
        x.ptr.p_double[1] = (double)(0);
        y.ptr.p_double[1] = v-v1;
        w.ptr.p_double[1] = (double)(1);
        x.ptr.p_double[2] = (double)(0);
        y.ptr.p_double[2] = v+v1;
        w.ptr.p_double[2] = (double)(1);
        x.ptr.p_double[3] = (double)(0);
        y.ptr.p_double[3] = v+v2;
        w.ptr.p_double[3] = (double)(1);
        refrms = ae_sqrt((ae_sqr(v1, _state)+ae_sqr(v2, _state))/2, _state);
        refavg = (ae_fabs(v1, _state)+ae_fabs(v2, _state))/2;
        if( pass==1 )
        {
            refavgrel = (double)(0);
        }
        else
        {
            refavgrel = 0.25*(ae_fabs(v2, _state)/ae_fabs(v-v2, _state)+ae_fabs(v1, _state)/ae_fabs(v-v1, _state)+ae_fabs(v1, _state)/ae_fabs(v+v1, _state)+ae_fabs(v2, _state)/ae_fabs(v+v2, _state));
        }
        refmax = ae_maxreal(v1, v2, _state);
        
        /*
         * Test cubic fitting
         */
        spline1dfitcubic(&x, &y, 4, 4, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *fiterrors = ae_true;
        }
        else
        {
            s = spline1dcalc(&c, (double)(0), _state);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
        
        /*
         * Test Hermite fitting
         */
        spline1dfithermite(&x, &y, 4, 4, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *fiterrors = ae_true;
        }
        else
        {
            s = spline1dcalc(&c, (double)(0), _state);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


static void testlsfitunit_testgeneralfitting(ae_bool* llserrors,
     ae_bool* nlserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    double nlthreshold;
    ae_int_t maxn;
    ae_int_t maxm;
    ae_int_t skind;
    ae_int_t pkind;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t pass;
    double xscale;
    double cscale;
    double wscale;
    double noiselevel;
    double tol;
    double diffstep;
    ae_vector x;
    ae_vector y;
    ae_vector y2;
    ae_vector w;
    ae_vector w2;
    ae_vector s;
    ae_vector c;
    ae_vector cstart;
    ae_vector cend;
    ae_vector c2;
    ae_matrix a;
    ae_matrix a2;
    ae_matrix cm;
    double v;
    double v1;
    double v2;
    lsfitreport rep;
    lsfitreport rep2;
    ae_int_t info;
    ae_int_t info2;
    double refrms;
    double refavg;
    double refavgrel;
    double refmax;
    double avgdeviationpar;
    double avgdeviationcurve;
    double avgdeviationnoise;
    double adccnt;
    double adpcnt;
    double adncnt;
    lsfitstate state;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&y2, 0, sizeof(y2));
    memset(&w, 0, sizeof(w));
    memset(&w2, 0, sizeof(w2));
    memset(&s, 0, sizeof(s));
    memset(&c, 0, sizeof(c));
    memset(&cstart, 0, sizeof(cstart));
    memset(&cend, 0, sizeof(cend));
    memset(&c2, 0, sizeof(c2));
    memset(&a, 0, sizeof(a));
    memset(&a2, 0, sizeof(a2));
    memset(&cm, 0, sizeof(cm));
    memset(&rep, 0, sizeof(rep));
    memset(&rep2, 0, sizeof(rep2));
    memset(&state, 0, sizeof(state));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cstart, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cend, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cm, 0, 0, DT_REAL, _state, ae_true);
    _lsfitreport_init(&rep, _state, ae_true);
    _lsfitreport_init(&rep2, _state, ae_true);
    _lsfitstate_init(&state, _state, ae_true);

    *llserrors = ae_false;
    *nlserrors = ae_false;
    threshold = 10000*ae_machineepsilon;
    nlthreshold = 0.00001;
    diffstep = 0.0001;
    maxn = 6;
    maxm = 6;
    passcount = 4;
    
    /*
     * Test constrained NLS problems
     */
    testlsfitunit_testbcnls(nlserrors, _state);
    testlsfitunit_testlcnls(nlserrors, _state);
    
    /*
     * Testing unconstrained least squares (linear/nonlinear)
     */
    for(n=1; n<=maxn; n++)
    {
        for(m=1; m<=maxm; m++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Solve non-degenerate linear least squares task
                 * Use Chebyshev basis. Its condition number is very good.
                 */
                ae_matrix_set_length(&a, n, m, _state);
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&y, n, _state);
                ae_vector_set_length(&w, n, _state);
                xscale = 0.9+0.1*ae_randomreal(_state);
                for(i=0; i<=n-1; i++)
                {
                    if( n==1 )
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    else
                    {
                        x.ptr.p_double[i] = xscale*((double)(2*i)/(double)(n-1)-1);
                    }
                    y.ptr.p_double[i] = 3*x.ptr.p_double[i]+ae_exp(x.ptr.p_double[i], _state);
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                    a.ptr.pp_double[i][0] = (double)(1);
                    if( m>1 )
                    {
                        a.ptr.pp_double[i][1] = x.ptr.p_double[i];
                    }
                    for(j=2; j<=m-1; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*x.ptr.p_double[i]*a.ptr.pp_double[i][j-1]-a.ptr.pp_double[i][j-2];
                    }
                }
                
                /*
                 * 1. test weighted fitting (optimality)
                 * 2. Solve degenerate least squares task built on the basis
                 *    of previous task
                 */
                lsfitlinearw(&y, &w, &a, n, m, &info, &c, &rep, _state);
                if( info<=0 )
                {
                    *llserrors = ae_true;
                }
                else
                {
                    *llserrors = *llserrors||!testlsfitunit_isglssolution(n, m, 0, &y, &w, &a, &cm, &c, _state);
                }
                ae_matrix_set_length(&a2, n, 2*m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        a2.ptr.pp_double[i][2*j+0] = a.ptr.pp_double[i][j];
                        a2.ptr.pp_double[i][2*j+1] = a.ptr.pp_double[i][j];
                    }
                }
                lsfitlinearw(&y, &w, &a2, n, 2*m, &info, &c2, &rep, _state);
                if( info<=0 )
                {
                    *llserrors = ae_true;
                }
                else
                {
                    
                    /*
                     * test answer correctness using design matrix properties
                     * and previous task solution
                     */
                    for(j=0; j<=m-1; j++)
                    {
                        *llserrors = *llserrors||ae_fp_greater(ae_fabs(c2.ptr.p_double[2*j+0]+c2.ptr.p_double[2*j+1]-c.ptr.p_double[j], _state),threshold);
                    }
                }
                
                /*
                 * test non-weighted fitting
                 */
                ae_vector_set_length(&w2, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    w2.ptr.p_double[i] = (double)(1);
                }
                lsfitlinearw(&y, &w2, &a, n, m, &info, &c, &rep, _state);
                lsfitlinear(&y, &a, n, m, &info2, &c2, &rep2, _state);
                if( info<=0||info2<=0 )
                {
                    *llserrors = ae_true;
                }
                else
                {
                    
                    /*
                     * test answer correctness
                     */
                    for(j=0; j<=m-1; j++)
                    {
                        *llserrors = *llserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[j]-c2.ptr.p_double[j], _state),threshold);
                    }
                    *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.taskrcond-rep2.taskrcond, _state),threshold);
                }
                
                /*
                 * test nonlinear fitting on the linear task
                 * (only non-degenerate tasks are tested)
                 * and compare with answer from linear fitting subroutine
                 */
                if( n>=m )
                {
                    ae_vector_set_length(&c2, m, _state);
                    
                    /*
                     * test function/gradient/Hessian-based weighted fitting
                     */
                    lsfitlinearw(&y, &w, &a, n, m, &info, &c, &rep, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatewf(&a, &y, &w, &c2, n, m, m, diffstep, &state, _state);
                    lsfitsetcond(&state, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 0, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatewfg(&a, &y, &w, &c2, n, m, m, ae_fp_greater(ae_randomreal(_state),0.5), &state, _state);
                    lsfitsetcond(&state, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 1, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatewfgh(&a, &y, &w, &c2, n, m, m, &state, _state);
                    lsfitsetcond(&state, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 2, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    
                    /*
                     * test gradient-only or Hessian-based fitting without weights
                     */
                    lsfitlinear(&y, &a, n, m, &info, &c, &rep, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatef(&a, &y, &c2, n, m, m, diffstep, &state, _state);
                    lsfitsetcond(&state, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 0, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatefg(&a, &y, &c2, n, m, m, ae_fp_greater(ae_randomreal(_state),0.5), &state, _state);
                    lsfitsetcond(&state, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 1, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatefgh(&a, &y, &c2, n, m, m, &state, _state);
                    lsfitsetcond(&state, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 2, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                }
            }
        }
        
        /*
         * test correctness of the RCond field
         */
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        ae_vector_set_length(&w, n-1+1, _state);
        v1 = ae_maxrealnumber;
        v2 = ae_minrealnumber;
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 0.1+0.9*ae_randomreal(_state);
            y.ptr.p_double[i] = 0.1+0.9*ae_randomreal(_state);
            w.ptr.p_double[i] = (double)(1);
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    a.ptr.pp_double[i][i] = 0.1+0.9*ae_randomreal(_state);
                    v1 = ae_minreal(v1, a.ptr.pp_double[i][i], _state);
                    v2 = ae_maxreal(v2, a.ptr.pp_double[i][i], _state);
                }
                else
                {
                    a.ptr.pp_double[i][j] = (double)(0);
                }
            }
        }
        lsfitlinearw(&y, &w, &a, n, n, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *llserrors = ae_true;
        }
        else
        {
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.taskrcond-v1/v2, _state),threshold);
        }
    }
    
    /*
     * Test constrained least squares
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * test for K<>0
                 */
                for(k=1; k<=m-1; k++)
                {
                    
                    /*
                     * Prepare Chebyshev basis. Its condition number is very good.
                     * Prepare constraints (random numbers)
                     */
                    ae_matrix_set_length(&a, n, m, _state);
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&y, n, _state);
                    ae_vector_set_length(&w, n, _state);
                    xscale = 0.9+0.1*ae_randomreal(_state);
                    for(i=0; i<=n-1; i++)
                    {
                        if( n==1 )
                        {
                            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        }
                        else
                        {
                            x.ptr.p_double[i] = xscale*((double)(2*i)/(double)(n-1)-1);
                        }
                        y.ptr.p_double[i] = 3*x.ptr.p_double[i]+ae_exp(x.ptr.p_double[i], _state);
                        w.ptr.p_double[i] = 1+ae_randomreal(_state);
                        a.ptr.pp_double[i][0] = (double)(1);
                        if( m>1 )
                        {
                            a.ptr.pp_double[i][1] = x.ptr.p_double[i];
                        }
                        for(j=2; j<=m-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*x.ptr.p_double[i]*a.ptr.pp_double[i][j-1]-a.ptr.pp_double[i][j-2];
                        }
                    }
                    ae_matrix_set_length(&cm, k, m+1, _state);
                    for(i=0; i<=k-1; i++)
                    {
                        for(j=0; j<=m; j++)
                        {
                            cm.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    
                    /*
                     * Solve constrained task
                     */
                    lsfitlinearwc(&y, &w, &a, &cm, n, m, k, &info, &c, &rep, _state);
                    if( info<=0 )
                    {
                        *llserrors = ae_true;
                    }
                    else
                    {
                        *llserrors = *llserrors||!testlsfitunit_isglssolution(n, m, k, &y, &w, &a, &cm, &c, _state);
                    }
                    
                    /*
                     * test non-weighted fitting
                     */
                    ae_vector_set_length(&w2, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        w2.ptr.p_double[i] = (double)(1);
                    }
                    lsfitlinearwc(&y, &w2, &a, &cm, n, m, k, &info, &c, &rep, _state);
                    lsfitlinearc(&y, &a, &cm, n, m, k, &info2, &c2, &rep2, _state);
                    if( info<=0||info2<=0 )
                    {
                        *llserrors = ae_true;
                    }
                    else
                    {
                        
                        /*
                         * test answer correctness
                         */
                        for(j=0; j<=m-1; j++)
                        {
                            *llserrors = *llserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[j]-c2.ptr.p_double[j], _state),threshold);
                        }
                        *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.taskrcond-rep2.taskrcond, _state),threshold);
                    }
                }
            }
        }
    }
    
    /*
     * nonlinear task for nonlinear fitting:
     *
     *     f(X,C) = 1/(1+C*X^2),
     *     C(true) = 2.
     */
    n = 100;
    ae_vector_set_length(&c, 1, _state);
    c.ptr.p_double[0] = 1+2*ae_randomreal(_state);
    ae_matrix_set_length(&a, n, 1, _state);
    ae_vector_set_length(&y, n, _state);
    for(i=0; i<=n-1; i++)
    {
        a.ptr.pp_double[i][0] = 4*ae_randomreal(_state)-2;
        y.ptr.p_double[i] = 1/(1+2*ae_sqr(a.ptr.pp_double[i][0], _state));
    }
    lsfitcreatefg(&a, &y, &c, n, 1, 1, ae_true, &state, _state);
    lsfitsetcond(&state, nlthreshold, 0, _state);
    while(lsfititeration(&state, _state))
    {
        if( state.needf )
        {
            state.f = 1/(1+state.c.ptr.p_double[0]*ae_sqr(state.x.ptr.p_double[0], _state));
        }
        if( state.needfg )
        {
            state.f = 1/(1+state.c.ptr.p_double[0]*ae_sqr(state.x.ptr.p_double[0], _state));
            state.g.ptr.p_double[0] = -ae_sqr(state.x.ptr.p_double[0], _state)/ae_sqr(1+state.c.ptr.p_double[0]*ae_sqr(state.x.ptr.p_double[0], _state), _state);
        }
    }
    lsfitresults(&state, &info, &c, &rep, _state);
    if( info<=0 )
    {
        ae_set_error_flag(nlserrors, ae_true, __FILE__, __LINE__, "testlsfitunit.ap:1389");
    }
    else
    {
        ae_set_error_flag(nlserrors, ae_fp_greater(ae_fabs(c.ptr.p_double[0]-2, _state),100*nlthreshold), __FILE__, __LINE__, "testlsfitunit.ap:1391");
    }
    
    /*
     * solve simple task (fitting by constant function) and check
     * correctness of the errors calculated by subroutines
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * test on task with non-zero Yi
         */
        n = 4;
        v1 = ae_randomreal(_state);
        v2 = ae_randomreal(_state);
        v = 1+ae_randomreal(_state);
        ae_vector_set_length(&c, 1, _state);
        c.ptr.p_double[0] = 1+2*ae_randomreal(_state);
        ae_matrix_set_length(&a, 4, 1, _state);
        ae_vector_set_length(&y, 4, _state);
        a.ptr.pp_double[0][0] = (double)(1);
        y.ptr.p_double[0] = v-v2;
        a.ptr.pp_double[1][0] = (double)(1);
        y.ptr.p_double[1] = v-v1;
        a.ptr.pp_double[2][0] = (double)(1);
        y.ptr.p_double[2] = v+v1;
        a.ptr.pp_double[3][0] = (double)(1);
        y.ptr.p_double[3] = v+v2;
        refrms = ae_sqrt((ae_sqr(v1, _state)+ae_sqr(v2, _state))/2, _state);
        refavg = (ae_fabs(v1, _state)+ae_fabs(v2, _state))/2;
        refavgrel = 0.25*(ae_fabs(v2, _state)/ae_fabs(v-v2, _state)+ae_fabs(v1, _state)/ae_fabs(v-v1, _state)+ae_fabs(v1, _state)/ae_fabs(v+v1, _state)+ae_fabs(v2, _state)/ae_fabs(v+v2, _state));
        refmax = ae_maxreal(v1, v2, _state);
        
        /*
         * Test LLS
         */
        lsfitlinear(&y, &a, 4, 1, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *llserrors = ae_true;
        }
        else
        {
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[0]-v, _state),threshold);
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
        
        /*
         * Test NLS
         */
        lsfitcreatefg(&a, &y, &c, 4, 1, 1, ae_true, &state, _state);
        lsfitsetcond(&state, nlthreshold, 0, _state);
        while(lsfititeration(&state, _state))
        {
            if( state.needf )
            {
                state.f = state.c.ptr.p_double[0];
            }
            if( state.needfg )
            {
                state.f = state.c.ptr.p_double[0];
                state.g.ptr.p_double[0] = (double)(1);
            }
        }
        lsfitresults(&state, &info, &c, &rep, _state);
        if( info<=0 )
        {
            ae_set_error_flag(nlserrors, ae_true, __FILE__, __LINE__, "testlsfitunit.ap:1456");
        }
        else
        {
            ae_set_error_flag(nlserrors, ae_fp_greater(ae_fabs(c.ptr.p_double[0]-v, _state),threshold), __FILE__, __LINE__, "testlsfitunit.ap:1459");
            ae_set_error_flag(nlserrors, ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold), __FILE__, __LINE__, "testlsfitunit.ap:1460");
            ae_set_error_flag(nlserrors, ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold), __FILE__, __LINE__, "testlsfitunit.ap:1461");
            ae_set_error_flag(nlserrors, ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold), __FILE__, __LINE__, "testlsfitunit.ap:1462");
            ae_set_error_flag(nlserrors, ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold), __FILE__, __LINE__, "testlsfitunit.ap:1463");
        }
    }
    
    /*
     * Check covariance matrix, errors-in-parameters.
     *
     * We test three different solvers:
     * * nonlinear solver
     * * unconstrained linear solver
     * * constrained linear solver with empty set of constrains
     * on two random problems:
     * * problem with known prior, noise, unit weights
     * * problem with known prior, noise, non-unit weights
     *
     * We test that:
     * * rep.ErrPar=sqrt(diag(Rep.CovPar))
     * * Rep.ErrPar is not too optimistic  - average value of ratio
     *   between  |c_fit-c_prior| and ErrPar[] is less than TOL
     * * Rep.ErrPar is not too pessimistic - average value of ratio
     *   is larger than 1/TOL
     * * similarly, Rep.ErrCurve gives good estimate of |A*c_fit - A*c_prior|
     *   - not optimistic, not pessimistic.
     * * similarly, per-point noise estimates are good enough (we use
     *   slightly different tolerances, though)
     * In order to have these estimates we perform many different tests
     * and calculate average deviation divided by ErrPar/ErrCurve. Then
     * we perform test.
     *
     * Due to stochastic nature of the test it is not good idea to
     * consider each case individually - it is better to average over
     * many runs.
     * 
     */
    tol = 10.0;
    for(n=1; n<=10; n++)
    {
        for(skind=0; skind<=2; skind++)
        {
            for(pkind=0; pkind<=1; pkind++)
            {
                
                /*
                 * Generate problem:
                 * * PKind=0 - unit weights
                 * * PKind=1 - non-unit weights, exact estimate of noise at I-th point
                 *
                 * We generate:
                 * * C      -   prior values of parameters
                 * * CStart -   random initial point
                 * * A      -   function matrix
                 * * Y      -   noisy version of A*C
                 * * W      -   weights vector
                 * * S      -   vector of per-point estimates of noise
                 */
                cscale = ae_pow(10.0, 2*randomnormal(_state), _state);
                xscale = ae_pow(10.0, 2*randomnormal(_state), _state);
                noiselevel = 0.01*cscale*xscale;
                ae_vector_set_length(&c, n, _state);
                ae_vector_set_length(&cstart, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    c.ptr.p_double[i] = cscale*randomnormal(_state);
                    cstart.ptr.p_double[i] = cscale*randomnormal(_state);
                }
                ae_matrix_set_length(&a, 1000, n, _state);
                ae_vector_set_length(&y, a.rows, _state);
                ae_vector_set_length(&w, a.rows, _state);
                ae_vector_set_length(&s, a.rows, _state);
                for(i=0; i<=a.rows-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = xscale*randomnormal(_state);
                    }
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &c.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    if( pkind==0 )
                    {
                        w.ptr.p_double[i] = (double)(1);
                        s.ptr.p_double[i] = noiselevel;
                        y.ptr.p_double[i] = v+s.ptr.p_double[i]*randomnormal(_state);
                    }
                    if( pkind==1 )
                    {
                        w.ptr.p_double[i] = 1/noiselevel;
                        s.ptr.p_double[i] = noiselevel;
                        y.ptr.p_double[i] = v+s.ptr.p_double[i]*randomnormal(_state);
                    }
                }
                
                /*
                 * Test different solvers:
                 * * SKind=0 - nonlinear solver
                 * * SKind=1 - linear unconstrained
                 * * SKind=2 - linear constrained with empty set of constraints
                 */
                info = -1;
                if( skind==0 )
                {
                    if( ae_fp_greater(ae_randomreal(_state),0.5) )
                    {
                        lsfitcreatefg(&a, &y, &cstart, a.rows, n, n, ae_true, &state, _state);
                    }
                    else
                    {
                        lsfitcreatef(&a, &y, &cstart, a.rows, n, n, 0.001*cscale, &state, _state);
                    }
                    lsfitsetcond(&state, 0.0, 10, _state);
                    while(lsfititeration(&state, _state))
                    {
                        if( state.needf )
                        {
                            state.f = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+state.c.ptr.p_double[i]*state.x.ptr.p_double[i];
                            }
                        }
                        if( state.needfg )
                        {
                            state.f = (double)(0);
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+state.c.ptr.p_double[i]*state.x.ptr.p_double[i];
                                state.g.ptr.p_double[i] = state.x.ptr.p_double[i];
                            }
                        }
                    }
                    lsfitresults(&state, &info, &cend, &rep, _state);
                }
                if( skind==1 )
                {
                    if( pkind==0 )
                    {
                        lsfitlinear(&y, &a, a.rows, n, &info, &cend, &rep, _state);
                    }
                    else
                    {
                        lsfitlinearw(&y, &w, &a, a.rows, n, &info, &cend, &rep, _state);
                    }
                }
                if( skind==2 )
                {
                    if( pkind==0 )
                    {
                        lsfitlinearc(&y, &a, &a2, a.rows, n, 0, &info, &cend, &rep, _state);
                    }
                    else
                    {
                        lsfitlinearwc(&y, &w, &a, &a2, a.rows, n, 0, &info, &cend, &rep, _state);
                    }
                }
                
                /*
                 * Tests:
                 * * check relation between CovPar and ErrPar
                 * * accumulate average deviation in parameters
                 * * accumulate average deviation in curve fit
                 * * accumulate average deviation in noise estimate
                 */
                avgdeviationpar = (double)(0);
                adpcnt = (double)(0);
                avgdeviationcurve = (double)(0);
                adccnt = (double)(0);
                avgdeviationnoise = (double)(0);
                adncnt = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    ae_set_error_flag(llserrors, ae_fp_greater(ae_fabs(rep.covpar.ptr.pp_double[i][i]-ae_sqr(rep.errpar.ptr.p_double[i], _state), _state),100*ae_machineepsilon*ae_maxreal(ae_sqr(rep.errpar.ptr.p_double[i], _state), rep.covpar.ptr.pp_double[i][i], _state)), __FILE__, __LINE__, "testlsfitunit.ap:1612");
                }
                for(i=0; i<=n-1; i++)
                {
                    avgdeviationpar = (avgdeviationpar*adpcnt+ae_fabs(c.ptr.p_double[i]-cend.ptr.p_double[i], _state)/rep.errpar.ptr.p_double[i])/(adpcnt+1);
                    adpcnt = adpcnt+1;
                }
                for(i=0; i<=a.rows-1; i++)
                {
                    v1 = ae_v_dotproduct(&c.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                    v2 = ae_v_dotproduct(&cend.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                    avgdeviationcurve = (avgdeviationcurve*adccnt+ae_fabs(v1-v2, _state)/rep.errcurve.ptr.p_double[i])/(adccnt+1);
                    adccnt = adccnt+1;
                    avgdeviationnoise = (avgdeviationnoise*adncnt+rep.noise.ptr.p_double[i]/s.ptr.p_double[i])/(adncnt+1);
                    adncnt = adncnt+1;
                }
                
                /*
                 * Check that estimates are not too optimistic.
                 * This test is performed always.
                 */
                ae_set_error_flag(llserrors, ae_fp_greater(avgdeviationpar,tol), __FILE__, __LINE__, "testlsfitunit.ap:1632");
                ae_set_error_flag(llserrors, ae_fp_greater(avgdeviationcurve,tol), __FILE__, __LINE__, "testlsfitunit.ap:1633");
                ae_set_error_flag(llserrors, ae_fp_greater(avgdeviationnoise,1.50), __FILE__, __LINE__, "testlsfitunit.ap:1634");
                ae_set_error_flag(llserrors, ae_fp_less(avgdeviationnoise,0.66), __FILE__, __LINE__, "testlsfitunit.ap:1635");
                
                /*
                 * Test for estimates being too pessimistic is performed only
                 * when we have more than 4 parameters.
                 */
                ae_set_error_flag(llserrors, n>=5&&ae_fp_less(avgdeviationcurve,0.01), __FILE__, __LINE__, "testlsfitunit.ap:1641");
                ae_set_error_flag(llserrors, n>=5&&ae_fp_less(avgdeviationpar,0.01), __FILE__, __LINE__, "testlsfitunit.ap:1642");
            }
        }
    }
    
    /*
     * Check special property of the LSFit solver: it does not include points with
     * zero weight in the estimate of the noise level. Such property seems to be
     * quite natural, but in fact it requires some additional code in order to
     * ignore such points.
     *
     * In order to test it we solve two problems: one 300xN, with 150 non-zero
     * weights and 150 zero weights - and another one with only 150 points with
     * non-zero weights. Both problems should give us same covariance matrix.
     */
    tol = (double)(10);
    for(n=1; n<=10; n++)
    {
        
        /*
         * Generate N-dimensional linear problem with 300 points:
         * * y = c'*x + noise
         * * prior values of coefficients C has scale CScale
         * * coordinates X has scale XScale
         * * noise in I-th point has magnitude 0.1*CScale*XScale*WScale/W[i]
         */
        cscale = ae_pow(10.0, 2*randomnormal(_state), _state);
        xscale = ae_pow(10.0, 2*randomnormal(_state), _state);
        wscale = ae_pow(10.0, 2*randomnormal(_state), _state);
        noiselevel = 0.1*cscale*xscale;
        ae_vector_set_length(&c, n, _state);
        ae_vector_set_length(&cstart, n, _state);
        for(i=0; i<=n-1; i++)
        {
            c.ptr.p_double[i] = cscale*randomnormal(_state);
            cstart.ptr.p_double[i] = cscale*randomnormal(_state);
        }
        ae_matrix_set_length(&a, 300, n, _state);
        ae_vector_set_length(&y, a.rows, _state);
        ae_vector_set_length(&w, a.rows, _state);
        for(i=0; i<=a.rows-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = xscale*randomnormal(_state);
            }
            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &c.ptr.p_double[0], 1, ae_v_len(0,n-1));
            if( i<a.rows/2 )
            {
                w.ptr.p_double[i] = ae_pow((double)(10), randomnormal(_state), _state)*wscale;
                y.ptr.p_double[i] = v+noiselevel/w.ptr.p_double[i]*randomnormal(_state);
            }
            else
            {
                w.ptr.p_double[i] = (double)(0);
                y.ptr.p_double[i] = v+noiselevel*randomnormal(_state);
            }
        }
        
        /*
         * Solve problem #1 (with zero weights).
         * We randomly choose between analytic gradient and numerical differentiation.
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            lsfitcreatewfg(&a, &y, &w, &cstart, a.rows, n, n, ae_true, &state, _state);
        }
        else
        {
            lsfitcreatewf(&a, &y, &w, &cstart, a.rows, n, n, 0.001*cscale, &state, _state);
        }
        lsfitsetcond(&state, 0.0, 10, _state);
        while(lsfititeration(&state, _state))
        {
            if( state.needf )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+state.c.ptr.p_double[i]*state.x.ptr.p_double[i];
                }
            }
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+state.c.ptr.p_double[i]*state.x.ptr.p_double[i];
                    state.g.ptr.p_double[i] = state.x.ptr.p_double[i];
                }
            }
        }
        lsfitresults(&state, &info, &c2, &rep, _state);
        
        /*
         * Solve problem #2 (only points with non-zero weights).
         * We randomly choose between analytic gradient and numerical differentiation.
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            lsfitcreatewfg(&a, &y, &w, &cstart, a.rows/2, n, n, ae_true, &state, _state);
        }
        else
        {
            lsfitcreatewf(&a, &y, &w, &cstart, a.rows/2, n, n, 0.001*cscale, &state, _state);
        }
        lsfitsetcond(&state, 0.0, 10, _state);
        while(lsfititeration(&state, _state))
        {
            if( state.needf )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+state.c.ptr.p_double[i]*state.x.ptr.p_double[i];
                }
            }
            if( state.needfg )
            {
                state.f = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+state.c.ptr.p_double[i]*state.x.ptr.p_double[i];
                    state.g.ptr.p_double[i] = state.x.ptr.p_double[i];
                }
            }
        }
        lsfitresults(&state, &info, &c2, &rep2, _state);
        
        /*
         * Compare covariance matrices, it should be enough to test algorithm
         */
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ae_set_error_flag(nlserrors, ae_fp_greater(ae_fabs(rep.covpar.ptr.pp_double[i][j]-rep2.covpar.ptr.pp_double[i][j], _state),1.0E-4*ae_maxreal(rep.covpar.ptr.pp_double[i][i], rep.covpar.ptr.pp_double[j][j], _state)), __FILE__, __LINE__, "testlsfitunit.ap:1762");
            }
        }
    }
    
    /*
     * Check correctness of Rep.R2
     * Solve several problems with different values of R2.
     *
     * NOTE: we check only LSFitLinear() because other functions should use same algorithm
     *       for calculation of Rep.R2
     */
    n = 4;
    ae_matrix_set_length(&a, 4, 2, _state);
    ae_vector_set_length(&y, 4, _state);
    a.ptr.pp_double[0][0] = (double)(1);
    a.ptr.pp_double[0][1] = (double)(-2);
    y.ptr.p_double[0] = (double)(-2);
    a.ptr.pp_double[1][0] = (double)(1);
    a.ptr.pp_double[1][1] = (double)(-1);
    y.ptr.p_double[1] = (double)(-1);
    a.ptr.pp_double[2][0] = (double)(1);
    a.ptr.pp_double[2][1] = (double)(1);
    y.ptr.p_double[2] = (double)(1);
    a.ptr.pp_double[3][0] = (double)(1);
    a.ptr.pp_double[3][1] = (double)(2);
    y.ptr.p_double[3] = (double)(2);
    lsfitlinear(&y, &a, 4, 1, &info, &c, &rep, _state);
    *llserrors = (*llserrors||info<=0)||ae_fp_greater(ae_fabs(rep.r2-0, _state),threshold);
    lsfitlinear(&y, &a, 4, 2, &info, &c, &rep, _state);
    *llserrors = (*llserrors||info<=0)||ae_fp_greater(ae_fabs(rep.r2-1, _state),threshold);
    a.ptr.pp_double[0][0] = (double)(1);
    a.ptr.pp_double[0][1] = (double)(-1);
    y.ptr.p_double[0] = (double)(-1);
    a.ptr.pp_double[1][0] = (double)(1);
    a.ptr.pp_double[1][1] = (double)(-1);
    y.ptr.p_double[1] = (double)(0);
    a.ptr.pp_double[2][0] = (double)(1);
    a.ptr.pp_double[2][1] = (double)(1);
    y.ptr.p_double[2] = (double)(1);
    a.ptr.pp_double[3][0] = (double)(1);
    a.ptr.pp_double[3][1] = (double)(1);
    y.ptr.p_double[3] = (double)(0);
    lsfitlinear(&y, &a, 4, 2, &info, &c, &rep, _state);
    *llserrors = (*llserrors||info<=0)||ae_fp_greater(ae_fabs(rep.r2-0.5, _state),threshold);
    n = 3;
    ae_matrix_set_length(&a, 3, 1, _state);
    ae_vector_set_length(&y, 3, _state);
    a.ptr.pp_double[0][0] = (double)(0);
    y.ptr.p_double[0] = (double)(0);
    a.ptr.pp_double[1][0] = (double)(0);
    y.ptr.p_double[1] = (double)(0);
    a.ptr.pp_double[2][0] = (double)(0);
    y.ptr.p_double[2] = (double)(0);
    lsfitlinear(&y, &a, 3, 1, &info, &c, &rep, _state);
    *llserrors = ((*llserrors||info<=0)||!ae_isfinite(rep.r2, _state))||ae_fp_neq(rep.r2,(double)(1));
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests RDP functionality. On error sets FitErrors flag variable;
on success - flag is not changed.
*************************************************************************/
static void testlsfitunit_testrdp(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    ae_vector x;
    ae_vector y;
    ae_vector e;
    ae_vector x2;
    ae_vector y2;
    ae_vector x3;
    ae_vector y3;
    ae_matrix xy;
    ae_matrix xy2;
    ae_matrix xy3;
    ae_vector idx2;
    ae_vector idx3;
    ae_int_t nsections;
    ae_int_t nsections3;
    double eps;
    double v;
    ae_int_t i;
    ae_int_t k;
    ae_int_t n;
    spline1dinterpolant s;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&e, 0, sizeof(e));
    memset(&x2, 0, sizeof(x2));
    memset(&y2, 0, sizeof(y2));
    memset(&x3, 0, sizeof(x3));
    memset(&y3, 0, sizeof(y3));
    memset(&xy, 0, sizeof(xy));
    memset(&xy2, 0, sizeof(xy2));
    memset(&xy3, 0, sizeof(xy3));
    memset(&idx2, 0, sizeof(idx2));
    memset(&idx3, 0, sizeof(idx3));
    memset(&s, 0, sizeof(s));
    _hqrndstate_init(&rs, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x3, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y3, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy3, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&idx2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&idx3, 0, DT_INT, _state, ae_true);
    _spline1dinterpolant_init(&s, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Non-parametric, single section basic test (fixed)
     */
    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&y, 2, _state);
    x.ptr.p_double[0] = (double)(0);
    x.ptr.p_double[1] = 1.5;
    y.ptr.p_double[0] = (double)(2);
    y.ptr.p_double[1] = (double)(3);
    lstfitpiecewiselinearrdpfixed(&x, &y, 2, 1, &x2, &y2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=1, __FILE__, __LINE__, "testlsfitunit.ap:1850");
    if( nsections==1 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(x2.ptr.p_double[0],(double)(0)), __FILE__, __LINE__, "testlsfitunit.ap:1853");
        ae_set_error_flag(errorflag, ae_fp_neq(x2.ptr.p_double[1],1.5), __FILE__, __LINE__, "testlsfitunit.ap:1854");
        ae_set_error_flag(errorflag, ae_fp_neq(y2.ptr.p_double[0],(double)(2)), __FILE__, __LINE__, "testlsfitunit.ap:1855");
        ae_set_error_flag(errorflag, ae_fp_neq(y2.ptr.p_double[1],(double)(3)), __FILE__, __LINE__, "testlsfitunit.ap:1856");
    }
    
    /*
     * Non-parametric, single section tied test (fixed)
     */
    ae_vector_set_length(&x, 5, _state);
    ae_vector_set_length(&y, 5, _state);
    x.ptr.p_double[0] = (double)(0);
    x.ptr.p_double[1] = 1.5;
    x.ptr.p_double[2] = (double)(0);
    x.ptr.p_double[3] = (double)(0);
    x.ptr.p_double[4] = 1.5;
    y.ptr.p_double[0] = (double)(2);
    y.ptr.p_double[1] = (double)(1);
    y.ptr.p_double[2] = (double)(3);
    y.ptr.p_double[3] = (double)(1);
    y.ptr.p_double[4] = (double)(5);
    lstfitpiecewiselinearrdpfixed(&x, &y, 5, 1, &x2, &y2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=1, __FILE__, __LINE__, "testlsfitunit.ap:1875");
    if( nsections==1 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(x2.ptr.p_double[0],(double)(0)), __FILE__, __LINE__, "testlsfitunit.ap:1878");
        ae_set_error_flag(errorflag, ae_fp_neq(x2.ptr.p_double[1],1.5), __FILE__, __LINE__, "testlsfitunit.ap:1879");
        ae_set_error_flag(errorflag, ae_fp_neq(y2.ptr.p_double[0],(double)(2)), __FILE__, __LINE__, "testlsfitunit.ap:1880");
        ae_set_error_flag(errorflag, ae_fp_neq(y2.ptr.p_double[1],(double)(3)), __FILE__, __LINE__, "testlsfitunit.ap:1881");
    }
    
    /*
     * Non-parametric, two-section test (fixed)
     */
    ae_vector_set_length(&x, 5, _state);
    ae_vector_set_length(&y, 5, _state);
    x.ptr.p_double[0] = (double)(0);
    x.ptr.p_double[1] = 0.5;
    x.ptr.p_double[2] = (double)(1);
    x.ptr.p_double[3] = 1.75;
    x.ptr.p_double[4] = (double)(2);
    y.ptr.p_double[0] = (double)(1);
    y.ptr.p_double[1] = 2.1;
    y.ptr.p_double[2] = (double)(3);
    y.ptr.p_double[3] = 5.21;
    y.ptr.p_double[4] = (double)(6);
    lstfitpiecewiselinearrdpfixed(&x, &y, 5, 2, &x2, &y2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=2, __FILE__, __LINE__, "testlsfitunit.ap:1900");
    if( nsections==2 )
    {
        ae_set_error_flag(errorflag, ae_fp_neq(x2.ptr.p_double[0],(double)(0)), __FILE__, __LINE__, "testlsfitunit.ap:1903");
        ae_set_error_flag(errorflag, ae_fp_neq(x2.ptr.p_double[1],(double)(1)), __FILE__, __LINE__, "testlsfitunit.ap:1904");
        ae_set_error_flag(errorflag, ae_fp_neq(x2.ptr.p_double[2],(double)(2)), __FILE__, __LINE__, "testlsfitunit.ap:1905");
        ae_set_error_flag(errorflag, ae_fp_neq(y2.ptr.p_double[0],(double)(1)), __FILE__, __LINE__, "testlsfitunit.ap:1906");
        ae_set_error_flag(errorflag, ae_fp_neq(y2.ptr.p_double[1],(double)(3)), __FILE__, __LINE__, "testlsfitunit.ap:1907");
        ae_set_error_flag(errorflag, ae_fp_neq(y2.ptr.p_double[2],(double)(6)), __FILE__, __LINE__, "testlsfitunit.ap:1908");
    }
    
    /*
     * Non-parametric, variable precision test (non-fixed), results are compared against fixed-section test
     */
    eps = 10.0;
    n = 100;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&y, n, _state);
    while(ae_fp_greater_eq(eps,0.0001))
    {
        
        /*
         * Generate set of randomly rearranged points
         */
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = ae_pi*i/(n-1);
            y.ptr.p_double[i] = ae_sin(x.ptr.p_double[i], _state)+0.01*(hqrnduniformr(&rs, _state)-0.5);
        }
        for(i=0; i<=n-2; i++)
        {
            k = i+hqrnduniformi(&rs, n-i, _state);
            v = x.ptr.p_double[i];
            x.ptr.p_double[i] = x.ptr.p_double[k];
            x.ptr.p_double[k] = v;
            v = y.ptr.p_double[i];
            y.ptr.p_double[i] = y.ptr.p_double[k];
            y.ptr.p_double[k] = v;
        }
        
        /*
         * Perform run of eps-based RDP algorithm
         */
        lstfitpiecewiselinearrdp(&x, &y, n, eps, &x2, &y2, &nsections, _state);
        ae_set_error_flag(errorflag, nsections==0, __FILE__, __LINE__, "testlsfitunit.ap:1943");
        if( nsections==0 )
        {
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Check properties
         */
        for(i=0; i<=nsections-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(x2.ptr.p_double[i],x2.ptr.p_double[i+1]), __FILE__, __LINE__, "testlsfitunit.ap:1951");
        }
        spline1dbuildlinear(&x2, &y2, nsections+1, &s, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(spline1dcalc(&s, x.ptr.p_double[i], _state)-y.ptr.p_double[i], _state),eps), __FILE__, __LINE__, "testlsfitunit.ap:1954");
        }
        
        /*
         * compare results with values returned by section-based algorithm
         */
        lstfitpiecewiselinearrdpfixed(&x, &y, n, nsections, &x3, &y3, &nsections3, _state);
        ae_set_error_flag(errorflag, nsections3!=nsections, __FILE__, __LINE__, "testlsfitunit.ap:1960");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=nsections-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(x2.ptr.p_double[i]-x3.ptr.p_double[i], _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testlsfitunit.ap:1965");
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(y2.ptr.p_double[i]-y3.ptr.p_double[i], _state),1000*ae_machineepsilon), __FILE__, __LINE__, "testlsfitunit.ap:1966");
        }
        
        /*
         * Next epsilon
         */
        eps = eps*0.5;
    }
    
    /*
     * Test that non-parametric RDP correctly handles requests for more than N-1 section
     */
    n = 100;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&y, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = ae_pi*hqrnduniformr(&rs, _state);
        y.ptr.p_double[i] = ae_sin(x.ptr.p_double[i], _state)+0.01*(hqrnduniformr(&rs, _state)-0.5);
    }
    lstfitpiecewiselinearrdpfixed(&x, &y, n, n, &x2, &y2, &nsections, _state);
    ae_set_error_flag(errorflag, nsections!=n-1, __FILE__, __LINE__, "testlsfitunit.ap:1987");
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests 4PL/5PL fitting. On error sets FitErrors flag variable;
on success - flag is not changed.
*************************************************************************/
static void testlsfitunit_testlogisticfitting(ae_bool* fiterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_vector y;
    ae_int_t n;
    ae_int_t nz;
    ae_int_t ntotal;
    ae_int_t i;
    ae_int_t k;
    double v;
    double vv;
    ae_int_t k0;
    ae_int_t k1;
    double v0;
    double v1;
    ae_int_t pass;
    ae_int_t idxa;
    ae_int_t idxb;
    ae_int_t idxc;
    ae_int_t idxd;
    ae_int_t idxg;
    ae_int_t idxx;
    double a;
    double b;
    double c;
    double d;
    double g;
    double ae;
    double be;
    double ce;
    double de;
    double ge;
    double scalex;
    double scaley;
    double noise;
    double tol;
    lsfitreport rep;
    hqrndstate rs;
    double er2;
    double erms;
    double eavg;
    double eavgrel;
    double emax;
    double rss;
    double tss;
    double meany;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _lsfitreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * 4PL/5PL calculation
     */
    tol = 1.0E-6;
    for(idxa=-4; idxa<=4; idxa++)
    {
        for(idxb=-3; idxb<=3; idxb++)
        {
            for(idxc=-4; idxc<=4; idxc++)
            {
                for(idxd=-4; idxd<=4; idxd++)
                {
                    for(idxg=-4; idxg<=4; idxg++)
                    {
                        for(idxx=-4; idxx<=4; idxx++)
                        {
                            
                            /*
                             * Convert Idx* to corresponding parameter value
                             */
                            a = (double)(idxa);
                            b = (double)(idxb);
                            c = ae_pow((double)(2), (double)(idxc), _state);
                            d = (double)(idxd);
                            g = ae_pow((double)(2), (double)(idxg), _state);
                            if( idxx!=0 )
                            {
                                v = ae_pow((double)(2), (double)(idxx), _state);
                            }
                            else
                            {
                                v = (double)(0);
                            }
                            
                            /*
                             * Test 4PL calculation
                             */
                            vv = 0.0;
                            if( ae_fp_neq(v,(double)(0)) )
                            {
                                vv = d+(a-d)/(1+ae_pow(v/c, b, _state));
                            }
                            else
                            {
                                if( ae_fp_greater(b,(double)(0)) )
                                {
                                    vv = a;
                                }
                                if( ae_fp_less(b,(double)(0)) )
                                {
                                    vv = d;
                                }
                                if( ae_fp_eq(b,(double)(0)) )
                                {
                                    vv = 0.5*(a+d);
                                }
                            }
                            v0 = logisticcalc4(v, a, b, c, d, _state);
                            ae_set_error_flag(fiterrors, ae_fp_greater(ae_fabs(vv-v0, _state),tol), __FILE__, __LINE__, "testlsfitunit.ap:2057");
                            
                            /*
                             * Test 5PL calculation
                             */
                            if( ae_fp_neq(v,(double)(0)) )
                            {
                                vv = d+(a-d)/ae_pow(1+ae_pow(v/c, b, _state), g, _state);
                            }
                            else
                            {
                                if( ae_fp_greater(b,(double)(0)) )
                                {
                                    vv = a;
                                }
                                if( ae_fp_less(b,(double)(0)) )
                                {
                                    vv = d;
                                }
                                if( ae_fp_eq(b,(double)(0)) )
                                {
                                    vv = d+(a-d)/ae_pow((double)(2), g, _state);
                                }
                            }
                            v0 = logisticcalc5(v, a, b, c, d, g, _state);
                            ae_set_error_flag(fiterrors, ae_fp_greater(ae_fabs(vv-v0, _state),tol), __FILE__, __LINE__, "testlsfitunit.ap:2074");
                        }
                    }
                }
            }
        }
    }
    
    /*
     * 4PL fitting
     *
     * Generate random AE/BE/CE/DE, generate random set of points and for
     * each point generate two function values: F(x)+eps and F(x)-eps.
     * Such problem has solution which is exactly AE/BE/CE/DE which were
     * used to generate points.
     *
     * This test checks both unconstrained and constrained fitting (latter
     * one is performed with A constrained to AE, B constrained to BE).
     */
    tol = 1.0E-6;
    for(pass=1; pass<=30; pass++)
    {
        
        /*
         * Generate 2*N points with non-zero X and 2*NZ points with
         * zero X. In most cases we choose N<>0 and NZ<>0, but in
         * some cases either N or NZ (but not both) is zero.
         *
         * X-values have scale equal to ScaleX
         */
        scalex = ae_pow((double)(10), 30*hqrnduniformr(&rs, _state)-15, _state);
        n = 40+hqrnduniformi(&rs, 40, _state);
        nz = 4+hqrnduniformi(&rs, 4, _state);
        if( ae_fp_less(hqrnduniformr(&rs, _state),0.1) )
        {
            if( ae_fp_less(hqrnduniformr(&rs, _state),0.5) )
            {
                n = 0;
            }
            else
            {
                nz = 0;
            }
        }
        ntotal = 2*(n+nz);
        ae_vector_set_length(&x, ntotal, _state);
        for(i=0; i<=n-1; i++)
        {
            v = scalex*ae_exp(ae_log((double)(5), _state)*(2*hqrnduniformr(&rs, _state)-1), _state);
            x.ptr.p_double[2*i+0] = v;
            x.ptr.p_double[2*i+1] = v;
        }
        for(i=0; i<=nz-1; i++)
        {
            x.ptr.p_double[2*n+2*i+0] = (double)(0);
            x.ptr.p_double[2*n+2*i+1] = (double)(0);
        }
        
        /*
         * Fenerate A/B/C/D:
         * * A/D are random with scale equal to ScaleY
         * * B is in +-[0.25,4.0]
         * * for C we choose one of X[], if N>0;
         *   if N=0, we set C=1.
         */
        scaley = ae_pow((double)(10), 30*hqrnduniformr(&rs, _state)-15, _state);
        ae = scaley*(hqrnduniformr(&rs, _state)-0.5);
        be = (2*hqrnduniformi(&rs, 2, _state)-1)*ae_exp(ae_log((double)(4), _state)*(2*hqrnduniformr(&rs, _state)-1), _state);
        ce = scalex*ae_exp(ae_log((double)(2), _state)*(2*hqrnduniformr(&rs, _state)-1), _state);
        de = ae+scaley*(2*hqrnduniformi(&rs, 2, _state)-1)*(hqrnduniformr(&rs, _state)+0.5);
        
        /*
         * Choose noise level and generate Y[].
         */
        noise = 0.05*scaley;
        ae_vector_set_length(&y, ntotal, _state);
        for(i=0; i<=ntotal/2-1; i++)
        {
            if( ae_fp_neq(x.ptr.p_double[2*i+0],(double)(0)) )
            {
                v = de+(ae-de)/(1.0+ae_pow(x.ptr.p_double[2*i+0]/ce, be, _state));
            }
            else
            {
                if( ae_fp_greater_eq(be,(double)(0)) )
                {
                    v = ae;
                }
                else
                {
                    v = de;
                }
            }
            y.ptr.p_double[2*i+0] = v+noise;
            y.ptr.p_double[2*i+1] = v-noise;
        }
        
        /*
         * Unconstrained fit and test
         *
         * NOTE: we test that B>=0 is returned. If BE<0, we use
         *       symmetry property of 4PL model.
         */
        logisticfit4(&x, &y, ntotal, &a, &b, &c, &d, &rep, _state);
        ae_set_error_flag(fiterrors, !ae_isfinite(a, _state), __FILE__, __LINE__, "testlsfitunit.ap:2162");
        ae_set_error_flag(fiterrors, !ae_isfinite(b, _state), __FILE__, __LINE__, "testlsfitunit.ap:2163");
        ae_set_error_flag(fiterrors, !ae_isfinite(c, _state), __FILE__, __LINE__, "testlsfitunit.ap:2164");
        ae_set_error_flag(fiterrors, !ae_isfinite(d, _state), __FILE__, __LINE__, "testlsfitunit.ap:2165");
        ae_set_error_flag(fiterrors, ae_fp_less(b,(double)(0)), __FILE__, __LINE__, "testlsfitunit.ap:2166");
        v = 0.0;
        for(i=0; i<=ntotal-1; i++)
        {
            if( ae_fp_neq(x.ptr.p_double[i],(double)(0)) )
            {
                vv = d+(a-d)/(1.0+ae_pow(x.ptr.p_double[i]/c, b, _state));
            }
            else
            {
                vv = a;
            }
            v = v+ae_sqr(y.ptr.p_double[i]-vv, _state);
        }
        v = ae_sqrt(v/ntotal, _state);
        ae_set_error_flag(fiterrors, ae_fp_greater(v,(1+tol)*noise), __FILE__, __LINE__, "testlsfitunit.ap:2177");
        
        /*
         * Constrained fit and test
         *
         * NOTE: we test that B>=0 is returned. If BE<0, we use
         *       symmetry property of 4PL model.
         */
        for(k0=0; k0<=1; k0++)
        {
            for(k1=0; k1<=1; k1++)
            {
                
                /*
                 * Choose constraints.
                 */
                if( k0==0 )
                {
                    v0 = _state->v_nan;
                }
                else
                {
                    if( ae_fp_greater_eq(be,(double)(0)) )
                    {
                        v0 = ae;
                    }
                    else
                    {
                        v0 = de;
                    }
                }
                if( k1==0 )
                {
                    v1 = _state->v_nan;
                }
                else
                {
                    if( ae_fp_greater_eq(be,(double)(0)) )
                    {
                        v1 = de;
                    }
                    else
                    {
                        v1 = ae;
                    }
                }
                
                /*
                 * Fit
                 */
                logisticfit4ec(&x, &y, ntotal, v0, v1, &a, &b, &c, &d, &rep, _state);
                
                /*
                 * Check
                 */
                ae_set_error_flag(fiterrors, !ae_isfinite(a, _state), __FILE__, __LINE__, "testlsfitunit.ap:2218");
                ae_set_error_flag(fiterrors, !ae_isfinite(b, _state), __FILE__, __LINE__, "testlsfitunit.ap:2219");
                ae_set_error_flag(fiterrors, !ae_isfinite(c, _state), __FILE__, __LINE__, "testlsfitunit.ap:2220");
                ae_set_error_flag(fiterrors, !ae_isfinite(d, _state), __FILE__, __LINE__, "testlsfitunit.ap:2221");
                ae_set_error_flag(fiterrors, ae_fp_less(b,(double)(0)), __FILE__, __LINE__, "testlsfitunit.ap:2222");
                ae_set_error_flag(fiterrors, k0!=0&&ae_fp_neq(a,v0), __FILE__, __LINE__, "testlsfitunit.ap:2223");
                ae_set_error_flag(fiterrors, k1!=0&&ae_fp_neq(d,v1), __FILE__, __LINE__, "testlsfitunit.ap:2224");
                v = 0.0;
                for(i=0; i<=ntotal-1; i++)
                {
                    if( ae_fp_neq(x.ptr.p_double[i],(double)(0)) )
                    {
                        vv = d+(a-d)/(1.0+ae_pow(x.ptr.p_double[i]/c, b, _state));
                    }
                    else
                    {
                        if( ae_fp_greater_eq(b,(double)(0)) )
                        {
                            vv = a;
                        }
                        else
                        {
                            vv = d;
                        }
                    }
                    v = v+ae_sqr(y.ptr.p_double[i]-vv, _state);
                }
                v = ae_sqrt(v/ntotal, _state);
                ae_set_error_flag(fiterrors, ae_fp_greater(v,(1+tol)*noise), __FILE__, __LINE__, "testlsfitunit.ap:2240");
            }
        }
    }
    
    /*
     * 5PL fitting
     *
     * Generate random AE/BE/CE/DE/GE, generate random set of points and for
     * each point generate two function values: F(x)+eps and F(x)-eps.
     * Such problem has solution which is exactly AE/BE/CE/DE which were
     * used to generate points.
     *
     * NOTE: because problem has higher condition number, we use lower
     *       tolerance for power parameters B and G.
     *
     * This test checks both unconstrained and constrained fitting.
     */
    tol = 1.0E-6;
    for(pass=1; pass<=10; pass++)
    {
        
        /*
         * Generate N points, N-1 of them with non-zero X and
         * last one with zero X.
         * X-values have scale equal to ScaleX
         */
        scalex = ae_pow((double)(10), 30*hqrnduniformr(&rs, _state)-15, _state);
        k = 50;
        n = 2*k+1;
        ae_vector_set_length(&x, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = scalex*ae_pow((double)(2), (double)(2*(i-k))/(double)k, _state);
        }
        x.ptr.p_double[n-1] = (double)(0);
        
        /*
         * Generate A/B/C/D/G:
         * * A/D are random with scale equal to ScaleY
         * * B is in +-[0.25,4.0]
         * * G is in   [0.25,4.0]
         * * C is in   [0.25,4.0]*ScaleX
         *   if N=0, we set C=1.
         * Generate Y[].
         */
        scaley = ae_pow((double)(10), 30*hqrnduniformr(&rs, _state)-15, _state);
        ae = scaley*(hqrnduniformr(&rs, _state)-0.5);
        be = (2*hqrnduniformi(&rs, 2, _state)-1)*ae_exp(ae_log((double)(2), _state)*(2*hqrnduniformr(&rs, _state)-1), _state);
        ce = scalex*ae_exp(ae_log((double)(2), _state)*(2*hqrnduniformr(&rs, _state)-1), _state);
        de = ae+scaley*(2*hqrnduniformi(&rs, 2, _state)-1)*(hqrnduniformr(&rs, _state)+0.5);
        ge = ae_exp(ae_log((double)(2), _state)*(2*hqrnduniformr(&rs, _state)-1), _state);
        ae_vector_set_length(&y, n, _state);
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_neq(x.ptr.p_double[i],(double)(0)) )
            {
                v = de+(ae-de)/ae_pow(1.0+ae_pow(x.ptr.p_double[i]/ce, be, _state), ge, _state);
            }
            else
            {
                if( ae_fp_greater_eq(be,(double)(0)) )
                {
                    v = ae;
                }
                else
                {
                    v = de;
                }
            }
            y.ptr.p_double[i] = v;
        }
        
        /*
         * Unconstrained fit and test
         *
         * NOTE: we test that B>=0 is returned. If BE<0, we use
         *       symmetry property of 4PL model.
         */
        logisticfit5(&x, &y, n, &a, &b, &c, &d, &g, &rep, _state);
        v = 0.0;
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_neq(x.ptr.p_double[i],(double)(0)) )
            {
                vv = d+(a-d)/ae_pow(1.0+ae_pow(x.ptr.p_double[i]/c, b, _state), g, _state);
            }
            else
            {
                if( ae_fp_greater_eq(b,(double)(0)) )
                {
                    vv = a;
                }
                else
                {
                    vv = d;
                }
            }
            v = v+ae_sqr(y.ptr.p_double[i]-vv, _state);
        }
        v = ae_sqrt(v/n, _state);
        ae_set_error_flag(fiterrors, ae_fp_greater(v,scaley*tol), __FILE__, __LINE__, "testlsfitunit.ap:2325");
        
        /*
         * Constrained fit and test
         */
        for(k0=0; k0<=1; k0++)
        {
            for(k1=0; k1<=1; k1++)
            {
                
                /*
                 * Choose constraints.
                 */
                if( k0==0 )
                {
                    v0 = _state->v_nan;
                }
                else
                {
                    if( ae_fp_greater_eq(be,(double)(0)) )
                    {
                        v0 = ae;
                    }
                    else
                    {
                        v0 = de;
                    }
                }
                if( k1==0 )
                {
                    v1 = _state->v_nan;
                }
                else
                {
                    if( ae_fp_greater_eq(be,(double)(0)) )
                    {
                        v1 = de;
                    }
                    else
                    {
                        v1 = ae;
                    }
                }
                
                /*
                 * Fit
                 */
                logisticfit5ec(&x, &y, n, v0, v1, &a, &b, &c, &d, &g, &rep, _state);
                ae_set_error_flag(fiterrors, !ae_isfinite(a, _state), __FILE__, __LINE__, "testlsfitunit.ap:2359");
                ae_set_error_flag(fiterrors, !ae_isfinite(b, _state), __FILE__, __LINE__, "testlsfitunit.ap:2360");
                ae_set_error_flag(fiterrors, !ae_isfinite(c, _state), __FILE__, __LINE__, "testlsfitunit.ap:2361");
                ae_set_error_flag(fiterrors, !ae_isfinite(d, _state), __FILE__, __LINE__, "testlsfitunit.ap:2362");
                if( ae_fp_greater(b,(double)(0)) )
                {
                    ae_set_error_flag(fiterrors, k0!=0&&ae_fp_neq(a,v0), __FILE__, __LINE__, "testlsfitunit.ap:2365");
                    ae_set_error_flag(fiterrors, k1!=0&&ae_fp_neq(d,v1), __FILE__, __LINE__, "testlsfitunit.ap:2366");
                }
                else
                {
                    ae_set_error_flag(fiterrors, k0!=0&&ae_fp_neq(d,v0), __FILE__, __LINE__, "testlsfitunit.ap:2370");
                    ae_set_error_flag(fiterrors, k1!=0&&ae_fp_neq(a,v1), __FILE__, __LINE__, "testlsfitunit.ap:2371");
                }
                v = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    if( ae_fp_neq(x.ptr.p_double[i],(double)(0)) )
                    {
                        vv = d+(a-d)/ae_pow(1.0+ae_pow(x.ptr.p_double[i]/c, b, _state), g, _state);
                    }
                    else
                    {
                        if( ae_fp_greater_eq(b,(double)(0)) )
                        {
                            vv = a;
                        }
                        else
                        {
                            vv = d;
                        }
                    }
                    v = v+ae_sqr(y.ptr.p_double[i]-vv, _state);
                }
                v = ae_sqrt(v/n, _state);
                ae_set_error_flag(fiterrors, ae_fp_greater(v,scaley*tol), __FILE__, __LINE__, "testlsfitunit.ap:2388");
            }
        }
    }
    
    /*
     * Test correctness of errors
     */
    tol = 1.0E-6;
    for(pass=1; pass<=20; pass++)
    {
        n = 10;
        meany = 0.0;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&y, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)(i);
            y.ptr.p_double[i] = hqrnduniformr(&rs, _state)-0.5;
            meany = meany+y.ptr.p_double[i];
        }
        meany = meany/n;
        x.ptr.p_double[1] = (double)(0);
        
        /*
         * Choose model fitting function to test
         */
        k = hqrnduniformi(&rs, 4, _state);
        a = (double)(0);
        d = (double)(0);
        c = (double)(1);
        b = (double)(1);
        g = (double)(1);
        if( k==0 )
        {
            logisticfit4(&x, &y, n, &a, &b, &c, &d, &rep, _state);
            g = 1.0;
        }
        if( k==1 )
        {
            logisticfit4ec(&x, &y, n, hqrnduniformr(&rs, _state)-0.5, hqrnduniformr(&rs, _state)-0.5, &a, &b, &c, &d, &rep, _state);
            g = 1.0;
        }
        if( k==2 )
        {
            logisticfit5(&x, &y, n, &a, &b, &c, &d, &g, &rep, _state);
        }
        if( k==3 )
        {
            logisticfit5ec(&x, &y, n, hqrnduniformr(&rs, _state)-0.5, hqrnduniformr(&rs, _state)-0.5, &a, &b, &c, &d, &g, &rep, _state);
        }
        k = 0;
        erms = (double)(0);
        eavg = (double)(0);
        eavgrel = (double)(0);
        emax = (double)(0);
        rss = 0.0;
        tss = 0.0;
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_neq(x.ptr.p_double[i],(double)(0)) )
            {
                v = d+(a-d)/ae_pow(1.0+ae_pow(x.ptr.p_double[i]/c, b, _state), g, _state);
            }
            else
            {
                if( ae_fp_greater_eq(b,(double)(0)) )
                {
                    v = a;
                }
                else
                {
                    v = d;
                }
            }
            v = v-y.ptr.p_double[i];
            rss = rss+v*v;
            tss = tss+ae_sqr(y.ptr.p_double[i]-meany, _state);
            erms = erms+ae_sqr(v, _state);
            eavg = eavg+ae_fabs(v, _state);
            if( ae_fp_neq(y.ptr.p_double[i],(double)(0)) )
            {
                eavgrel = eavgrel+ae_fabs(v/y.ptr.p_double[i], _state);
                k = k+1;
            }
            emax = ae_maxreal(emax, ae_fabs(v, _state), _state);
        }
        er2 = 1.0-rss/tss;
        erms = ae_sqrt(erms/n, _state);
        eavg = eavg/n;
        if( k>0 )
        {
            eavgrel = eavgrel/k;
        }
        ae_set_error_flag(fiterrors, ae_fp_greater(ae_fabs(erms-rep.rmserror, _state),tol), __FILE__, __LINE__, "testlsfitunit.ap:2477");
        ae_set_error_flag(fiterrors, ae_fp_greater(ae_fabs(eavg-rep.avgerror, _state),tol), __FILE__, __LINE__, "testlsfitunit.ap:2478");
        ae_set_error_flag(fiterrors, ae_fp_greater(ae_fabs(emax-rep.maxerror, _state),tol), __FILE__, __LINE__, "testlsfitunit.ap:2479");
        ae_set_error_flag(fiterrors, ae_fp_greater(ae_fabs(eavgrel-rep.avgrelerror, _state),tol), __FILE__, __LINE__, "testlsfitunit.ap:2480");
        ae_set_error_flag(fiterrors, ae_fp_greater(ae_fabs(er2-rep.r2, _state),tol), __FILE__, __LINE__, "testlsfitunit.ap:2481");
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test for box constrained problemsNLS.
On failure sets error flag, leaves it unchanged on success.
*************************************************************************/
static void testlsfitunit_testbcnls(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    lsfitstate state;
    lsfitreport rep;
    ae_vector bl;
    ae_vector bu;
    ae_int_t nx;
    ae_int_t nc;
    ae_int_t m;
    ae_vector x;
    ae_vector c0;
    ae_vector c1;
    ae_vector c2;
    ae_matrix xx;
    ae_vector y;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ii;
    double v;
    double h;
    hqrndstate rs;
    double epsx;
    double f0;
    double f1;
    ae_int_t tmpkind;
    ae_int_t terminationtype;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    memset(&c0, 0, sizeof(c0));
    memset(&c1, 0, sizeof(c1));
    memset(&c2, 0, sizeof(c2));
    memset(&xx, 0, sizeof(xx));
    memset(&y, 0, sizeof(y));
    memset(&rs, 0, sizeof(rs));
    _lsfitstate_init(&state, _state, ae_true);
    _lsfitreport_init(&rep, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xx, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Fit
     *
     *                 [                              ]    
     *    f(X|C)= SUM_j[ (alpha*c_j+power(c_j,3))*x_j ]
     *                 [                              ]
     *
     * subject to non-negativity constraints on c_j
     */
    epsx = 1.0E-9;
    for(tmpkind=0; tmpkind<=1; tmpkind++)
    {
        for(nc=1; nc<=20; nc++)
        {
            nx = nc;
            m = nc+hqrnduniformi(&rs, nc, _state);
            ae_vector_set_length(&bl, nc, _state);
            ae_vector_set_length(&bu, nc, _state);
            ae_vector_set_length(&c0, nc, _state);
            ae_vector_set_length(&x, nx, _state);
            for(i=0; i<=nc-1; i++)
            {
                bl.ptr.p_double[i] = (double)(0);
                bu.ptr.p_double[i] = _state->v_posinf;
                c0.ptr.p_double[i] = 1+hqrnduniformr(&rs, _state);
            }
            ae_matrix_set_length(&xx, m, nx, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xx.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
                y.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            if( tmpkind==0 )
            {
                lsfitcreatef(&xx, &y, &c0, m, nx, nc, 10*epsx, &state, _state);
            }
            if( tmpkind==1 )
            {
                lsfitcreatefg(&xx, &y, &c0, m, nx, nc, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &state, _state);
            }
            lsfitsetcond(&state, epsx, 0, _state);
            lsfitsetbc(&state, &bl, &bu, _state);
            while(lsfititeration(&state, _state))
            {
                for(i=0; i<=nc-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_less(state.c.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2554");
                    ae_set_error_flag(errorflag, ae_fp_greater(state.c.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2555");
                }
                if( state.needf )
                {
                    testlsfitunit_testfunc1(nx, &state.x, &state.c, &state.f, ae_true, &state.g, ae_false, _state);
                    continue;
                }
                if( state.needfg )
                {
                    testlsfitunit_testfunc1(nx, &state.x, &state.c, &state.f, ae_true, &state.g, ae_true, _state);
                    continue;
                }
                ae_assert(ae_false, "minlm test: integrity check failed", _state);
            }
            lsfitresults(&state, &terminationtype, &c1, &rep, _state);
            ae_set_error_flag(errorflag, terminationtype<=0, __FILE__, __LINE__, "testlsfitunit.ap:2570");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            f0 = (double)(0);
            for(ii=0; ii<=m-1; ii++)
            {
                ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                testlsfitunit_testfunc1(nx, &x, &c1, &v, ae_true, &state.g, ae_false, _state);
                f0 = f0+ae_sqr(v-y.ptr.p_double[ii], _state);
            }
            h = 0.001;
            ae_vector_set_length(&c2, nc, _state);
            for(i=0; i<=nc-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_less(c1.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2587");
                ae_set_error_flag(errorflag, ae_fp_greater(c1.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2588");
                if( ae_fp_greater_eq(c1.ptr.p_double[i]+h,bl.ptr.p_double[i]) )
                {
                    for(j=0; j<=nc-1; j++)
                    {
                        c2.ptr.p_double[j] = c1.ptr.p_double[j];
                    }
                    c2.ptr.p_double[i] = c2.ptr.p_double[i]+h;
                    f1 = (double)(0);
                    for(ii=0; ii<=m-1; ii++)
                    {
                        ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                        testlsfitunit_testfunc1(nx, &x, &c2, &v, ae_true, &state.g, ae_false, _state);
                        f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:2603");
                }
                if( ae_fp_greater_eq(c1.ptr.p_double[i]-h,bl.ptr.p_double[i]) )
                {
                    for(j=0; j<=nc-1; j++)
                    {
                        c2.ptr.p_double[j] = c1.ptr.p_double[j];
                    }
                    c2.ptr.p_double[i] = c2.ptr.p_double[i]-h;
                    f1 = (double)(0);
                    for(ii=0; ii<=m-1; ii++)
                    {
                        ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                        testlsfitunit_testfunc1(nx, &x, &c2, &v, ae_true, &state.g, ae_false, _state);
                        f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:2619");
                }
            }
        }
    }
    
    /*
     * Fit
     *
     *                 [                              ]    
     *    f(X|C)= SUM_j[ (alpha*c_j+power(c_j,3))*x_j ]
     *                 [                              ]
     *
     * subject to random box constraints on c_j
     */
    epsx = 1.0E-9;
    for(tmpkind=0; tmpkind<=1; tmpkind++)
    {
        for(nc=1; nc<=20; nc++)
        {
            nx = nc;
            m = nc+hqrnduniformi(&rs, nc, _state);
            ae_vector_set_length(&bl, nc, _state);
            ae_vector_set_length(&bu, nc, _state);
            ae_vector_set_length(&c0, nc, _state);
            ae_vector_set_length(&x, nx, _state);
            for(i=0; i<=nc-1; i++)
            {
                bl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bu.ptr.p_double[i] = bl.ptr.p_double[i]+hqrnduniformr(&rs, _state);
                c0.ptr.p_double[i] = 1+hqrnduniformr(&rs, _state);
            }
            ae_matrix_set_length(&xx, m, nx, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xx.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
                y.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            if( tmpkind==0 )
            {
                lsfitcreatef(&xx, &y, &c0, m, nx, nc, 10*epsx, &state, _state);
            }
            if( tmpkind==1 )
            {
                lsfitcreatefg(&xx, &y, &c0, m, nx, nc, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &state, _state);
            }
            lsfitsetcond(&state, epsx, 0, _state);
            lsfitsetbc(&state, &bl, &bu, _state);
            while(lsfititeration(&state, _state))
            {
                for(i=0; i<=nc-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_less(state.c.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2668");
                    ae_set_error_flag(errorflag, ae_fp_greater(state.c.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2669");
                }
                if( state.needf )
                {
                    testlsfitunit_testfunc1(nx, &state.x, &state.c, &state.f, ae_true, &state.g, ae_false, _state);
                    continue;
                }
                if( state.needfg )
                {
                    testlsfitunit_testfunc1(nx, &state.x, &state.c, &state.f, ae_true, &state.g, ae_true, _state);
                    continue;
                }
                ae_assert(ae_false, "minlm test: integrity check failed", _state);
            }
            lsfitresults(&state, &terminationtype, &c1, &rep, _state);
            ae_set_error_flag(errorflag, terminationtype<=0, __FILE__, __LINE__, "testlsfitunit.ap:2684");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            f0 = (double)(0);
            for(ii=0; ii<=m-1; ii++)
            {
                ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                testlsfitunit_testfunc1(nx, &x, &c1, &v, ae_true, &state.g, ae_false, _state);
                f0 = f0+ae_sqr(v-y.ptr.p_double[ii], _state);
            }
            h = 0.001;
            ae_vector_set_length(&c2, nc, _state);
            for(i=0; i<=nc-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_less(c1.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2701");
                ae_set_error_flag(errorflag, ae_fp_greater(c1.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2702");
                if( ae_fp_greater_eq(c1.ptr.p_double[i]+h,bl.ptr.p_double[i])&&ae_fp_less_eq(c1.ptr.p_double[i]+h,bu.ptr.p_double[i]) )
                {
                    for(j=0; j<=nc-1; j++)
                    {
                        c2.ptr.p_double[j] = c1.ptr.p_double[j];
                    }
                    c2.ptr.p_double[i] = c2.ptr.p_double[i]+h;
                    f1 = (double)(0);
                    for(ii=0; ii<=m-1; ii++)
                    {
                        ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                        testlsfitunit_testfunc1(nx, &x, &c2, &v, ae_true, &state.g, ae_false, _state);
                        f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:2717");
                }
                if( ae_fp_greater_eq(c1.ptr.p_double[i]-h,bl.ptr.p_double[i])&&ae_fp_less_eq(c1.ptr.p_double[i]-h,bu.ptr.p_double[i]) )
                {
                    for(j=0; j<=nc-1; j++)
                    {
                        c2.ptr.p_double[j] = c1.ptr.p_double[j];
                    }
                    c2.ptr.p_double[i] = c2.ptr.p_double[i]-h;
                    f1 = (double)(0);
                    for(ii=0; ii<=m-1; ii++)
                    {
                        ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                        testlsfitunit_testfunc1(nx, &x, &c2, &v, ae_true, &state.g, ae_false, _state);
                        f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:2733");
                }
            }
        }
    }
    
    /*
     * Fit
     *
     *               
     *    f(X|C)= c_0
     *               
     *
     * subject to random box constraints on c_0, where X is N-dimensional vector
     * (f does not depend on X, and it is not an error! we just test that sizes
     * of X and C are correctly handled).
     */
    epsx = 1.0E-9;
    for(tmpkind=0; tmpkind<=1; tmpkind++)
    {
        for(nx=1; nx<=20; nx++)
        {
            nc = 1;
            m = nx+hqrnduniformi(&rs, nx, _state);
            ae_vector_set_length(&bl, nc, _state);
            ae_vector_set_length(&bu, nc, _state);
            ae_vector_set_length(&c0, nc, _state);
            ae_vector_set_length(&x, nx, _state);
            for(i=0; i<=nc-1; i++)
            {
                bl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bu.ptr.p_double[i] = bl.ptr.p_double[i]+hqrnduniformr(&rs, _state);
                c0.ptr.p_double[i] = 1+hqrnduniformr(&rs, _state);
            }
            ae_matrix_set_length(&xx, m, nx, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xx.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
                y.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            if( tmpkind==0 )
            {
                lsfitcreatef(&xx, &y, &c0, m, nx, nc, 10*epsx, &state, _state);
            }
            if( tmpkind==1 )
            {
                lsfitcreatefg(&xx, &y, &c0, m, nx, nc, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &state, _state);
            }
            lsfitsetcond(&state, epsx, 0, _state);
            lsfitsetbc(&state, &bl, &bu, _state);
            while(lsfititeration(&state, _state))
            {
                for(i=0; i<=nc-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_less(state.c.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2784");
                    ae_set_error_flag(errorflag, ae_fp_greater(state.c.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2785");
                }
                if( state.needf )
                {
                    testlsfitunit_testfunc2(&state.x, nx, &state.c, nc, &state.f, ae_true, &state.g, ae_false, _state);
                    continue;
                }
                if( state.needfg )
                {
                    testlsfitunit_testfunc2(&state.x, nx, &state.c, nc, &state.f, ae_true, &state.g, ae_true, _state);
                    continue;
                }
                ae_assert(ae_false, "minlm test: integrity check failed", _state);
            }
            lsfitresults(&state, &terminationtype, &c1, &rep, _state);
            ae_set_error_flag(errorflag, terminationtype<=0, __FILE__, __LINE__, "testlsfitunit.ap:2800");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            f0 = (double)(0);
            for(ii=0; ii<=m-1; ii++)
            {
                ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                testlsfitunit_testfunc2(&x, nx, &c1, nc, &v, ae_true, &state.g, ae_false, _state);
                f0 = f0+ae_sqr(v-y.ptr.p_double[ii], _state);
            }
            h = 0.001;
            ae_vector_set_length(&c2, nc, _state);
            for(i=0; i<=nc-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_less(c1.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2817");
                ae_set_error_flag(errorflag, ae_fp_greater(c1.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2818");
                if( ae_fp_greater_eq(c1.ptr.p_double[i]+h,bl.ptr.p_double[i])&&ae_fp_less_eq(c1.ptr.p_double[i]+h,bu.ptr.p_double[i]) )
                {
                    for(j=0; j<=nc-1; j++)
                    {
                        c2.ptr.p_double[j] = c1.ptr.p_double[j];
                    }
                    c2.ptr.p_double[i] = c2.ptr.p_double[i]+h;
                    f1 = (double)(0);
                    for(ii=0; ii<=m-1; ii++)
                    {
                        ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                        testlsfitunit_testfunc2(&x, nx, &c2, nc, &v, ae_true, &state.g, ae_false, _state);
                        f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:2833");
                }
                if( ae_fp_greater_eq(c1.ptr.p_double[i]-h,bl.ptr.p_double[i])&&ae_fp_less_eq(c1.ptr.p_double[i]-h,bu.ptr.p_double[i]) )
                {
                    for(j=0; j<=nc-1; j++)
                    {
                        c2.ptr.p_double[j] = c1.ptr.p_double[j];
                    }
                    c2.ptr.p_double[i] = c2.ptr.p_double[i]-h;
                    f1 = (double)(0);
                    for(ii=0; ii<=m-1; ii++)
                    {
                        ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                        testlsfitunit_testfunc2(&x, nx, &c2, nc, &v, ae_true, &state.g, ae_false, _state);
                        f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:2849");
                }
            }
        }
    }
    
    /*
     * Fit
     *
     *               
     *    f(X|C)= c_0 + c_1*x0 + c_2*x0^2 + ...
     *               
     *
     * subject to random box constraints on c.
     */
    epsx = 1.0E-9;
    for(tmpkind=0; tmpkind<=1; tmpkind++)
    {
        for(nc=1; nc<=5; nc++)
        {
            nx = 1;
            m = 10+nc+hqrnduniformi(&rs, nc, _state);
            ae_vector_set_length(&bl, nc, _state);
            ae_vector_set_length(&bu, nc, _state);
            ae_vector_set_length(&c0, nc, _state);
            ae_vector_set_length(&x, nx, _state);
            for(i=0; i<=nc-1; i++)
            {
                bl.ptr.p_double[i] = hqrndnormal(&rs, _state);
                bu.ptr.p_double[i] = bl.ptr.p_double[i]+hqrnduniformr(&rs, _state);
                c0.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_matrix_set_length(&xx, m, nx, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xx.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
                y.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            if( tmpkind==0 )
            {
                lsfitcreatef(&xx, &y, &c0, m, nx, nc, 10*epsx, &state, _state);
            }
            if( tmpkind==1 )
            {
                lsfitcreatefg(&xx, &y, &c0, m, nx, nc, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &state, _state);
            }
            lsfitsetcond(&state, epsx, 0, _state);
            lsfitsetbc(&state, &bl, &bu, _state);
            while(lsfititeration(&state, _state))
            {
                for(i=0; i<=nc-1; i++)
                {
                    ae_set_error_flag(errorflag, ae_fp_less(state.c.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2898");
                    ae_set_error_flag(errorflag, ae_fp_greater(state.c.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2899");
                }
                if( state.needf )
                {
                    testlsfitunit_testfunc3(&state.x, nx, &state.c, nc, &state.f, ae_true, &state.g, ae_false, _state);
                    continue;
                }
                if( state.needfg )
                {
                    testlsfitunit_testfunc3(&state.x, nx, &state.c, nc, &state.f, ae_true, &state.g, ae_true, _state);
                    continue;
                }
                ae_assert(ae_false, "minlm test: integrity check failed", _state);
            }
            lsfitresults(&state, &terminationtype, &c1, &rep, _state);
            ae_set_error_flag(errorflag, terminationtype<=0, __FILE__, __LINE__, "testlsfitunit.ap:2914");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            f0 = (double)(0);
            for(ii=0; ii<=m-1; ii++)
            {
                ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                testlsfitunit_testfunc3(&x, nx, &c1, nc, &v, ae_true, &state.g, ae_false, _state);
                f0 = f0+ae_sqr(v-y.ptr.p_double[ii], _state);
            }
            h = 0.001;
            ae_vector_set_length(&c2, nc, _state);
            for(i=0; i<=nc-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_less(c1.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2931");
                ae_set_error_flag(errorflag, ae_fp_greater(c1.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:2932");
                if( ae_fp_greater_eq(c1.ptr.p_double[i]+h,bl.ptr.p_double[i])&&ae_fp_less_eq(c1.ptr.p_double[i]+h,bu.ptr.p_double[i]) )
                {
                    for(j=0; j<=nc-1; j++)
                    {
                        c2.ptr.p_double[j] = c1.ptr.p_double[j];
                    }
                    c2.ptr.p_double[i] = c2.ptr.p_double[i]+h;
                    f1 = (double)(0);
                    for(ii=0; ii<=m-1; ii++)
                    {
                        ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                        testlsfitunit_testfunc3(&x, nx, &c2, nc, &v, ae_true, &state.g, ae_false, _state);
                        f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:2947");
                }
                if( ae_fp_greater_eq(c1.ptr.p_double[i]-h,bl.ptr.p_double[i])&&ae_fp_less_eq(c1.ptr.p_double[i]-h,bu.ptr.p_double[i]) )
                {
                    for(j=0; j<=nc-1; j++)
                    {
                        c2.ptr.p_double[j] = c1.ptr.p_double[j];
                    }
                    c2.ptr.p_double[i] = c2.ptr.p_double[i]-h;
                    f1 = (double)(0);
                    for(ii=0; ii<=m-1; ii++)
                    {
                        ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                        testlsfitunit_testfunc3(&x, nx, &c2, nc, &v, ae_true, &state.g, ae_false, _state);
                        f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
                    }
                    ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:2963");
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test for linearly constrained NLS problems.
On failure sets error flag, leaves it unchanged on success.
*************************************************************************/
static void testlsfitunit_testlcnls(ae_bool* errorflag, ae_state *_state)
{
    ae_frame _frame_block;
    lsfitstate state;
    lsfitreport rep;
    ae_vector bl;
    ae_vector bu;
    ae_int_t nx;
    ae_int_t nc;
    ae_int_t m;
    ae_vector x;
    ae_vector y;
    ae_vector c0;
    ae_vector c1;
    ae_vector c2;
    ae_matrix rawc;
    ae_vector rawct;
    ae_int_t rawccnt;
    ae_matrix xx;
    ae_matrix xx12;
    ae_matrix z;
    ae_int_t trialidx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ii;
    double v;
    double h;
    ae_int_t optkind;
    hqrndstate rs;
    double epsx;
    double xtol;
    double f0;
    double f1;
    ae_bool bflag;
    ae_int_t terminationtype;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&c0, 0, sizeof(c0));
    memset(&c1, 0, sizeof(c1));
    memset(&c2, 0, sizeof(c2));
    memset(&rawc, 0, sizeof(rawc));
    memset(&rawct, 0, sizeof(rawct));
    memset(&xx, 0, sizeof(xx));
    memset(&xx12, 0, sizeof(xx12));
    memset(&z, 0, sizeof(z));
    memset(&rs, 0, sizeof(rs));
    _lsfitstate_init(&state, _state, ae_true);
    _lsfitreport_init(&rep, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rawc, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rawct, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xx, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xx12, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Minimize
     *
     *          [      [                             ]2  ]
     *     SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ]   ]
     *          [      [                             ]   ]
     *
     * subject to mix of box and linear inequality constraints on x_j
     *
     * We check correctness of solution by sampling a few random points
     * around one returned by optimizer, and comparing function value
     * with target. Sampling is performed with respect to inequality
     * constraints.
     */
    epsx = 1.0E-12;
    xtol = 1.0E-8;
    optkind = 1;
    for(nc=5; nc<=20; nc++)
    {
        
        /*
         * Generate problem
         */
        nx = nc;
        m = nc+hqrnduniformi(&rs, nc, _state);
        ae_vector_set_length(&bl, nc, _state);
        ae_vector_set_length(&bu, nc, _state);
        ae_vector_set_length(&c0, nc, _state);
        ae_vector_set_length(&x, nx, _state);
        for(i=0; i<=nc-1; i++)
        {
            bl.ptr.p_double[i] = hqrndnormal(&rs, _state);
            bu.ptr.p_double[i] = bl.ptr.p_double[i]+0.01+hqrnduniformr(&rs, _state);
            c0.ptr.p_double[i] = bl.ptr.p_double[i]+(bu.ptr.p_double[i]-bl.ptr.p_double[i])*hqrnduniformr(&rs, _state);
        }
        ae_matrix_set_length(&xx, m, nx, _state);
        ae_vector_set_length(&y, m, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=nx-1; j++)
            {
                xx.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
            }
            y.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        ae_assert(nc>=5, "Assertion failed", _state);
        rawccnt = 3;
        ae_matrix_set_length(&rawc, rawccnt, nc+1, _state);
        ae_vector_set_length(&rawct, rawccnt, _state);
        for(i=0; i<=rawccnt-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=nc-1; j++)
            {
                rawc.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                v = v+c0.ptr.p_double[j]*rawc.ptr.pp_double[i][j];
            }
            rawc.ptr.pp_double[i][nc] = v;
            rawct.ptr.p_int[i] = 2*hqrnduniformi(&rs, 2, _state)-1;
        }
        
        /*
         * Solve
         */
        if( optkind==0 )
        {
            lsfitcreatef(&xx, &y, &c0, m, nx, nc, 1.0E-6, &state, _state);
        }
        if( optkind==1 )
        {
            lsfitcreatefg(&xx, &y, &c0, m, nx, nc, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &state, _state);
        }
        lsfitsetcond(&state, epsx, 0, _state);
        lsfitsetbc(&state, &bl, &bu, _state);
        lsfitsetlc(&state, &rawc, &rawct, rawccnt, _state);
        while(lsfititeration(&state, _state))
        {
            for(i=0; i<=nc-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_less(state.c.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:3072");
                ae_set_error_flag(errorflag, ae_fp_greater(state.c.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:3073");
            }
            if( state.needf )
            {
                testlsfitunit_testfunc1(nx, &state.x, &state.c, &state.f, ae_true, &state.g, ae_false, _state);
                continue;
            }
            if( state.needfg )
            {
                testlsfitunit_testfunc1(nx, &state.x, &state.c, &state.f, ae_true, &state.g, ae_true, _state);
                continue;
            }
            ae_assert(ae_false, "lsfit test: integrity check failed", _state);
        }
        lsfitresults(&state, &terminationtype, &c1, &rep, _state);
        ae_set_error_flag(errorflag, terminationtype<=0, __FILE__, __LINE__, "testlsfitunit.ap:3088");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Test feasibility w.r.t. box and linear inequality constraints
         */
        for(i=0; i<=nc-1; i++)
        {
            ae_set_error_flag(errorflag, ae_fp_less(c1.ptr.p_double[i],bl.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:3097");
            ae_set_error_flag(errorflag, ae_fp_greater(c1.ptr.p_double[i],bu.ptr.p_double[i]), __FILE__, __LINE__, "testlsfitunit.ap:3098");
        }
        for(i=0; i<=rawccnt-1; i++)
        {
            v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &c1.ptr.p_double[0], 1, ae_v_len(0,nc-1));
            v = v-rawc.ptr.pp_double[i][nc];
            if( rawct.ptr.p_int[i]>0 )
            {
                ae_set_error_flag(errorflag, ae_fp_less(v,-xtol), __FILE__, __LINE__, "testlsfitunit.ap:3105");
            }
            if( rawct.ptr.p_int[i]<0 )
            {
                ae_set_error_flag(errorflag, ae_fp_greater(v,xtol), __FILE__, __LINE__, "testlsfitunit.ap:3107");
            }
        }
        
        /*
         * Make several random trial steps and:
         * 0) generate small random trial step
         * 1) if step is infeasible, skip to next trial
         * 2) compare function value in the trial point against one in other points
         */
        f0 = (double)(0);
        for(ii=0; ii<=m-1; ii++)
        {
            ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
            testlsfitunit_testfunc1(nx, &x, &c1, &v, ae_true, &state.g, ae_false, _state);
            f0 = f0+ae_sqr(v-y.ptr.p_double[ii], _state);
        }
        ae_vector_set_length(&c2, nc, _state);
        for(trialidx=0; trialidx<=10*nc; trialidx++)
        {
            h = 0.001;
            for(i=0; i<=nc-1; i++)
            {
                do
                {
                    c2.ptr.p_double[i] = c1.ptr.p_double[i]+(hqrnduniformr(&rs, _state)*2-1)*h;
                }
                while(!(ae_fp_greater_eq(c2.ptr.p_double[i],bl.ptr.p_double[i])&&ae_fp_less_eq(c2.ptr.p_double[i],bu.ptr.p_double[i])));
            }
            bflag = ae_false;
            for(i=0; i<=rawccnt-1; i++)
            {
                ae_assert(rawct.ptr.p_int[i]!=0, "Assertion failed", _state);
                v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &c2.ptr.p_double[0], 1, ae_v_len(0,nc-1));
                v = v-rawc.ptr.pp_double[i][nc];
                bflag = bflag||(rawct.ptr.p_int[i]>0&&ae_fp_less(v,(double)(0)));
                bflag = bflag||(rawct.ptr.p_int[i]<0&&ae_fp_greater(v,(double)(0)));
            }
            if( bflag )
            {
                continue;
            }
            f1 = (double)(0);
            for(ii=0; ii<=m-1; ii++)
            {
                ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                testlsfitunit_testfunc1(nx, &x, &c2, &v, ae_true, &state.g, ae_false, _state);
                f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
            }
            ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:3156");
        }
    }
    
    /*
     * Minimize
     *
     *          [      [                             ]2  ]
     *     SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ]   ]
     *          [      [                             ]   ]
     *
     * subject to linear EQUALITY constraints on x_j.
     *
     * We check correctness of solution by sampling a few random points
     * around one returned by optimizer, and comparing function value
     * with target. Sampling is performed with respect to equality
     * constraints. In order to simplify algorithm we use orthogonal
     * equality constraints.
     *
     * NOTE: we solve problem using VJ mode (analytic Jacobian) because
     *       roundoff errors from numerical differentiation sometimes
     *       prevent us from converging with good precision.
     */
    epsx = 1.0E-12;
    xtol = 1.0E-8;
    optkind = 1;
    for(nc=10; nc<=20; nc++)
    {
        
        /*
         * Generate problem
         */
        nx = nc;
        m = nc+hqrnduniformi(&rs, nc, _state);
        ae_vector_set_length(&c0, nc, _state);
        for(i=0; i<=nc-1; i++)
        {
            c0.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        ae_matrix_set_length(&xx, m, nx, _state);
        ae_vector_set_length(&y, m, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=nx-1; j++)
            {
                xx.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
            }
            y.ptr.p_double[i] = hqrndnormal(&rs, _state);
        }
        ae_assert(nc>=10, "Assertion failed", _state);
        rawccnt = 1+hqrnduniformi(&rs, 5, _state);
        rmatrixrndorthogonal(nc, &z, _state);
        ae_matrix_set_length(&rawc, rawccnt, nc+1, _state);
        ae_vector_set_length(&rawct, rawccnt, _state);
        for(i=0; i<=rawccnt-1; i++)
        {
            v = (double)(0);
            for(j=0; j<=nc-1; j++)
            {
                rawc.ptr.pp_double[i][j] = z.ptr.pp_double[i][j];
                v = v+c0.ptr.p_double[j]*rawc.ptr.pp_double[i][j];
            }
            rawc.ptr.pp_double[i][nc] = v;
            rawct.ptr.p_int[i] = 0;
        }
        
        /*
         * Solve
         */
        if( optkind==0 )
        {
            lsfitcreatef(&xx, &y, &c0, m, nx, nc, 1.0E-6, &state, _state);
        }
        if( optkind==1 )
        {
            lsfitcreatefg(&xx, &y, &c0, m, nx, nc, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &state, _state);
        }
        lsfitsetcond(&state, epsx, 0, _state);
        lsfitsetlc(&state, &rawc, &rawct, rawccnt, _state);
        while(lsfititeration(&state, _state))
        {
            if( state.needf )
            {
                testlsfitunit_testfunc1(nx, &state.x, &state.c, &state.f, ae_true, &state.g, ae_false, _state);
                continue;
            }
            if( state.needfg )
            {
                testlsfitunit_testfunc1(nx, &state.x, &state.c, &state.f, ae_true, &state.g, ae_true, _state);
                continue;
            }
            ae_assert(ae_false, "lsfit test: integrity check failed", _state);
        }
        lsfitresults(&state, &terminationtype, &c1, &rep, _state);
        ae_set_error_flag(errorflag, terminationtype<=0, __FILE__, __LINE__, "testlsfitunit.ap:3242");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Test feasibility w.r.t. linear equality constraints
         */
        for(i=0; i<=rawccnt-1; i++)
        {
            v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &c1.ptr.p_double[0], 1, ae_v_len(0,nc-1));
            v = v-rawc.ptr.pp_double[i][nc];
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(v, _state),xtol), __FILE__, __LINE__, "testlsfitunit.ap:3253");
        }
        
        /*
         * Make several random trial steps and:
         * 0) generate small random trial step
         * 1) project it onto equality constrained subspace
         * 2) compare function value in the trial point against one in other points
         */
        f0 = (double)(0);
        for(ii=0; ii<=m-1; ii++)
        {
            ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
            testlsfitunit_testfunc1(nx, &x, &c1, &v, ae_true, &state.g, ae_false, _state);
            f0 = f0+ae_sqr(v-y.ptr.p_double[ii], _state);
        }
        ae_vector_set_length(&c2, nc, _state);
        for(trialidx=0; trialidx<=nc; trialidx++)
        {
            h = 0.001;
            for(i=0; i<=nc-1; i++)
            {
                c2.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            for(i=0; i<=rawccnt-1; i++)
            {
                v = ae_v_dotproduct(&rawc.ptr.pp_double[i][0], 1, &c2.ptr.p_double[0], 1, ae_v_len(0,nc-1));
                ae_v_subd(&c2.ptr.p_double[0], 1, &rawc.ptr.pp_double[i][0], 1, ae_v_len(0,nc-1), v);
            }
            v = ae_v_dotproduct(&c2.ptr.p_double[0], 1, &c2.ptr.p_double[0], 1, ae_v_len(0,nc-1));
            ae_assert(ae_fp_greater(v,(double)(0)), "Assertion failed", _state);
            v = h/ae_sqrt(v, _state);
            ae_v_muld(&c2.ptr.p_double[0], 1, ae_v_len(0,nc-1), v);
            ae_v_add(&c2.ptr.p_double[0], 1, &c1.ptr.p_double[0], 1, ae_v_len(0,nc-1));
            f1 = (double)(0);
            for(ii=0; ii<=m-1; ii++)
            {
                ae_v_move(&x.ptr.p_double[0], 1, &xx.ptr.pp_double[ii][0], 1, ae_v_len(0,nx-1));
                testlsfitunit_testfunc1(nx, &x, &c2, &v, ae_true, &state.g, ae_false, _state);
                f1 = f1+ae_sqr(v-y.ptr.p_double[ii], _state);
            }
            ae_set_error_flag(errorflag, ae_fp_less(f1,f0), __FILE__, __LINE__, "testlsfitunit.ap:3295");
        }
    }
    
    /*
     * Fit
     *
     *               
     *    f(X|C)= c_0
     *               
     *
     * subject to single general linear equality constraint on c_0.
     *
     * Here X has dimensionality NX, and C has dimensionality 1.
     *
     * We do not test convergence to solution, only feasibility of constraint.
     * The aim of this test is to assert that optimizer correctly handles
     * situations when dimensions of X and C differ.
     */
    epsx = 1.0E-9;
    for(optkind=0; optkind<=1; optkind++)
    {
        for(nx=1; nx<=20; nx++)
        {
            nc = 1;
            m = nx+hqrnduniformi(&rs, nx, _state);
            ae_vector_set_length(&c0, nc, _state);
            for(i=0; i<=nc-1; i++)
            {
                c0.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_matrix_set_length(&xx, m, nx, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xx.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
                y.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_assert(nc==1, "Assertion failed", _state);
            rawccnt = 1;
            ae_matrix_set_length(&rawc, 1, 2, _state);
            ae_vector_set_length(&rawct, 1, _state);
            rawc.ptr.pp_double[0][0] = (double)(1);
            rawc.ptr.pp_double[0][1] = hqrndnormal(&rs, _state);
            rawct.ptr.p_int[0] = 0;
            if( optkind==0 )
            {
                lsfitcreatef(&xx, &y, &c0, m, nx, nc, 10*epsx, &state, _state);
            }
            if( optkind==1 )
            {
                lsfitcreatefg(&xx, &y, &c0, m, nx, nc, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &state, _state);
            }
            lsfitsetcond(&state, epsx, 0, _state);
            lsfitsetlc(&state, &rawc, &rawct, rawccnt, _state);
            while(lsfititeration(&state, _state))
            {
                if( state.needf )
                {
                    testlsfitunit_testfunc2(&state.x, nx, &state.c, nc, &state.f, ae_true, &state.g, ae_false, _state);
                    continue;
                }
                if( state.needfg )
                {
                    testlsfitunit_testfunc2(&state.x, nx, &state.c, nc, &state.f, ae_true, &state.g, ae_true, _state);
                    continue;
                }
                ae_assert(ae_false, "minlm test: integrity check failed", _state);
            }
            lsfitresults(&state, &terminationtype, &c1, &rep, _state);
            ae_set_error_flag(errorflag, terminationtype<=0, __FILE__, __LINE__, "testlsfitunit.ap:3360");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(c1.ptr.p_double[0]-rawc.ptr.pp_double[0][1], _state),1.0E-6), __FILE__, __LINE__, "testlsfitunit.ap:3365");
        }
    }
    
    /*
     * Fit
     *
     *               
     *    f(X|C)= c_0 + c_1*x0 + c_2*x0^2 + ...
     *               
     *
     * subject to single general linear equality constraint on c.
     *
     * Here X has dimensionality 1, and C has dimensionality NC.
     *
     * We do not test convergence to solution, only feasibility of constraint.
     * The aim of this test is to assert that optimizer correctly handles
     * situations when dimensions of X and C differ.
     */
    epsx = 1.0E-9;
    for(optkind=0; optkind<=1; optkind++)
    {
        for(nc=1; nc<=5; nc++)
        {
            nx = 1;
            m = 10+nc+hqrnduniformi(&rs, nc, _state);
            ae_vector_set_length(&c0, nc, _state);
            for(i=0; i<=nc-1; i++)
            {
                c0.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_matrix_set_length(&xx, m, nx, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xx.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                }
                y.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            rawccnt = 1;
            ae_matrix_set_length(&rawc, 1, nc+1, _state);
            ae_vector_set_length(&rawct, 1, _state);
            for(j=0; j<=nc; j++)
            {
                rawc.ptr.pp_double[0][j] = (2*hqrnduniformi(&rs, 2, _state)-1)*(0.1+hqrnduniformr(&rs, _state));
            }
            rawct.ptr.p_int[0] = 0;
            if( optkind==0 )
            {
                lsfitcreatef(&xx, &y, &c0, m, nx, nc, 10*epsx, &state, _state);
            }
            if( optkind==1 )
            {
                lsfitcreatefg(&xx, &y, &c0, m, nx, nc, ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)), &state, _state);
            }
            lsfitsetcond(&state, epsx, 0, _state);
            lsfitsetlc(&state, &rawc, &rawct, rawccnt, _state);
            while(lsfititeration(&state, _state))
            {
                if( state.needf )
                {
                    testlsfitunit_testfunc3(&state.x, nx, &state.c, nc, &state.f, ae_true, &state.g, ae_false, _state);
                    continue;
                }
                if( state.needfg )
                {
                    testlsfitunit_testfunc3(&state.x, nx, &state.c, nc, &state.f, ae_true, &state.g, ae_true, _state);
                    continue;
                }
                ae_assert(ae_false, "minlm test: integrity check failed", _state);
            }
            lsfitresults(&state, &terminationtype, &c1, &rep, _state);
            ae_set_error_flag(errorflag, terminationtype<=0, __FILE__, __LINE__, "testlsfitunit.ap:3428");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            v = -rawc.ptr.pp_double[0][nc];
            for(j=0; j<=nc-1; j++)
            {
                v = v+rawc.ptr.pp_double[0][j]*c1.ptr.p_double[j];
            }
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(v, _state),1.0E-6), __FILE__, __LINE__, "testlsfitunit.ap:3436");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests whether C is solution of (possibly) constrained LLS problem
*************************************************************************/
static ae_bool testlsfitunit_isglssolution(ae_int_t n,
     ae_int_t m,
     ae_int_t k,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* fmatrix,
     /* Real    */ ae_matrix* cmatrix,
     /* Real    */ ae_vector* c,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector _c;
    ae_int_t i;
    ae_int_t j;
    ae_vector c2;
    ae_vector sv;
    ae_vector deltac;
    ae_vector deltaproj;
    ae_matrix u;
    ae_matrix vt;
    double v;
    double s1;
    double s2;
    double s3;
    double delta;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&_c, 0, sizeof(_c));
    memset(&c2, 0, sizeof(c2));
    memset(&sv, 0, sizeof(sv));
    memset(&deltac, 0, sizeof(deltac));
    memset(&deltaproj, 0, sizeof(deltaproj));
    memset(&u, 0, sizeof(u));
    memset(&vt, 0, sizeof(vt));
    ae_vector_init_copy(&_c, c, _state, ae_true);
    c = &_c;
    ae_vector_init(&c2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&deltac, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&deltaproj, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Setup.
     * Threshold is small because CMatrix may be ill-conditioned
     */
    delta = 0.001;
    threshold = ae_sqrt(ae_machineepsilon, _state);
    ae_vector_set_length(&c2, m, _state);
    ae_vector_set_length(&deltac, m, _state);
    ae_vector_set_length(&deltaproj, m, _state);
    
    /*
     * test whether C is feasible point or not (projC must be close to C)
     */
    for(i=0; i<=k-1; i++)
    {
        v = ae_v_dotproduct(&cmatrix->ptr.pp_double[i][0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        if( ae_fp_greater(ae_fabs(v-cmatrix->ptr.pp_double[i][m], _state),threshold) )
        {
            result = ae_false;
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * find orthogonal basis of Null(CMatrix) (stored in rows from K to M-1)
     */
    if( k>0 )
    {
        rmatrixsvd(cmatrix, k, m, 0, 2, 2, &sv, &u, &vt, _state);
    }
    
    /*
     * Test result
     */
    result = ae_true;
    s1 = testlsfitunit_getglserror(n, m, y, w, fmatrix, c, _state);
    for(j=0; j<=m-1; j++)
    {
        
        /*
         * prepare modification of C which leave us in the feasible set.
         *
         * let deltaC be increment on Jth coordinate, then project
         * deltaC in the Null(CMatrix) and store result in DeltaProj
         */
        ae_v_move(&c2.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        for(i=0; i<=m-1; i++)
        {
            if( i==j )
            {
                deltac.ptr.p_double[i] = delta;
            }
            else
            {
                deltac.ptr.p_double[i] = (double)(0);
            }
        }
        if( k==0 )
        {
            ae_v_move(&deltaproj.ptr.p_double[0], 1, &deltac.ptr.p_double[0], 1, ae_v_len(0,m-1));
        }
        else
        {
            for(i=0; i<=m-1; i++)
            {
                deltaproj.ptr.p_double[i] = (double)(0);
            }
            for(i=k; i<=m-1; i++)
            {
                v = ae_v_dotproduct(&vt.ptr.pp_double[i][0], 1, &deltac.ptr.p_double[0], 1, ae_v_len(0,m-1));
                ae_v_addd(&deltaproj.ptr.p_double[0], 1, &vt.ptr.pp_double[i][0], 1, ae_v_len(0,m-1), v);
            }
        }
        
        /*
         * now we have DeltaProj such that if C is feasible,
         * then C+DeltaProj is feasible too
         */
        ae_v_move(&c2.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        ae_v_add(&c2.ptr.p_double[0], 1, &deltaproj.ptr.p_double[0], 1, ae_v_len(0,m-1));
        s2 = testlsfitunit_getglserror(n, m, y, w, fmatrix, &c2, _state);
        ae_v_move(&c2.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        ae_v_sub(&c2.ptr.p_double[0], 1, &deltaproj.ptr.p_double[0], 1, ae_v_len(0,m-1));
        s3 = testlsfitunit_getglserror(n, m, y, w, fmatrix, &c2, _state);
        result = (result&&ae_fp_greater_eq(s2,s1/(1+threshold)))&&ae_fp_greater_eq(s3,s1/(1+threshold));
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Tests whether C is solution of LLS problem
*************************************************************************/
static double testlsfitunit_getglserror(ae_int_t n,
     ae_int_t m,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* fmatrix,
     /* Real    */ ae_vector* c,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    double result;


    result = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        v = ae_v_dotproduct(&fmatrix->ptr.pp_double[i][0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        result = result+ae_sqr(w->ptr.p_double[i]*(v-y->ptr.p_double[i]), _state);
    }
    return result;
}


/*************************************************************************
Subroutine for nonlinear fitting of linear problem

DerAvailable:
* 0     when only function value should be used
* 1     when we can provide gradient/function
* 2     when we can provide Hessian/gradient/function

When something which is not permitted by DerAvailable is requested,
this function sets NLSErrors to True.
*************************************************************************/
static void testlsfitunit_fitlinearnonlinear(ae_int_t m,
     ae_int_t deravailable,
     /* Real    */ ae_matrix* xy,
     lsfitstate* state,
     ae_bool* nlserrors,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;


    while(lsfititeration(state, _state))
    {
        
        /*
         * assume that one and only one of flags is set
         * test that we didn't request hessian in hessian-free setting
         */
        if( deravailable<1&&state->needfg )
        {
            *nlserrors = ae_true;
        }
        if( deravailable<2&&state->needfgh )
        {
            *nlserrors = ae_true;
        }
        i = 0;
        if( state->needf )
        {
            i = i+1;
        }
        if( state->needfg )
        {
            i = i+1;
        }
        if( state->needfgh )
        {
            i = i+1;
        }
        if( i!=1 )
        {
            *nlserrors = ae_true;
        }
        
        /*
         * test that PointIndex is consistent with actual point passed
         */
        for(i=0; i<=m-1; i++)
        {
            *nlserrors = *nlserrors||ae_fp_neq(xy->ptr.pp_double[state->pointindex][i],state->x.ptr.p_double[i]);
        }
        
        /*
         * calculate
         */
        if( state->needf )
        {
            v = ae_v_dotproduct(&state->x.ptr.p_double[0], 1, &state->c.ptr.p_double[0], 1, ae_v_len(0,m-1));
            state->f = v;
            continue;
        }
        if( state->needfg )
        {
            v = ae_v_dotproduct(&state->x.ptr.p_double[0], 1, &state->c.ptr.p_double[0], 1, ae_v_len(0,m-1));
            state->f = v;
            ae_v_move(&state->g.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,m-1));
            continue;
        }
        if( state->needfgh )
        {
            v = ae_v_dotproduct(&state->x.ptr.p_double[0], 1, &state->c.ptr.p_double[0], 1, ae_v_len(0,m-1));
            state->f = v;
            ae_v_move(&state->g.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,m-1));
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    state->h.ptr.pp_double[i][j] = (double)(0);
                }
            }
            continue;
        }
    }
}


/*************************************************************************
This function tests, that gradient verified correctly.
*************************************************************************/
static void testlsfitunit_testgradientcheck(ae_bool* testg,
     ae_state *_state)
{
    ae_frame _frame_block;
    lsfitstate state;
    lsfitreport rep;
    ae_int_t n;
    ae_int_t m;
    ae_int_t k;
    ae_vector c;
    ae_vector cres;
    ae_matrix x;
    ae_vector y;
    ae_vector x0;
    ae_int_t info;
    ae_vector bl;
    ae_vector bu;
    ae_int_t infcomp;
    double teststep;
    double noise;
    ae_int_t nbrcomp;
    double spp;
    ae_int_t func;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_int_t j;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&c, 0, sizeof(c));
    memset(&cres, 0, sizeof(cres));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&x0, 0, sizeof(x0));
    memset(&bl, 0, sizeof(bl));
    memset(&bu, 0, sizeof(bu));
    *testg = ae_false;
    _lsfitstate_init(&state, _state, ae_true);
    _lsfitreport_init(&rep, _state, ae_true);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cres, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);

    passcount = 35;
    spp = 1.0;
    teststep = 0.01;
    for(pass=1; pass<=passcount; pass++)
    {
        m = ae_randominteger(5, _state)+1;
        ae_vector_set_length(&x0, m, _state);
        k = ae_randominteger(5, _state)+1;
        ae_vector_set_length(&c, k, _state);
        ae_vector_set_length(&bl, k, _state);
        ae_vector_set_length(&bu, k, _state);
        
        /*
         * Prepare test's parameters
         */
        func = ae_randominteger(3, _state)+1;
        n = ae_randominteger(8, _state)+3;
        ae_matrix_set_length(&x, n, m, _state);
        ae_vector_set_length(&y, n, _state);
        nbrcomp = ae_randominteger(k, _state);
        noise = (double)(2*ae_randominteger(2, _state)-1);
        
        /*
         * Prepare function's parameters
         */
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                x.ptr.pp_double[i][j] = spp*(2*ae_randomreal(_state)-1);
            }
            y.ptr.p_double[i] = spp*(2*ae_randomreal(_state)-1);
        }
        for(i=0; i<=k-1; i++)
        {
            c.ptr.p_double[i] = spp*(2*ae_randomreal(_state)-1);
        }
        for(i=0; i<=m-1; i++)
        {
            x0.ptr.p_double[i] = 10*(2*ae_randomreal(_state)-1);
        }
        
        /*
         * Prepare boundary parameters
         */
        for(i=0; i<=k-1; i++)
        {
            bl.ptr.p_double[i] = ae_randomreal(_state)-spp;
            bu.ptr.p_double[i] = ae_randomreal(_state)+spp-1;
        }
        infcomp = ae_randominteger(k+1, _state);
        if( infcomp<k )
        {
            bl.ptr.p_double[infcomp] = _state->v_neginf;
        }
        infcomp = ae_randominteger(k+1, _state);
        if( infcomp<k )
        {
            bu.ptr.p_double[infcomp] = _state->v_posinf;
        }
        lsfitcreatefg(&x, &y, &c, n, m, k, ae_true, &state, _state);
        lsfitsetgradientcheck(&state, teststep, _state);
        lsfitsetcond(&state, 0.0, 100, _state);
        lsfitsetbc(&state, &bl, &bu, _state);
        
        /*
         * Check that the criterion passes a derivative if it is correct
         */
        while(lsfititeration(&state, _state))
        {
            if( state.needfg )
            {
                testlsfitunit_funcderiv(&state.c, &state.x, &x0, k, m, func, &state.f, &state.g, _state);
            }
        }
        lsfitresults(&state, &info, &cres, &rep, _state);
        
        /*
         * Check that error code does not equal to -7 and parameter .VarIdx
         * equal to -1.
         */
        if( info==-7||rep.varidx!=-1 )
        {
            *testg = ae_true;
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Create again and...
         */
        lsfitcreatefg(&x, &y, &c, n, m, k, ae_true, &state, _state);
        lsfitsetgradientcheck(&state, teststep, _state);
        lsfitsetcond(&state, 0.0, 100, _state);
        lsfitsetbc(&state, &bl, &bu, _state);
        
        /*
         * Check that the criterion does not miss a derivative if
         * it is incorrect
         */
        while(lsfititeration(&state, _state))
        {
            if( state.needfg )
            {
                testlsfitunit_funcderiv(&state.c, &state.x, &x0, k, m, func, &state.f, &state.g, _state);
                state.g.ptr.p_double[nbrcomp] = state.g.ptr.p_double[nbrcomp]+noise;
            }
        }
        lsfitresults(&state, &info, &cres, &rep, _state);
        
        /*
         * Check that error code equal to -7 and parameter .VarIdx
         * equal to number of incorrect component.
         */
        if( info!=-7||rep.varidx!=nbrcomp )
        {
            *testg = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    *testg = ae_false;
    ae_frame_leave(_state);
}


/*************************************************************************
This function return function's value(F=F(X,C)) and it derivatives(DF=dF/dC).
Function dimension is M. Length(C) is K.
    Function's list:
        * funcType=1:
            K>M:
            F(X)=C0^2*(X0-CX0)^2+C1^2*(X1-CX1)^2+...+CM^2*(XM-CXM)^2
                +C(M+1)^2+...+CK^2;
            K<M:
            F(X)=C0^2*(X0-CX0)^2+C1^2*(X1-CX1)^2+...+CK^2*(XK-CXK)^2
                +(X(K+1)-CX(K+1))^2+...+(XM-CXM)^2;
        * funcType=2:
            K>M:
            F(X)=C0*sin(X0-CX0)^2+C1*sin(X1-CX1)^2+...+CM*sin(XM-CXM)^2
                +C(M+1)^3+...+CK^3;
            K<M
            F(X)=C0*sin(X0-CX0)^2+C1*sin(X1-CX1)^2+...+CK*sin(XK-CXK)^2
                +sin(X(K+1)-CX(K+1))^2+...+sin(XM-CXM)^2;
        * funcType=3:
            F(X)=C0^2+C1^2+...+CK^2+(X0-CX0)^2+(X1-CX1)^2+...+(XM-CXM)^2.
*************************************************************************/
static void testlsfitunit_funcderiv(/* Real    */ ae_vector* c,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* x0,
     ae_int_t k,
     ae_int_t m,
     ae_int_t functype,
     double* f,
     /* Real    */ ae_vector* g,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(functype>=1&&functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).", _state);
    ae_assert(k>0, "FuncDeriv: K<=0", _state);
    ae_assert(m>0, "FuncDeriv: M<=0", _state);
    ae_assert(x->cnt>=m, "FuncDeriv: Length(X)<M", _state);
    ae_assert(isfinitevector(x, m, _state), "FuncDeriv: X contains NaN or Infinite.", _state);
    ae_assert(x0->cnt>=m, "FuncDeriv: Length(X0)<M", _state);
    ae_assert(isfinitevector(x0, m, _state), "FuncDeriv: X0 contains NaN or Infinite.", _state);
    ae_assert(c->cnt>=k, "FuncDeriv: Length(X)<K", _state);
    ae_assert(isfinitevector(c, k, _state), "FuncDeriv: C contains NaN or Infinite.", _state);
    if( functype==1 )
    {
        *f = (double)(0);
        for(i=0; i<=ae_minint(m, k, _state)-1; i++)
        {
            *f = *f+ae_sqr(c->ptr.p_double[i]*(x->ptr.p_double[i]-x0->ptr.p_double[i]), _state);
            g->ptr.p_double[i] = 2*c->ptr.p_double[i]*ae_sqr(x->ptr.p_double[i]-x0->ptr.p_double[i], _state);
        }
        if( k>m )
        {
            for(i=m; i<=k-1; i++)
            {
                *f = *f+ae_sqr(c->ptr.p_double[i], _state);
                g->ptr.p_double[i] = 2*c->ptr.p_double[i];
            }
        }
        if( k<m )
        {
            for(i=k; i<=m-1; i++)
            {
                *f = *f+ae_sqr(x->ptr.p_double[i]-x0->ptr.p_double[i], _state);
            }
        }
        return;
    }
    if( functype==2 )
    {
        *f = (double)(0);
        for(i=0; i<=ae_minint(m, k, _state)-1; i++)
        {
            *f = *f+c->ptr.p_double[i]*ae_sqr(ae_sin(x->ptr.p_double[i]-x0->ptr.p_double[i], _state), _state);
            g->ptr.p_double[i] = ae_sqr(ae_sin(x->ptr.p_double[i]-x0->ptr.p_double[i], _state), _state);
        }
        if( k>m )
        {
            for(i=m; i<=k-1; i++)
            {
                *f = *f+c->ptr.p_double[i]*c->ptr.p_double[i]*c->ptr.p_double[i];
                g->ptr.p_double[i] = 3*ae_sqr(c->ptr.p_double[i], _state);
            }
        }
        if( k<m )
        {
            for(i=k; i<=m-1; i++)
            {
                *f = *f+ae_sqr(ae_sin(x->ptr.p_double[i]-x0->ptr.p_double[i], _state), _state);
            }
        }
        return;
    }
    if( functype==3 )
    {
        *f = (double)(0);
        for(i=0; i<=m-1; i++)
        {
            *f = *f+ae_sqr(x->ptr.p_double[i]-x0->ptr.p_double[i], _state);
        }
        for(i=0; i<=k-1; i++)
        {
            *f = *f+c->ptr.p_double[i]*c->ptr.p_double[i];
        }
        for(i=0; i<=k-1; i++)
        {
            g->ptr.p_double[i] = 2*c->ptr.p_double[i];
        }
        return;
    }
}


/*************************************************************************
Test function 1:

    F(K, X, Z) = SUM( (power(z_j,3)+alpha*z_j)*x_ij )
    
here X is a space of points, Z is a space of parameters
*************************************************************************/
static void testlsfitunit_testfunc1(ae_int_t k,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* z,
     double* f,
     ae_bool needf,
     /* Real    */ ae_vector* g,
     ae_bool needg,
     ae_state *_state)
{
    ae_int_t j;
    double v;
    double alpha;


    alpha = 0.01;
    v = (double)(0);
    for(j=0; j<=k-1; j++)
    {
        v = v+(alpha*z->ptr.p_double[j]+ae_pow(z->ptr.p_double[j], (double)(3), _state))*x->ptr.p_double[j];
        if( needg )
        {
            g->ptr.p_double[j] = (alpha+3*ae_pow(z->ptr.p_double[j], (double)(2), _state))*x->ptr.p_double[j];
        }
    }
    if( needf )
    {
        *f = v;
    }
}


/*************************************************************************
Test function 2:

    F(X|C) = c_0
    
here X is a space of points, C is a space of parameters
*************************************************************************/
static void testlsfitunit_testfunc2(/* Real    */ ae_vector* x,
     ae_int_t nx,
     /* Real    */ ae_vector* c,
     ae_int_t nc,
     double* f,
     ae_bool needf,
     /* Real    */ ae_vector* g,
     ae_bool needg,
     ae_state *_state)
{


    ae_assert(nc==1, "TestFunc2: integrity check failure", _state);
    if( needf )
    {
        *f = c->ptr.p_double[0];
    }
    if( needg )
    {
        g->ptr.p_double[0] = (double)(1);
    }
}


/*************************************************************************
Test function 3:

    F(X|C) = c_0 + c_1*x0 + c_2*x0^2 + ...
    
here X is a space of points, C is a space of parameters
*************************************************************************/
static void testlsfitunit_testfunc3(/* Real    */ ae_vector* x,
     ae_int_t nx,
     /* Real    */ ae_vector* c,
     ae_int_t nc,
     double* f,
     ae_bool needf,
     /* Real    */ ae_vector* g,
     ae_bool needg,
     ae_state *_state)
{
    double v;
    ae_int_t i;


    ae_assert(nx==1, "TestFunc3: integrity check failure", _state);
    v = (double)(0);
    for(i=0; i<=nc-1; i++)
    {
        v = v+c->ptr.p_double[i]*ae_pow(x->ptr.p_double[0], (double)(i), _state);
        if( needg )
        {
            g->ptr.p_double[i] = ae_pow(x->ptr.p_double[0], (double)(i), _state);
        }
    }
    if( needf )
    {
        *f = v;
    }
}



static void testspline2dunit_lconst(spline2dinterpolant* c,
     /* Real    */ ae_vector* lx,
     /* Real    */ ae_vector* ly,
     ae_int_t m,
     ae_int_t n,
     double lstep,
     double* lc,
     double* lcx,
     double* lcy,
     double* lcxy,
     ae_state *_state);
static void testspline2dunit_twodnumder(spline2dinterpolant* c,
     double x,
     double y,
     double h,
     double* f,
     double* fx,
     double* fy,
     double* fxy,
     ae_state *_state);
static ae_bool testspline2dunit_testunpack(spline2dinterpolant* c,
     /* Real    */ ae_vector* lx,
     /* Real    */ ae_vector* ly,
     ae_state *_state);
static ae_bool testspline2dunit_testlintrans(spline2dinterpolant* c,
     ae_int_t d,
     double ax,
     double bx,
     double ay,
     double by,
     ae_state *_state);
static void testspline2dunit_unsetspline2d(spline2dinterpolant* c,
     ae_state *_state);
static ae_bool testspline2dunit_testspline2dvf(ae_bool silent,
     ae_state *_state);
static void testspline2dunit_testfittingprior(ae_bool* errorflag,
     ae_state *_state);
static void testspline2dunit_testfittingpenalty(ae_bool* errorflag,
     ae_state *_state);
static void testspline2dunit_testfittingblocksolver(ae_bool* errorflag,
     ae_state *_state);
static void testspline2dunit_testfittingfastddmsolver(ae_bool* errorflag,
     ae_state *_state);





ae_bool testspline2d(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool blerrors;
    ae_bool bcerrors;
    ae_bool dserrors;
    ae_bool cperrors;
    ae_bool sererrors;
    ae_bool uperrors;
    ae_bool lterrors;
    ae_bool syerrors;
    ae_bool rlerrors;
    ae_bool rcerrors;
    ae_bool vferrors;
    ae_bool fiterrorsfastddmsolver;
    ae_bool fiterrorsblocksolver;
    ae_bool fiterrorspenalty;
    ae_bool fiterrorsprior;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t jobtype;
    double lstep;
    double h;
    ae_vector x;
    ae_vector y;
    spline2dinterpolant c;
    spline2dinterpolant c2;
    ae_vector lx;
    ae_vector ly;
    ae_vector fv1;
    ae_vector fvd;
    ae_matrix f;
    ae_matrix fr;
    ae_matrix ft;
    double ax;
    double ay;
    double bx;
    double by;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t d;
    ae_int_t n2;
    ae_int_t m2;
    double err;
    double t;
    double t1;
    double t2;
    double l1;
    double l1x;
    double l1y;
    double l1xy;
    double l2;
    double l2x;
    double l2y;
    double l2xy;
    double fm;
    double f1;
    double f2;
    double f3;
    double f4;
    double v1;
    double v1x;
    double v1y;
    double v1xy;
    double v2;
    double v2x;
    double v2y;
    double v2xy;
    double mf;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&c, 0, sizeof(c));
    memset(&c2, 0, sizeof(c2));
    memset(&lx, 0, sizeof(lx));
    memset(&ly, 0, sizeof(ly));
    memset(&fv1, 0, sizeof(fv1));
    memset(&fvd, 0, sizeof(fvd));
    memset(&f, 0, sizeof(f));
    memset(&fr, 0, sizeof(fr));
    memset(&ft, 0, sizeof(ft));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _spline2dinterpolant_init(&c, _state, ae_true);
    _spline2dinterpolant_init(&c2, _state, ae_true);
    ae_vector_init(&lx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ly, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&fv1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&fvd, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&f, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fr, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ft, 0, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    passcount = 10;
    h = 0.00001;
    lstep = 0.001;
    blerrors = ae_false;
    bcerrors = ae_false;
    dserrors = ae_false;
    cperrors = ae_false;
    sererrors = ae_false;
    uperrors = ae_false;
    lterrors = ae_false;
    syerrors = ae_false;
    rlerrors = ae_false;
    rcerrors = ae_false;
    vferrors = ae_false;
    fiterrorsfastddmsolver = ae_false;
    fiterrorsblocksolver = ae_false;
    fiterrorsprior = ae_false;
    fiterrorspenalty = ae_false;
    
    /*
     * Fitting functions
     */
    testspline2dunit_testfittingfastddmsolver(&fiterrorsfastddmsolver, _state);
    testspline2dunit_testfittingprior(&fiterrorsprior, _state);
    testspline2dunit_testfittingblocksolver(&fiterrorsblocksolver, _state);
    testspline2dunit_testfittingpenalty(&fiterrorspenalty, _state);
    
    /*
     * Test: bilinear, bicubic
     */
    for(n=2; n<=7; n++)
    {
        for(m=2; m<=7; m++)
        {
            d = ae_randominteger(2, _state)+2;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, m, _state);
            ae_vector_set_length(&lx, 2*n-1, _state);
            ae_vector_set_length(&ly, 2*m-1, _state);
            ae_matrix_set_length(&f, m, n, _state);
            ae_vector_set_length(&fv1, m*n, _state);
            ae_vector_set_length(&fvd, m*n*d, _state);
            ae_matrix_set_length(&ft, n, m, _state);
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Prepare task:
                 * * X and Y stores grid
                 * * F stores function values
                 * * LX and LY stores twice dense grid (for Lipschitz testing)
                 */
                ax = -1-ae_randomreal(_state);
                bx = 1+ae_randomreal(_state);
                ay = -1-ae_randomreal(_state);
                by = 1+ae_randomreal(_state);
                for(j=0; j<=n-1; j++)
                {
                    x.ptr.p_double[j] = 0.5*(bx+ax)-0.5*(bx-ax)*ae_cos(ae_pi*(2*j+1)/(2*n), _state);
                    if( j==0 )
                    {
                        x.ptr.p_double[j] = ax;
                    }
                    if( j==n-1 )
                    {
                        x.ptr.p_double[j] = bx;
                    }
                    lx.ptr.p_double[2*j] = x.ptr.p_double[j];
                    if( j>0 )
                    {
                        lx.ptr.p_double[2*j-1] = 0.5*(x.ptr.p_double[j]+x.ptr.p_double[j-1]);
                    }
                }
                for(j=0; j<=n-1; j++)
                {
                    k = ae_randominteger(n, _state);
                    if( k!=j )
                    {
                        t = x.ptr.p_double[j];
                        x.ptr.p_double[j] = x.ptr.p_double[k];
                        x.ptr.p_double[k] = t;
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    y.ptr.p_double[i] = 0.5*(by+ay)-0.5*(by-ay)*ae_cos(ae_pi*(2*i+1)/(2*m), _state);
                    if( i==0 )
                    {
                        y.ptr.p_double[i] = ay;
                    }
                    if( i==m-1 )
                    {
                        y.ptr.p_double[i] = by;
                    }
                    ly.ptr.p_double[2*i] = y.ptr.p_double[i];
                    if( i>0 )
                    {
                        ly.ptr.p_double[2*i-1] = 0.5*(y.ptr.p_double[i]+y.ptr.p_double[i-1]);
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    k = ae_randominteger(m, _state);
                    if( k!=i )
                    {
                        t = y.ptr.p_double[i];
                        y.ptr.p_double[i] = y.ptr.p_double[k];
                        y.ptr.p_double[k] = t;
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        f.ptr.pp_double[i][j] = ae_exp(0.6*x.ptr.p_double[j], _state)-ae_exp(-0.3*y.ptr.p_double[i]+0.08*x.ptr.p_double[j], _state)+2*ae_cos(ae_pi*(x.ptr.p_double[j]+1.2*y.ptr.p_double[i]), _state)+0.1*ae_cos(20*x.ptr.p_double[j]+15*y.ptr.p_double[i], _state);
                        fv1.ptr.p_double[i*n+j] = f.ptr.pp_double[i][j];
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        for(k=0; k<=d-1; k++)
                        {
                            fvd.ptr.p_double[d*(n*j+i)+k] = ae_exp(0.6*x.ptr.p_double[i], _state)-ae_exp(-0.3*y.ptr.p_double[j]+0.08*x.ptr.p_double[i], _state)+2*ae_cos(ae_pi*(x.ptr.p_double[i]+1.2*y.ptr.p_double[j]+k), _state)+0.1*ae_cos(20*x.ptr.p_double[i]+15*y.ptr.p_double[j]+k, _state);
                        }
                    }
                }
                
                /*
                 * Test scalar bilinear interpolation:
                 * * interpolation at the nodes
                 * * linearity
                 * * continuity
                 * * differentiation in the inner points
                 */
                spline2dbuildbilinearv(&x, n, &y, m, &fv1, 1, &c, _state);
                err = (double)(0);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        err = ae_maxreal(err, ae_fabs(f.ptr.pp_double[i][j]-spline2dcalc(&c, x.ptr.p_double[j], y.ptr.p_double[i], _state), _state), _state);
                    }
                }
                blerrors = blerrors||ae_fp_greater(err,10000*ae_machineepsilon);
                err = (double)(0);
                for(i=0; i<=m-2; i++)
                {
                    for(j=0; j<=n-2; j++)
                    {
                        
                        /*
                         * Test for linearity between grid points
                         * (test point - geometric center of the cell)
                         */
                        fm = spline2dcalc(&c, lx.ptr.p_double[2*j+1], ly.ptr.p_double[2*i+1], _state);
                        f1 = spline2dcalc(&c, lx.ptr.p_double[2*j], ly.ptr.p_double[2*i], _state);
                        f2 = spline2dcalc(&c, lx.ptr.p_double[2*j+2], ly.ptr.p_double[2*i], _state);
                        f3 = spline2dcalc(&c, lx.ptr.p_double[2*j+2], ly.ptr.p_double[2*i+2], _state);
                        f4 = spline2dcalc(&c, lx.ptr.p_double[2*j], ly.ptr.p_double[2*i+2], _state);
                        err = ae_maxreal(err, ae_fabs(0.25*(f1+f2+f3+f4)-fm, _state), _state);
                    }
                }
                blerrors = blerrors||ae_fp_greater(err,10000*ae_machineepsilon);
                testspline2dunit_lconst(&c, &lx, &ly, m, n, lstep, &l1, &l1x, &l1y, &l1xy, _state);
                testspline2dunit_lconst(&c, &lx, &ly, m, n, lstep/3, &l2, &l2x, &l2y, &l2xy, _state);
                blerrors = blerrors||ae_fp_greater(l2/l1,1.2);
                err = (double)(0);
                for(i=0; i<=m-2; i++)
                {
                    for(j=0; j<=n-2; j++)
                    {
                        spline2ddiff(&c, lx.ptr.p_double[2*j+1], ly.ptr.p_double[2*i+1], &v1, &v1x, &v1y, &v1xy, _state);
                        testspline2dunit_twodnumder(&c, lx.ptr.p_double[2*j+1], ly.ptr.p_double[2*i+1], h, &v2, &v2x, &v2y, &v2xy, _state);
                        err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1x-v2x, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1y-v2y, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1xy-v2xy, _state), _state);
                    }
                }
                dserrors = dserrors||ae_fp_greater(err,1.0E-3);
                uperrors = uperrors||!testspline2dunit_testunpack(&c, &lx, &ly, _state);
                lterrors = lterrors||!testspline2dunit_testlintrans(&c, 1, ax, bx, ay, by, _state);
                
                /*
                 * Lin.Trans. test for multicomponent vector function
                 */
                spline2dbuildbilinearv(&x, n, &y, m, &fvd, d, &c, _state);
                lterrors = lterrors||!testspline2dunit_testlintrans(&c, d, ax, bx, ay, by, _state);
                
                /*
                 * Test bicubic interpolation.
                 * * interpolation at the nodes
                 * * smoothness
                 * * differentiation
                 */
                spline2dbuildbicubicv(&x, n, &y, m, &fv1, 1, &c, _state);
                err = (double)(0);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        err = ae_maxreal(err, ae_fabs(f.ptr.pp_double[i][j]-spline2dcalc(&c, x.ptr.p_double[j], y.ptr.p_double[i], _state), _state), _state);
                    }
                }
                bcerrors = bcerrors||ae_fp_greater(err,10000*ae_machineepsilon);
                testspline2dunit_lconst(&c, &lx, &ly, m, n, lstep, &l1, &l1x, &l1y, &l1xy, _state);
                testspline2dunit_lconst(&c, &lx, &ly, m, n, lstep/3, &l2, &l2x, &l2y, &l2xy, _state);
                bcerrors = bcerrors||ae_fp_greater(l2/l1,1.2);
                bcerrors = bcerrors||ae_fp_greater(l2x/l1x,1.2);
                bcerrors = bcerrors||ae_fp_greater(l2y/l1y,1.2);
                if( ae_fp_greater(l2xy,0.01)&&ae_fp_greater(l1xy,0.01) )
                {
                    
                    /*
                     * Cross-derivative continuity is tested only when
                     * bigger than 0.01. When the task size is too
                     * small, the d2F/dXdY is nearly zero and Lipschitz
                     * constant ratio is ill-conditioned.
                     */
                    bcerrors = bcerrors||ae_fp_greater(l2xy/l1xy,1.2);
                }
                err = (double)(0);
                for(i=0; i<=2*m-2; i++)
                {
                    for(j=0; j<=2*n-2; j++)
                    {
                        spline2ddiff(&c, lx.ptr.p_double[j], ly.ptr.p_double[i], &v1, &v1x, &v1y, &v1xy, _state);
                        testspline2dunit_twodnumder(&c, lx.ptr.p_double[j], ly.ptr.p_double[i], h, &v2, &v2x, &v2y, &v2xy, _state);
                        err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1x-v2x, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1y-v2y, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1xy-v2xy, _state), _state);
                    }
                }
                dserrors = dserrors||ae_fp_greater(err,1.0E-3);
                uperrors = uperrors||!testspline2dunit_testunpack(&c, &lx, &ly, _state);
                lterrors = lterrors||!testspline2dunit_testlintrans(&c, 1, ax, bx, ay, by, _state);
                
                /*
                 * Lin.Trans. test for vector-function
                 */
                spline2dbuildbicubicv(&x, n, &y, m, &fvd, d, &c, _state);
                lterrors = lterrors||!testspline2dunit_testlintrans(&c, d, ax, bx, ay, by, _state);
                
                /*
                 * Copy test
                 */
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    spline2dbuildbicubic(&x, &y, &f, m, n, &c, _state);
                }
                else
                {
                    spline2dbuildbilinear(&x, &y, &f, m, n, &c, _state);
                }
                testspline2dunit_unsetspline2d(&c2, _state);
                spline2dcopy(&c, &c2, _state);
                err = (double)(0);
                for(i=1; i<=5; i++)
                {
                    t1 = ax+(bx-ax)*ae_randomreal(_state);
                    t2 = ay+(by-ay)*ae_randomreal(_state);
                    err = ae_maxreal(err, ae_fabs(spline2dcalc(&c, t1, t2, _state)-spline2dcalc(&c2, t1, t2, _state), _state), _state);
                }
                cperrors = cperrors||ae_fp_greater(err,10000*ae_machineepsilon);
                
                /*
                 * Serialization test
                 */
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    spline2dbuildbicubic(&x, &y, &f, m, n, &c, _state);
                }
                else
                {
                    spline2dbuildbilinear(&x, &y, &f, m, n, &c, _state);
                }
                testspline2dunit_unsetspline2d(&c2, _state);
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    spline2dalloc(&_local_serializer, &c, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                    ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    spline2dserialize(&_local_serializer, &c, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    spline2dunserialize(&_local_serializer, &c2, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
                err = (double)(0);
                for(i=1; i<=5; i++)
                {
                    t1 = ax+(bx-ax)*ae_randomreal(_state);
                    t2 = ay+(by-ay)*ae_randomreal(_state);
                    err = ae_maxreal(err, ae_fabs(spline2dcalc(&c, t1, t2, _state)-spline2dcalc(&c2, t1, t2, _state), _state), _state);
                }
                ae_set_error_flag(&sererrors, ae_fp_greater(err,10000*ae_machineepsilon), __FILE__, __LINE__, "testspline2dunit.ap:292");
                
                /*
                 * Special symmetry test
                 */
                err = (double)(0);
                for(jobtype=0; jobtype<=1; jobtype++)
                {
                    
                    /*
                     * Prepare
                     */
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ft.ptr.pp_double[j][i] = f.ptr.pp_double[i][j];
                        }
                    }
                    if( jobtype==0 )
                    {
                        spline2dbuildbilinear(&x, &y, &f, m, n, &c, _state);
                        spline2dbuildbilinear(&y, &x, &ft, n, m, &c2, _state);
                    }
                    else
                    {
                        spline2dbuildbicubic(&x, &y, &f, m, n, &c, _state);
                        spline2dbuildbicubic(&y, &x, &ft, n, m, &c2, _state);
                    }
                    
                    /*
                     * Test
                     */
                    for(i=1; i<=10; i++)
                    {
                        t1 = ax+(bx-ax)*ae_randomreal(_state);
                        t2 = ay+(by-ay)*ae_randomreal(_state);
                        err = ae_maxreal(err, ae_fabs(spline2dcalc(&c, t1, t2, _state)-spline2dcalc(&c2, t2, t1, _state), _state), _state);
                    }
                }
                syerrors = syerrors||ae_fp_greater(err,10000*ae_machineepsilon);
            }
        }
    }
    
    /*
     * Test resample
     */
    for(m=2; m<=6; m++)
    {
        for(n=2; n<=6; n++)
        {
            ae_matrix_set_length(&f, m-1+1, n-1+1, _state);
            ae_vector_set_length(&x, n-1+1, _state);
            ae_vector_set_length(&y, m-1+1, _state);
            for(j=0; j<=n-1; j++)
            {
                x.ptr.p_double[j] = (double)j/(double)(n-1);
            }
            for(i=0; i<=m-1; i++)
            {
                y.ptr.p_double[i] = (double)i/(double)(m-1);
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    f.ptr.pp_double[i][j] = ae_exp(0.6*x.ptr.p_double[j], _state)-ae_exp(-0.3*y.ptr.p_double[i]+0.08*x.ptr.p_double[j], _state)+2*ae_cos(ae_pi*(x.ptr.p_double[j]+1.2*y.ptr.p_double[i]), _state)+0.1*ae_cos(20*x.ptr.p_double[j]+15*y.ptr.p_double[i], _state);
                }
            }
            for(m2=2; m2<=6; m2++)
            {
                for(n2=2; n2<=6; n2++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        for(jobtype=0; jobtype<=1; jobtype++)
                        {
                            if( jobtype==0 )
                            {
                                spline2dresamplebilinear(&f, m, n, &fr, m2, n2, _state);
                                spline2dbuildbilinear(&x, &y, &f, m, n, &c, _state);
                            }
                            if( jobtype==1 )
                            {
                                spline2dresamplebicubic(&f, m, n, &fr, m2, n2, _state);
                                spline2dbuildbicubic(&x, &y, &f, m, n, &c, _state);
                            }
                            err = (double)(0);
                            mf = (double)(0);
                            for(i=0; i<=m2-1; i++)
                            {
                                for(j=0; j<=n2-1; j++)
                                {
                                    v1 = spline2dcalc(&c, (double)j/(double)(n2-1), (double)i/(double)(m2-1), _state);
                                    v2 = fr.ptr.pp_double[i][j];
                                    err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                                    mf = ae_maxreal(mf, ae_fabs(v1, _state), _state);
                                }
                            }
                            if( jobtype==0 )
                            {
                                rlerrors = rlerrors||ae_fp_greater(err/mf,10000*ae_machineepsilon);
                            }
                            if( jobtype==1 )
                            {
                                rcerrors = rcerrors||ae_fp_greater(err/mf,10000*ae_machineepsilon);
                            }
                        }
                    }
                }
            }
        }
    }
    
    /*
     * Test for vector-function
     */
    vferrors = testspline2dunit_testspline2dvf(ae_true, _state);
    
    /*
     * Report
     */
    waserrors = (((((((((((((blerrors||bcerrors)||dserrors)||cperrors)||sererrors)||uperrors)||lterrors)||syerrors)||rlerrors)||rcerrors)||vferrors)||fiterrorsprior)||fiterrorspenalty)||fiterrorsblocksolver)||fiterrorsfastddmsolver;
    if( !silent )
    {
        printf("TESTING 2D SPLINE\n");
        
        /*
         * Normal tests
         */
        printf("INTERPOLATION:\n");
        printf("* bilinear                               ");
        if( blerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* bicubic                                ");
        if( bcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* differentiation                        ");
        if( dserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* copying                                ");
        if( cperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* serialization                          ");
        if( sererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* unpacking                              ");
        if( uperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* lin.trans.                             ");
        if( lterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* special symmetry props                 ");
        if( syerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* vector spline                          ");
        if( vferrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("FITTING:\n");
        printf("* linear prior term                      ");
        if( fiterrorsprior )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* nonlinearity penalty term              ");
        if( fiterrorspenalty )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* block solver                           ");
        if( fiterrorsblocksolver )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* fast DDM solver                        ");
        if( fiterrorsfastddmsolver )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RESAMPLING:\n");
        printf("* bilinear                               ");
        if( rlerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* bicubic                                ");
        if( rcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        
        /*
         * Summary
         */
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Lipschitz constants for spline inself, first and second derivatives.
*************************************************************************/
static void testspline2dunit_lconst(spline2dinterpolant* c,
     /* Real    */ ae_vector* lx,
     /* Real    */ ae_vector* ly,
     ae_int_t m,
     ae_int_t n,
     double lstep,
     double* lc,
     double* lcx,
     double* lcy,
     double* lcxy,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double f1;
    double f2;
    double f3;
    double f4;
    double fx1;
    double fx2;
    double fx3;
    double fx4;
    double fy1;
    double fy2;
    double fy3;
    double fy4;
    double fxy1;
    double fxy2;
    double fxy3;
    double fxy4;
    double s2lstep;

    *lc = 0;
    *lcx = 0;
    *lcy = 0;
    *lcxy = 0;

    *lc = (double)(0);
    *lcx = (double)(0);
    *lcy = (double)(0);
    *lcxy = (double)(0);
    s2lstep = ae_sqrt((double)(2), _state)*lstep;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Calculate
             */
            testspline2dunit_twodnumder(c, lx->ptr.p_double[j]-lstep/2, ly->ptr.p_double[i]-lstep/2, lstep/4, &f1, &fx1, &fy1, &fxy1, _state);
            testspline2dunit_twodnumder(c, lx->ptr.p_double[j]+lstep/2, ly->ptr.p_double[i]-lstep/2, lstep/4, &f2, &fx2, &fy2, &fxy2, _state);
            testspline2dunit_twodnumder(c, lx->ptr.p_double[j]+lstep/2, ly->ptr.p_double[i]+lstep/2, lstep/4, &f3, &fx3, &fy3, &fxy3, _state);
            testspline2dunit_twodnumder(c, lx->ptr.p_double[j]-lstep/2, ly->ptr.p_double[i]+lstep/2, lstep/4, &f4, &fx4, &fy4, &fxy4, _state);
            
            /*
             * Lipschitz constant for the function itself
             */
            *lc = ae_maxreal(*lc, ae_fabs((f1-f2)/lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f2-f3)/lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f3-f4)/lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f4-f1)/lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f1-f3)/s2lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f2-f4)/s2lstep, _state), _state);
            
            /*
             * Lipschitz constant for the first derivative
             */
            *lcx = ae_maxreal(*lcx, ae_fabs((fx1-fx2)/lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx2-fx3)/lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx3-fx4)/lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx4-fx1)/lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx1-fx3)/s2lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx2-fx4)/s2lstep, _state), _state);
            
            /*
             * Lipschitz constant for the first derivative
             */
            *lcy = ae_maxreal(*lcy, ae_fabs((fy1-fy2)/lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy2-fy3)/lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy3-fy4)/lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy4-fy1)/lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy1-fy3)/s2lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy2-fy4)/s2lstep, _state), _state);
            
            /*
             * Lipschitz constant for the cross-derivative
             */
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy1-fxy2)/lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy2-fxy3)/lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy3-fxy4)/lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy4-fxy1)/lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy1-fxy3)/s2lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy2-fxy4)/s2lstep, _state), _state);
        }
    }
}


/*************************************************************************
Numerical differentiation.
*************************************************************************/
static void testspline2dunit_twodnumder(spline2dinterpolant* c,
     double x,
     double y,
     double h,
     double* f,
     double* fx,
     double* fy,
     double* fxy,
     ae_state *_state)
{

    *f = 0;
    *fx = 0;
    *fy = 0;
    *fxy = 0;

    *f = spline2dcalc(c, x, y, _state);
    *fx = (spline2dcalc(c, x+h, y, _state)-spline2dcalc(c, x-h, y, _state))/(2*h);
    *fy = (spline2dcalc(c, x, y+h, _state)-spline2dcalc(c, x, y-h, _state))/(2*h);
    *fxy = (spline2dcalc(c, x+h, y+h, _state)-spline2dcalc(c, x-h, y+h, _state)-spline2dcalc(c, x+h, y-h, _state)+spline2dcalc(c, x-h, y-h, _state))/ae_sqr(2*h, _state);
}


/*************************************************************************
Unpack test
*************************************************************************/
static ae_bool testspline2dunit_testunpack(spline2dinterpolant* c,
     /* Real    */ ae_vector* lx,
     /* Real    */ ae_vector* ly,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t m;
    ae_int_t ci;
    ae_int_t cj;
    ae_int_t p;
    double err;
    double tx;
    double ty;
    double v1;
    double v2;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix tbl;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&tbl, 0, sizeof(tbl));
    ae_matrix_init(&tbl, 0, 0, DT_REAL, _state, ae_true);

    passcount = 20;
    err = (double)(0);
    spline2dunpack(c, &m, &n, &tbl, _state);
    for(i=0; i<=m-2; i++)
    {
        for(j=0; j<=n-2; j++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                p = (n-1)*i+j;
                tx = (0.001+0.999*ae_randomreal(_state))*(tbl.ptr.pp_double[p][1]-tbl.ptr.pp_double[p][0]);
                ty = (0.001+0.999*ae_randomreal(_state))*(tbl.ptr.pp_double[p][3]-tbl.ptr.pp_double[p][2]);
                
                /*
                 * Interpolation properties
                 */
                v1 = (double)(0);
                for(ci=0; ci<=3; ci++)
                {
                    for(cj=0; cj<=3; cj++)
                    {
                        v1 = v1+tbl.ptr.pp_double[p][4+ci*4+cj]*ae_pow(tx, (double)(ci), _state)*ae_pow(ty, (double)(cj), _state);
                    }
                }
                v2 = spline2dcalc(c, tbl.ptr.pp_double[p][0]+tx, tbl.ptr.pp_double[p][2]+ty, _state);
                err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                
                /*
                 * Grid correctness
                 */
                err = ae_maxreal(err, ae_fabs(lx->ptr.p_double[2*j]-tbl.ptr.pp_double[p][0], _state), _state);
                err = ae_maxreal(err, ae_fabs(lx->ptr.p_double[2*(j+1)]-tbl.ptr.pp_double[p][1], _state), _state);
                err = ae_maxreal(err, ae_fabs(ly->ptr.p_double[2*i]-tbl.ptr.pp_double[p][2], _state), _state);
                err = ae_maxreal(err, ae_fabs(ly->ptr.p_double[2*(i+1)]-tbl.ptr.pp_double[p][3], _state), _state);
            }
        }
    }
    result = ae_fp_less(err,10000*ae_machineepsilon);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
LinTrans test for scalar
*************************************************************************/
static ae_bool testspline2dunit_testlintrans(spline2dinterpolant* c,
     ae_int_t d,
     double ax,
     double bx,
     double ay,
     double by,
     ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double a1;
    double a2;
    double b1;
    double b2;
    double tx;
    double ty;
    double vx;
    double vy;
    ae_vector v1;
    ae_vector v2;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t xjob;
    ae_int_t yjob;
    spline2dinterpolant c2;
    ae_int_t di;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&v1, 0, sizeof(v1));
    memset(&v2, 0, sizeof(v2));
    memset(&c2, 0, sizeof(c2));
    ae_vector_init(&v1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&v2, 0, DT_REAL, _state, ae_true);
    _spline2dinterpolant_init(&c2, _state, ae_true);

    passcount = 5;
    err = (double)(0);
    for(xjob=0; xjob<=1; xjob++)
    {
        for(yjob=0; yjob<=1; yjob++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Prepare
                 */
                do
                {
                    a1 = 2*ae_randomreal(_state)-1;
                }
                while(ae_fp_eq(a1,(double)(0)));
                a1 = a1*xjob;
                b1 = 2*ae_randomreal(_state)-1;
                do
                {
                    a2 = 2*ae_randomreal(_state)-1;
                }
                while(ae_fp_eq(a2,(double)(0)));
                a2 = a2*yjob;
                b2 = 2*ae_randomreal(_state)-1;
                
                /*
                 * Test XY
                 */
                spline2dcopy(c, &c2, _state);
                spline2dlintransxy(&c2, a1, b1, a2, b2, _state);
                tx = ax+ae_randomreal(_state)*(bx-ax);
                ty = ay+ae_randomreal(_state)*(by-ay);
                if( xjob==0 )
                {
                    tx = b1;
                    vx = ax+ae_randomreal(_state)*(bx-ax);
                }
                else
                {
                    vx = (tx-b1)/a1;
                }
                if( yjob==0 )
                {
                    ty = b2;
                    vy = ay+ae_randomreal(_state)*(by-ay);
                }
                else
                {
                    vy = (ty-b2)/a2;
                }
                spline2dcalcv(c, tx, ty, &v1, _state);
                spline2dcalcv(&c2, vx, vy, &v2, _state);
                for(di=0; di<=d-1; di++)
                {
                    err = ae_maxreal(err, ae_fabs(v1.ptr.p_double[di]-v2.ptr.p_double[di], _state), _state);
                }
                
                /*
                 * Test F
                 */
                spline2dcopy(c, &c2, _state);
                spline2dlintransf(&c2, a1, b1, _state);
                tx = ax+ae_randomreal(_state)*(bx-ax);
                ty = ay+ae_randomreal(_state)*(by-ay);
                spline2dcalcv(c, tx, ty, &v1, _state);
                spline2dcalcv(&c2, tx, ty, &v2, _state);
                for(di=0; di<=d-1; di++)
                {
                    err = ae_maxreal(err, ae_fabs(a1*v1.ptr.p_double[di]+b1-v2.ptr.p_double[di], _state), _state);
                }
            }
        }
    }
    result = ae_fp_less(err,10000*ae_machineepsilon);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unset spline, i.e. initialize it with random garbage
*************************************************************************/
static void testspline2dunit_unsetspline2d(spline2dinterpolant* c,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_vector y;
    ae_matrix f;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&f, 0, sizeof(f));
    _spline2dinterpolant_clear(c);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&f, 0, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&y, 2, _state);
    ae_matrix_set_length(&f, 2, 2, _state);
    x.ptr.p_double[0] = (double)(-1);
    x.ptr.p_double[1] = (double)(1);
    y.ptr.p_double[0] = (double)(-1);
    y.ptr.p_double[1] = (double)(1);
    f.ptr.pp_double[0][0] = (double)(0);
    f.ptr.pp_double[0][1] = (double)(0);
    f.ptr.pp_double[1][0] = (double)(0);
    f.ptr.pp_double[1][1] = (double)(0);
    spline2dbuildbilinear(&x, &y, &f, 2, 2, c, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
The function check, that follow functions works correctly:
    Spline2DBilinearV, Spline2DBicubicV, Spline2DCalcV, Spline2DCalcVI,
    Spline2DDiffVI and Spline2DUnpackV.
*************************************************************************/
static ae_bool testspline2dunit_testspline2dvf(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    spline2dinterpolant vc;
    spline2dinterpolant sc;
    double range;
    ae_vector x;
    ae_vector y;
    ae_vector f;
    double rndx;
    double rndy;
    ae_vector resf;
    ae_matrix ef;
    double resef;
    ae_int_t m;
    ae_int_t n;
    ae_int_t d;
    ae_int_t tstn;
    ae_int_t tstm;
    ae_int_t tstd;
    ae_matrix tsttbl0;
    ae_matrix tsttbl1;
    double eps;
    double st;
    ae_int_t p0;
    ae_int_t p1;
    ae_int_t variant;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t i0;
    ae_int_t j0;
    double xmin;
    double xmax;
    double ymin;
    double ymax;
    double vi;
    double vxi;
    double vyi;
    double vxyi;
    double si;
    double sxi;
    double syi;
    double sxyi;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&vc, 0, sizeof(vc));
    memset(&sc, 0, sizeof(sc));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&f, 0, sizeof(f));
    memset(&resf, 0, sizeof(resf));
    memset(&ef, 0, sizeof(ef));
    memset(&tsttbl0, 0, sizeof(tsttbl0));
    memset(&tsttbl1, 0, sizeof(tsttbl1));
    _spline2dinterpolant_init(&vc, _state, ae_true);
    _spline2dinterpolant_init(&sc, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&f, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&resf, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ef, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&tsttbl0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&tsttbl1, 0, 0, DT_REAL, _state, ae_true);

    eps = 10000.0*ae_machineepsilon;
    st = 0.1;
    passcount = 5;
    result = ae_false;
    for(pass=1; pass<=passcount; pass++)
    {
        for(variant=1; variant<=2; variant++)
        {
            range = (double)(ae_randominteger(71, _state)+30);
            m = ae_randominteger(4, _state)+2;
            n = ae_randominteger(4, _state)+2;
            d = ae_randominteger(3, _state)+1;
            rvectorsetlengthatleast(&x, n, _state);
            rvectorsetlengthatleast(&y, m, _state);
            rvectorsetlengthatleast(&f, n*m*d, _state);
            rmatrixsetlengthatleast(&ef, m, n, _state);
            
            /*
             * Build a grid for spline
             */
            x.ptr.p_double[0] = range*(2*ae_randomreal(_state)-1);
            y.ptr.p_double[0] = range*(2*ae_randomreal(_state)-1);
            for(i=1; i<=n-1; i++)
            {
                x.ptr.p_double[i] = x.ptr.p_double[i-1]+st+ae_randomreal(_state);
            }
            for(i=1; i<=m-1; i++)
            {
                y.ptr.p_double[i] = y.ptr.p_double[i-1]+st+ae_randomreal(_state);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    for(k=0; k<=d-1; k++)
                    {
                        f.ptr.p_double[d*(n*j+i)+k] = range*(2*ae_randomreal(_state)-1);
                    }
                }
            }
            xmin = x.ptr.p_double[0];
            xmax = x.ptr.p_double[n-1];
            ymin = y.ptr.p_double[0];
            ymax = y.ptr.p_double[m-1];
            
            /*
             * Build a spline
             */
            if( variant==1 )
            {
                spline2dbuildbilinearv(&x, n, &y, m, &f, d, &vc, _state);
            }
            if( variant==2 )
            {
                spline2dbuildbicubicv(&x, n, &y, m, &f, d, &vc, _state);
            }
            
            /*
             * Part of test, which shows that Spline2DBuildBilinearV function
             * works correctly.
             * And there is test for Spline2DUnpackV.
             */
            spline2dunpackv(&vc, &tstm, &tstn, &tstd, &tsttbl1, _state);
            if( (tstm!=m||tstn!=n)||tstd!=d )
            {
                if( !silent )
                {
                    printf("TestSpline2DVF fail Spline2DUnpack:\n");
                    printf("    TstM=%0d; M=%0d;\n    TstN=%0d; N=%0d;\n    TstD=%0d; D=%0d.\n",
                        (int)(tstm),
                        (int)(m),
                        (int)(tstn),
                        (int)(n),
                        (int)(tstd),
                        (int)(d));
                }
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(k=0; k<=d-1; k++)
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ef.ptr.pp_double[i][j] = f.ptr.p_double[d*(i*n+j)+k];
                    }
                }
                if( variant==1 )
                {
                    spline2dbuildbilinear(&x, &y, &ef, m, n, &sc, _state);
                }
                if( variant==2 )
                {
                    spline2dbuildbicubic(&x, &y, &ef, m, n, &sc, _state);
                }
                spline2dunpack(&sc, &tstm, &tstn, &tsttbl0, _state);
                if( tstm!=m||tstn!=n )
                {
                    if( !silent )
                    {
                        printf("TestSpline2DVF fail Spline2DUnpack:\n");
                        printf("    TstM=%0d; M=%0d;\n    TstN=%0d; N=%0d.\n",
                            (int)(tstm),
                            (int)(m),
                            (int)(tstn),
                            (int)(n));
                    }
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=m-2; i++)
                {
                    for(j=0; j<=n-2; j++)
                    {
                        p0 = i*(n-1)+j;
                        p1 = d*p0;
                        for(i0=0; i0<=19; i0++)
                        {
                            if( ae_fp_neq(tsttbl1.ptr.pp_double[p1+k][i0],tsttbl0.ptr.pp_double[p0][i0]) )
                            {
                                if( !silent )
                                {
                                    printf("TestSpline2DVF: Tbl error\n");
                                }
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
            }
            
            /*
             * Part of test, which shows that functions Spline2DCalcVBuf, Spline2DDiffVI,
             * Spline2DCalcVI and Spline2DCalcV work correctly
             */
            for(k=0; k<=d-1; k++)
            {
                
                /*
                 * Build 1D spline corresponding to selected dimension (already tested)
                 */
                for(i0=0; i0<=m-1; i0++)
                {
                    for(j0=0; j0<=n-1; j0++)
                    {
                        ef.ptr.pp_double[i0][j0] = f.ptr.p_double[d*(i0*n+j0)+k];
                    }
                }
                if( variant==1 )
                {
                    spline2dbuildbilinear(&x, &y, &ef, m, n, &sc, _state);
                }
                if( variant==2 )
                {
                    spline2dbuildbicubic(&x, &y, &ef, m, n, &sc, _state);
                }
                
                /*
                 * test at grid points
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        
                        /*
                         * Test calculation at grid points
                         */
                        spline2dcalcv(&vc, x.ptr.p_double[i], y.ptr.p_double[j], &resf, _state);
                        resef = spline2dcalc(&sc, x.ptr.p_double[i], y.ptr.p_double[j], _state);
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(resf.ptr.p_double[k]-resef, _state),eps), __FILE__, __LINE__, "testspline2dunit.ap:914");
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(spline2dcalcvi(&vc, x.ptr.p_double[i], y.ptr.p_double[j], k, _state)-resef, _state),eps), __FILE__, __LINE__, "testspline2dunit.ap:915");
                        
                        /*
                         * Test differentiation at grid points
                         */
                        spline2ddiffvi(&vc, x.ptr.p_double[i], y.ptr.p_double[j], k, &vi, &vxi, &vyi, &vxyi, _state);
                        spline2ddiff(&sc, x.ptr.p_double[i], y.ptr.p_double[j], &si, &sxi, &syi, &sxyi, _state);
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vi-si, _state),eps*(ae_fabs(si, _state)+1)), __FILE__, __LINE__, "testspline2dunit.ap:922");
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vxi-sxi, _state),eps*(ae_fabs(sxi, _state)+1)), __FILE__, __LINE__, "testspline2dunit.ap:923");
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vyi-syi, _state),eps*(ae_fabs(syi, _state)+1)), __FILE__, __LINE__, "testspline2dunit.ap:924");
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vxyi-sxyi, _state),eps*(ae_fabs(sxyi, _state)+1)), __FILE__, __LINE__, "testspline2dunit.ap:925");
                    }
                }
                
                /*
                 * Test at random points
                 */
                for(i=0; i<=n*m-1; i++)
                {
                    rndx = xmin+(xmax-xmin)*ae_randomreal(_state);
                    rndy = ymin+(ymax-ymin)*ae_randomreal(_state);
                    
                    /*
                     * Test calculation at random points
                     */
                    spline2dcalcv(&vc, rndx, rndy, &resf, _state);
                    resef = spline2dcalc(&sc, rndx, rndy, _state);
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(resf.ptr.p_double[k]-resef, _state),eps), __FILE__, __LINE__, "testspline2dunit.ap:941");
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(spline2dcalcvi(&vc, rndx, rndy, k, _state)-resef, _state),eps), __FILE__, __LINE__, "testspline2dunit.ap:942");
                    
                    /*
                     * Test differentiation at random points
                     */
                    spline2ddiffvi(&vc, rndx, rndy, k, &vi, &vxi, &vyi, &vxyi, _state);
                    spline2ddiff(&sc, rndx, rndy, &si, &sxi, &syi, &sxyi, _state);
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vi-si, _state),eps*(ae_fabs(si, _state)+1)), __FILE__, __LINE__, "testspline2dunit.ap:949");
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vxi-sxi, _state),eps*(ae_fabs(sxi, _state)+1)), __FILE__, __LINE__, "testspline2dunit.ap:950");
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vyi-syi, _state),eps*(ae_fabs(syi, _state)+1)), __FILE__, __LINE__, "testspline2dunit.ap:951");
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vxyi-sxyi, _state),eps*(ae_fabs(sxyi, _state)+1)), __FILE__, __LINE__, "testspline2dunit.ap:952");
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Fitting, properties of linear prior term
*************************************************************************/
static void testspline2dunit_testfittingprior(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector kv;
    ae_int_t d;
    ae_int_t kidx;
    ae_int_t areatype;
    ae_int_t solvertype;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    hqrndstate rs;
    double tol;
    spline2dbuilder state;
    spline2dinterpolant s;
    spline2dinterpolant s2;
    spline2dinterpolant s3;
    spline2dfitreport rep;
    spline2dfitreport rep2;
    spline2dfitreport rep3;
    ae_int_t kx;
    ae_int_t ky;
    ae_int_t npoints;
    ae_matrix xy;
    ae_matrix xy2;
    ae_matrix xy3;
    ae_matrix vterm;
    double vprior;
    double v;
    ae_bool bad;
    ae_vector meany;
    ae_vector tmp0;
    ae_vector tmp1;

    ae_frame_make(_state, &_frame_block);
    memset(&kv, 0, sizeof(kv));
    memset(&rs, 0, sizeof(rs));
    memset(&state, 0, sizeof(state));
    memset(&s, 0, sizeof(s));
    memset(&s2, 0, sizeof(s2));
    memset(&s3, 0, sizeof(s3));
    memset(&rep, 0, sizeof(rep));
    memset(&rep2, 0, sizeof(rep2));
    memset(&rep3, 0, sizeof(rep3));
    memset(&xy, 0, sizeof(xy));
    memset(&xy2, 0, sizeof(xy2));
    memset(&xy3, 0, sizeof(xy3));
    memset(&vterm, 0, sizeof(vterm));
    memset(&meany, 0, sizeof(meany));
    memset(&tmp0, 0, sizeof(tmp0));
    memset(&tmp1, 0, sizeof(tmp1));
    ae_vector_init(&kv, 0, DT_INT, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);
    _spline2dbuilder_init(&state, _state, ae_true);
    _spline2dinterpolant_init(&s, _state, ae_true);
    _spline2dinterpolant_init(&s2, _state, ae_true);
    _spline2dinterpolant_init(&s3, _state, ae_true);
    _spline2dfitreport_init(&rep, _state, ae_true);
    _spline2dfitreport_init(&rep2, _state, ae_true);
    _spline2dfitreport_init(&rep3, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy3, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vterm, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&meany, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    tol = 1.0E-5;
    
    /*
     * Check properties of underlying linear prior.
     */
    ae_vector_set_length(&kv, 3, _state);
    kv.ptr.p_int[0] = 5;
    kv.ptr.p_int[1] = 10;
    kv.ptr.p_int[2] = 16;
    for(kidx=0; kidx<=kv.cnt-1; kidx++)
    {
        for(d=1; d<=3; d++)
        {
            for(areatype=0; areatype<=0; areatype++)
            {
                for(solvertype=1; solvertype<=2; solvertype++)
                {
                    
                    /*
                     * Select grid size and create builder
                     */
                    kx = 1+hqrnduniformi(&rs, kv.ptr.p_int[kidx], _state);
                    ky = 1+hqrnduniformi(&rs, kv.ptr.p_int[kidx], _state);
                    if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
                    {
                        kx = kv.ptr.p_int[kidx];
                    }
                    else
                    {
                        ky = kv.ptr.p_int[kidx];
                    }
                    spline2dbuildercreate(d, &state, _state);
                    spline2dbuildersetgrid(&state, kx, ky, _state);
                    if( solvertype==1 )
                    {
                        spline2dbuildersetalgoblocklls(&state, 0.0, _state);
                    }
                    else
                    {
                        if( solvertype==2 )
                        {
                            spline2dbuildersetalgonaivells(&state, 0.0, _state);
                        }
                        else
                        {
                            ae_assert(ae_false, "Assertion failed", _state);
                        }
                    }
                    
                    /*
                     * Generate perfectly linear dataset, check against linear prior.
                     * At least 8 points is required to get stable results.
                     *
                     * NOTE: we also check that constant term does NOT work.
                     */
                    ae_matrix_set_length(&vterm, d, 3, _state);
                    for(i=0; i<=d-1; i++)
                    {
                        for(j=0; j<=2; j++)
                        {
                            vterm.ptr.pp_double[i][j] = hqrndnormal(&rs, _state);
                        }
                    }
                    npoints = 8+ae_round(10*hqrnduniformr(&rs, _state), _state);
                    ae_vector_set_length(&meany, d, _state);
                    for(j=0; j<=d-1; j++)
                    {
                        meany.ptr.p_double[j] = (double)(0);
                    }
                    ae_matrix_set_length(&xy, npoints, 2+d, _state);
                    for(i=0; i<=npoints-1; i++)
                    {
                        xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                        xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                        for(j=0; j<=d-1; j++)
                        {
                            xy.ptr.pp_double[i][2+j] = vterm.ptr.pp_double[j][0]*xy.ptr.pp_double[i][0]+vterm.ptr.pp_double[j][1]*xy.ptr.pp_double[i][1]+vterm.ptr.pp_double[j][2];
                            meany.ptr.p_double[j] = meany.ptr.p_double[j]+xy.ptr.pp_double[i][2+j]/npoints;
                        }
                    }
                    spline2dbuildersetpoints(&state, &xy, npoints, _state);
                    spline2dbuildersetlinterm(&state, _state);
                    spline2dfit(&state, &s, &rep, _state);
                    ae_vector_set_length(&tmp0, 3, _state);
                    for(k=0; k<=10; k++)
                    {
                        tmp0.ptr.p_double[0] = 3*hqrnduniformr(&rs, _state)-1;
                        tmp0.ptr.p_double[1] = 3*hqrnduniformr(&rs, _state)-1;
                        tmp0.ptr.p_double[2] = 1.0;
                        spline2dcalcv(&s, tmp0.ptr.p_double[0], tmp0.ptr.p_double[1], &tmp1, _state);
                        for(j=0; j<=d-1; j++)
                        {
                            v = vterm.ptr.pp_double[j][0]*tmp0.ptr.p_double[0]+vterm.ptr.pp_double[j][1]*tmp0.ptr.p_double[1]+vterm.ptr.pp_double[j][2];
                            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(tmp1.ptr.p_double[j]-v, _state)/ae_maxreal(ae_fabs(v, _state), (double)(1), _state),tol), __FILE__, __LINE__, "testspline2dunit.ap:1094");
                        }
                    }
                    ae_set_error_flag(errorflag, ae_fp_greater(rep.rmserror,tol), __FILE__, __LINE__, "testspline2dunit.ap:1097");
                    ae_set_error_flag(errorflag, ae_fp_greater(rep.avgerror,tol), __FILE__, __LINE__, "testspline2dunit.ap:1098");
                    ae_set_error_flag(errorflag, ae_fp_greater(rep.maxerror,tol), __FILE__, __LINE__, "testspline2dunit.ap:1099");
                    spline2dbuildersetconstterm(&state, _state);
                    spline2dfit(&state, &s, &rep, _state);
                    ae_vector_set_length(&tmp0, 3, _state);
                    bad = ae_false;
                    for(k=0; k<=10; k++)
                    {
                        tmp0.ptr.p_double[0] = 2+hqrnduniformr(&rs, _state);
                        tmp0.ptr.p_double[1] = 2+hqrnduniformr(&rs, _state);
                        tmp0.ptr.p_double[2] = 1.0;
                        spline2dcalcv(&s, tmp0.ptr.p_double[0], tmp0.ptr.p_double[1], &tmp1, _state);
                        for(j=0; j<=d-1; j++)
                        {
                            v = vterm.ptr.pp_double[j][0]*tmp0.ptr.p_double[0]+vterm.ptr.pp_double[j][1]*tmp0.ptr.p_double[1]+vterm.ptr.pp_double[j][2];
                            bad = bad||ae_fp_greater(ae_fabs(tmp1.ptr.p_double[j]-v, _state),tol);
                        }
                    }
                    ae_set_error_flag(errorflag, !bad, __FILE__, __LINE__, "testspline2dunit.ap:1118");
                    
                    /*
                     * Generate perfectly constant dataset, check against constant prior.
                     * At least 8 points is required to get stable results.
                     */
                    ae_matrix_set_length(&vterm, d, 3, _state);
                    for(i=0; i<=d-1; i++)
                    {
                        for(j=0; j<=1; j++)
                        {
                            vterm.ptr.pp_double[i][j] = (double)(0);
                        }
                        vterm.ptr.pp_double[i][2] = hqrndnormal(&rs, _state);
                    }
                    npoints = 8+ae_round(10*hqrnduniformr(&rs, _state), _state);
                    ae_vector_set_length(&meany, d, _state);
                    for(j=0; j<=d-1; j++)
                    {
                        meany.ptr.p_double[j] = (double)(0);
                    }
                    ae_matrix_set_length(&xy, npoints, 2+d, _state);
                    for(i=0; i<=npoints-1; i++)
                    {
                        xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                        xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                        for(j=0; j<=d-1; j++)
                        {
                            xy.ptr.pp_double[i][2+j] = vterm.ptr.pp_double[j][0]*xy.ptr.pp_double[i][0]+vterm.ptr.pp_double[j][1]*xy.ptr.pp_double[i][1]+vterm.ptr.pp_double[j][2];
                            meany.ptr.p_double[j] = meany.ptr.p_double[j]+xy.ptr.pp_double[i][2+j]/npoints;
                        }
                    }
                    spline2dbuildersetpoints(&state, &xy, npoints, _state);
                    spline2dbuildersetconstterm(&state, _state);
                    spline2dfit(&state, &s, &rep, _state);
                    ae_vector_set_length(&tmp0, 3, _state);
                    for(k=0; k<=10; k++)
                    {
                        tmp0.ptr.p_double[0] = 3*hqrnduniformr(&rs, _state)-1;
                        tmp0.ptr.p_double[1] = 3*hqrnduniformr(&rs, _state)-1;
                        tmp0.ptr.p_double[2] = 1.0;
                        spline2dcalcv(&s, tmp0.ptr.p_double[0], tmp0.ptr.p_double[1], &tmp1, _state);
                        for(j=0; j<=d-1; j++)
                        {
                            v = vterm.ptr.pp_double[j][0]*tmp0.ptr.p_double[0]+vterm.ptr.pp_double[j][1]*tmp0.ptr.p_double[1]+vterm.ptr.pp_double[j][2];
                            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(tmp1.ptr.p_double[j]-v, _state)/ae_maxreal(ae_fabs(v, _state), (double)(1), _state),tol), __FILE__, __LINE__, "testspline2dunit.ap:1159");
                        }
                    }
                    ae_set_error_flag(errorflag, ae_fp_greater(rep.rmserror,tol), __FILE__, __LINE__, "testspline2dunit.ap:1162");
                    ae_set_error_flag(errorflag, ae_fp_greater(rep.avgerror,tol), __FILE__, __LINE__, "testspline2dunit.ap:1163");
                    ae_set_error_flag(errorflag, ae_fp_greater(rep.maxerror,tol), __FILE__, __LINE__, "testspline2dunit.ap:1164");
                    
                    /*
                     * Generate perfectly constant dataset, check against user-defined constant prior.
                     * At least 8 points is required to get stable results.
                     */
                    vprior = hqrndnormal(&rs, _state);
                    npoints = 8+ae_round(10*hqrnduniformr(&rs, _state), _state);
                    ae_vector_set_length(&meany, d, _state);
                    for(j=0; j<=d-1; j++)
                    {
                        meany.ptr.p_double[j] = (double)(0);
                    }
                    ae_matrix_set_length(&xy, npoints, 2+d, _state);
                    for(i=0; i<=npoints-1; i++)
                    {
                        xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                        xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                        for(j=0; j<=d-1; j++)
                        {
                            xy.ptr.pp_double[i][2+j] = vprior;
                            meany.ptr.p_double[j] = meany.ptr.p_double[j]+xy.ptr.pp_double[i][2+j]/npoints;
                        }
                    }
                    spline2dbuildersetpoints(&state, &xy, npoints, _state);
                    spline2dbuildersetuserterm(&state, vprior, _state);
                    spline2dfit(&state, &s, &rep, _state);
                    ae_vector_set_length(&tmp0, 3, _state);
                    for(k=0; k<=10; k++)
                    {
                        tmp0.ptr.p_double[0] = 3*hqrnduniformr(&rs, _state)-1;
                        tmp0.ptr.p_double[1] = 3*hqrnduniformr(&rs, _state)-1;
                        tmp0.ptr.p_double[2] = 1.0;
                        spline2dcalcv(&s, tmp0.ptr.p_double[0], tmp0.ptr.p_double[1], &tmp1, _state);
                        for(j=0; j<=d-1; j++)
                        {
                            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(tmp1.ptr.p_double[j]-vprior, _state),tol), __FILE__, __LINE__, "testspline2dunit.ap:1199");
                        }
                    }
                    ae_set_error_flag(errorflag, ae_fp_greater(rep.rmserror,tol), __FILE__, __LINE__, "testspline2dunit.ap:1201");
                    ae_set_error_flag(errorflag, ae_fp_greater(rep.avgerror,tol), __FILE__, __LINE__, "testspline2dunit.ap:1202");
                    ae_set_error_flag(errorflag, ae_fp_greater(rep.maxerror,tol), __FILE__, __LINE__, "testspline2dunit.ap:1203");
                    spline2dbuildersetuserterm(&state, vprior+(2*hqrnduniformi(&rs, 2, _state)-1), _state);
                    spline2dfit(&state, &s, &rep, _state);
                    ae_vector_set_length(&tmp0, 3, _state);
                    for(k=0; k<=10; k++)
                    {
                        tmp0.ptr.p_double[0] = 2+hqrnduniformr(&rs, _state);
                        tmp0.ptr.p_double[1] = 2+hqrnduniformr(&rs, _state);
                        tmp0.ptr.p_double[2] = 1.0;
                        spline2dcalcv(&s, tmp0.ptr.p_double[0], tmp0.ptr.p_double[1], &tmp1, _state);
                        for(j=0; j<=d-1; j++)
                        {
                            ae_set_error_flag(errorflag, ae_fp_less_eq(ae_fabs(tmp1.ptr.p_double[j]-vprior, _state),tol), __FILE__, __LINE__, "testspline2dunit.ap:1216");
                        }
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Nonlinearity penalty test
*************************************************************************/
static void testspline2dunit_testfittingpenalty(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t kidx;
    ae_int_t pidx;
    ae_int_t solvertype;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t d;
    ae_int_t kx;
    ae_int_t ky;
    ae_int_t npoints;
    ae_matrix xy;
    spline2dinterpolant s;
    spline2dinterpolant s2;
    spline2dfitreport rep;
    spline2dfitreport rep2;
    hqrndstate rs;
    spline2dbuilder state;
    double rmserror;
    ae_vector penalty;
    ae_vector penalty2;
    double v;
    double v0;
    double v1;
    double f0;
    double f1;
    double f2;
    double f3;

    ae_frame_make(_state, &_frame_block);
    memset(&xy, 0, sizeof(xy));
    memset(&s, 0, sizeof(s));
    memset(&s2, 0, sizeof(s2));
    memset(&rep, 0, sizeof(rep));
    memset(&rep2, 0, sizeof(rep2));
    memset(&rs, 0, sizeof(rs));
    memset(&state, 0, sizeof(state));
    memset(&penalty, 0, sizeof(penalty));
    memset(&penalty2, 0, sizeof(penalty2));
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _spline2dinterpolant_init(&s, _state, ae_true);
    _spline2dinterpolant_init(&s2, _state, ae_true);
    _spline2dfitreport_init(&rep, _state, ae_true);
    _spline2dfitreport_init(&rep2, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);
    _spline2dbuilder_init(&state, _state, ae_true);
    ae_vector_init(&penalty, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&penalty2, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Penalized fitting problem:
     * * square KxK grid, K>=8 (points do not "touch" each other by their far fields)
     * * four points in the corners, perfectly linear value
     * * zero prior term
     *
     * Check that:
     * * with significant nonlinearity penalty we have nearly-linear fit,
     *   central value is an average of corner values
     * * without nonlinearity penalty, central value is zero
     */
    for(kidx=3; kidx<=4; kidx++)
    {
        for(solvertype=1; solvertype<=2; solvertype++)
        {
            
            /*
             * Generate dataset
             */
            kx = ae_round(ae_pow((double)(2), (double)(kidx), _state), _state);
            ky = kx;
            npoints = 4;
            ae_matrix_set_length(&xy, npoints, 3, _state);
            xy.ptr.pp_double[0][0] = (double)(-1);
            xy.ptr.pp_double[0][1] = (double)(-1);
            xy.ptr.pp_double[0][2] = hqrndnormal(&rs, _state);
            xy.ptr.pp_double[1][0] = (double)(1);
            xy.ptr.pp_double[1][1] = (double)(-1);
            xy.ptr.pp_double[1][2] = hqrndnormal(&rs, _state);
            xy.ptr.pp_double[2][0] = (double)(-1);
            xy.ptr.pp_double[2][1] = (double)(1);
            xy.ptr.pp_double[2][2] = hqrndnormal(&rs, _state);
            xy.ptr.pp_double[3][0] = (double)(1);
            xy.ptr.pp_double[3][1] = (double)(1);
            xy.ptr.pp_double[3][2] = xy.ptr.pp_double[0][2]+(xy.ptr.pp_double[1][2]-xy.ptr.pp_double[0][2])+(xy.ptr.pp_double[2][2]-xy.ptr.pp_double[0][2]);
            
            /*
             * Create solver, set grid, area and other properties
             * according to chosen test mode.
             */
            spline2dbuildercreate(1, &state, _state);
            spline2dbuildersetgrid(&state, kx, ky, _state);
            spline2dbuildersetarea(&state, (double)(-1), (double)(1), (double)(-1), (double)(1), _state);
            spline2dbuildersetpoints(&state, &xy, npoints, _state);
            spline2dbuildersetzeroterm(&state, _state);
            
            /*
             * Check fitting with penalty
             */
            if( solvertype==1 )
            {
                spline2dbuildersetalgoblocklls(&state, 1.0, _state);
            }
            else
            {
                if( solvertype==2 )
                {
                    spline2dbuildersetalgonaivells(&state, 1.0, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            spline2dfit(&state, &s, &rep, _state);
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(spline2dcalc(&s, 0.0, 0.0, _state)-0.25*(xy.ptr.pp_double[0][2]+xy.ptr.pp_double[1][2]+xy.ptr.pp_double[2][2]+xy.ptr.pp_double[3][2]), _state),1.0E-4), __FILE__, __LINE__, "testspline2dunit.ap:1305");
            
            /*
             * Check fitting without penalty
             */
            if( solvertype==1 )
            {
                spline2dbuildersetalgoblocklls(&state, 0.0, _state);
            }
            else
            {
                if( solvertype==2 )
                {
                    spline2dbuildersetalgonaivells(&state, 0.0, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            spline2dfit(&state, &s2, &rep2, _state);
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(spline2dcalc(&s2, 0.0, 0.0, _state), _state),1.0E-4), __FILE__, __LINE__, "testspline2dunit.ap:1318");
        }
    }
    
    /*
     * Solve a sequence of problems with increasing value of penalty coefficient;
     * test that increasing penalty coefficient results in:
     * * increased residuals
     * * decreased value of penalty function
     */
    for(solvertype=1; solvertype<=2; solvertype++)
    {
        for(d=1; d<=3; d++)
        {
            
            /*
             * Prepare solver and dataset
             */
            kx = 6+hqrnduniformi(&rs, 5, _state);
            ky = 6+hqrnduniformi(&rs, 5, _state);
            npoints = 3*(kx+2)*(ky+2)+30;
            ae_matrix_set_length(&xy, npoints, 2+d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                }
            }
            spline2dbuildercreate(d, &state, _state);
            spline2dbuildersetgrid(&state, kx, ky, _state);
            if( solvertype==1 )
            {
                spline2dbuildersetalgoblocklls(&state, 0.0, _state);
            }
            else
            {
                if( solvertype==2 )
                {
                    spline2dbuildersetalgonaivells(&state, 0.0, _state);
                }
                else
                {
                    ae_assert(ae_false, "Assertion failed", _state);
                }
            }
            spline2dbuildersetarea(&state, (double)(0), (double)(1), (double)(0), (double)(1), _state);
            spline2dbuildersetpoints(&state, &xy, npoints, _state);
            spline2dbuildersetzeroterm(&state, _state);
            
            /*
             * Solve with zero penalty
             */
            spline2dfit(&state, &s, &rep, _state);
            rmserror = rep.rmserror;
            ae_vector_set_length(&penalty, d, _state);
            for(k=0; k<=d-1; k++)
            {
                penalty.ptr.p_double[k] = 0.0;
            }
            v = 1.0E-3;
            for(i=0; i<=kx-1; i++)
            {
                for(j=0; j<=ky-1; j++)
                {
                    v0 = (double)i/(double)(kx-1);
                    v1 = (double)j/(double)(ky-1);
                    for(k=0; k<=d-1; k++)
                    {
                        f0 = spline2dcalcvi(&s, v0-v, v1, k, _state);
                        f1 = spline2dcalcvi(&s, v0, v1, k, _state);
                        f2 = spline2dcalcvi(&s, v0+v, v1, k, _state);
                        penalty.ptr.p_double[k] = penalty.ptr.p_double[k]+ae_sqr((f0+f2-2*f1)/ae_sqr(v, _state), _state);
                    }
                    for(k=0; k<=d-1; k++)
                    {
                        f0 = spline2dcalcvi(&s, v0, v1-v, k, _state);
                        f1 = spline2dcalcvi(&s, v0, v1, k, _state);
                        f2 = spline2dcalcvi(&s, v0, v1+v, k, _state);
                        penalty.ptr.p_double[k] = penalty.ptr.p_double[k]+ae_sqr((f0+f2-2*f1)/ae_sqr(v, _state), _state);
                    }
                    for(k=0; k<=d-1; k++)
                    {
                        f0 = spline2dcalcvi(&s, v0-v, v1-v, k, _state);
                        f1 = spline2dcalcvi(&s, v0+v, v1-v, k, _state);
                        f2 = spline2dcalcvi(&s, v0-v, v1+v, k, _state);
                        f3 = spline2dcalcvi(&s, v0+v, v1+v, k, _state);
                        penalty.ptr.p_double[k] = penalty.ptr.p_double[k]+2*ae_sqr((f3-f2-f1+f0)/ae_sqr(v, _state), _state);
                    }
                }
            }
            
            /*
             * Solve with non-zero penalty
             */
            for(pidx=-6; pidx<=3; pidx++)
            {
                if( solvertype==1 )
                {
                    spline2dbuildersetalgoblocklls(&state, ae_pow((double)(10), (double)pidx/(double)2, _state), _state);
                }
                else
                {
                    if( solvertype==2 )
                    {
                        spline2dbuildersetalgonaivells(&state, ae_pow((double)(10), (double)pidx/(double)2, _state), _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
                spline2dfit(&state, &s, &rep, _state);
                ae_vector_set_length(&penalty2, d, _state);
                for(k=0; k<=d-1; k++)
                {
                    penalty2.ptr.p_double[k] = 0.0;
                }
                v = 1.0E-3;
                for(i=0; i<=kx-1; i++)
                {
                    for(j=0; j<=ky-1; j++)
                    {
                        v0 = (double)i/(double)(kx-1);
                        v1 = (double)j/(double)(ky-1);
                        for(k=0; k<=d-1; k++)
                        {
                            f0 = spline2dcalcvi(&s, v0-v, v1, k, _state);
                            f1 = spline2dcalcvi(&s, v0, v1, k, _state);
                            f2 = spline2dcalcvi(&s, v0+v, v1, k, _state);
                            penalty2.ptr.p_double[k] = penalty2.ptr.p_double[k]+ae_sqr((f0+f2-2*f1)/ae_sqr(v, _state), _state);
                        }
                        for(k=0; k<=d-1; k++)
                        {
                            f0 = spline2dcalcvi(&s, v0, v1-v, k, _state);
                            f1 = spline2dcalcvi(&s, v0, v1, k, _state);
                            f2 = spline2dcalcvi(&s, v0, v1+v, k, _state);
                            penalty2.ptr.p_double[k] = penalty2.ptr.p_double[k]+ae_sqr((f0+f2-2*f1)/ae_sqr(v, _state), _state);
                        }
                        for(k=0; k<=d-1; k++)
                        {
                            f0 = spline2dcalcvi(&s, v0-v, v1-v, k, _state);
                            f1 = spline2dcalcvi(&s, v0+v, v1-v, k, _state);
                            f2 = spline2dcalcvi(&s, v0-v, v1+v, k, _state);
                            f3 = spline2dcalcvi(&s, v0+v, v1+v, k, _state);
                            penalty2.ptr.p_double[k] = penalty2.ptr.p_double[k]+2*ae_sqr((f3-f2-f1+f0)/ae_sqr(v, _state), _state);
                        }
                    }
                }
                ae_set_error_flag(errorflag, ae_fp_less_eq(rep.rmserror,rmserror), __FILE__, __LINE__, "testspline2dunit.ap:1448");
                for(k=0; k<=d-1; k++)
                {
                    ae_set_error_flag(errorflag, ae_fp_greater_eq(penalty2.ptr.p_double[k],penalty.ptr.p_double[k]), __FILE__, __LINE__, "testspline2dunit.ap:1450");
                }
                rmserror = rep.rmserror;
                for(k=0; k<=d-1; k++)
                {
                    penalty.ptr.p_double[k] = penalty2.ptr.p_double[k];
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Block solver test for fitting functionality
*************************************************************************/
static void testspline2dunit_testfittingblocksolver(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector kv;
    ae_int_t d;
    ae_int_t kidx;
    ae_int_t areatype;
    ae_int_t gridtype;
    ae_int_t solvertype;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    hqrndstate rs;
    double errtol;
    spline2dbuilder state;
    spline2dinterpolant s;
    spline2dinterpolant s2;
    spline2dinterpolant s3;
    spline2dfitreport rep;
    spline2dfitreport rep2;
    spline2dfitreport rep3;
    ae_int_t kx;
    ae_int_t ky;
    double w1;
    double xc;
    double yc;
    ae_int_t npoints;
    ae_matrix xy;
    ae_matrix xy2;
    ae_matrix xy3;
    ae_matrix vterm;
    ae_int_t priortype;
    double mx;
    double rmserror;
    double avgerror;
    double maxerror;
    double rss;
    double tss;
    double r2;
    double v;
    double v0;
    double v1;
    ae_vector meany;
    ae_vector tmp0;
    ae_vector tmp1;

    ae_frame_make(_state, &_frame_block);
    memset(&kv, 0, sizeof(kv));
    memset(&rs, 0, sizeof(rs));
    memset(&state, 0, sizeof(state));
    memset(&s, 0, sizeof(s));
    memset(&s2, 0, sizeof(s2));
    memset(&s3, 0, sizeof(s3));
    memset(&rep, 0, sizeof(rep));
    memset(&rep2, 0, sizeof(rep2));
    memset(&rep3, 0, sizeof(rep3));
    memset(&xy, 0, sizeof(xy));
    memset(&xy2, 0, sizeof(xy2));
    memset(&xy3, 0, sizeof(xy3));
    memset(&vterm, 0, sizeof(vterm));
    memset(&meany, 0, sizeof(meany));
    memset(&tmp0, 0, sizeof(tmp0));
    memset(&tmp1, 0, sizeof(tmp1));
    ae_vector_init(&kv, 0, DT_INT, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);
    _spline2dbuilder_init(&state, _state, ae_true);
    _spline2dinterpolant_init(&s, _state, ae_true);
    _spline2dinterpolant_init(&s2, _state, ae_true);
    _spline2dinterpolant_init(&s3, _state, ae_true);
    _spline2dfitreport_init(&rep, _state, ae_true);
    _spline2dfitreport_init(&rep2, _state, ae_true);
    _spline2dfitreport_init(&rep3, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy3, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vterm, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&meany, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    errtol = 1.0E-6;
    
    /*
     * Check that default state (no dataset) results in zero model being returned.
     */
    for(priortype=0; priortype<=2; priortype++)
    {
        for(d=1; d<=3; d++)
        {
            for(areatype=0; areatype<=1; areatype++)
            {
                for(gridtype=0; gridtype<=1; gridtype++)
                {
                    for(solvertype=1; solvertype<=2; solvertype++)
                    {
                        
                        /*
                         * Create solver, set grid, area and other properties
                         * according to chosen test mode.
                         */
                        kx = ae_maxint(4, 1+hqrnduniformi(&rs, 10, _state), _state);
                        ky = ae_maxint(4, 1+hqrnduniformi(&rs, 10, _state), _state);
                        spline2dbuildercreate(d, &state, _state);
                        if( gridtype==1 )
                        {
                            spline2dbuildersetgrid(&state, kx, ky, _state);
                        }
                        if( solvertype==1 )
                        {
                            spline2dbuildersetalgoblocklls(&state, 0.0, _state);
                        }
                        else
                        {
                            if( solvertype==2 )
                            {
                                spline2dbuildersetalgonaivells(&state, 0.0, _state);
                            }
                            else
                            {
                                ae_assert(ae_false, "Assertion failed", _state);
                            }
                        }
                        if( areatype==0 )
                        {
                            spline2dbuildersetareaauto(&state, _state);
                        }
                        else
                        {
                            if( areatype==1 )
                            {
                                spline2dbuildersetarea(&state, (double)(0), (double)(1), (double)(0), (double)(1), _state);
                            }
                            else
                            {
                                ae_assert(ae_false, "Assertion failed", _state);
                            }
                        }
                        if( priortype==0 )
                        {
                            spline2dbuildersetlinterm(&state, _state);
                        }
                        else
                        {
                            if( priortype==1 )
                            {
                                spline2dbuildersetconstterm(&state, _state);
                            }
                            else
                            {
                                if( priortype==2 )
                                {
                                    spline2dbuildersetzeroterm(&state, _state);
                                }
                                else
                                {
                                    ae_assert(ae_false, "Assertion failed", _state);
                                }
                            }
                        }
                        spline2dfit(&state, &s, &rep, _state);
                        
                        /*
                         * Test
                         */
                        for(j=0; j<=d-1; j++)
                        {
                            ae_set_error_flag(errorflag, ae_fp_neq(spline2dcalcvi(&s, hqrndnormal(&rs, _state), hqrndnormal(&rs, _state), j, _state),(double)(0)), __FILE__, __LINE__, "testspline2dunit.ap:1550");
                        }
                        ae_set_error_flag(errorflag, ae_fp_neq(rep.rmserror,(double)(0)), __FILE__, __LINE__, "testspline2dunit.ap:1551");
                        ae_set_error_flag(errorflag, ae_fp_neq(rep.avgerror,(double)(0)), __FILE__, __LINE__, "testspline2dunit.ap:1552");
                        ae_set_error_flag(errorflag, ae_fp_neq(rep.maxerror,(double)(0)), __FILE__, __LINE__, "testspline2dunit.ap:1553");
                        ae_set_error_flag(errorflag, ae_fp_neq(rep.r2,(double)(1)), __FILE__, __LINE__, "testspline2dunit.ap:1554");
                    }
                }
            }
        }
    }
    
    /*
     * Check properties of regression spline fitting WITHOUT nonlinearity
     * penalty. Absence of penalty term allows us to perform several indirect
     * tests which are impossible when working under penalty.
     *
     * Section below performs following tests on completely random dataset:
     * * correctness of report fields
     * * indirect tests for fitting solver (fitting on residuals from previous fit)
     */
    ae_vector_set_length(&kv, 5, _state);
    kv.ptr.p_int[0] = 4;
    kv.ptr.p_int[1] = 5;
    kv.ptr.p_int[2] = 10;
    kv.ptr.p_int[3] = 12;
    kv.ptr.p_int[4] = 15;
    for(kidx=0; kidx<=kv.cnt-1; kidx++)
    {
        for(d=1; d<=3; d++)
        {
            for(areatype=0; areatype<=1; areatype++)
            {
                for(gridtype=1; gridtype<=1; gridtype++)
                {
                    for(solvertype=1; solvertype<=2; solvertype++)
                    {
                        
                        /*
                         * Generate dataset
                         */
                        kx = ae_maxint(4, 1+hqrnduniformi(&rs, kv.ptr.p_int[kidx], _state), _state);
                        ky = ae_maxint(4, 1+hqrnduniformi(&rs, kv.ptr.p_int[kidx], _state), _state);
                        if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
                        {
                            kx = kv.ptr.p_int[kidx];
                        }
                        else
                        {
                            ky = kv.ptr.p_int[kidx];
                        }
                        w1 = ae_pow((double)(10), hqrndnormal(&rs, _state), _state);
                        xc = w1*(2*hqrnduniformr(&rs, _state)-1);
                        yc = w1*(2*hqrnduniformr(&rs, _state)-1);
                        ae_vector_set_length(&meany, d, _state);
                        for(j=0; j<=d-1; j++)
                        {
                            meany.ptr.p_double[j] = (double)(0);
                        }
                        npoints = ae_round((kx+2)*(ky+2)*(3+hqrnduniformr(&rs, _state)), _state);
                        ae_matrix_set_length(&xy, npoints, 2+d, _state);
                        for(i=0; i<=npoints-1; i++)
                        {
                            xy.ptr.pp_double[i][0] = xc+w1*(2*hqrnduniformr(&rs, _state)-1)*kx;
                            xy.ptr.pp_double[i][1] = yc+w1*(2*hqrnduniformr(&rs, _state)-1)*ky;
                            for(j=0; j<=d-1; j++)
                            {
                                xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                                meany.ptr.p_double[j] = meany.ptr.p_double[j]+xy.ptr.pp_double[i][2+j]/npoints;
                            }
                        }
                        
                        /*
                         * Create solver, set grid, area and other properties
                         * according to chosen test mode.
                         */
                        spline2dbuildercreate(d, &state, _state);
                        if( gridtype==1 )
                        {
                            spline2dbuildersetgrid(&state, kx, ky, _state);
                        }
                        else
                        {
                            ae_assert(ae_false, "Assertion failed", _state);
                        }
                        if( solvertype==1 )
                        {
                            spline2dbuildersetalgoblocklls(&state, 0.0, _state);
                        }
                        else
                        {
                            if( solvertype==2 )
                            {
                                spline2dbuildersetalgonaivells(&state, 0.0, _state);
                            }
                            else
                            {
                                ae_assert(ae_false, "Assertion failed", _state);
                            }
                        }
                        if( areatype==0 )
                        {
                            spline2dbuildersetareaauto(&state, _state);
                        }
                        else
                        {
                            if( areatype==1 )
                            {
                                spline2dbuildersetarea(&state, xc-w1*kx, xc+w1*kx, yc-w1*ky, yc+w1*ky, _state);
                            }
                            else
                            {
                                ae_assert(ae_false, "Assertion failed", _state);
                            }
                        }
                        spline2dbuildersetpoints(&state, &xy, npoints, _state);
                        spline2dfit(&state, &s, &rep, _state);
                        
                        /*
                         * Test report fields
                         */
                        rmserror = (double)(0);
                        avgerror = (double)(0);
                        maxerror = (double)(0);
                        rss = 0.0;
                        tss = 0.0;
                        for(i=0; i<=npoints-1; i++)
                        {
                            spline2dcalcv(&s, xy.ptr.pp_double[i][0], xy.ptr.pp_double[i][1], &tmp0, _state);
                            for(j=0; j<=d-1; j++)
                            {
                                v = xy.ptr.pp_double[i][2+j]-tmp0.ptr.p_double[j];
                                rss = rss+v*v;
                                tss = tss+ae_sqr(xy.ptr.pp_double[i][2+j]-meany.ptr.p_double[j], _state);
                                rmserror = rmserror+ae_sqr(v, _state);
                                avgerror = avgerror+ae_fabs(v, _state)/(npoints*d);
                                maxerror = ae_maxreal(maxerror, ae_fabs(v, _state), _state);
                            }
                        }
                        rmserror = ae_sqrt(rmserror/(npoints*d), _state);
                        r2 = 1.0-rss/coalesce(tss, (double)(1), _state);
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(rmserror-rep.rmserror, _state),errtol), __FILE__, __LINE__, "testspline2dunit.ap:1654");
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(avgerror-rep.avgerror, _state),errtol), __FILE__, __LINE__, "testspline2dunit.ap:1655");
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(maxerror-rep.maxerror, _state),errtol), __FILE__, __LINE__, "testspline2dunit.ap:1656");
                        if( ae_fp_greater(tss,1.0E-3) )
                        {
                            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(r2-rep.r2, _state),errtol), __FILE__, __LINE__, "testspline2dunit.ap:1658");
                        }
                        
                        /*
                         * Minimalistic test for fitting quality: R2 is better than zero
                         * (zero value = fitting by median value).
                         */
                        ae_set_error_flag(errorflag, ae_fp_less(rep.r2,0.0), __FILE__, __LINE__, "testspline2dunit.ap:1664");
                        
                        /*
                         * Indirect test for solver: test that fitting on residuals from
                         * previous fitting does not significantly decrease residuals.
                         *
                         * In our test we require that residuals from residuals are
                         * greater than 0.8 times original residuals.
                         *
                         * In the ideal world, fitting on residuals will return same
                         * residuals, but during spline construction we made several
                         * compromises (regularization, pre-model phase). So, we just
                         * check that residuals are weakly decreased.
                         *
                         * NOTE: this test is performed only for significantly non-zero residuals
                         */
                        if( ae_fp_greater(rep.rmserror,0.05) )
                        {
                            ae_matrix_set_length(&xy2, npoints, 2+d, _state);
                            for(i=0; i<=npoints-1; i++)
                            {
                                xy2.ptr.pp_double[i][0] = xy.ptr.pp_double[i][0];
                                xy2.ptr.pp_double[i][1] = xy.ptr.pp_double[i][1];
                                spline2dcalcv(&s, xy.ptr.pp_double[i][0], xy.ptr.pp_double[i][1], &tmp1, _state);
                                for(j=0; j<=d-1; j++)
                                {
                                    xy2.ptr.pp_double[i][2+j] = xy.ptr.pp_double[i][2+j]-tmp1.ptr.p_double[j];
                                }
                            }
                            spline2dbuildersetpoints(&state, &xy2, npoints, _state);
                            spline2dfit(&state, &s2, &rep2, _state);
                            ae_set_error_flag(errorflag, ae_fp_less(rep2.rmserror,0.8*rep.rmserror), __FILE__, __LINE__, "testspline2dunit.ap:1693");
                        }
                    }
                }
            }
        }
    }
    
    /*
     * Direct test:
     * * points on regular grid which is exactly the same as one used
     *   for regression, but with some noise added to point locations
     * * we test that without penalty we get nearly-zero RMS error
     */
    ae_vector_set_length(&kv, 5, _state);
    kv.ptr.p_int[0] = 4;
    kv.ptr.p_int[1] = 5;
    kv.ptr.p_int[2] = 6;
    kv.ptr.p_int[3] = 8;
    kv.ptr.p_int[4] = 12;
    for(kidx=0; kidx<=kv.cnt-1; kidx++)
    {
        for(d=1; d<=2; d++)
        {
            for(solvertype=1; solvertype<=2; solvertype++)
            {
                
                /*
                 * Generate dataset
                 */
                kx = ae_maxint(4, 1+hqrnduniformi(&rs, kv.ptr.p_int[kidx], _state), _state);
                ky = ae_maxint(4, 1+hqrnduniformi(&rs, kv.ptr.p_int[kidx], _state), _state);
                if( ae_fp_greater(hqrndnormal(&rs, _state),(double)(0)) )
                {
                    kx = kv.ptr.p_int[kidx];
                }
                else
                {
                    ky = kv.ptr.p_int[kidx];
                }
                npoints = kx*ky;
                ae_matrix_set_length(&xy, npoints, 2+d, _state);
                for(i=0; i<=npoints-1; i++)
                {
                    xy.ptr.pp_double[i][0] = boundval(i%kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(kx-1), _state);
                    xy.ptr.pp_double[i][1] = boundval(i/kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(ky-1), _state);
                    for(j=0; j<=d-1; j++)
                    {
                        xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                    }
                }
                
                /*
                 * Create solver, set grid, area and other properties
                 * according to chosen test mode.
                 */
                spline2dbuildercreate(d, &state, _state);
                spline2dbuildersetgrid(&state, kx, ky, _state);
                if( solvertype==1 )
                {
                    spline2dbuildersetalgoblocklls(&state, 0.0, _state);
                }
                else
                {
                    if( solvertype==2 )
                    {
                        spline2dbuildersetalgonaivells(&state, 0.0, _state);
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
                spline2dbuildersetareaauto(&state, _state);
                spline2dbuildersetpoints(&state, &xy, npoints, _state);
                spline2dfit(&state, &s, &rep, _state);
                
                /*
                 * Test
                 */
                for(i=0; i<=npoints-1; i++)
                {
                    for(k=0; k<=d-1; k++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(spline2dcalcvi(&s, xy.ptr.pp_double[i][0], xy.ptr.pp_double[i][1], k, _state)-xy.ptr.pp_double[i][2+k], _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:1754");
                    }
                }
            }
        }
    }
    
    /*
     * Compare results obtained with block solver vs naive one
     */
    mx = (double)(0);
    for(kx=4; kx<=8; kx++)
    {
        for(ky=4; ky<=8; ky++)
        {
            for(d=1; d<=2; d++)
            {
                
                /*
                 * Generate dataset
                 */
                npoints = 300;
                ae_matrix_set_length(&xy, npoints, 2+d, _state);
                for(i=0; i<=npoints-1; i++)
                {
                    xy.ptr.pp_double[i][0] = (kx-1)*hqrnduniformr(&rs, _state);
                    xy.ptr.pp_double[i][1] = (ky-1)*hqrnduniformr(&rs, _state);
                    for(j=0; j<=d-1; j++)
                    {
                        xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                    }
                }
                
                /*
                 * Create solver, set grid, area and other properties
                 * according to chosen test mode.
                 *
                 * NOTE: we perform just one iteration of LSQR in order
                 *       to test subtle differences of two implementations -
                 *       reference one (NaiveLLS) and high-performance one.
                 */
                spline2dbuildercreate(d, &state, _state);
                spline2dbuildersetgrid(&state, kx, ky, _state);
                spline2dbuildersetarea(&state, (double)(0), (double)(kx-1), (double)(0), (double)(ky-1), _state);
                spline2dbuildersetpoints(&state, &xy, npoints, _state);
                state.lsqrcnt = 1;
                spline2dbuildersetalgonaivells(&state, 0.0, _state);
                spline2dfit(&state, &s, &rep, _state);
                spline2dbuildersetalgoblocklls(&state, 0.0, _state);
                spline2dfit(&state, &s2, &rep2, _state);
                for(i=0; i<=npoints-1; i++)
                {
                    spline2dcalcv(&s, xy.ptr.pp_double[i][0], xy.ptr.pp_double[i][1], &tmp0, _state);
                    spline2dcalcv(&s2, xy.ptr.pp_double[i][0], xy.ptr.pp_double[i][1], &tmp1, _state);
                    for(j=0; j<=d-1; j++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(tmp0.ptr.p_double[j]-tmp1.ptr.p_double[j], _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:1801");
                        mx = ae_maxreal(mx, ae_fabs(tmp0.ptr.p_double[j]-tmp1.ptr.p_double[j], _state), _state);
                    }
                }
            }
        }
    }
    
    /*
     * Investigate stability against minor perturbations
     */
    d = 1;
    kx = 20;
    ky = 20;
    npoints = 5*kx*ky;
    ae_matrix_set_length(&xy, npoints, 2+d, _state);
    for(i=0; i<=npoints-1; i++)
    {
        xy.ptr.pp_double[i][0] = (kx-1)*hqrnduniformr(&rs, _state);
        xy.ptr.pp_double[i][1] = (ky-1)*hqrnduniformr(&rs, _state);
        xy.ptr.pp_double[i][2] = hqrndnormal(&rs, _state);
    }
    spline2dbuildercreate(d, &state, _state);
    spline2dbuildersetgrid(&state, kx, ky, _state);
    spline2dbuildersetarea(&state, (double)(0), (double)(kx-1), (double)(0), (double)(ky-1), _state);
    spline2dbuildersetalgoblocklls(&state, 0.001, _state);
    spline2dbuildersetpoints(&state, &xy, npoints, _state);
    spline2dfit(&state, &s, &rep, _state);
    for(i=0; i<=npoints-1; i++)
    {
        j = i+hqrnduniformi(&rs, npoints-i, _state);
        swaprows(&xy, i, j, -1, _state);
        xy.ptr.pp_double[i][2] = xy.ptr.pp_double[i][2]+ae_machineepsilon*hqrndnormal(&rs, _state);
    }
    spline2dbuildersetpoints(&state, &xy, npoints, _state);
    spline2dfit(&state, &s2, &rep2, _state);
    mx = (double)(0);
    maxerror = (double)(0);
    for(i=0; i<=npoints-1; i++)
    {
        v0 = (kx-1)*hqrnduniformr(&rs, _state);
        v1 = (ky-1)*hqrnduniformr(&rs, _state);
        maxerror = ae_maxreal(maxerror, ae_fabs(spline2dcalc(&s, v0, v1, _state)-spline2dcalc(&s2, v0, v1, _state), _state), _state);
        mx = ae_maxreal(mx, ae_fabs(spline2dcalc(&s, v0, v1, _state), _state), _state);
    }
    ae_set_error_flag(errorflag, ae_fp_greater(maxerror/mx,1.0E-12), __FILE__, __LINE__, "testspline2dunit.ap:1846");
    ae_frame_leave(_state);
}


/*************************************************************************
Block solver test for fitting functionality
*************************************************************************/
static void testspline2dunit_testfittingfastddmsolver(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    hqrndstate rs;
    ae_int_t pass;
    ae_int_t tilesize;
    ae_int_t interfacesize;
    ae_int_t npoints;
    ae_int_t d;
    ae_int_t kx;
    ae_int_t ky;
    ae_int_t kxcur;
    ae_int_t kycur;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v0;
    double v1;
    double vx;
    double vy;
    double f0;
    double f1;
    double f2;
    double f3;
    double h;
    double smax;
    double ernd;
    double ept;
    double emax;
    double eavg;
    double vmax;
    ae_int_t padding;
    ae_int_t nlayers;
    ae_matrix xy;
    ae_matrix xy1;
    spline2dbuilder state;
    spline2dinterpolant sa0;
    spline2dinterpolant sa1;
    spline2dinterpolant s1;
    spline2dinterpolant s2;
    spline2dfitreport rep0;
    spline2dfitreport rep1;
    spline2dfitreport rep2;
    ae_vector meany;
    ae_vector lambdaa;
    ae_vector penaltya;
    ae_vector rmsa;
    double rmserror;
    double avgerror;
    double maxerror;
    double rss;
    double tss;
    double r2;
    double v;

    ae_frame_make(_state, &_frame_block);
    memset(&rs, 0, sizeof(rs));
    memset(&xy, 0, sizeof(xy));
    memset(&xy1, 0, sizeof(xy1));
    memset(&state, 0, sizeof(state));
    memset(&sa0, 0, sizeof(sa0));
    memset(&sa1, 0, sizeof(sa1));
    memset(&s1, 0, sizeof(s1));
    memset(&s2, 0, sizeof(s2));
    memset(&rep0, 0, sizeof(rep0));
    memset(&rep1, 0, sizeof(rep1));
    memset(&rep2, 0, sizeof(rep2));
    memset(&meany, 0, sizeof(meany));
    memset(&lambdaa, 0, sizeof(lambdaa));
    memset(&penaltya, 0, sizeof(penaltya));
    memset(&rmsa, 0, sizeof(rmsa));
    _hqrndstate_init(&rs, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy1, 0, 0, DT_REAL, _state, ae_true);
    _spline2dbuilder_init(&state, _state, ae_true);
    _spline2dinterpolant_init(&sa0, _state, ae_true);
    _spline2dinterpolant_init(&sa1, _state, ae_true);
    _spline2dinterpolant_init(&s1, _state, ae_true);
    _spline2dinterpolant_init(&s2, _state, ae_true);
    _spline2dfitreport_init(&rep0, _state, ae_true);
    _spline2dfitreport_init(&rep1, _state, ae_true);
    _spline2dfitreport_init(&rep2, _state, ae_true);
    ae_vector_init(&meany, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaa, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&penaltya, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rmsa, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    
    /*
     * Basic test:
     * * small grid whose size is TileSize+1 or less
     * * random dataset
     * * compared with single run of naiveLLS
     * * we also test correctness of report fields
     */
    for(pass=1; pass<=3; pass++)
    {
        for(d=1; d<=3; d++)
        {
            tilesize = 8+hqrnduniformi(&rs, 8, _state);
            kx = ae_maxint(1+hqrnduniformi(&rs, tilesize+1, _state), 4, _state);
            ky = ae_maxint(1+hqrnduniformi(&rs, tilesize+1, _state), 4, _state);
            npoints = 10*kx*ky;
            ae_matrix_set_length(&xy, npoints, 2+d, _state);
            ae_vector_set_length(&meany, d, _state);
            for(j=0; j<=d-1; j++)
            {
                meany.ptr.p_double[j] = (double)(0);
            }
            for(i=0; i<=npoints-1; i++)
            {
                xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                    meany.ptr.p_double[j] = meany.ptr.p_double[j]+xy.ptr.pp_double[i][2+j]/npoints;
                }
            }
            
            /*
             * Create solver, set grid, area and other properties.
             * Fit with NaiveLLS first.
             */
            spline2dbuildercreate(d, &state, _state);
            spline2dbuildersetarea(&state, 0.0, 1.0, 0.0, 1.0, _state);
            state.maxcoresize = tilesize;
            state.adddegreeoffreedom = ae_false;
            spline2dbuildersetalgonaivells(&state, 0.0, _state);
            spline2dbuildersetpoints(&state, &xy, npoints, _state);
            spline2dbuildersetgrid(&state, kx, ky, _state);
            spline2dfit(&state, &sa0, &rep0, _state);
            
            /*
             * Fit with FastDDM solver with NLayers in [-2,+2]
             */
            spline2dbuildersetpoints(&state, &xy, npoints, _state);
            spline2dbuildersetgrid(&state, kx, ky, _state);
            spline2dbuildersetalgofastddm(&state, hqrnduniformi(&rs, 5, _state)-2, 0.0, _state);
            spline2dfit(&state, &s1, &rep1, _state);
            
            /*
             * Compare at random locations
             */
            smax = 0.0;
            ernd = 0.0;
            for(k=0; k<=20*kx*ky-1; k++)
            {
                vx = hqrnduniformr(&rs, _state);
                vy = hqrnduniformr(&rs, _state);
                for(j=0; j<=d-1; j++)
                {
                    v0 = spline2dcalcvi(&sa0, vx, vy, j, _state);
                    v1 = spline2dcalcvi(&s1, vx, vy, j, _state);
                    smax = ae_maxreal(smax, ae_fabs(v0, _state), _state);
                    ernd = ae_maxreal(ernd, ae_fabs(v0-v1, _state), _state);
                }
            }
            ae_set_error_flag(errorflag, ae_fp_greater(ernd/smax,0.001), __FILE__, __LINE__, "testspline2dunit.ap:1964");
            
            /*
             * Test report fields
             */
            rmserror = (double)(0);
            avgerror = (double)(0);
            maxerror = (double)(0);
            rss = 0.0;
            tss = 0.0;
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=d-1; j++)
                {
                    v = xy.ptr.pp_double[i][2+j]-spline2dcalcvi(&s1, xy.ptr.pp_double[i][0], xy.ptr.pp_double[i][1], j, _state);
                    rss = rss+v*v;
                    tss = tss+ae_sqr(xy.ptr.pp_double[i][2+j]-meany.ptr.p_double[j], _state);
                    rmserror = rmserror+ae_sqr(v, _state);
                    avgerror = avgerror+ae_fabs(v, _state)/(npoints*d);
                    maxerror = ae_maxreal(maxerror, ae_fabs(v, _state), _state);
                }
            }
            rmserror = ae_sqrt(rmserror/(npoints*d), _state);
            r2 = 1.0-rss/coalesce(tss, (double)(1), _state);
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(rmserror-rep1.rmserror, _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:1988");
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(avgerror-rep1.avgerror, _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:1989");
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(maxerror-rep1.maxerror, _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:1990");
            if( ae_fp_greater(tss,1.0E-3) )
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(r2-rep1.r2, _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:1992");
            }
        }
    }
    
    /*
     * Basic test: two-layer task; "reference" model is built by
     * subsequent fitting of two "traditional" splines; test model
     * is built by FastDDM algorithm.
     */
    tilesize = 15;
    for(d=1; d<=2; d++)
    {
        
        /*
         * Generate dataset
         */
        kx = 2*tilesize+1;
        ky = 2*tilesize+1;
        npoints = kx*ky;
        ae_matrix_set_length(&xy, npoints, 2+d, _state);
        for(i=0; i<=npoints-1; i++)
        {
            xy.ptr.pp_double[i][0] = boundval(i%kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(kx-1), _state);
            xy.ptr.pp_double[i][1] = boundval(i/kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(ky-1), _state);
            for(j=0; j<=d-1; j++)
            {
                xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
            }
        }
        
        /*
         * Create solver, set grid, area and other properties
         */
        spline2dbuildercreate(d, &state, _state);
        spline2dbuildersetarea(&state, (double)(0), (double)(kx-1), (double)(0), (double)(ky-1), _state);
        state.maxcoresize = tilesize;
        
        /*
         * Fit with BlockLLS solver
         */
        state.adddegreeoffreedom = ae_false;
        spline2dbuildersetalgoblocklls(&state, 0.0, _state);
        spline2dbuildersetpoints(&state, &xy, npoints, _state);
        spline2dbuildersetgrid(&state, (kx-1)/2+1, (ky-1)/2+1, _state);
        spline2dfit(&state, &sa0, &rep0, _state);
        ae_matrix_set_length(&xy1, npoints, 2+d, _state);
        for(i=0; i<=npoints-1; i++)
        {
            xy1.ptr.pp_double[i][0] = xy.ptr.pp_double[i][0];
            xy1.ptr.pp_double[i][1] = xy.ptr.pp_double[i][1];
            for(j=0; j<=d-1; j++)
            {
                xy1.ptr.pp_double[i][2+j] = xy.ptr.pp_double[i][2+j]-spline2dcalcvi(&sa0, xy.ptr.pp_double[i][0], xy.ptr.pp_double[i][1], j, _state);
            }
        }
        spline2dbuildersetpoints(&state, &xy1, npoints, _state);
        spline2dbuildersetgrid(&state, kx, ky, _state);
        spline2dfit(&state, &sa1, &rep0, _state);
        
        /*
         * Fit with another solver
         */
        spline2dbuildersetpoints(&state, &xy, npoints, _state);
        spline2dbuildersetgrid(&state, kx, ky, _state);
        spline2dbuildersetalgofastddm(&state, 0, 0.0, _state);
        spline2dfit(&state, &s1, &rep1, _state);
        
        /*
         * Compare
         */
        padding = 0;
        smax = 0.0;
        ernd = 0.0;
        for(k=0; k<=20*kx*ky-1; k++)
        {
            vx = (kx-1-2*padding)*hqrnduniformr(&rs, _state)+padding;
            vy = (ky-1-2*padding)*hqrnduniformr(&rs, _state)+padding;
            for(j=0; j<=d-1; j++)
            {
                v0 = spline2dcalcvi(&sa0, vx, vy, j, _state)+spline2dcalcvi(&sa1, vx, vy, j, _state);
                v1 = spline2dcalcvi(&s1, vx, vy, j, _state);
                smax = ae_maxreal(smax, ae_fabs(v0, _state), _state);
                ernd = ae_maxreal(ernd, ae_fabs(v0-v1, _state), _state);
            }
        }
        ae_set_error_flag(errorflag, ae_fp_greater(ernd/smax,0.010), __FILE__, __LINE__, "testspline2dunit.ap:2071");
        ept = 0.0;
        for(i=0; i<=npoints-1; i++)
        {
            vx = xy.ptr.pp_double[i][0];
            vy = xy.ptr.pp_double[i][1];
            for(j=0; j<=d-1; j++)
            {
                v0 = spline2dcalcvi(&sa0, vx, vy, j, _state)+spline2dcalcvi(&sa1, vx, vy, j, _state);
                v1 = spline2dcalcvi(&s1, vx, vy, j, _state);
                smax = ae_maxreal(smax, ae_fabs(v0, _state), _state);
                ept = ae_maxreal(ept, ae_fabs(v0-v1, _state), _state);
            }
        }
        ae_set_error_flag(errorflag, ae_fp_greater(ept/smax,0.001), __FILE__, __LINE__, "testspline2dunit.ap:2085");
    }
    
    /*
     * Test correctness of error reporting in multilevel setting:
     * * grid whose size is larger than TileSize+1
     * * random dataset
     * * we just test correctness of report fields
     */
    for(pass=1; pass<=2; pass++)
    {
        for(d=1; d<=2; d++)
        {
            tilesize = 5+hqrnduniformi(&rs, 3, _state);
            kx = ae_maxint(1+hqrnduniformi(&rs, 5*tilesize+1, _state), 4, _state);
            ky = ae_maxint(1+hqrnduniformi(&rs, 5*tilesize+1, _state), 4, _state);
            npoints = 10*kx*ky;
            ae_matrix_set_length(&xy, npoints, 2+d, _state);
            ae_vector_set_length(&meany, d, _state);
            for(j=0; j<=d-1; j++)
            {
                meany.ptr.p_double[j] = (double)(0);
            }
            for(i=0; i<=npoints-1; i++)
            {
                xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                    meany.ptr.p_double[j] = meany.ptr.p_double[j]+xy.ptr.pp_double[i][2+j]/npoints;
                }
            }
            
            /*
             * Create solver, set grid, area and other properties.
             * Fit with FastDDM solver
             */
            spline2dbuildercreate(d, &state, _state);
            spline2dbuildersetarea(&state, 0.0, 1.0, 0.0, 1.0, _state);
            state.maxcoresize = tilesize;
            spline2dbuildersetpoints(&state, &xy, npoints, _state);
            spline2dbuildersetgrid(&state, kx, ky, _state);
            spline2dbuildersetalgofastddm(&state, hqrnduniformi(&rs, 5, _state)-2, 0.0, _state);
            spline2dfit(&state, &s1, &rep1, _state);
            
            /*
             * Test report fields
             */
            rmserror = (double)(0);
            avgerror = (double)(0);
            maxerror = (double)(0);
            rss = 0.0;
            tss = 0.0;
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=d-1; j++)
                {
                    v = xy.ptr.pp_double[i][2+j]-spline2dcalcvi(&s1, xy.ptr.pp_double[i][0], xy.ptr.pp_double[i][1], j, _state);
                    rss = rss+v*v;
                    tss = tss+ae_sqr(xy.ptr.pp_double[i][2+j]-meany.ptr.p_double[j], _state);
                    rmserror = rmserror+ae_sqr(v, _state);
                    avgerror = avgerror+ae_fabs(v, _state)/(npoints*d);
                    maxerror = ae_maxreal(maxerror, ae_fabs(v, _state), _state);
                }
            }
            rmserror = ae_sqrt(rmserror/(npoints*d), _state);
            r2 = 1.0-rss/coalesce(tss, (double)(1), _state);
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(rmserror-rep1.rmserror, _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:2152");
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(avgerror-rep1.avgerror, _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:2153");
            ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(maxerror-rep1.maxerror, _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:2154");
            if( ae_fp_greater(tss,1.0E-3) )
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(r2-rep1.r2, _state),1.0E-6), __FILE__, __LINE__, "testspline2dunit.ap:2156");
            }
        }
    }
    
    /*
     * Test correctness of handling NLayers>0, NLayers=0 and NLayers<0.
     *
     * NOTE: we use reduced tile size and interface size, because we do
     *       not test model quality, just identity with NLayers<=0
     */
    d = 1;
    tilesize = 5;
    interfacesize = 2;
    kx = 1+4*(tilesize-hqrnduniformi(&rs, 2, _state));
    ky = 1+4*(tilesize-hqrnduniformi(&rs, 2, _state));
    nlayers = 3;
    npoints = kx*ky;
    ae_matrix_set_length(&xy, npoints, 2+d, _state);
    for(i=0; i<=npoints-1; i++)
    {
        xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
        xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
        for(j=0; j<=d-1; j++)
        {
            xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
        }
    }
    
    /*
     * Create solver, set grid, area and other properties.
     */
    spline2dbuildercreate(d, &state, _state);
    spline2dbuildersetarea(&state, 0.0, 1.0, 0.0, 1.0, _state);
    state.maxcoresize = tilesize;
    state.interfacesize = interfacesize;
    spline2dbuildersetpoints(&state, &xy, npoints, _state);
    spline2dbuildersetgrid(&state, kx, ky, _state);
    
    /*
     * Try different NLayers>0, compare with corresponding non-positive values
     */
    for(k=1; k<=nlayers+1; k++)
    {
        spline2dbuildersetalgofastddm(&state, k, 0.0, _state);
        spline2dfit(&state, &s1, &rep1, _state);
        spline2dbuildersetalgofastddm(&state, imin2(k-nlayers, 0, _state), 0.0, _state);
        spline2dfit(&state, &s2, &rep2, _state);
        
        /*
         * Test report fields
         */
        vmax = (double)(0);
        for(i=0; i<=10; i++)
        {
            vx = hqrnduniformr(&rs, _state);
            vy = hqrnduniformr(&rs, _state);
            vmax = ae_maxreal(vmax, ae_fabs(spline2dcalc(&s1, vx, vy, _state)-spline2dcalc(&s2, vx, vy, _state), _state), _state);
        }
        ae_set_error_flag(errorflag, ae_fp_greater(vmax,1.0E-12), __FILE__, __LINE__, "testspline2dunit.ap:2212");
    }
    
    /*
     * Ability to reproduce target function in multilayer setup:
     * * small tile size
     * * nearly regular KX*KY dataset, for random KX>TileSize, KY>TileSize
     * * (2*KX)x(2*KY) grid, flexible enough
     *
     * We check that maximum and average errors at nodes are small
     */
    vmax = 0.0;
    for(pass=1; pass<=10; pass++)
    {
        d = 1;
        tilesize = 5+hqrnduniformi(&rs, 4, _state);
        kx = 4+hqrnduniformi(&rs, 4*tilesize+1, _state);
        ky = 4+hqrnduniformi(&rs, 4*tilesize+1, _state);
        npoints = kx*ky;
        ae_matrix_set_length(&xy, npoints, 2+d, _state);
        for(i=0; i<=npoints-1; i++)
        {
            xy.ptr.pp_double[i][0] = boundval(i%kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(kx-1), _state);
            xy.ptr.pp_double[i][1] = boundval(i/kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(ky-1), _state);
            for(j=0; j<=d-1; j++)
            {
                xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
            }
        }
        
        /*
         * Create solver, set grid, area and other properties.
         */
        spline2dbuildercreate(d, &state, _state);
        state.maxcoresize = tilesize;
        spline2dbuildersetpoints(&state, &xy, npoints, _state);
        spline2dbuildersetgrid(&state, ae_round(2.0*kx, _state), ae_round(2.0*ky, _state), _state);
        spline2dbuildersetarea(&state, 0.0, (double)(kx-1), 0.0, (double)(ky-1), _state);
        spline2dbuildersetalgofastddm(&state, 0, 0.0, _state);
        spline2dfit(&state, &s1, &rep1, _state);
        
        /*
         * Compare
         */
        emax = 0.0;
        eavg = 0.0;
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=d-1; j++)
            {
                v = ae_fabs(spline2dcalcvi(&s1, xy.ptr.pp_double[i][0], xy.ptr.pp_double[i][1], j, _state)-xy.ptr.pp_double[i][2+j], _state);
                emax = ae_maxreal(emax, v, _state);
                eavg = eavg+v/(npoints*d);
            }
        }
        ae_set_error_flag(errorflag, ae_fp_greater(emax,0.00100), __FILE__, __LINE__, "testspline2dunit.ap:2265");
        ae_set_error_flag(errorflag, ae_fp_greater(eavg,0.00010), __FILE__, __LINE__, "testspline2dunit.ap:2266");
        vmax = ae_maxreal(vmax, ae_fabs(emax, _state), _state);
    }
    
    /*
     * Test that increasing LambdaV results in decrease of S'' integral;
     * although (unlike BlockLLS) FastDDM does NOT minimize S'' explicitly,
     * it shows similar behavior with respect to regularization, so we can
     * expect that penalty decreases.
     *
     * NOTE: we use ultra-small values of tile size and interface size because
     *       precision is not important here.
     */
    for(pass=0; pass<=2; pass++)
    {
        d = 1;
        tilesize = 5;
        kx = 1+4*(tilesize-hqrnduniformi(&rs, 2, _state));
        ky = 1+4*(tilesize-hqrnduniformi(&rs, 2, _state));
        npoints = -1;
        vx = (double)(0);
        vy = (double)(0);
        if( pass==0 )
        {
            npoints = 1+ae_round(0.5*kx*ky, _state);
            ae_matrix_set_length(&xy, npoints, 2+d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                }
            }
            vx = (double)1/(double)(kx-1);
            vy = (double)1/(double)(ky-1);
        }
        if( pass==1 )
        {
            npoints = kx*ky;
            ae_matrix_set_length(&xy, npoints, 2+d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                xy.ptr.pp_double[i][0] = boundval(i%kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(kx-1), _state);
                xy.ptr.pp_double[i][1] = boundval(i/kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(ky-1), _state);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                }
            }
            vx = (double)(1);
            vy = (double)(1);
        }
        if( pass==2 )
        {
            npoints = 1+ae_round(3.0*kx*ky, _state);
            ae_matrix_set_length(&xy, npoints, 2+d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                }
            }
            vx = (double)1/(double)(kx-1);
            vy = (double)1/(double)(ky-1);
        }
        ae_assert(ae_fp_greater(vx,(double)(0)), "Spline2D: unit test failed integrity check", _state);
        ae_assert(ae_fp_greater(vy,(double)(0)), "Spline2D: unit test failed integrity check", _state);
        
        /*
         * Create solver, set grid, area and other properties.
         */
        spline2dbuildercreate(d, &state, _state);
        spline2dbuildersetarea(&state, (double)(0), (double)(kx-1), (double)(0), (double)(ky-1), _state);
        state.maxcoresize = tilesize;
        spline2dbuildersetpoints(&state, &xy, npoints, _state);
        spline2dbuildersetgrid(&state, kx, ky, _state);
        
        /*
         * Try different values of LambdaV, calculate penalty
         */
        ae_vector_set_length(&lambdaa, 4, _state);
        lambdaa.ptr.p_double[0] = 0.0;
        lambdaa.ptr.p_double[1] = 1.0E-1;
        lambdaa.ptr.p_double[2] = 1.0E+1;
        lambdaa.ptr.p_double[3] = 1.0E+2;
        ae_vector_set_length(&penaltya, lambdaa.cnt, _state);
        ae_vector_set_length(&rmsa, lambdaa.cnt, _state);
        for(k=0; k<=lambdaa.cnt-1; k++)
        {
            spline2dbuildersetalgofastddm(&state, 0, lambdaa.ptr.p_double[k], _state);
            spline2dfit(&state, &s1, &rep1, _state);
            h = 1.0E-4;
            v = 0.0;
            for(i=0; i<=kx-1; i++)
            {
                for(j=0; j<=ky-1; j++)
                {
                    v0 = i*vx;
                    v1 = j*vy;
                    f0 = spline2dcalc(&s1, v0-h, v1, _state);
                    f1 = spline2dcalc(&s1, v0, v1, _state);
                    f2 = spline2dcalc(&s1, v0+h, v1, _state);
                    v = v+ae_sqr((f0+f2-2*f1)/ae_sqr(h, _state), _state);
                    f0 = spline2dcalc(&s1, v0, v1-h, _state);
                    f1 = spline2dcalc(&s1, v0, v1, _state);
                    f2 = spline2dcalc(&s1, v0, v1+h, _state);
                    v = v+ae_sqr((f0+f2-2*f1)/ae_sqr(h, _state), _state);
                    f0 = spline2dcalc(&s1, v0-h, v1-h, _state);
                    f1 = spline2dcalc(&s1, v0+h, v1-h, _state);
                    f2 = spline2dcalc(&s1, v0-h, v1+h, _state);
                    f3 = spline2dcalc(&s1, v0+h, v1+h, _state);
                    v = v+2*ae_sqr((f3-f2-f1+f0)/ae_sqr(h, _state), _state);
                }
            }
            penaltya.ptr.p_double[k] = v;
            rmsa.ptr.p_double[k] = rep1.rmserror;
        }
        for(k=0; k<=lambdaa.cnt-2; k++)
        {
            ae_set_error_flag(errorflag, ae_fp_less(penaltya.ptr.p_double[k],penaltya.ptr.p_double[k+1]), __FILE__, __LINE__, "testspline2dunit.ap:2387");
        }
    }
    
    /*
     * Test that refining grid in "K:=2*K-1" fashion results in
     * decrease of RMS error; although it seems quite silly test,
     * in fact it checks various failures in the algorithm.
     *
     * NOTE: we set high penalty coefficient in order to avoid nearly
     *       precise fits (such fits are hard to test reliably)
     *
     * NOTE: we use ultra-small values of tile size and interface size because
     *       precision is not important here.
     */
    for(pass=0; pass<=2; pass++)
    {
        d = 1;
        tilesize = 5;
        kx = 1+4*tilesize;
        ky = 1+4*tilesize;
        nlayers = 3;
        npoints = -1;
        if( pass==0 )
        {
            npoints = 1+ae_round(0.1*kx*ky, _state);
            ae_matrix_set_length(&xy, npoints, 2+d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                }
            }
            vx = (double)1/(double)(kx-1);
            vy = (double)1/(double)(ky-1);
        }
        if( pass==1 )
        {
            npoints = kx*ky;
            ae_matrix_set_length(&xy, npoints, 2+d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                xy.ptr.pp_double[i][0] = boundval(i%kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(kx-1), _state);
                xy.ptr.pp_double[i][1] = boundval(i/kx+0.05*hqrndnormal(&rs, _state), (double)(0), (double)(ky-1), _state);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                }
            }
            vx = (double)(1);
            vy = (double)(1);
        }
        if( pass==2 )
        {
            npoints = 1+ae_round(2.0*kx*ky, _state);
            ae_matrix_set_length(&xy, npoints, 2+d, _state);
            for(i=0; i<=npoints-1; i++)
            {
                xy.ptr.pp_double[i][0] = hqrnduniformr(&rs, _state);
                xy.ptr.pp_double[i][1] = hqrnduniformr(&rs, _state);
                for(j=0; j<=d-1; j++)
                {
                    xy.ptr.pp_double[i][2+j] = hqrndnormal(&rs, _state);
                }
            }
            vx = (double)1/(double)(kx-1);
            vy = (double)1/(double)(ky-1);
        }
        
        /*
         * Create solver, set grid, area and other properties.
         */
        spline2dbuildercreate(d, &state, _state);
        spline2dbuildersetarea(&state, (double)(0), (double)(kx-1), (double)(0), (double)(ky-1), _state);
        state.maxcoresize = tilesize;
        spline2dbuildersetpoints(&state, &xy, npoints, _state);
        
        /*
         * Try different values of LambdaV, calculate penalty
         */
        ae_vector_set_length(&rmsa, nlayers, _state);
        kxcur = tilesize+1;
        kycur = tilesize+1;
        for(k=0; k<=rmsa.cnt-1; k++)
        {
            spline2dbuildersetgrid(&state, kxcur, kycur, _state);
            spline2dbuildersetalgofastddm(&state, 0, 0.0, _state);
            spline2dfit(&state, &s1, &rep1, _state);
            rmsa.ptr.p_double[k] = rep1.rmserror;
            kxcur = 2*kxcur-1;
            kycur = 2*kycur-1;
        }
        for(k=0; k<=rmsa.cnt-2; k++)
        {
            ae_set_error_flag(errorflag, ae_fp_less_eq(rmsa.ptr.p_double[k],rmsa.ptr.p_double[k+1]), __FILE__, __LINE__, "testspline2dunit.ap:2479");
        }
    }
    ae_frame_leave(_state);
}



static double testrbfunit_tol = 1.0E-10;
static ae_int_t testrbfunit_mxits = 0;
static double testrbfunit_heps = 1.0E-12;
static ae_bool testrbfunit_specialtest(ae_state *_state);
static ae_bool testrbfunit_basicrbftest(ae_state *_state);
static ae_bool testrbfunit_irregularrbftest(ae_state *_state);
static ae_bool testrbfunit_linearitymodelrbftest(ae_state *_state);
static ae_bool testrbfunit_serializationtest(ae_state *_state);
static ae_bool testrbfunit_searcherr(/* Real    */ ae_matrix* y0,
     /* Real    */ ae_matrix* y1,
     ae_int_t n,
     ae_int_t ny,
     ae_int_t errtype,
     /* Real    */ ae_vector* b1,
     /* Real    */ ae_vector* delta,
     ae_state *_state);
static ae_bool testrbfunit_basicmultilayerrbftest(ae_state *_state);
static void testrbfunit_gridcalc23test(ae_bool* errorflag,
     ae_state *_state);
static ae_bool testrbfunit_basichrbftest(ae_state *_state);
static ae_bool testrbfunit_scaledhrbftest(ae_state *_state);
static ae_bool testrbfunit_spechrbftest(ae_state *_state);
static ae_bool testrbfunit_gridhrbftest(ae_state *_state);





ae_bool testrbf(ae_bool silent, ae_state *_state)
{
    ae_bool specialerrors;
    ae_bool basicrbferrors;
    ae_bool irregularrbferrors;
    ae_bool linearitymodelrbferr;
    ae_bool sqrdegmatrixrbferr;
    ae_bool sererrors;
    ae_bool multilayerrbf1derrors;
    ae_bool multilayerrbferrors;
    ae_bool gridcalc23errors;
    ae_bool hrbfbasicerrors;
    ae_bool hrbfscaleerrors;
    ae_bool hrbfspecerrors;
    ae_bool hrbfgriderrors;
    ae_bool hrbferrors;
    ae_bool waserrors;
    ae_bool result;


    
    /*
     * HRBF tests
     */
    hrbfbasicerrors = testrbfunit_basichrbftest(_state);
    hrbfspecerrors = testrbfunit_spechrbftest(_state);
    hrbfscaleerrors = testrbfunit_scaledhrbftest(_state);
    hrbfgriderrors = testrbfunit_gridhrbftest(_state);
    hrbferrors = ((hrbfbasicerrors||hrbfspecerrors)||hrbfscaleerrors)||hrbfgriderrors;
    
    /*
     * Other tests
     */
    specialerrors = testrbfunit_specialtest(_state);
    basicrbferrors = testrbfunit_basicrbftest(_state);
    irregularrbferrors = testrbfunit_irregularrbftest(_state);
    linearitymodelrbferr = testrbfunit_linearitymodelrbftest(_state);
    sqrdegmatrixrbferr = sqrdegmatrixrbftest(ae_true, _state);
    multilayerrbf1derrors = ae_false;
    multilayerrbferrors = testrbfunit_basicmultilayerrbftest(_state);
    sererrors = testrbfunit_serializationtest(_state);
    gridcalc23errors = ae_false;
    testrbfunit_gridcalc23test(&gridcalc23errors, _state);
    
    /*
     * report
     */
    waserrors = ((((((((specialerrors||basicrbferrors)||irregularrbferrors)||linearitymodelrbferr)||sqrdegmatrixrbferr)||sererrors)||multilayerrbf1derrors)||multilayerrbferrors)||gridcalc23errors)||hrbferrors;
    if( !silent )
    {
        printf("TESTING RBF\n");
        printf("GENERAL TESTS:\n");
        printf("* serialization test:                              ");
        if( sererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* special properties:                              ");
        if( specialerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RBF-V2:\n");
        printf("* basic HRBF test:                                 ");
        if( hrbfbasicerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* scale-related tests:                             ");
        if( hrbfscaleerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* grid calculation tests:                          ");
        if( hrbfgriderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* special properties:                              ");
        if( hrbfspecerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RBF-V1:\n");
        printf("* basicRBFTest:                                    ");
        if( basicrbferrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* irregularRBFTest:                                ");
        if( irregularrbferrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* linearity test:                                  ");
        if( linearitymodelrbferr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SqrDegMatrixRBFTest:                             ");
        if( sqrdegmatrixrbferr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* MultiLayerRBFErrors in 1D test:                  ");
        if( multilayerrbf1derrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* MultiLayerRBFErrors in 2-3D test:                ");
        if( multilayerrbferrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* GridCalc2/3V:                                    ");
        if( gridcalc23errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        
        /*
         * was errors?
         */
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
The test  has  to  check, that  algorithm can solve problems of matrix are
degenerate.
    * used model with linear term;
    * points locate in a subspace of dimension less than an original space.

  -- ALGLIB --
     Copyright 13.12.2011 by Bochkanov Sergey
*************************************************************************/
ae_bool sqrdegmatrixrbftest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfreport rep;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t np;
    double sx;
    double sy;
    double zx;
    double px;
    double zy;
    double py;
    double q;
    double z;
    ae_vector point;
    ae_matrix a;
    ae_vector d0;
    ae_vector d1;
    ae_int_t gen;
    ae_vector pvd0;
    ae_vector pvd1;
    double pvdnorm;
    double vnorm;
    double dd0;
    double dd1;
    ae_matrix gp;
    ae_vector x;
    ae_vector y;
    ae_int_t unx;
    ae_int_t uny;
    ae_matrix xwr;
    ae_matrix v;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double eps;
    ae_int_t modelversion;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&point, 0, sizeof(point));
    memset(&a, 0, sizeof(a));
    memset(&d0, 0, sizeof(d0));
    memset(&d1, 0, sizeof(d1));
    memset(&pvd0, 0, sizeof(pvd0));
    memset(&pvd1, 0, sizeof(pvd1));
    memset(&gp, 0, sizeof(gp));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&xwr, 0, sizeof(xwr));
    memset(&v, 0, sizeof(v));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    ae_vector_init(&point, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&pvd0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&pvd1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xwr, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);

    zx = (double)(10);
    px = (double)(15);
    zy = (double)(10);
    py = (double)(15);
    eps = 1.0E-6;
    ny = 1;
    for(nx=2; nx<=3; nx++)
    {
        
        /*
         * prepare test problem
         */
        sx = ae_pow(zx, px*(ae_randominteger(3, _state)-1), _state);
        sy = ae_pow(zy, py*(ae_randominteger(3, _state)-1), _state);
        ae_vector_set_length(&x, nx, _state);
        ae_vector_set_length(&y, ny, _state);
        ae_vector_set_length(&point, nx, _state);
        rbfcreate(nx, ny, &s, _state);
        rbfsetcond(&s, testrbfunit_heps, testrbfunit_heps, testrbfunit_mxits, _state);
        q = 0.25+ae_randomreal(_state);
        z = 4.5+ae_randomreal(_state);
        rbfsetalgoqnn(&s, q, z, _state);
        
        /*
         * start points for grid
         */
        for(i=0; i<=nx-1; i++)
        {
            point.ptr.p_double[i] = sx*(2*ae_randomreal(_state)-1);
        }
        if( nx==2 )
        {
            for(k0=2; k0<=4; k0++)
            {
                rmatrixrndorthogonal(nx, &a, _state);
                ae_vector_set_length(&d0, nx, _state);
                ae_v_move(&d0.ptr.p_double[0], 1, &a.ptr.pp_double[0][0], a.stride, ae_v_len(0,nx-1));
                np = k0;
                ae_matrix_set_length(&gp, np, nx+ny, _state);
                
                /*
                 * create grid
                 */
                for(i=0; i<=k0-1; i++)
                {
                    gp.ptr.pp_double[i][0] = point.ptr.p_double[0]+sx*i*d0.ptr.p_double[0];
                    gp.ptr.pp_double[i][1] = point.ptr.p_double[1]+sx*i*d0.ptr.p_double[1];
                    for(k=0; k<=ny-1; k++)
                    {
                        gp.ptr.pp_double[i][nx+k] = sy*(2*ae_randomreal(_state)-1);
                    }
                }
                rbfsetpoints(&s, &gp, np, _state);
                rbfbuildmodel(&s, &rep, _state);
                for(i=0; i<=np-1; i++)
                {
                    x.ptr.p_double[0] = gp.ptr.pp_double[i][0];
                    x.ptr.p_double[1] = gp.ptr.pp_double[i][1];
                    rbfcalc(&s, &x, &y, _state);
                    for(j=0; j<=ny-1; j++)
                    {
                        if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                        {
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                }
            }
        }
        if( nx==3 )
        {
            for(k0=2; k0<=4; k0++)
            {
                for(k1=2; k1<=4; k1++)
                {
                    for(gen=1; gen<=2; gen++)
                    {
                        rmatrixrndorthogonal(nx, &a, _state);
                        ae_vector_set_length(&d0, nx, _state);
                        ae_v_move(&d0.ptr.p_double[0], 1, &a.ptr.pp_double[0][0], a.stride, ae_v_len(0,nx-1));
                        
                        /*
                         * create grid
                         */
                        np = -1;
                        if( gen==1 )
                        {
                            np = k0;
                            ae_matrix_set_length(&gp, np, nx+ny, _state);
                            for(i=0; i<=k0-1; i++)
                            {
                                gp.ptr.pp_double[i][0] = point.ptr.p_double[0]+sx*i*d0.ptr.p_double[0];
                                gp.ptr.pp_double[i][1] = point.ptr.p_double[1]+sx*i*d0.ptr.p_double[1];
                                gp.ptr.pp_double[i][2] = point.ptr.p_double[2]+sx*i*d0.ptr.p_double[2];
                                for(k=0; k<=ny-1; k++)
                                {
                                    gp.ptr.pp_double[i][nx+k] = sy*(2*ae_randomreal(_state)-1);
                                }
                            }
                        }
                        if( gen==2 )
                        {
                            ae_vector_set_length(&d1, nx, _state);
                            ae_v_move(&d1.ptr.p_double[0], 1, &a.ptr.pp_double[0][1], a.stride, ae_v_len(0,nx-1));
                            np = k0*k1;
                            ae_matrix_set_length(&gp, np, nx+ny, _state);
                            for(i=0; i<=k0-1; i++)
                            {
                                for(j=0; j<=k1-1; j++)
                                {
                                    gp.ptr.pp_double[i*k1+j][0] = sx*i*d0.ptr.p_double[0]+sx*j*d1.ptr.p_double[0];
                                    gp.ptr.pp_double[i*k1+j][1] = sx*i*d0.ptr.p_double[1]+sx*j*d1.ptr.p_double[1];
                                    gp.ptr.pp_double[i*k1+j][2] = sx*i*d0.ptr.p_double[2]+sx*j*d1.ptr.p_double[2];
                                    for(k=0; k<=ny-1; k++)
                                    {
                                        gp.ptr.pp_double[i*k1+j][nx+k] = sy*(2*ae_randomreal(_state)-1);
                                    }
                                }
                            }
                        }
                        ae_assert(np>=0, "rbf test: integrity error", _state);
                        rbfsetpoints(&s, &gp, np, _state);
                        rbfbuildmodel(&s, &rep, _state);
                        for(i=0; i<=np-1; i++)
                        {
                            x.ptr.p_double[0] = gp.ptr.pp_double[i][0];
                            x.ptr.p_double[1] = gp.ptr.pp_double[i][1];
                            x.ptr.p_double[2] = gp.ptr.pp_double[i][2];
                            rbfcalc(&s, &x, &y, _state);
                            for(j=0; j<=ny-1; j++)
                            {
                                if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                {
                                    result = ae_true;
                                    ae_frame_leave(_state);
                                    return result;
                                }
                            }
                        }
                        if( gen==2 )
                        {
                            rbfunpack(&s, &unx, &uny, &xwr, &np, &v, &modelversion, _state);
                            dd0 = (d0.ptr.p_double[0]*v.ptr.pp_double[0][0]+d0.ptr.p_double[1]*v.ptr.pp_double[0][1]+d0.ptr.p_double[2]*v.ptr.pp_double[0][2])/(d0.ptr.p_double[0]*d0.ptr.p_double[0]+d0.ptr.p_double[1]*d0.ptr.p_double[1]+d0.ptr.p_double[2]*d0.ptr.p_double[2]);
                            dd1 = (d1.ptr.p_double[0]*v.ptr.pp_double[0][0]+d1.ptr.p_double[1]*v.ptr.pp_double[0][1]+d1.ptr.p_double[2]*v.ptr.pp_double[0][2])/(d1.ptr.p_double[0]*d1.ptr.p_double[0]+d1.ptr.p_double[1]*d1.ptr.p_double[1]+d1.ptr.p_double[2]*d1.ptr.p_double[2]);
                            ae_vector_set_length(&pvd0, nx, _state);
                            ae_vector_set_length(&pvd1, nx, _state);
                            for(i=0; i<=nx-1; i++)
                            {
                                pvd0.ptr.p_double[i] = dd0*d0.ptr.p_double[i];
                                pvd1.ptr.p_double[i] = dd1*d1.ptr.p_double[i];
                            }
                            pvdnorm = ae_sqrt(ae_sqr(v.ptr.pp_double[0][0]-pvd0.ptr.p_double[0]-pvd1.ptr.p_double[0], _state)+ae_sqr(v.ptr.pp_double[0][1]-pvd0.ptr.p_double[1]-pvd1.ptr.p_double[1], _state)+ae_sqr(v.ptr.pp_double[0][2]-pvd0.ptr.p_double[2]-pvd1.ptr.p_double[2], _state), _state);
                            vnorm = ae_sqrt(ae_sqr(v.ptr.pp_double[0][0], _state)+ae_sqr(v.ptr.pp_double[0][1], _state)+ae_sqr(v.ptr.pp_double[0][2], _state), _state);
                            if( ae_fp_greater(pvdnorm,vnorm*testrbfunit_tol) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing basic functionality of RBF module on regular grids with
multi-layer algorithm in 1D.

  -- ALGLIB --
     Copyright 2.03.2012 by Bochkanov Sergey
*************************************************************************/
ae_bool basicmultilayerrbf1dtest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfreport rep;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t linterm;
    ae_int_t n;
    double q;
    double r;
    ae_int_t errtype;
    ae_vector delta;
    ae_int_t nlayers;
    double a;
    double b;
    double f1;
    double f2;
    ae_vector a1;
    ae_vector b1;
    ae_matrix gp;
    ae_vector x;
    ae_vector y;
    ae_matrix mody0;
    ae_matrix mody1;
    ae_matrix gy;
    ae_vector gpgx0;
    ae_vector gpgx1;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&delta, 0, sizeof(delta));
    memset(&a1, 0, sizeof(a1));
    memset(&b1, 0, sizeof(b1));
    memset(&gp, 0, sizeof(gp));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&mody0, 0, sizeof(mody0));
    memset(&mody1, 0, sizeof(mody1));
    memset(&gy, 0, sizeof(gy));
    memset(&gpgx0, 0, sizeof(gpgx0));
    memset(&gpgx1, 0, sizeof(gpgx1));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    ae_vector_init(&delta, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mody0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mody1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gpgx0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gpgx1, 0, DT_REAL, _state, ae_true);

    a = 1.0;
    b = (double)1/(double)9;
    f1 = 1.0;
    f2 = 10.0;
    passcount = 5;
    n = 100;
    ae_vector_set_length(&gpgx0, n, _state);
    ae_vector_set_length(&gpgx1, n, _state);
    for(i=0; i<=n-1; i++)
    {
        gpgx0.ptr.p_double[i] = (double)i/(double)n;
        gpgx1.ptr.p_double[i] = (double)(0);
    }
    r = (double)(1);
    for(pass=0; pass<=passcount-1; pass++)
    {
        nx = ae_randominteger(2, _state)+2;
        ny = ae_randominteger(3, _state)+1;
        linterm = ae_randominteger(3, _state)+1;
        ae_vector_set_length(&x, nx, _state);
        ae_vector_set_length(&y, ny, _state);
        ae_vector_set_length(&a1, ny, _state);
        ae_vector_set_length(&b1, ny, _state);
        ae_vector_set_length(&delta, ny, _state);
        ae_matrix_set_length(&mody0, n, ny, _state);
        ae_matrix_set_length(&mody1, n, ny, _state);
        for(i=0; i<=ny-1; i++)
        {
            a1.ptr.p_double[i] = a+0.01*a*(2*ae_randomreal(_state)-1);
            b1.ptr.p_double[i] = b+0.01*b*(2*ae_randomreal(_state)-1);
            delta.ptr.p_double[i] = 0.35*b1.ptr.p_double[i];
        }
        ae_matrix_set_length(&gp, n, nx+ny, _state);
        
        /*
         * create grid
         */
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=nx-1; j++)
            {
                gp.ptr.pp_double[i][j] = (double)(0);
            }
            gp.ptr.pp_double[i][0] = (double)i/(double)n;
            for(j=0; j<=ny-1; j++)
            {
                gp.ptr.pp_double[i][nx+j] = a1.ptr.p_double[j]*ae_cos(f1*2*ae_pi*gp.ptr.pp_double[i][0], _state)+b1.ptr.p_double[j]*ae_cos(f2*2*ae_pi*gp.ptr.pp_double[i][0], _state);
                mody0.ptr.pp_double[i][j] = gp.ptr.pp_double[i][nx+j];
            }
        }
        q = (double)(1);
        nlayers = 1;
        errtype = 1;
        
        /*
         * test multilayer algorithm with different parameters
         */
        while(ae_fp_greater_eq(q,1/(2*f2)))
        {
            rbfcreate(nx, ny, &s, _state);
            rbfsetalgomultilayer(&s, r, nlayers, 0.0, _state);
            if( linterm==1 )
            {
                rbfsetlinterm(&s, _state);
            }
            if( linterm==2 )
            {
                rbfsetconstterm(&s, _state);
            }
            if( linterm==3 )
            {
                rbfsetzeroterm(&s, _state);
            }
            rbfsetpoints(&s, &gp, n, _state);
            rbfbuildmodel(&s, &rep, _state);
            if( ny==1 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=nx-1; j++)
                    {
                        x.ptr.p_double[j] = gp.ptr.pp_double[i][j];
                    }
                    if( nx==2 )
                    {
                        mody1.ptr.pp_double[i][0] = rbfcalc2(&s, x.ptr.p_double[0], x.ptr.p_double[1], _state);
                    }
                    else
                    {
                        if( nx==3 )
                        {
                            mody1.ptr.pp_double[i][0] = rbfcalc3(&s, x.ptr.p_double[0], x.ptr.p_double[1], x.ptr.p_double[2], _state);
                        }
                        else
                        {
                            ae_assert(ae_false, "BasicMultiLayerRBFTest1D: Invalid variable NX(NX neither 2 nor 3)", _state);
                        }
                    }
                }
                if( testrbfunit_searcherr(&mody0, &mody1, n, ny, errtype, &b1, &delta, _state) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                if( nx==2 )
                {
                    rbfgridcalc2(&s, &gpgx0, n, &gpgx1, n, &gy, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        mody1.ptr.pp_double[i][0] = gy.ptr.pp_double[i][0];
                    }
                }
                if( testrbfunit_searcherr(&mody0, &mody1, n, ny, errtype, &b1, &delta, _state) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    x.ptr.p_double[j] = gp.ptr.pp_double[i][j];
                }
                rbfcalc(&s, &x, &y, _state);
                for(j=0; j<=ny-1; j++)
                {
                    mody1.ptr.pp_double[i][j] = y.ptr.p_double[j];
                }
            }
            if( testrbfunit_searcherr(&mody0, &mody1, n, ny, errtype, &b1, &delta, _state) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    x.ptr.p_double[j] = gp.ptr.pp_double[i][j];
                }
                rbfcalcbuf(&s, &x, &y, _state);
                for(j=0; j<=ny-1; j++)
                {
                    mody1.ptr.pp_double[i][j] = y.ptr.p_double[j];
                }
            }
            if( testrbfunit_searcherr(&mody0, &mody1, n, ny, errtype, &b1, &delta, _state) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            q = q/2;
            nlayers = nlayers+1;
            if( errtype==1&&ae_fp_less_eq(q,1/f2) )
            {
                errtype = 2;
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests special cases:
* uninitialized RBF model will correctly return zero values
* RBF correctly handles 1 or 2 distinct points
* when  we have many uniformly spaced points and one outlier, filter which
  is applied to radii, makes all radii equal (RBF-QNN).
* RBF-ML with NLayers=0 gives linear model
* Hierarchical RBF with NLayers=0 gives linear model

  -- ALGLIB --
     Copyright 13.12.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_specialtest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfreport rep;
    ae_int_t n;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t t;
    ae_matrix xy;
    ae_matrix vf;
    ae_vector x;
    ae_vector y;
    ae_int_t termtype;
    double errtol;
    ae_int_t tmpnx;
    ae_int_t tmpny;
    ae_int_t tmpnc;
    ae_matrix xwr;
    ae_matrix v;
    double sx;
    double z;
    double va;
    double vb;
    double vc;
    double vd;
    ae_int_t modelversion;
    double vv;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&vf, 0, sizeof(vf));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&xwr, 0, sizeof(xwr));
    memset(&v, 0, sizeof(v));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vf, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xwr, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);

    errtol = 1.0E-9;
    result = ae_false;
    
    /*
     * Create model in the default state (no parameters/points specified).
     * With probability 0.5 we do one of the following:
     * * test that default state of the model is a zero model (all Calc()
     *   functions return zero)
     * * call RBFBuildModel() (without specifying anything) and test  that
     *   all Calc() functions return zero.
     *
     * NOTE: because NX varies between 1 and 4, both V1 (old) and V2 RBFs
     *       are tested.
     */
    for(nx=1; nx<=4; nx++)
    {
        for(ny=1; ny<=3; ny++)
        {
            rbfcreate(nx, ny, &s, _state);
            if( ae_fp_greater(ae_randomreal(_state),0.5) )
            {
                rbfbuildmodel(&s, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:202");
                    ae_frame_leave(_state);
                    return result;
                }
            }
            ae_vector_set_length(&x, nx, _state);
            ae_vector_set_length(&y, 1, _state);
            for(i=0; i<=nx-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            rbfcalc(&s, &x, &y, _state);
            if( y.cnt!=ny )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:213");
                ae_frame_leave(_state);
                return result;
            }
            for(i=0; i<=ny-1; i++)
            {
                if( ae_fp_neq(y.ptr.p_double[i],(double)(0)) )
                {
                    ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:219");
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    
    /*
     * Create default model with 1 point and different types of linear term.
     * Test algorithm on such dataset.
     *
     * NOTE: because NX varies between 1 and 4, both V1 (old) and V2 RBFs
     *       are tested.
     */
    for(nx=1; nx<=4; nx++)
    {
        for(ny=1; ny<=3; ny++)
        {
            rbfcreate(nx, ny, &s, _state);
            for(termtype=0; termtype<=1; termtype++)
            {
                if( termtype==0 )
                {
                    rbfsetlinterm(&s, _state);
                }
                if( termtype==1 )
                {
                    rbfsetconstterm(&s, _state);
                }
                ae_matrix_set_length(&xy, 1, nx+ny, _state);
                for(i=0; i<=nx+ny-1; i++)
                {
                    xy.ptr.pp_double[0][i] = 2*ae_randomreal(_state)-1;
                }
                rbfsetpoints(&s, &xy, 1, _state);
                rbfbuildmodel(&s, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:248");
                    ae_frame_leave(_state);
                    return result;
                }
                
                /*
                 * First, test that model exactly reproduces our dataset in the specified point
                 */
                ae_vector_set_length(&x, nx, _state);
                for(i=0; i<=nx-1; i++)
                {
                    x.ptr.p_double[i] = xy.ptr.pp_double[0][i];
                }
                rbfcalc(&s, &x, &y, _state);
                if( y.cnt!=ny )
                {
                    ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:261");
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=ny-1; i++)
                {
                    if( ae_fp_greater(ae_fabs(y.ptr.p_double[i]-xy.ptr.pp_double[0][nx+i], _state),errtol) )
                    {
                        ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:267");
                        ae_frame_leave(_state);
                        return result;
                    }
                }
                
                /*
                 * Second, test that model is constant
                 */
                for(i=0; i<=nx-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                rbfcalc(&s, &x, &y, _state);
                if( y.cnt!=ny )
                {
                    ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:279");
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=ny-1; i++)
                {
                    if( ae_fp_greater(ae_fabs(y.ptr.p_double[i]-xy.ptr.pp_double[0][nx+i], _state),errtol) )
                    {
                        ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:285");
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
    }
    
    /*
     * Create model with 2 points and different types of linear term.
     * Test algorithm on such dataset.
     */
    for(nx=2; nx<=3; nx++)
    {
        for(ny=1; ny<=3; ny++)
        {
            rbfcreate(nx, ny, &s, _state);
            for(termtype=0; termtype<=1; termtype++)
            {
                if( termtype==0 )
                {
                    rbfsetlinterm(&s, _state);
                }
                if( termtype==1 )
                {
                    rbfsetconstterm(&s, _state);
                }
                if( termtype==2 )
                {
                    rbfsetzeroterm(&s, _state);
                }
                ae_matrix_set_length(&xy, 2, nx+ny, _state);
                for(i=0; i<=nx+ny-1; i++)
                {
                    xy.ptr.pp_double[0][i] = 2*ae_randomreal(_state)-1;
                }
                for(i=0; i<=nx+ny-1; i++)
                {
                    xy.ptr.pp_double[1][i] = xy.ptr.pp_double[0][i]+1.0;
                }
                rbfsetpoints(&s, &xy, 2, _state);
                rbfbuildmodel(&s, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                for(j=0; j<=1; j++)
                {
                    ae_vector_set_length(&x, nx, _state);
                    for(i=0; i<=nx-1; i++)
                    {
                        x.ptr.p_double[i] = xy.ptr.pp_double[j][i];
                    }
                    rbfcalc(&s, &x, &y, _state);
                    if( y.cnt!=ny )
                    {
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                    for(i=0; i<=ny-1; i++)
                    {
                        if( ae_fp_greater(ae_fabs(y.ptr.p_double[i]-xy.ptr.pp_double[j][nx+i], _state),errtol) )
                        {
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                    }
                }
            }
        }
    }
    
    /*
     * Generate a set of points (xi,yi) = (SX*i,0), and one
     * outlier (x_far,y_far)=(-1000*SX,0).
     *
     * Radii filtering should place a bound on the radius of outlier.
     */
    for(nx=2; nx<=3; nx++)
    {
        for(ny=1; ny<=3; ny++)
        {
            sx = ae_exp(-5+10*ae_randomreal(_state), _state);
            rbfcreate(nx, ny, &s, _state);
            ae_matrix_set_length(&xy, 20, nx+ny, _state);
            for(i=0; i<=xy.rows-1; i++)
            {
                xy.ptr.pp_double[i][0] = sx*i;
                for(j=1; j<=nx-1; j++)
                {
                    xy.ptr.pp_double[i][j] = (double)(0);
                }
                for(j=0; j<=ny-1; j++)
                {
                    xy.ptr.pp_double[i][nx+j] = ae_randomreal(_state);
                }
            }
            xy.ptr.pp_double[xy.rows-1][0] = -1000*sx;
            rbfsetpoints(&s, &xy, xy.rows, _state);
            
            /*
             * Try random Z from [1,5]
             */
            z = 1+ae_randomreal(_state)*4;
            rbfsetalgoqnn(&s, 1.0, z, _state);
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            rbfunpack(&s, &tmpnx, &tmpny, &xwr, &tmpnc, &v, &modelversion, _state);
            if( ((((tmpnx!=nx||tmpny!=ny)||tmpnc!=xy.rows)||xwr.cols!=nx+ny+1)||xwr.rows!=tmpnc)||modelversion!=1 )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(i=0; i<=tmpnc-2; i++)
            {
                if( ae_fp_greater(ae_fabs(xwr.ptr.pp_double[i][nx+ny]-sx, _state),errtol) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            if( ae_fp_greater(ae_fabs(xwr.ptr.pp_double[tmpnc-1][nx+ny]-z*sx, _state),errtol) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    
    /*
     * RBF-ML with NLayers=0 gives us linear model.
     *
     * In order to perform this test, we use test function which
     * is perfectly linear and see whether RBF model is able to
     * reproduce such function.
     */
    n = 5;
    for(ny=1; ny<=3; ny++)
    {
        va = 2*ae_randomreal(_state)-1;
        vb = 2*ae_randomreal(_state)-1;
        vc = 2*ae_randomreal(_state)-1;
        vd = 2*ae_randomreal(_state)-1;
        
        /*
         * Test NX=2.
         * Generate linear function using random coefficients VA/VB/VC.
         * Function is K-dimensional vector-valued, each component has slightly
         * different coefficients.
         */
        ae_matrix_set_length(&xy, n*n, 2+ny, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[n*i+j][0] = (double)(i);
                xy.ptr.pp_double[n*i+j][1] = (double)(j);
                for(k=0; k<=ny-1; k++)
                {
                    xy.ptr.pp_double[n*i+j][2+k] = (va+0.1*k)*i+(vb+0.2*k)*j+(vc+0.3*k);
                }
            }
        }
        rbfcreate(2, ny, &s, _state);
        rbfsetpoints(&s, &xy, n*n, _state);
        rbfsetalgomultilayer(&s, 1.0, 0, 0.01, _state);
        rbfbuildmodel(&s, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = (n-1)*ae_randomreal(_state);
        x.ptr.p_double[1] = (n-1)*ae_randomreal(_state);
        if( ny==1&&ae_fp_greater(ae_fabs(rbfcalc2(&s, x.ptr.p_double[0], x.ptr.p_double[1], _state)-(va*x.ptr.p_double[0]+vb*x.ptr.p_double[1]+vc), _state),errtol) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        rbfcalc(&s, &x, &y, _state);
        for(k=0; k<=ny-1; k++)
        {
            if( ae_fp_greater(ae_fabs(y.ptr.p_double[k]-((va+0.1*k)*x.ptr.p_double[0]+(vb+0.2*k)*x.ptr.p_double[1]+(vc+0.3*k)), _state),errtol) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
        
        /*
         * Test NX=3.
         * Generate linear function using random coefficients VA/VB/VC/VC.
         * Function is K-dimensional vector-valued, each component has slightly
         * different coefficients.
         */
        ae_matrix_set_length(&xy, n*n*n, 3+ny, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                for(t=0; t<=n-1; t++)
                {
                    xy.ptr.pp_double[n*n*i+n*j+t][0] = (double)(i);
                    xy.ptr.pp_double[n*n*i+n*j+t][1] = (double)(j);
                    xy.ptr.pp_double[n*n*i+n*j+t][2] = (double)(t);
                    for(k=0; k<=ny-1; k++)
                    {
                        xy.ptr.pp_double[n*n*i+n*j+t][3+k] = (va+0.1*k)*i+(vb+0.2*k)*j+(vc+0.3*k)*t+(vd+0.4*k);
                    }
                }
            }
        }
        rbfcreate(3, ny, &s, _state);
        rbfsetpoints(&s, &xy, n*n*n, _state);
        rbfsetalgomultilayer(&s, 1.0, 0, 0.01, _state);
        rbfbuildmodel(&s, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        ae_vector_set_length(&x, 3, _state);
        x.ptr.p_double[0] = (n-1)*ae_randomreal(_state);
        x.ptr.p_double[1] = (n-1)*ae_randomreal(_state);
        x.ptr.p_double[2] = (n-1)*ae_randomreal(_state);
        if( ny==1&&ae_fp_greater(ae_fabs(rbfcalc3(&s, x.ptr.p_double[0], x.ptr.p_double[1], x.ptr.p_double[2], _state)-(va*x.ptr.p_double[0]+vb*x.ptr.p_double[1]+vc*x.ptr.p_double[2]+vd), _state),errtol) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        rbfcalc(&s, &x, &y, _state);
        for(k=0; k<=ny-1; k++)
        {
            if( ae_fp_greater(ae_fabs(y.ptr.p_double[k]-((va+0.1*k)*x.ptr.p_double[0]+(vb+0.2*k)*x.ptr.p_double[1]+(vc+0.3*k)*x.ptr.p_double[2]+(vd+0.4*k)), _state),errtol) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    
    /*
     * HierarchicalRBF with NLayers=0 gives us linear model.
     *
     * In order to perform this test, we use test function which
     * is perfectly linear and see whether RBF model is able to
     * reproduce such function.
     */
    n = 15;
    for(nx=1; nx<=5; nx++)
    {
        for(ny=1; ny<=3; ny++)
        {
            ae_matrix_set_length(&vf, ny, nx+1, _state);
            for(i=0; i<=ny-1; i++)
            {
                for(j=0; j<=nx; j++)
                {
                    vf.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
            }
            ae_matrix_set_length(&xy, n, nx+ny, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                }
                for(j=0; j<=ny-1; j++)
                {
                    xy.ptr.pp_double[i][nx+j] = vf.ptr.pp_double[j][nx];
                    for(k=0; k<=nx-1; k++)
                    {
                        xy.ptr.pp_double[i][nx+j] = xy.ptr.pp_double[i][nx+j]+vf.ptr.pp_double[j][k]*xy.ptr.pp_double[i][k];
                    }
                }
            }
            rbfcreate(nx, ny, &s, _state);
            rbfsetpoints(&s, &xy, n, _state);
            rbfsetalgohierarchical(&s, 1.0, 0, 0.0, _state);
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:525");
                ae_frame_leave(_state);
                return result;
            }
            ae_vector_set_length(&x, nx, _state);
            for(i=0; i<=nx-1; i++)
            {
                x.ptr.p_double[i] = ae_randomreal(_state);
            }
            rbfcalc(&s, &x, &y, _state);
            for(k=0; k<=ny-1; k++)
            {
                vv = vf.ptr.pp_double[k][nx];
                for(j=0; j<=nx-1; j++)
                {
                    vv = vv+vf.ptr.pp_double[k][j]*x.ptr.p_double[j];
                }
                ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vv-y.ptr.p_double[k], _state),errtol), __FILE__, __LINE__, "testrbfunit.ap:537");
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing basic functionality of RBF module on regular grids.

  -- ALGLIB --
     Copyright 13.12.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_basicrbftest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfreport rep;
    rbfcalcbuffer calcbuf;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t k2;
    ae_int_t linterm;
    ae_int_t np;
    double sx;
    double sy;
    double zx;
    double px;
    double zy;
    double py;
    double q;
    double z;
    ae_vector point;
    ae_matrix gp;
    ae_vector x;
    ae_vector y;
    ae_matrix gy;
    ae_int_t unx;
    ae_int_t uny;
    ae_matrix xwr;
    ae_matrix v;
    ae_vector gpgx0;
    ae_vector gpgx1;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    ae_int_t fidx;
    double eps;
    ae_int_t modelversion;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&calcbuf, 0, sizeof(calcbuf));
    memset(&point, 0, sizeof(point));
    memset(&gp, 0, sizeof(gp));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&gy, 0, sizeof(gy));
    memset(&xwr, 0, sizeof(xwr));
    memset(&v, 0, sizeof(v));
    memset(&gpgx0, 0, sizeof(gpgx0));
    memset(&gpgx1, 0, sizeof(gpgx1));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    _rbfcalcbuffer_init(&calcbuf, _state, ae_true);
    ae_vector_init(&point, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xwr, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gpgx0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gpgx1, 0, DT_REAL, _state, ae_true);

    zx = (double)(10);
    px = (double)(15);
    zy = (double)(10);
    py = (double)(15);
    eps = 1.0E-6;
    
    /*
     * Problem types:
     * * 2 and 3-dimensional problems
     * * problems with zero, constant, linear terms
     * * different scalings of X and Y values (1.0, 1E-15, 1E+15)
     * * regular grids different grid sizes (from 2 to 4 points for each dimension)
     *
     * We check that:
     * * RBF model correctly reproduces function value (testes with different Calc() functions)
     * * unpacked model containt correct radii
     * * linear term has correct form
     */
    for(nx=2; nx<=3; nx++)
    {
        for(ny=1; ny<=3; ny++)
        {
            for(linterm=1; linterm<=3; linterm++)
            {
                
                /*
                 * prepare test problem
                 */
                sx = ae_pow(zx, px*(ae_randominteger(3, _state)-1), _state);
                sy = ae_pow(zy, py*(ae_randominteger(3, _state)-1), _state);
                ae_vector_set_length(&x, nx, _state);
                ae_vector_set_length(&y, ny, _state);
                ae_vector_set_length(&point, nx, _state);
                rbfcreate(nx, ny, &s, _state);
                rbfsetcond(&s, testrbfunit_heps, testrbfunit_heps, testrbfunit_mxits, _state);
                q = 0.25+ae_randomreal(_state);
                z = 4.5+ae_randomreal(_state);
                rbfsetalgoqnn(&s, q, z, _state);
                if( linterm==1 )
                {
                    rbfsetlinterm(&s, _state);
                }
                if( linterm==2 )
                {
                    rbfsetconstterm(&s, _state);
                }
                if( linterm==3 )
                {
                    rbfsetzeroterm(&s, _state);
                }
                
                /*
                 * start points for grid
                 */
                for(i=0; i<=nx-1; i++)
                {
                    point.ptr.p_double[i] = sx*(2*ae_randomreal(_state)-1);
                }
                
                /*
                 * 2-dimensional test problem
                 */
                if( nx==2 )
                {
                    for(k0=2; k0<=4; k0++)
                    {
                        for(k1=2; k1<=4; k1++)
                        {
                            np = k0*k1;
                            ae_matrix_set_length(&gp, np, nx+ny, _state);
                            
                            /*
                             * create grid
                             */
                            for(i=0; i<=k0-1; i++)
                            {
                                for(j=0; j<=k1-1; j++)
                                {
                                    gp.ptr.pp_double[i*k1+j][0] = point.ptr.p_double[0]+sx*i;
                                    gp.ptr.pp_double[i*k1+j][1] = point.ptr.p_double[1]+sx*j;
                                    for(k=0; k<=ny-1; k++)
                                    {
                                        gp.ptr.pp_double[i*k1+j][nx+k] = sy*(2*ae_randomreal(_state)-1);
                                    }
                                }
                            }
                            rbfsetpoints(&s, &gp, np, _state);
                            rbfbuildmodel(&s, &rep, _state);
                            rbfcreatecalcbuffer(&s, &calcbuf, _state);
                            if( ny==1 )
                            {
                                ae_vector_set_length(&gpgx0, k0, _state);
                                ae_vector_set_length(&gpgx1, k1, _state);
                                for(i=0; i<=k0-1; i++)
                                {
                                    gpgx0.ptr.p_double[i] = point.ptr.p_double[0]+sx*i;
                                }
                                for(i=0; i<=k1-1; i++)
                                {
                                    gpgx1.ptr.p_double[i] = point.ptr.p_double[1]+sx*i;
                                }
                                rbfgridcalc2(&s, &gpgx0, k0, &gpgx1, k1, &gy, _state);
                                for(i=0; i<=k0-1; i++)
                                {
                                    for(j=0; j<=k1-1; j++)
                                    {
                                        if( ae_fp_greater(ae_fabs(gy.ptr.pp_double[i][j]-gp.ptr.pp_double[i*k1+j][nx], _state),sy*eps) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                }
                            }
                            for(i=0; i<=np-1; i++)
                            {
                                
                                /*
                                 * For each row we randomly choose a function to test
                                 * and call it. We do not call multiple functions per
                                 * row because carry-over effects may mask errors in
                                 * some function (say, it is possible that function
                                 * simply returns results from previous call of some
                                 * other function which were stored in the RBF model;
                                 * in this case, previous call with same parameters
                                 * may hide deficiencies in the function).
                                 */
                                x.ptr.p_double[0] = gp.ptr.pp_double[i][0];
                                x.ptr.p_double[1] = gp.ptr.pp_double[i][1];
                                fidx = ae_randominteger(4, _state);
                                if( fidx==0&&ny==1 )
                                {
                                    y.ptr.p_double[0] = rbfcalc2(&s, x.ptr.p_double[0], x.ptr.p_double[1], _state);
                                    if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx]-y.ptr.p_double[0], _state),sy*eps) )
                                    {
                                        result = ae_true;
                                        ae_frame_leave(_state);
                                        return result;
                                    }
                                }
                                if( fidx==1 )
                                {
                                    rbfcalc(&s, &x, &y, _state);
                                    for(j=0; j<=ny-1; j++)
                                    {
                                        if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                }
                                if( fidx==2 )
                                {
                                    rbfcalcbuf(&s, &x, &y, _state);
                                    for(j=0; j<=ny-1; j++)
                                    {
                                        if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                }
                                if( fidx==3 )
                                {
                                    rbftscalcbuf(&s, &calcbuf, &x, &y, _state);
                                    for(j=0; j<=ny-1; j++)
                                    {
                                        if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                }
                            }
                            
                            /*
                             * test for RBFUnpack
                             */
                            rbfunpack(&s, &unx, &uny, &xwr, &np, &v, &modelversion, _state);
                            if( (((((nx!=unx||ny!=uny)||xwr.rows!=np)||xwr.cols!=nx+ny+1)||v.rows!=ny)||v.cols!=nx+1)||modelversion!=1 )
                            {
                                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:735");
                                ae_frame_leave(_state);
                                return result;
                            }
                            for(i=0; i<=np-1; i++)
                            {
                                if( ae_fp_greater(ae_fabs(xwr.ptr.pp_double[i][unx+uny]-q*sx, _state),sx*eps) )
                                {
                                    result = ae_true;
                                    ae_frame_leave(_state);
                                    return result;
                                }
                            }
                            if( linterm==2 )
                            {
                                for(i=0; i<=unx-1; i++)
                                {
                                    for(j=0; j<=uny-1; j++)
                                    {
                                        if( ae_fp_neq(v.ptr.pp_double[j][i],(double)(0)) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                }
                            }
                            if( linterm==3 )
                            {
                                for(i=0; i<=unx; i++)
                                {
                                    for(j=0; j<=uny-1; j++)
                                    {
                                        if( ae_fp_neq(v.ptr.pp_double[j][i],(double)(0)) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                
                /*
                 * 3-dimensional test problems
                 */
                if( nx==3 )
                {
                    for(k0=2; k0<=4; k0++)
                    {
                        for(k1=2; k1<=4; k1++)
                        {
                            for(k2=2; k2<=4; k2++)
                            {
                                np = k0*k1*k2;
                                ae_matrix_set_length(&gp, np, nx+ny, _state);
                                
                                /*
                                 * create grid
                                 */
                                for(i=0; i<=k0-1; i++)
                                {
                                    for(j=0; j<=k1-1; j++)
                                    {
                                        for(k=0; k<=k2-1; k++)
                                        {
                                            gp.ptr.pp_double[(i*k1+j)*k2+k][0] = point.ptr.p_double[0]+sx*i;
                                            gp.ptr.pp_double[(i*k1+j)*k2+k][1] = point.ptr.p_double[1]+sx*j;
                                            gp.ptr.pp_double[(i*k1+j)*k2+k][2] = point.ptr.p_double[2]+sx*k;
                                            for(l=0; l<=ny-1; l++)
                                            {
                                                gp.ptr.pp_double[(i*k1+j)*k2+k][nx+l] = sy*(2*ae_randomreal(_state)-1);
                                            }
                                        }
                                    }
                                }
                                rbfsetpoints(&s, &gp, np, _state);
                                rbfbuildmodel(&s, &rep, _state);
                                rbfcreatecalcbuffer(&s, &calcbuf, _state);
                                for(i=0; i<=np-1; i++)
                                {
                                    
                                    /*
                                     * For each row we randomly choose a function to test
                                     * and call it. We do not call multiple functions per
                                     * row because carry-over effects may mask errors in
                                     * some function (say, it is possible that function
                                     * simply returns results from previous call of some
                                     * other function which were stored in the RBF model;
                                     * in this case, previous call with same parameters
                                     * may hide deficiencies in the function).
                                     */
                                    x.ptr.p_double[0] = gp.ptr.pp_double[i][0];
                                    x.ptr.p_double[1] = gp.ptr.pp_double[i][1];
                                    x.ptr.p_double[2] = gp.ptr.pp_double[i][2];
                                    fidx = ae_randominteger(4, _state);
                                    if( fidx==0&&ny==1 )
                                    {
                                        y.ptr.p_double[0] = rbfcalc3(&s, x.ptr.p_double[0], x.ptr.p_double[1], x.ptr.p_double[2], _state);
                                        if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx]-y.ptr.p_double[0], _state),sy*eps) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                    if( fidx==1 )
                                    {
                                        rbfcalc(&s, &x, &y, _state);
                                        for(j=0; j<=ny-1; j++)
                                        {
                                            if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                            {
                                                result = ae_true;
                                                ae_frame_leave(_state);
                                                return result;
                                            }
                                        }
                                    }
                                    if( fidx==2 )
                                    {
                                        rbfcalcbuf(&s, &x, &y, _state);
                                        for(j=0; j<=ny-1; j++)
                                        {
                                            if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                            {
                                                result = ae_true;
                                                ae_frame_leave(_state);
                                                return result;
                                            }
                                        }
                                    }
                                    if( fidx==3 )
                                    {
                                        rbftscalcbuf(&s, &calcbuf, &x, &y, _state);
                                        for(j=0; j<=ny-1; j++)
                                        {
                                            if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                            {
                                                result = ae_true;
                                                ae_frame_leave(_state);
                                                return result;
                                            }
                                        }
                                    }
                                }
                                
                                /*
                                 * test for RBFUnpack
                                 */
                                rbfunpack(&s, &unx, &uny, &xwr, &np, &v, &modelversion, _state);
                                if( (((((nx!=unx||ny!=uny)||xwr.rows!=np)||xwr.cols!=nx+ny+1)||v.rows!=ny)||v.cols!=nx+1)||modelversion!=1 )
                                {
                                    ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:852");
                                    ae_frame_leave(_state);
                                    return result;
                                }
                                for(i=0; i<=np-1; i++)
                                {
                                    if( ae_fp_greater(ae_fabs(xwr.ptr.pp_double[i][unx+uny]-q*sx, _state),sx*eps) )
                                    {
                                        result = ae_true;
                                        ae_frame_leave(_state);
                                        return result;
                                    }
                                }
                                if( linterm==2 )
                                {
                                    for(i=0; i<=unx-1; i++)
                                    {
                                        for(j=0; j<=uny-1; j++)
                                        {
                                            if( ae_fp_neq(v.ptr.pp_double[j][i],(double)(0)) )
                                            {
                                                result = ae_true;
                                                ae_frame_leave(_state);
                                                return result;
                                            }
                                        }
                                    }
                                }
                                if( linterm==3 )
                                {
                                    for(i=0; i<=unx; i++)
                                    {
                                        for(j=0; j<=uny-1; j++)
                                        {
                                            if( ae_fp_neq(v.ptr.pp_double[j][i],(double)(0)) )
                                            {
                                                result = ae_true;
                                                ae_frame_leave(_state);
                                                return result;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing RBF module on irregular grids.

  -- ALGLIB --
     Copyright 13.12.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_irregularrbftest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfreport rep;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t k2;
    ae_int_t linterm;
    ae_int_t np;
    double sx;
    double sy;
    double zx;
    double px;
    double zy;
    double py;
    double q;
    double z;
    ae_vector point;
    ae_matrix gp;
    ae_vector x;
    ae_vector y;
    ae_matrix gy;
    double noiselevel;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&point, 0, sizeof(point));
    memset(&gp, 0, sizeof(gp));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&gy, 0, sizeof(gy));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    ae_vector_init(&point, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gy, 0, 0, DT_REAL, _state, ae_true);

    zx = (double)(10);
    px = (double)(15);
    zy = (double)(10);
    py = (double)(15);
    noiselevel = 0.1;
    eps = 1.0E-6;
    
    /*
     * Problem types:
     * * 2 and 3-dimensional problems
     * * problems with zero, constant, linear terms
     * * different scalings of X and Y values (1.0, 1E-15, 1E+15)
     * * noisy grids, which are just regular grids with different grid sizes
     *   (from 2 to 4 points for each dimension) and moderate amount of random
     *   noise added to all node positions.
     *
     * We check that:
     * * RBF model correctly reproduces function value (testes with different Calc() functions)
     */
    for(nx=2; nx<=3; nx++)
    {
        for(ny=1; ny<=3; ny++)
        {
            for(linterm=1; linterm<=3; linterm++)
            {
                
                /*
                 * prepare test problem
                 */
                sx = ae_pow(zx, px*(ae_randominteger(3, _state)-1), _state);
                sy = ae_pow(zy, py*(ae_randominteger(3, _state)-1), _state);
                ae_vector_set_length(&x, nx, _state);
                ae_vector_set_length(&y, ny, _state);
                ae_vector_set_length(&point, nx, _state);
                rbfcreate(nx, ny, &s, _state);
                rbfsetcond(&s, testrbfunit_heps, testrbfunit_heps, testrbfunit_mxits, _state);
                q = 0.25+ae_randomreal(_state);
                z = 4.5+ae_randomreal(_state);
                rbfsetalgoqnn(&s, q, z, _state);
                if( linterm==1 )
                {
                    rbfsetlinterm(&s, _state);
                }
                if( linterm==2 )
                {
                    rbfsetconstterm(&s, _state);
                }
                if( linterm==3 )
                {
                    rbfsetzeroterm(&s, _state);
                }
                
                /*
                 * start points for grid
                 */
                for(i=0; i<=nx-1; i++)
                {
                    point.ptr.p_double[i] = sx*(2*ae_randomreal(_state)-1);
                }
                
                /*
                 * 2-dimensional test problems
                 */
                if( nx==2 )
                {
                    for(k0=2; k0<=4; k0++)
                    {
                        for(k1=2; k1<=4; k1++)
                        {
                            np = k0*k1;
                            ae_matrix_set_length(&gp, np, nx+ny, _state);
                            
                            /*
                             * create grid
                             */
                            for(i=0; i<=k0-1; i++)
                            {
                                for(j=0; j<=k1-1; j++)
                                {
                                    gp.ptr.pp_double[i*k1+j][0] = point.ptr.p_double[0]+sx*i+noiselevel*sx*(2*ae_randomreal(_state)-1);
                                    gp.ptr.pp_double[i*k1+j][1] = point.ptr.p_double[1]+sx*j+noiselevel*sx*(2*ae_randomreal(_state)-1);
                                    for(k=0; k<=ny-1; k++)
                                    {
                                        gp.ptr.pp_double[i*k1+j][nx+k] = sy*(2*ae_randomreal(_state)-1);
                                    }
                                }
                            }
                            rbfsetpoints(&s, &gp, np, _state);
                            rbfbuildmodel(&s, &rep, _state);
                            for(i=0; i<=np-1; i++)
                            {
                                x.ptr.p_double[0] = gp.ptr.pp_double[i][0];
                                x.ptr.p_double[1] = gp.ptr.pp_double[i][1];
                                if( ny==1 )
                                {
                                    y.ptr.p_double[0] = rbfcalc2(&s, x.ptr.p_double[0], x.ptr.p_double[1], _state);
                                    if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx]-y.ptr.p_double[0], _state),sy*eps) )
                                    {
                                        result = ae_true;
                                        ae_frame_leave(_state);
                                        return result;
                                    }
                                }
                                rbfcalc(&s, &x, &y, _state);
                                for(j=0; j<=ny-1; j++)
                                {
                                    if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                    {
                                        result = ae_true;
                                        ae_frame_leave(_state);
                                        return result;
                                    }
                                }
                                rbfcalcbuf(&s, &x, &y, _state);
                                for(j=0; j<=ny-1; j++)
                                {
                                    if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                    {
                                        result = ae_true;
                                        ae_frame_leave(_state);
                                        return result;
                                    }
                                }
                            }
                        }
                    }
                }
                
                /*
                 * 2-dimensional test problems
                 */
                if( nx==3 )
                {
                    for(k0=2; k0<=4; k0++)
                    {
                        for(k1=2; k1<=4; k1++)
                        {
                            for(k2=2; k2<=4; k2++)
                            {
                                np = k0*k1*k2;
                                ae_matrix_set_length(&gp, np, nx+ny, _state);
                                
                                /*
                                 * create grid
                                 */
                                for(i=0; i<=k0-1; i++)
                                {
                                    for(j=0; j<=k1-1; j++)
                                    {
                                        for(k=0; k<=k2-1; k++)
                                        {
                                            gp.ptr.pp_double[(i*k1+j)*k2+k][0] = point.ptr.p_double[0]+sx*i+noiselevel*sx*(2*ae_randomreal(_state)-1);
                                            gp.ptr.pp_double[(i*k1+j)*k2+k][1] = point.ptr.p_double[1]+sx*j+noiselevel*sx*(2*ae_randomreal(_state)-1);
                                            gp.ptr.pp_double[(i*k1+j)*k2+k][2] = point.ptr.p_double[2]+sx*k+noiselevel*sx*(2*ae_randomreal(_state)-1);
                                            for(l=0; l<=ny-1; l++)
                                            {
                                                gp.ptr.pp_double[(i*k1+j)*k2+k][nx+l] = sy*(2*ae_randomreal(_state)-1);
                                            }
                                        }
                                    }
                                }
                                rbfsetpoints(&s, &gp, np, _state);
                                rbfbuildmodel(&s, &rep, _state);
                                for(i=0; i<=np-1; i++)
                                {
                                    x.ptr.p_double[0] = gp.ptr.pp_double[i][0];
                                    x.ptr.p_double[1] = gp.ptr.pp_double[i][1];
                                    x.ptr.p_double[2] = gp.ptr.pp_double[i][2];
                                    if( ny==1 )
                                    {
                                        y.ptr.p_double[0] = rbfcalc3(&s, x.ptr.p_double[0], x.ptr.p_double[1], x.ptr.p_double[2], _state);
                                        if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx]-y.ptr.p_double[0], _state),sy*eps) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                    rbfcalc(&s, &x, &y, _state);
                                    for(j=0; j<=ny-1; j++)
                                    {
                                        if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                    rbfcalcbuf(&s, &x, &y, _state);
                                    for(j=0; j<=ny-1; j++)
                                    {
                                        if( ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),sy*eps) )
                                        {
                                            result = ae_true;
                                            ae_frame_leave(_state);
                                            return result;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
The test does  check, that  algorithm  can build linear model for the data
sets, when Y depends on X linearly.

  -- ALGLIB --
     Copyright 13.12.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_linearitymodelrbftest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfreport rep;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t k2;
    ae_int_t linterm;
    ae_int_t np;
    double sx;
    double sy;
    double zx;
    double px;
    double zy;
    double py;
    double q;
    double z;
    ae_vector point;
    ae_vector a;
    ae_matrix gp;
    ae_vector x;
    ae_vector y;
    ae_int_t unx;
    ae_int_t uny;
    ae_matrix xwr;
    ae_matrix v;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    ae_int_t modelversion;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&point, 0, sizeof(point));
    memset(&a, 0, sizeof(a));
    memset(&gp, 0, sizeof(gp));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&xwr, 0, sizeof(xwr));
    memset(&v, 0, sizeof(v));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    ae_vector_init(&point, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xwr, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);

    zx = (double)(10);
    px = (double)(15);
    zy = (double)(10);
    py = (double)(15);
    ny = 1;
    for(nx=2; nx<=3; nx++)
    {
        for(linterm=1; linterm<=3; linterm++)
        {
            
            /*
             * prepare test problem
             */
            sx = ae_pow(zx, px*(ae_randominteger(3, _state)-1), _state);
            sy = ae_pow(zy, py*(ae_randominteger(3, _state)-1), _state);
            ae_vector_set_length(&x, nx, _state);
            ae_vector_set_length(&y, ny, _state);
            ae_vector_set_length(&point, nx, _state);
            rbfcreate(nx, ny, &s, _state);
            q = 0.25+ae_randomreal(_state);
            z = 4.5+ae_randomreal(_state);
            rbfsetalgoqnn(&s, q, z, _state);
            ae_vector_set_length(&a, nx+1, _state);
            if( linterm==1 )
            {
                rbfsetlinterm(&s, _state);
                for(i=0; i<=nx-1; i++)
                {
                    a.ptr.p_double[i] = sy*(2*ae_randomreal(_state)-1)/sx;
                }
                a.ptr.p_double[nx] = sy*(2*ae_randomreal(_state)-1);
            }
            if( linterm==2 )
            {
                rbfsetconstterm(&s, _state);
                for(i=0; i<=nx-1; i++)
                {
                    a.ptr.p_double[i] = (double)(0);
                }
                a.ptr.p_double[nx] = sy*(2*ae_randomreal(_state)-1);
            }
            if( linterm==3 )
            {
                rbfsetzeroterm(&s, _state);
                for(i=0; i<=nx; i++)
                {
                    a.ptr.p_double[i] = (double)(0);
                }
            }
            
            /*
             * start points for grid
             */
            for(i=0; i<=nx-1; i++)
            {
                point.ptr.p_double[i] = sx*(2*ae_randomreal(_state)-1);
            }
            if( nx==2 )
            {
                for(k0=2; k0<=4; k0++)
                {
                    for(k1=2; k1<=4; k1++)
                    {
                        np = k0*k1;
                        ae_matrix_set_length(&gp, np, nx+ny, _state);
                        
                        /*
                         * create grid
                         */
                        for(i=0; i<=k0-1; i++)
                        {
                            for(j=0; j<=k1-1; j++)
                            {
                                gp.ptr.pp_double[i*k1+j][0] = point.ptr.p_double[0]+sx*i;
                                gp.ptr.pp_double[i*k1+j][1] = point.ptr.p_double[1]+sx*j;
                                gp.ptr.pp_double[i*k1+j][nx] = a.ptr.p_double[nx];
                                for(k=0; k<=nx-1; k++)
                                {
                                    gp.ptr.pp_double[i*k1+j][nx] = gp.ptr.pp_double[i*k1+j][nx]+gp.ptr.pp_double[i*k1+j][k]*a.ptr.p_double[k];
                                }
                            }
                        }
                        rbfsetpoints(&s, &gp, np, _state);
                        rbfbuildmodel(&s, &rep, _state);
                        
                        /*
                         * test for RBFUnpack
                         */
                        rbfunpack(&s, &unx, &uny, &xwr, &np, &v, &modelversion, _state);
                        if( (((((nx!=unx||ny!=uny)||xwr.rows!=np)||xwr.cols!=nx+ny+1)||v.rows!=ny)||v.cols!=nx+1)||modelversion!=1 )
                        {
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                        for(i=0; i<=nx-1; i++)
                        {
                            if( ae_fp_greater(ae_fabs(v.ptr.pp_double[0][i]-a.ptr.p_double[i], _state),sy/sx*testrbfunit_tol) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                        if( ae_fp_greater(ae_fabs(v.ptr.pp_double[0][nx]-a.ptr.p_double[nx], _state),sy*testrbfunit_tol) )
                        {
                            result = ae_true;
                            ae_frame_leave(_state);
                            return result;
                        }
                        for(i=0; i<=np-1; i++)
                        {
                            if( ae_fp_greater(ae_fabs(xwr.ptr.pp_double[i][unx], _state),sy*testrbfunit_tol) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                        }
                    }
                }
            }
            if( nx==3 )
            {
                for(k0=2; k0<=4; k0++)
                {
                    for(k1=2; k1<=4; k1++)
                    {
                        for(k2=2; k2<=4; k2++)
                        {
                            np = k0*k1*k2;
                            ae_matrix_set_length(&gp, np, nx+ny, _state);
                            
                            /*
                             * create grid
                             */
                            for(i=0; i<=k0-1; i++)
                            {
                                for(j=0; j<=k1-1; j++)
                                {
                                    for(k=0; k<=k2-1; k++)
                                    {
                                        gp.ptr.pp_double[(i*k1+j)*k2+k][0] = point.ptr.p_double[0]+sx*i;
                                        gp.ptr.pp_double[(i*k1+j)*k2+k][1] = point.ptr.p_double[1]+sx*j;
                                        gp.ptr.pp_double[(i*k1+j)*k2+k][2] = point.ptr.p_double[2]+sx*k;
                                        gp.ptr.pp_double[(i*k1+j)*k2+k][nx] = a.ptr.p_double[nx];
                                        for(l=0; l<=nx-1; l++)
                                        {
                                            gp.ptr.pp_double[(i*k1+j)*k2+k][nx] = gp.ptr.pp_double[(i*k1+j)*k2+k][nx]+gp.ptr.pp_double[(i*k1+j)*k2+k][l]*a.ptr.p_double[l];
                                        }
                                    }
                                }
                            }
                            rbfsetpoints(&s, &gp, np, _state);
                            rbfbuildmodel(&s, &rep, _state);
                            
                            /*
                             * test for RBFUnpack
                             */
                            rbfunpack(&s, &unx, &uny, &xwr, &np, &v, &modelversion, _state);
                            if( (((((nx!=unx||ny!=uny)||xwr.rows!=np)||xwr.cols!=nx+ny+1)||v.rows!=ny)||v.cols!=nx+1)||modelversion!=1 )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                            for(i=0; i<=nx-1; i++)
                            {
                                if( ae_fp_greater(ae_fabs(v.ptr.pp_double[0][i]-a.ptr.p_double[i], _state),sy/sx*testrbfunit_tol) )
                                {
                                    result = ae_true;
                                    ae_frame_leave(_state);
                                    return result;
                                }
                            }
                            if( ae_fp_greater(ae_fabs(v.ptr.pp_double[0][nx]-a.ptr.p_double[nx], _state),sy*testrbfunit_tol) )
                            {
                                result = ae_true;
                                ae_frame_leave(_state);
                                return result;
                            }
                            for(i=0; i<=np-1; i++)
                            {
                                if( ae_fp_greater(ae_fabs(xwr.ptr.pp_double[i][unx], _state),sy*testrbfunit_tol) )
                                {
                                    result = ae_true;
                                    ae_frame_leave(_state);
                                    return result;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function tests serialization

  -- ALGLIB --
     Copyright 02.02.2012 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_serializationtest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfmodel s2;
    rbfreport rep;
    ae_int_t n;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t k2;
    ae_int_t i;
    ae_int_t i0;
    ae_int_t i1;
    ae_int_t i2;
    ae_int_t j;
    ae_int_t k;
    double rbase;
    ae_int_t nlayers;
    ae_int_t bf;
    ae_int_t gridsize;
    ae_matrix xy;
    ae_vector testpoint;
    ae_vector y0;
    ae_vector y1;
    ae_vector scalevec;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&s2, 0, sizeof(s2));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&testpoint, 0, sizeof(testpoint));
    memset(&y0, 0, sizeof(y0));
    memset(&y1, 0, sizeof(y1));
    memset(&scalevec, 0, sizeof(scalevec));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfmodel_init(&s2, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&testpoint, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scalevec, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    
    /*
     * This function generates random 2 or 3 dimensional problem,
     * builds RBF model (QNN is used), serializes/unserializes it, then compares
     * models by calculating model value at some random point.
     *
     * Additionally we test that new model (one which was restored
     * after serialization) has lost all model construction settings,
     * i.e. if we call RBFBuildModel() on a NEW model, we will get
     * empty (zero) model.
     */
    for(nx=2; nx<=3; nx++)
    {
        for(ny=1; ny<=2; ny++)
        {
            
            /*
             * prepare test problem
             */
            rbfcreate(nx, ny, &s, _state);
            rbfsetalgoqnn(&s, 1.0, 5.0, _state);
            rbfsetlinterm(&s, _state);
            if( nx==2 )
            {
                
                /*
                 * 2-dimensional problem
                 */
                k0 = 2+ae_randominteger(4, _state);
                k1 = 2+ae_randominteger(4, _state);
                ae_matrix_set_length(&xy, k0*k1, nx+ny, _state);
                for(i0=0; i0<=k0-1; i0++)
                {
                    for(i1=0; i1<=k1-1; i1++)
                    {
                        xy.ptr.pp_double[i0*k1+i1][0] = i0+0.1*(2*ae_randomreal(_state)-1);
                        xy.ptr.pp_double[i0*k1+i1][1] = i1+0.1*(2*ae_randomreal(_state)-1);
                        for(j=0; j<=ny-1; j++)
                        {
                            xy.ptr.pp_double[i0*k1+i1][nx+j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                }
                ae_vector_set_length(&testpoint, nx, _state);
                testpoint.ptr.p_double[0] = ae_randomreal(_state)*(k0-1);
                testpoint.ptr.p_double[1] = ae_randomreal(_state)*(k1-1);
            }
            else
            {
                
                /*
                 * 3-dimensional problem
                 */
                k0 = 2+ae_randominteger(4, _state);
                k1 = 2+ae_randominteger(4, _state);
                k2 = 2+ae_randominteger(4, _state);
                ae_matrix_set_length(&xy, k0*k1*k2, nx+ny, _state);
                for(i0=0; i0<=k0-1; i0++)
                {
                    for(i1=0; i1<=k1-1; i1++)
                    {
                        for(i2=0; i2<=k2-1; i2++)
                        {
                            xy.ptr.pp_double[i0*k1*k2+i1*k2+i2][0] = i0+0.1*(2*ae_randomreal(_state)-1);
                            xy.ptr.pp_double[i0*k1*k2+i1*k2+i2][1] = i1+0.1*(2*ae_randomreal(_state)-1);
                            xy.ptr.pp_double[i0*k1*k2+i1*k2+i2][2] = i2+0.1*(2*ae_randomreal(_state)-1);
                            for(j=0; j<=ny-1; j++)
                            {
                                xy.ptr.pp_double[i0*k1*k2+i1*k2+i2][nx+j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                    }
                }
                ae_vector_set_length(&testpoint, nx, _state);
                testpoint.ptr.p_double[0] = ae_randomreal(_state)*(k0-1);
                testpoint.ptr.p_double[1] = ae_randomreal(_state)*(k1-1);
                testpoint.ptr.p_double[2] = ae_randomreal(_state)*(k2-1);
            }
            rbfsetpoints(&s, &xy, xy.rows, _state);
            
            /*
             * Build model, serialize, compare
             */
            rbfbuildmodel(&s, &rep, _state);
            {
                /*
                 * This code passes data structure through serializers
                 * (serializes it to string and loads back)
                 */
                ae_serializer _local_serializer;
                ae_int_t _local_ssize;
                ae_frame _local_frame_block;
                ae_dyn_block _local_dynamic_block;
                
                ae_frame_make(_state, &_local_frame_block);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_alloc_start(&_local_serializer);
                rbfalloc(&_local_serializer, &s, _state);
                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                rbfserialize(&_local_serializer, &s, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                rbfunserialize(&_local_serializer, &s2, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_frame_leave(_state);
            }
            rbfcalc(&s, &testpoint, &y0, _state);
            rbfcalc(&s2, &testpoint, &y1, _state);
            if( y0.cnt!=ny||y1.cnt!=ny )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(j=0; j<=ny-1; j++)
            {
                if( ae_fp_neq(y0.ptr.p_double[j],y1.ptr.p_double[j]) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            
            /*
             * Check that calling RBFBuildModel() on S2 (new model)
             * will result in construction of zero model, i.e. test
             * that serialization restores model, but not dataset
             * which was used to build model.
             */
            rbfbuildmodel(&s2, &rep, _state);
            rbfcalc(&s2, &testpoint, &y1, _state);
            if( y1.cnt!=ny )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(j=0; j<=ny-1; j++)
            {
                if( ae_fp_neq(y1.ptr.p_double[j],(double)(0)) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    
    /*
     * This function generates random 2 or 3 dimensional problem,
     * builds model using RBF-ML algo, serializes/unserializes it,
     * then compares models by calculating model value at some
     * random point.
     *
     * Additionally we test that new model (one which was restored
     * after serialization) has lost all model construction settings,
     * i.e. if we call RBFBuildModel() on a NEW model, we will get
     * empty (zero) model.
     */
    for(nx=2; nx<=3; nx++)
    {
        for(ny=1; ny<=2; ny++)
        {
            
            /*
             * prepare test problem
             */
            rbfcreate(nx, ny, &s, _state);
            rbfsetalgomultilayer(&s, 5.0, 5, 1.0E-3, _state);
            rbfsetlinterm(&s, _state);
            if( nx==2 )
            {
                
                /*
                 * 2-dimensional problem
                 */
                k0 = 2+ae_randominteger(4, _state);
                k1 = 2+ae_randominteger(4, _state);
                ae_matrix_set_length(&xy, k0*k1, nx+ny, _state);
                for(i0=0; i0<=k0-1; i0++)
                {
                    for(i1=0; i1<=k1-1; i1++)
                    {
                        xy.ptr.pp_double[i0*k1+i1][0] = i0+0.1*(2*ae_randomreal(_state)-1);
                        xy.ptr.pp_double[i0*k1+i1][1] = i1+0.1*(2*ae_randomreal(_state)-1);
                        for(j=0; j<=ny-1; j++)
                        {
                            xy.ptr.pp_double[i0*k1+i1][nx+j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                }
                ae_vector_set_length(&testpoint, nx, _state);
                testpoint.ptr.p_double[0] = ae_randomreal(_state)*(k0-1);
                testpoint.ptr.p_double[1] = ae_randomreal(_state)*(k1-1);
            }
            else
            {
                
                /*
                 * 3-dimensional problem
                 */
                k0 = 2+ae_randominteger(4, _state);
                k1 = 2+ae_randominteger(4, _state);
                k2 = 2+ae_randominteger(4, _state);
                ae_matrix_set_length(&xy, k0*k1*k2, nx+ny, _state);
                for(i0=0; i0<=k0-1; i0++)
                {
                    for(i1=0; i1<=k1-1; i1++)
                    {
                        for(i2=0; i2<=k2-1; i2++)
                        {
                            xy.ptr.pp_double[i0*k1*k2+i1*k2+i2][0] = i0+0.1*(2*ae_randomreal(_state)-1);
                            xy.ptr.pp_double[i0*k1*k2+i1*k2+i2][1] = i1+0.1*(2*ae_randomreal(_state)-1);
                            xy.ptr.pp_double[i0*k1*k2+i1*k2+i2][2] = i2+0.1*(2*ae_randomreal(_state)-1);
                            for(j=0; j<=ny-1; j++)
                            {
                                xy.ptr.pp_double[i0*k1*k2+i1*k2+i2][nx+j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                    }
                }
                ae_vector_set_length(&testpoint, nx, _state);
                testpoint.ptr.p_double[0] = ae_randomreal(_state)*(k0-1);
                testpoint.ptr.p_double[1] = ae_randomreal(_state)*(k1-1);
                testpoint.ptr.p_double[2] = ae_randomreal(_state)*(k2-1);
            }
            rbfsetpoints(&s, &xy, xy.rows, _state);
            
            /*
             * Build model, serialize, compare
             */
            rbfbuildmodel(&s, &rep, _state);
            {
                /*
                 * This code passes data structure through serializers
                 * (serializes it to string and loads back)
                 */
                ae_serializer _local_serializer;
                ae_int_t _local_ssize;
                ae_frame _local_frame_block;
                ae_dyn_block _local_dynamic_block;
                
                ae_frame_make(_state, &_local_frame_block);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_alloc_start(&_local_serializer);
                rbfalloc(&_local_serializer, &s, _state);
                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                rbfserialize(&_local_serializer, &s, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                rbfunserialize(&_local_serializer, &s2, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_frame_leave(_state);
            }
            rbfcalc(&s, &testpoint, &y0, _state);
            rbfcalc(&s2, &testpoint, &y1, _state);
            if( y0.cnt!=ny||y1.cnt!=ny )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(j=0; j<=ny-1; j++)
            {
                if( ae_fp_neq(y0.ptr.p_double[j],y1.ptr.p_double[j]) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            
            /*
             * Check that calling RBFBuildModel() on S2 (new model)
             * will result in construction of zero model, i.e. test
             * that serialization restores model, but not dataset
             * which was used to build model.
             */
            rbfbuildmodel(&s2, &rep, _state);
            rbfcalc(&s2, &testpoint, &y1, _state);
            if( y1.cnt!=ny )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(j=0; j<=ny-1; j++)
            {
                if( ae_fp_neq(y1.ptr.p_double[j],(double)(0)) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    
    /*
     * This function generates random 1...4-dimensional problem,
     * builds model using RBF-H algo, serializes/unserializes it,
     * then compares models by calculating model value at some
     * random point.
     *
     * NOTE: we choose at random whether to use default scaling -
     *       or user-supplied one.
     *
     * Additionally we test that new model (one which was restored
     * after serialization) has lost all model construction settings,
     * i.e. if we call RBFBuildModel() on a NEW model, we will get
     * empty (zero) model.
     */
    for(nx=1; nx<=4; nx++)
    {
        for(ny=1; ny<=2; ny++)
        {
            
            /*
             * problem setup
             */
            n = 150;
            rbase = 0.33;
            nlayers = 5;
            gridsize = ae_round(ae_pow((double)(n), (double)1/(double)nx, _state), _state)+1;
            bf = ae_randominteger(2, _state);
            n = ae_round(ae_pow((double)(gridsize), (double)(nx), _state), _state);
            ae_matrix_set_length(&xy, n, nx+ny, _state);
            ae_assert(gridsize>1, "Assertion failed", _state);
            ae_assert(ae_fp_eq((double)(n),ae_pow((double)(gridsize), (double)(nx), _state)), "Assertion failed", _state);
            for(i=0; i<=n-1; i++)
            {
                k = i;
                for(j=0; j<=nx-1; j++)
                {
                    xy.ptr.pp_double[i][j] = (double)(k%gridsize)/(double)(gridsize-1);
                    k = k/gridsize;
                }
                for(j=0; j<=ny-1; j++)
                {
                    xy.ptr.pp_double[i][nx+j] = ae_randomreal(_state)-0.5;
                }
            }
            ae_vector_set_length(&testpoint, nx, _state);
            for(j=0; j<=nx-1; j++)
            {
                testpoint.ptr.p_double[j] = ae_randomreal(_state);
            }
            ae_vector_set_length(&scalevec, nx, _state);
            for(j=0; j<=nx-1; j++)
            {
                scalevec.ptr.p_double[j] = ae_pow((double)(2), 2*ae_randomreal(_state)-1, _state);
            }
            
            /*
             * prepare test problem
             */
            rbfcreate(nx, ny, &s, _state);
            rbfsetv2bf(&s, bf, _state);
            rbfsetalgohierarchical(&s, rbase, nlayers, 0.0, _state);
            rbfsetlinterm(&s, _state);
            if( ae_fp_greater(ae_randomreal(_state),0.5) )
            {
                rbfsetpoints(&s, &xy, xy.rows, _state);
            }
            else
            {
                rbfsetpointsandscales(&s, &xy, xy.rows, &scalevec, _state);
            }
            
            /*
             * Build model, serialize, compare
             */
            rbfbuildmodel(&s, &rep, _state);
            {
                /*
                 * This code passes data structure through serializers
                 * (serializes it to string and loads back)
                 */
                ae_serializer _local_serializer;
                ae_int_t _local_ssize;
                ae_frame _local_frame_block;
                ae_dyn_block _local_dynamic_block;
                
                ae_frame_make(_state, &_local_frame_block);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_alloc_start(&_local_serializer);
                rbfalloc(&_local_serializer, &s, _state);
                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                rbfserialize(&_local_serializer, &s, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                rbfunserialize(&_local_serializer, &s2, _state);
                ae_serializer_stop(&_local_serializer, _state);
                ae_serializer_clear(&_local_serializer);
                
                ae_frame_leave(_state);
            }
            rbfcalc(&s, &testpoint, &y0, _state);
            rbfcalc(&s2, &testpoint, &y1, _state);
            if( y0.cnt!=ny||y1.cnt!=ny )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(j=0; j<=ny-1; j++)
            {
                if( ae_fp_neq(y0.ptr.p_double[j],y1.ptr.p_double[j]) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
            
            /*
             * Check that calling RBFBuildModel() on S2 (new model)
             * will result in construction of zero model, i.e. test
             * that serialization restores model, but not dataset
             * which was used to build model.
             */
            rbfbuildmodel(&s2, &rep, _state);
            rbfcalc(&s2, &testpoint, &y1, _state);
            if( y1.cnt!=ny )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            for(j=0; j<=ny-1; j++)
            {
                if( ae_fp_neq(y1.ptr.p_double[j],(double)(0)) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


static ae_bool testrbfunit_searcherr(/* Real    */ ae_matrix* y0,
     /* Real    */ ae_matrix* y1,
     ae_int_t n,
     ae_int_t ny,
     ae_int_t errtype,
     /* Real    */ ae_vector* b1,
     /* Real    */ ae_vector* delta,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix _y0;
    ae_matrix _y1;
    ae_vector _b1;
    ae_vector _delta;
    double oralerr;
    double iralerr;
    ae_vector irerr;
    ae_vector orerr;
    ae_int_t lb;
    ae_int_t rb;
    ae_int_t i;
    ae_int_t j;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&_y0, 0, sizeof(_y0));
    memset(&_y1, 0, sizeof(_y1));
    memset(&_b1, 0, sizeof(_b1));
    memset(&_delta, 0, sizeof(_delta));
    memset(&irerr, 0, sizeof(irerr));
    memset(&orerr, 0, sizeof(orerr));
    ae_matrix_init_copy(&_y0, y0, _state, ae_true);
    y0 = &_y0;
    ae_matrix_init_copy(&_y1, y1, _state, ae_true);
    y1 = &_y1;
    ae_vector_init_copy(&_b1, b1, _state, ae_true);
    b1 = &_b1;
    ae_vector_init_copy(&_delta, delta, _state, ae_true);
    delta = &_delta;
    ae_vector_init(&irerr, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&orerr, 0, DT_REAL, _state, ae_true);

    ae_assert(n>0, "SearchErr: invalid parameter N(N<=0).", _state);
    ae_assert(ny>0, "SearchErr: invalid parameter NY(NY<=0).", _state);
    oralerr = 1.0E-1;
    iralerr = 1.0E-2;
    lb = 25;
    rb = 75;
    ae_vector_set_length(&orerr, ny, _state);
    ae_vector_set_length(&irerr, ny, _state);
    for(j=0; j<=ny-1; j++)
    {
        orerr.ptr.p_double[j] = (double)(0);
        irerr.ptr.p_double[j] = (double)(0);
    }
    if( errtype==1 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=ny-1; j++)
            {
                if( ae_fp_less(orerr.ptr.p_double[j],ae_fabs(y0->ptr.pp_double[i][j]-y1->ptr.pp_double[i][j], _state)) )
                {
                    orerr.ptr.p_double[j] = ae_fabs(y0->ptr.pp_double[i][j]-y1->ptr.pp_double[i][j], _state);
                }
            }
        }
        for(i=0; i<=ny-1; i++)
        {
            if( ae_fp_greater(orerr.ptr.p_double[i],b1->ptr.p_double[i]+delta->ptr.p_double[i])||ae_fp_less(orerr.ptr.p_double[i],b1->ptr.p_double[i]-delta->ptr.p_double[i]) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    else
    {
        if( errtype==2 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=ny-1; j++)
                {
                    if( i>lb&&i<rb )
                    {
                        if( ae_fp_less(irerr.ptr.p_double[j],ae_fabs(y0->ptr.pp_double[i][j]-y1->ptr.pp_double[i][j], _state)) )
                        {
                            irerr.ptr.p_double[j] = ae_fabs(y0->ptr.pp_double[i][j]-y1->ptr.pp_double[i][j], _state);
                        }
                    }
                    else
                    {
                        if( ae_fp_less(orerr.ptr.p_double[j],ae_fabs(y0->ptr.pp_double[i][j]-y1->ptr.pp_double[i][j], _state)) )
                        {
                            orerr.ptr.p_double[j] = ae_fabs(y0->ptr.pp_double[i][j]-y1->ptr.pp_double[i][j], _state);
                        }
                    }
                }
            }
            for(i=0; i<=ny-1; i++)
            {
                if( ae_fp_greater(orerr.ptr.p_double[i],oralerr)||ae_fp_greater(irerr.ptr.p_double[i],iralerr) )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
        else
        {
            ae_assert(ae_false, "SearchErr: invalid argument ErrType(ErrType neither 1 nor 2)", _state);
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing basic functionality of RBF module on regular grids with
multi-layer algorithm in 2-3D.

  -- ALGLIB --
     Copyright 2.03.2012 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_basicmultilayerrbftest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfreport rep;
    rbfcalcbuffer calcbuf;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t k0;
    ae_int_t k1;
    ae_int_t k2;
    ae_int_t linterm;
    ae_int_t np;
    double q;
    ae_int_t layers;
    double eps;
    ae_int_t range;
    double s1;
    double s2;
    double gstep;
    ae_vector point;
    ae_matrix gp;
    ae_vector x;
    ae_vector y;
    ae_matrix gy;
    ae_vector gpgx0;
    ae_vector gpgx1;
    ae_vector gpgx2;
    ae_vector gcy;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    ae_int_t fidx;
    double r0;
    ae_int_t margin;
    ae_int_t gridsize;
    double threshold;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&calcbuf, 0, sizeof(calcbuf));
    memset(&point, 0, sizeof(point));
    memset(&gp, 0, sizeof(gp));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&gy, 0, sizeof(gy));
    memset(&gpgx0, 0, sizeof(gpgx0));
    memset(&gpgx1, 0, sizeof(gpgx1));
    memset(&gpgx2, 0, sizeof(gpgx2));
    memset(&gcy, 0, sizeof(gcy));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    _rbfcalcbuffer_init(&calcbuf, _state, ae_true);
    ae_vector_init(&point, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&gy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gpgx0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gpgx1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gpgx2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gcy, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    range = 10;
    passcount = 10;
    eps = 1.0E-6;
    
    /*
     * Test that RBF model with sufficient layers will exactly reproduce
     * target function.
     */
    for(pass=0; pass<=passcount-1; pass++)
    {
        
        /*
         * prepare test problem
         */
        k0 = 6+ae_randominteger(3, _state);
        k1 = 6+ae_randominteger(3, _state);
        k2 = 6+ae_randominteger(3, _state);
        s1 = ae_pow((double)(range), (double)(ae_randominteger(3, _state)-1), _state);
        s2 = ae_pow((double)(range), (double)(ae_randominteger(3, _state)-1), _state);
        nx = ae_randominteger(2, _state)+2;
        ny = ae_randominteger(2, _state)+1;
        linterm = ae_randominteger(3, _state)+1;
        layers = 5;
        gstep = s1/6;
        q = s1;
        
        /*
         * Create RBF structure and auxiliary structures
         */
        ae_vector_set_length(&x, nx, _state);
        ae_vector_set_length(&y, ny, _state);
        ae_vector_set_length(&point, nx, _state);
        rbfcreate(nx, ny, &s, _state);
        rbfsetalgomultilayer(&s, q, layers, 0.0, _state);
        if( linterm==1 )
        {
            rbfsetlinterm(&s, _state);
        }
        if( linterm==2 )
        {
            rbfsetconstterm(&s, _state);
        }
        if( linterm==3 )
        {
            rbfsetzeroterm(&s, _state);
        }
        for(i=0; i<=nx-1; i++)
        {
            point.ptr.p_double[i] = s1*(2*ae_randomreal(_state)-1);
        }
        
        /*
         * 2-dimensional test problem
         */
        if( nx==2 )
        {
            np = k0*k1;
            ae_matrix_set_length(&gp, np, nx+ny, _state);
            
            /*
             * create grid
             */
            for(i=0; i<=k0-1; i++)
            {
                for(j=0; j<=k1-1; j++)
                {
                    gp.ptr.pp_double[i*k1+j][0] = point.ptr.p_double[0]+gstep*i;
                    gp.ptr.pp_double[i*k1+j][1] = point.ptr.p_double[1]+gstep*j;
                    for(k=0; k<=ny-1; k++)
                    {
                        gp.ptr.pp_double[i*k1+j][nx+k] = s2*(2*ae_randomreal(_state)-1);
                    }
                }
            }
            rbfsetpoints(&s, &gp, np, _state);
            rbfbuildmodel(&s, &rep, _state);
            rbfcreatecalcbuffer(&s, &calcbuf, _state);
            if( ny==1 )
            {
                ae_vector_set_length(&gpgx0, k0, _state);
                ae_vector_set_length(&gpgx1, k1, _state);
                for(i=0; i<=k0-1; i++)
                {
                    gpgx0.ptr.p_double[i] = point.ptr.p_double[0]+gstep*i;
                }
                for(i=0; i<=k1-1; i++)
                {
                    gpgx1.ptr.p_double[i] = point.ptr.p_double[1]+gstep*i;
                }
                rbfgridcalc2(&s, &gpgx0, k0, &gpgx1, k1, &gy, _state);
                for(i=0; i<=k0-1; i++)
                {
                    for(j=0; j<=k1-1; j++)
                    {
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(gy.ptr.pp_double[i][j]-gp.ptr.pp_double[i*k1+j][nx], _state),s2*eps), __FILE__, __LINE__, "testrbfunit.ap:2216");
                    }
                }
            }
            for(i=0; i<=np-1; i++)
            {
                
                /*
                 * For each row we randomly choose a function to test
                 * and call it. We do not call multiple functions per
                 * row because carry-over effects may mask errors in
                 * some function (say, it is possible that function
                 * simply returns results from previous call of some
                 * other function which were stored in the RBF model;
                 * in this case, previous call with same parameters
                 * may hide deficiencies in the function).
                 */
                x.ptr.p_double[0] = gp.ptr.pp_double[i][0];
                x.ptr.p_double[1] = gp.ptr.pp_double[i][1];
                fidx = ae_randominteger(4, _state);
                if( fidx==0&&ny!=1 )
                {
                    continue;
                }
                if( fidx==0 )
                {
                    y.ptr.p_double[0] = rbfcalc2(&s, x.ptr.p_double[0], x.ptr.p_double[1], _state);
                }
                if( fidx==1 )
                {
                    rbfcalc(&s, &x, &y, _state);
                }
                if( fidx==2 )
                {
                    rbfcalcbuf(&s, &x, &y, _state);
                }
                if( fidx==3 )
                {
                    rbftscalcbuf(&s, &calcbuf, &x, &y, _state);
                }
                for(j=0; j<=ny-1; j++)
                {
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),s2*eps), __FILE__, __LINE__, "testrbfunit.ap:2244");
                }
            }
        }
        
        /*
         * 3-dimensional test problems
         */
        if( nx==3 )
        {
            np = k0*k1*k2;
            ae_matrix_set_length(&gp, np, nx+ny, _state);
            
            /*
             * create grid, build model
             */
            ae_vector_set_length(&gpgx0, k0, _state);
            ae_vector_set_length(&gpgx1, k1, _state);
            ae_vector_set_length(&gpgx2, k2, _state);
            for(i=0; i<=k0-1; i++)
            {
                gpgx0.ptr.p_double[i] = point.ptr.p_double[0]+gstep*i;
            }
            for(i=0; i<=k1-1; i++)
            {
                gpgx1.ptr.p_double[i] = point.ptr.p_double[1]+gstep*i;
            }
            for(i=0; i<=k2-1; i++)
            {
                gpgx2.ptr.p_double[i] = point.ptr.p_double[2]+gstep*i;
            }
            for(i=0; i<=k0-1; i++)
            {
                for(j=0; j<=k1-1; j++)
                {
                    for(k=0; k<=k2-1; k++)
                    {
                        gp.ptr.pp_double[(i*k1+j)*k2+k][0] = gpgx0.ptr.p_double[i];
                        gp.ptr.pp_double[(i*k1+j)*k2+k][1] = gpgx1.ptr.p_double[j];
                        gp.ptr.pp_double[(i*k1+j)*k2+k][2] = gpgx2.ptr.p_double[k];
                        for(l=0; l<=ny-1; l++)
                        {
                            gp.ptr.pp_double[(i*k1+j)*k2+k][nx+l] = s2*(2*ae_randomreal(_state)-1);
                        }
                    }
                }
            }
            rbfsetpoints(&s, &gp, np, _state);
            rbfbuildmodel(&s, &rep, _state);
            rbfcreatecalcbuffer(&s, &calcbuf, _state);
            
            /*
             * Test RBFCalc3(), RBFCalc() and RBFCalcBuf() vs expected values on the grid (we expect good fit).
             */
            for(i=0; i<=np-1; i++)
            {
                
                /*
                 * For each row we randomly choose a function to test
                 * and call it. We do not call multiple functions per
                 * row because carry-over effects may mask errors in
                 * some function (say, it is possible that function
                 * simply returns results from previous call of some
                 * other function which were stored in the RBF model;
                 * in this case, previous call with same parameters
                 * may hide deficiencies in the function).
                 */
                x.ptr.p_double[0] = gp.ptr.pp_double[i][0];
                x.ptr.p_double[1] = gp.ptr.pp_double[i][1];
                x.ptr.p_double[2] = gp.ptr.pp_double[i][2];
                fidx = ae_randominteger(4, _state);
                if( fidx==0&&ny!=1 )
                {
                    continue;
                }
                if( fidx==0 )
                {
                    y.ptr.p_double[0] = rbfcalc3(&s, x.ptr.p_double[0], x.ptr.p_double[1], x.ptr.p_double[2], _state);
                }
                if( fidx==1 )
                {
                    rbfcalc(&s, &x, &y, _state);
                }
                if( fidx==2 )
                {
                    rbfcalcbuf(&s, &x, &y, _state);
                }
                if( fidx==3 )
                {
                    rbftscalcbuf(&s, &calcbuf, &x, &y, _state);
                }
                for(j=0; j<=ny-1; j++)
                {
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(gp.ptr.pp_double[i][nx+j]-y.ptr.p_double[j], _state),s2*eps), __FILE__, __LINE__, "testrbfunit.ap:2312");
                }
            }
            
            /*
             * Test RBFGridCalc3V vs RBFCalc()
             */
            rbfgridcalc3v(&s, &gpgx0, k0, &gpgx1, k1, &gpgx2, k2, &gcy, _state);
            for(i=0; i<=k0-1; i++)
            {
                for(j=0; j<=k1-1; j++)
                {
                    for(k=0; k<=k2-1; k++)
                    {
                        x.ptr.p_double[0] = gpgx0.ptr.p_double[i];
                        x.ptr.p_double[1] = gpgx1.ptr.p_double[j];
                        x.ptr.p_double[2] = gpgx2.ptr.p_double[k];
                        rbfcalcbuf(&s, &x, &y, _state);
                        for(l=0; l<=ny-1; l++)
                        {
                            ae_set_error_flag(&result, ae_fp_greater(ae_fabs(y.ptr.p_double[l]-gcy.ptr.p_double[l+ny*(i+j*k0+k*k0*k1)], _state),1.0E-9*s2), __FILE__, __LINE__, "testrbfunit.ap:2329");
                        }
                    }
                }
            }
        }
    }
    
    /*
     * Test smoothing properties of RBF model: model with just one layer
     * and large initial radius will produce "average" value of neighbors.
     *
     * In order to test it we create regular grid, fill it with regular
     * +1/-1 pattern, and test model values in the inner points. Model
     * values should be around zero (we use handcrafted threshold to test
     * it). Radius is chosen to be several times larger than grid step.
     *
     * We perform test for 2D model, because same behavior is expected
     * regardless of dimensionality.
     */
    r0 = (double)(3);
    margin = 5;
    gridsize = 2*margin+20;
    threshold = 0.1;
    nx = 2;
    ny = 1;
    rbfcreate(nx, ny, &s, _state);
    rbfsetalgomultilayer(&s, r0, 1, 0.0, _state);
    rbfsetzeroterm(&s, _state);
    ae_matrix_set_length(&gp, gridsize*gridsize, nx+ny, _state);
    for(i=0; i<=gridsize-1; i++)
    {
        for(j=0; j<=gridsize-1; j++)
        {
            gp.ptr.pp_double[i*gridsize+j][0] = (double)(i);
            gp.ptr.pp_double[i*gridsize+j][1] = (double)(j);
            gp.ptr.pp_double[i*gridsize+j][2] = 0.10*ae_randomreal(_state)-0.05+(2*((i+j)%2)-1);
        }
    }
    rbfsetpoints(&s, &gp, gridsize*gridsize, _state);
    rbfbuildmodel(&s, &rep, _state);
    v = 0.0;
    for(i=margin; i<=gridsize-margin-1; i++)
    {
        for(j=margin; j<=gridsize-margin-1; j++)
        {
            v = ae_maxreal(v, ae_fabs(rbfcalc2(&s, (double)(i), (double)(j), _state), _state), _state);
        }
    }
    ae_set_error_flag(&result, ae_fp_greater(v,threshold), __FILE__, __LINE__, "testrbfunit.ap:2376");
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing basic functionality of RBF module on regular grids with
multi-layer algorithm in 2-3D.

  -- ALGLIB --
     Copyright 2.03.2012 by Bochkanov Sergey
*************************************************************************/
static void testrbfunit_gridcalc23test(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfreport rep;
    hqrndstate rs;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    double perturbation;
    ae_vector kx;
    double sx;
    double sy;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t linterm;
    ae_int_t layers;
    ae_int_t npoints;
    ae_matrix xy;
    ae_vector gf;
    ae_vector rf;
    ae_vector x0;
    ae_vector x1;
    ae_vector x2;
    ae_vector gy;
    ae_vector gy2;
    ae_vector x;
    ae_vector y;
    double sparsity;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    memset(&kx, 0, sizeof(kx));
    memset(&xy, 0, sizeof(xy));
    memset(&gf, 0, sizeof(gf));
    memset(&rf, 0, sizeof(rf));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&gy, 0, sizeof(gy));
    memset(&gy2, 0, sizeof(gy2));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);
    ae_vector_init(&kx, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gf, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&rf, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gy, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&gy2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);

    hqrndrandomize(&rs, _state);
    for(pass=0; pass<=24; pass++)
    {
        
        /*
         * prepare test problem
         */
        ae_vector_set_length(&kx, 3, _state);
        for(i=0; i<=2; i++)
        {
            
            /*
             * 66% of cases - large grid
             */
            if( hqrnduniformi(&rs, 3, _state)==0 )
            {
                kx.ptr.p_int[i] = ae_round(10*ae_pow((double)(10), hqrnduniformr(&rs, _state), _state), _state);
                continue;
            }
            
            /*
             * 33% of cases - small grid
             */
            k = hqrnduniformi(&rs, 3, _state);
            if( k==0 )
            {
                kx.ptr.p_int[i] = 1;
            }
            if( k==1 )
            {
                kx.ptr.p_int[i] = 2;
            }
            if( k==2 )
            {
                kx.ptr.p_int[i] = 10;
            }
        }
        sx = ae_pow((double)(10), (double)(hqrnduniformi(&rs, 3, _state)-1), _state);
        sy = ae_pow((double)(10), (double)(hqrnduniformi(&rs, 3, _state)-1), _state);
        nx = 3;
        ny = 1+hqrnduniformi(&rs, 5, _state);
        linterm = hqrnduniformi(&rs, 3, _state)+1;
        layers = hqrnduniformi(&rs, 3, _state)+1;
        npoints = 100;
        ae_matrix_set_length(&xy, npoints, nx+ny, _state);
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nx-1; j++)
            {
                xy.ptr.pp_double[i][j] = hqrnduniformr(&rs, _state)*sx;
            }
            for(j=0; j<=ny-1; j++)
            {
                xy.ptr.pp_double[i][nx+j] = hqrnduniformr(&rs, _state)*sy;
            }
        }
        
        /*
         * Create RBF model
         */
        rbfcreate(nx, ny, &s, _state);
        rbfsetalgomultilayer(&s, 0.1*sx, layers, 0.0, _state);
        if( linterm==1 )
        {
            rbfsetlinterm(&s, _state);
        }
        if( linterm==2 )
        {
            rbfsetconstterm(&s, _state);
        }
        if( linterm==3 )
        {
            rbfsetzeroterm(&s, _state);
        }
        rbfsetpoints(&s, &xy, npoints, _state);
        rbfbuildmodel(&s, &rep, _state);
        ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testrbfunit.ap:2473");
        if( *errorflag )
        {
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Prepare test grid
         */
        ae_vector_set_length(&x0, kx.ptr.p_int[0], _state);
        for(i=0; i<=kx.ptr.p_int[0]-1; i++)
        {
            perturbation = 0.5*(hqrnduniformr(&rs, _state)-0.5);
            x0.ptr.p_double[i] = sx*(i+perturbation)/kx.ptr.p_int[0];
        }
        ae_vector_set_length(&x1, kx.ptr.p_int[1], _state);
        for(i=0; i<=kx.ptr.p_int[1]-1; i++)
        {
            perturbation = 0.5*(hqrnduniformr(&rs, _state)-0.5);
            x1.ptr.p_double[i] = sx*(i+perturbation)/kx.ptr.p_int[1];
        }
        ae_vector_set_length(&x2, kx.ptr.p_int[2], _state);
        for(i=0; i<=kx.ptr.p_int[2]-1; i++)
        {
            perturbation = 0.5*(hqrnduniformr(&rs, _state)-0.5);
            x2.ptr.p_double[i] = sx*(i+perturbation)/kx.ptr.p_int[2];
        }
        
        /*
         * Test calculation on grid
         */
        ae_vector_set_length(&x, nx, _state);
        ae_vector_set_length(&y, ny, _state);
        unsetrealarray(&gy, _state);
        rbfgridcalc3v(&s, &x0, kx.ptr.p_int[0], &x1, kx.ptr.p_int[1], &x2, kx.ptr.p_int[2], &gy, _state);
        for(i=0; i<=kx.ptr.p_int[0]-1; i++)
        {
            for(j=0; j<=kx.ptr.p_int[1]-1; j++)
            {
                for(k=0; k<=kx.ptr.p_int[2]-1; k++)
                {
                    x.ptr.p_double[0] = x0.ptr.p_double[i];
                    x.ptr.p_double[1] = x1.ptr.p_double[j];
                    x.ptr.p_double[2] = x2.ptr.p_double[k];
                    rbfcalcbuf(&s, &x, &y, _state);
                    for(l=0; l<=ny-1; l++)
                    {
                        ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(y.ptr.p_double[l]-gy.ptr.p_double[l+ny*(i+j*kx.ptr.p_int[0]+k*kx.ptr.p_int[0]*kx.ptr.p_int[1])], _state),1.0E-9*sy), __FILE__, __LINE__, "testrbfunit.ap:2516");
                    }
                }
            }
        }
        
        /*
         * Test calculation on subset of regular grid:
         * * select sparsity coefficient (from 1.0 to 0.001)
         * * fill bitmap array
         * * Test 1: compare full and subset versions
         * * Test 2: check sparsity. Subset function may perform additional
         *   evaluations because it processes data micro-row by micro-row.
         *   So, we can't check that all elements which were not flagged
         *   are zero - some of them may become non-zero. However, if entire
         *   row is empty, we can reasonably expect (informal guarantee)
         *   that it is not processed. So, we check empty (completely
         *   unflagged) rows 
         *
         */
        sparsity = ae_pow((double)(10), (double)(-hqrnduniformi(&rs, 4, _state)), _state);
        ae_vector_set_length(&gf, kx.ptr.p_int[0]*kx.ptr.p_int[1]*kx.ptr.p_int[2], _state);
        ae_vector_set_length(&rf, kx.ptr.p_int[1]*kx.ptr.p_int[2], _state);
        for(i=0; i<=kx.ptr.p_int[1]*kx.ptr.p_int[2]-1; i++)
        {
            rf.ptr.p_bool[i] = ae_false;
        }
        for(i=0; i<=kx.ptr.p_int[0]*kx.ptr.p_int[1]*kx.ptr.p_int[2]-1; i++)
        {
            gf.ptr.p_bool[i] = ae_fp_less(hqrnduniformr(&rs, _state),sparsity);
            if( gf.ptr.p_bool[i] )
            {
                rf.ptr.p_bool[i/kx.ptr.p_int[0]] = ae_true;
            }
        }
        ae_vector_set_length(&x, nx, _state);
        ae_vector_set_length(&y, ny, _state);
        unsetrealarray(&gy, _state);
        unsetrealarray(&gy2, _state);
        rbfgridcalc3vsubset(&s, &x0, kx.ptr.p_int[0], &x1, kx.ptr.p_int[1], &x2, kx.ptr.p_int[2], &gf, &gy, _state);
        rbfgridcalc3v(&s, &x0, kx.ptr.p_int[0], &x1, kx.ptr.p_int[1], &x2, kx.ptr.p_int[2], &gy2, _state);
        for(i=0; i<=ny*kx.ptr.p_int[0]*kx.ptr.p_int[1]*kx.ptr.p_int[2]-1; i++)
        {
            ae_set_error_flag(errorflag, gf.ptr.p_bool[i/ny]&&ae_fp_greater(ae_fabs(gy.ptr.p_double[i]-gy2.ptr.p_double[i], _state),1.0E-9*sy), __FILE__, __LINE__, "testrbfunit.ap:2558");
            ae_set_error_flag(errorflag, !rf.ptr.p_bool[i/(ny*kx.ptr.p_int[0])]&&ae_fp_neq(gy.ptr.p_double[i],0.0), __FILE__, __LINE__, "testrbfunit.ap:2559");
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Function for testing basic functionality of RBF module with hierarchical
algorithm.

  -- ALGLIB --
     Copyright 20.06.2016 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_basichrbftest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfmodel s2;
    rbfreport rep;
    rbfcalcbuffer tsbuf;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t linterm;
    ae_int_t bf;
    double rbase;
    ae_int_t nlayers;
    double errtol;
    double scalefactor;
    ae_matrix xy;
    ae_matrix xytest;
    ae_matrix uxwr;
    ae_matrix uv;
    ae_matrix xy2;
    ae_vector x;
    ae_vector y;
    ae_vector y2;
    ae_vector xzero;
    ae_vector yref;
    ae_vector scalevec;
    ae_int_t n;
    ae_int_t ntest;
    ae_int_t gridsize;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double r0;
    ae_int_t margin;
    double threshold;
    double v;
    ae_int_t functype;
    ae_int_t densitytype;
    double width;
    double lowprec;
    double highprec;
    ae_int_t modeltype;
    ae_int_t shaketype;
    double maxerr;
    ae_bool fractionalerror;
    ae_int_t unx;
    ae_int_t uny;
    ae_int_t unc;
    ae_int_t modelversion;
    ae_bool hasscale;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&s2, 0, sizeof(s2));
    memset(&rep, 0, sizeof(rep));
    memset(&tsbuf, 0, sizeof(tsbuf));
    memset(&xy, 0, sizeof(xy));
    memset(&xytest, 0, sizeof(xytest));
    memset(&uxwr, 0, sizeof(uxwr));
    memset(&uv, 0, sizeof(uv));
    memset(&xy2, 0, sizeof(xy2));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&y2, 0, sizeof(y2));
    memset(&xzero, 0, sizeof(xzero));
    memset(&yref, 0, sizeof(yref));
    memset(&scalevec, 0, sizeof(scalevec));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfmodel_init(&s2, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    _rbfcalcbuffer_init(&tsbuf, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xytest, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&uxwr, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&uv, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xzero, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yref, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scalevec, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    
    /*
     * First test - random problem, ability to build model
     * which reproduces function value in all points with
     * good precision.
     *
     * We also test properties of the linear term - that
     * model value in far away points is either constant
     * or exactly zero (for corresponding kinds of linear
     * term).
     *
     * All dataset points are located in unit cube on
     * regular grid. We do not use smoothing for this test.
     *
     * We use/test following functions:
     * * RBFCalc()
     * * RBFCalc2()
     * * RBFCalc3()
     * * RBFCalcBuf()
     * * RBFTsCalcBuf()
     */
    errtol = 1.0E-6;
    for(nx=1; nx<=4; nx++)
    {
        for(ny=1; ny<=3; ny++)
        {
            
            /*
             * problem setup
             */
            n = 150;
            rbase = 0.33;
            nlayers = 10;
            gridsize = ae_round(ae_pow((double)(n), (double)1/(double)nx, _state), _state)+1;
            linterm = 1+ae_randominteger(3, _state);
            bf = ae_randominteger(2, _state);
            n = ae_round(ae_pow((double)(gridsize), (double)(nx), _state), _state);
            ae_matrix_set_length(&xy, n, nx+ny, _state);
            ae_assert(gridsize>1, "Assertion failed", _state);
            ae_assert(ae_fp_eq((double)(n),ae_pow((double)(gridsize), (double)(nx), _state)), "Assertion failed", _state);
            for(i=0; i<=n-1; i++)
            {
                k = i;
                for(j=0; j<=nx-1; j++)
                {
                    xy.ptr.pp_double[i][j] = (double)(k%gridsize)/(double)(gridsize-1);
                    k = k/gridsize;
                }
                for(j=0; j<=ny-1; j++)
                {
                    xy.ptr.pp_double[i][nx+j] = ae_randomreal(_state)-0.5;
                }
            }
            
            /*
             * Build model
             */
            rbfcreate(nx, ny, &s, _state);
            rbfsetv2bf(&s, bf, _state);
            rbfsetalgohierarchical(&s, rbase, nlayers, 0.0, _state);
            if( linterm==1 )
            {
                rbfsetlinterm(&s, _state);
            }
            if( linterm==2 )
            {
                rbfsetconstterm(&s, _state);
            }
            if( linterm==3 )
            {
                rbfsetzeroterm(&s, _state);
            }
            rbfsetpoints(&s, &xy, n, _state);
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:2681");
                ae_frame_leave(_state);
                return result;
            }
            rbfcreatecalcbuffer(&s, &tsbuf, _state);
            
            /*
             * Test ability to reproduce function value
             *
             * NOTE: we use RBFCalc(XZero) to guarantee that internal state of
             *       RBF model is "reset" between subsequent calls of different
             *       functions. It allows us to make sure that we have no bug
             *       like function simply returning latest result
             */
            ae_vector_set_length(&x, nx, _state);
            ae_vector_set_length(&xzero, nx, _state);
            ae_vector_set_length(&y, ny, _state);
            for(j=0; j<=nx-1; j++)
            {
                xzero.ptr.p_double[j] = (double)(0);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    x.ptr.p_double[j] = xy.ptr.pp_double[i][j];
                }
                rbfcalc(&s, &xzero, &y, _state);
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    ae_vector_set_length(&yref, ny+1, _state);
                }
                rbfcalc(&s, &x, &yref, _state);
                for(j=0; j<=ny-1; j++)
                {
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(yref.ptr.p_double[j]-xy.ptr.pp_double[i][nx+j], _state),errtol), __FILE__, __LINE__, "testrbfunit.ap:2710");
                }
                ae_set_error_flag(&result, yref.cnt!=ny, __FILE__, __LINE__, "testrbfunit.ap:2711");
                if( nx==1&&ny==1 )
                {
                    rbfcalc(&s, &xzero, &y, _state);
                    ae_set_error_flag(&result, ae_fp_neq(rbfcalc1(&s, x.ptr.p_double[0], _state),yref.ptr.p_double[0]), __FILE__, __LINE__, "testrbfunit.ap:2717");
                }
                if( nx==2&&ny==1 )
                {
                    rbfcalc(&s, &xzero, &y, _state);
                    ae_set_error_flag(&result, ae_fp_neq(rbfcalc2(&s, x.ptr.p_double[0], x.ptr.p_double[1], _state),yref.ptr.p_double[0]), __FILE__, __LINE__, "testrbfunit.ap:2724");
                }
                if( nx==3&&ny==1 )
                {
                    rbfcalc(&s, &xzero, &y, _state);
                    ae_set_error_flag(&result, ae_fp_neq(rbfcalc3(&s, x.ptr.p_double[0], x.ptr.p_double[1], x.ptr.p_double[2], _state),yref.ptr.p_double[0]), __FILE__, __LINE__, "testrbfunit.ap:2731");
                }
                rbfcalc(&s, &xzero, &y, _state);
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    ae_vector_set_length(&y, ny+1, _state);
                    rbfcalcbuf(&s, &x, &y, _state);
                    ae_set_error_flag(&result, y.cnt!=ny+1, __FILE__, __LINE__, "testrbfunit.ap:2740");
                }
                else
                {
                    ae_vector_set_length(&y, ny-1, _state);
                    rbfcalcbuf(&s, &x, &y, _state);
                    ae_set_error_flag(&result, y.cnt!=ny, __FILE__, __LINE__, "testrbfunit.ap:2746");
                }
                for(j=0; j<=ny-1; j++)
                {
                    ae_set_error_flag(&result, ae_fp_neq(y.ptr.p_double[j],yref.ptr.p_double[j]), __FILE__, __LINE__, "testrbfunit.ap:2749");
                }
                rbfcalc(&s, &xzero, &y, _state);
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    ae_vector_set_length(&y, ny+1, _state);
                    rbftscalcbuf(&s, &tsbuf, &x, &y, _state);
                    ae_set_error_flag(&result, y.cnt!=ny+1, __FILE__, __LINE__, "testrbfunit.ap:2757");
                }
                else
                {
                    ae_vector_set_length(&y, ny-1, _state);
                    rbftscalcbuf(&s, &tsbuf, &x, &y, _state);
                    ae_set_error_flag(&result, y.cnt!=ny, __FILE__, __LINE__, "testrbfunit.ap:2763");
                }
                for(j=0; j<=ny-1; j++)
                {
                    ae_set_error_flag(&result, ae_fp_neq(y.ptr.p_double[j],yref.ptr.p_double[j]), __FILE__, __LINE__, "testrbfunit.ap:2766");
                }
            }
            
            /*
             * Test that:
             * a) model with zero linear term is zero far away from dataset
             * b) model with constant linear term is constant far away from dataset
             */
            ae_vector_set_length(&x, nx, _state);
            if( linterm==2 )
            {
                for(j=0; j<=nx-1; j++)
                {
                    if( ae_fp_greater(ae_randomreal(_state),0.5) )
                    {
                        x.ptr.p_double[j] = 1+1000*rbase;
                    }
                    else
                    {
                        x.ptr.p_double[j] = 0-1000*rbase;
                    }
                }
                rbfcalc(&s, &x, &y, _state);
                for(j=0; j<=nx-1; j++)
                {
                    if( ae_fp_greater(ae_randomreal(_state),0.5) )
                    {
                        x.ptr.p_double[j] = 1+1000*rbase;
                    }
                    else
                    {
                        x.ptr.p_double[j] = 0-1000*rbase;
                    }
                }
                rbfcalc(&s, &x, &y2, _state);
                for(j=0; j<=ny-1; j++)
                {
                    ae_set_error_flag(&result, ae_fp_neq(y.ptr.p_double[j],y2.ptr.p_double[j]), __FILE__, __LINE__, "testrbfunit.ap:2790");
                }
            }
            if( linterm==3 )
            {
                for(j=0; j<=nx-1; j++)
                {
                    if( ae_fp_greater(ae_randomreal(_state),0.5) )
                    {
                        x.ptr.p_double[j] = 1+1000*rbase;
                    }
                    else
                    {
                        x.ptr.p_double[j] = 0-1000*rbase;
                    }
                }
                rbfcalc(&s, &x, &y, _state);
                for(j=0; j<=ny-1; j++)
                {
                    ae_set_error_flag(&result, ae_fp_neq(y.ptr.p_double[j],(double)(0)), __FILE__, __LINE__, "testrbfunit.ap:2801");
                }
            }
        }
    }
    
    /*
     * Test rbfunpack()
     */
    for(nx=1; nx<=2; nx++)
    {
        for(ny=1; ny<=2; ny++)
        {
            
            /*
             * problem setup
             */
            n = 150;
            rbase = 0.33;
            nlayers = 5;
            gridsize = ae_round(ae_pow((double)(n), (double)1/(double)nx, _state), _state)+1;
            linterm = 1+ae_randominteger(3, _state);
            bf = ae_randominteger(2, _state);
            n = ae_round(ae_pow((double)(gridsize), (double)(nx), _state), _state);
            ae_matrix_set_length(&xy, n, nx+ny, _state);
            ae_assert(gridsize>1, "Assertion failed", _state);
            ae_assert(ae_fp_eq((double)(n),ae_pow((double)(gridsize), (double)(nx), _state)), "Assertion failed", _state);
            for(i=0; i<=n-1; i++)
            {
                k = i;
                for(j=0; j<=nx-1; j++)
                {
                    xy.ptr.pp_double[i][j] = (double)(k%gridsize)/(double)(gridsize-1);
                    k = k/gridsize;
                }
                for(j=0; j<=ny-1; j++)
                {
                    xy.ptr.pp_double[i][nx+j] = ae_randomreal(_state)-0.5;
                }
            }
            hasscale = ae_randominteger(2, _state)==0;
            ae_vector_set_length(&scalevec, nx, _state);
            if( hasscale )
            {
                for(j=0; j<=nx-1; j++)
                {
                    scalevec.ptr.p_double[j] = ae_pow((double)(2), 2*ae_randomreal(_state)-1, _state);
                }
            }
            
            /*
             * Build model
             */
            rbfcreate(nx, ny, &s, _state);
            rbfsetv2bf(&s, bf, _state);
            rbfsetalgohierarchical(&s, rbase, nlayers, 0.0, _state);
            if( linterm==1 )
            {
                rbfsetlinterm(&s, _state);
            }
            if( linterm==2 )
            {
                rbfsetconstterm(&s, _state);
            }
            if( linterm==3 )
            {
                rbfsetzeroterm(&s, _state);
            }
            if( hasscale )
            {
                rbfsetpointsandscales(&s, &xy, n, &scalevec, _state);
            }
            else
            {
                rbfsetpoints(&s, &xy, n, _state);
            }
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:2862");
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Test RBFUnpack()
             */
            rbfunpack(&s, &unx, &uny, &uxwr, &unc, &uv, &modelversion, _state);
            if( modelversion!=2 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:2872");
                ae_frame_leave(_state);
                return result;
            }
            if( unx!=nx||uny!=ny )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:2877");
                ae_frame_leave(_state);
                return result;
            }
            if( uv.cols!=nx+1||uv.rows!=ny )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:2882");
                ae_frame_leave(_state);
                return result;
            }
            if( linterm==2 )
            {
                for(i=0; i<=ny-1; i++)
                {
                    for(j=0; j<=nx-1; j++)
                    {
                        ae_set_error_flag(&result, ae_fp_neq(uv.ptr.pp_double[i][j],(double)(0)), __FILE__, __LINE__, "testrbfunit.ap:2889");
                    }
                }
            }
            if( linterm==3 )
            {
                for(i=0; i<=ny-1; i++)
                {
                    for(j=0; j<=nx; j++)
                    {
                        ae_set_error_flag(&result, ae_fp_neq(uv.ptr.pp_double[i][j],(double)(0)), __FILE__, __LINE__, "testrbfunit.ap:2895");
                    }
                }
            }
            ae_vector_set_length(&x, nx, _state);
            ae_vector_set_length(&y, ny, _state);
            for(i=0; i<=9; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    x.ptr.p_double[j] = ae_randomreal(_state);
                }
                rbfcalc(&s, &x, &yref, _state);
                for(j=0; j<=ny-1; j++)
                {
                    y.ptr.p_double[j] = uv.ptr.pp_double[j][nx];
                    for(k=0; k<=nx-1; k++)
                    {
                        y.ptr.p_double[j] = y.ptr.p_double[j]+x.ptr.p_double[k]*uv.ptr.pp_double[j][k];
                    }
                }
                for(k=0; k<=unc-1; k++)
                {
                    v = (double)(0);
                    for(j=0; j<=nx-1; j++)
                    {
                        v = v+ae_sqr(uxwr.ptr.pp_double[k][j]-x.ptr.p_double[j], _state)/ae_sqr(uxwr.ptr.pp_double[k][nx+ny+j], _state);
                    }
                    if( ae_fp_less(v,rbfv2farradius(bf, _state)*rbfv2farradius(bf, _state)) )
                    {
                        v = rbfv2basisfunc(bf, v, _state);
                    }
                    else
                    {
                        v = (double)(0);
                    }
                    for(j=0; j<=ny-1; j++)
                    {
                        y.ptr.p_double[j] = y.ptr.p_double[j]+v*uxwr.ptr.pp_double[k][nx+j];
                    }
                }
                for(j=0; j<=ny-1; j++)
                {
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(y.ptr.p_double[j]-yref.ptr.p_double[j], _state),1.0E-9), __FILE__, __LINE__, "testrbfunit.ap:2930");
                }
            }
        }
    }
    
    /*
     * Test that smooth 1D function is reproduced (between nodes)
     * with good precision. We test two model types: model with
     * three layers and moderate initial radius, and model with
     * large initial radius and large number of layers.
     *
     * This test:
     * * generates test function on [-Width,+Width]. Two sets of
     *   nodes are generated - "model" ones and "test" ones.
     * * builds RBF model using "model" dataset
     * * test model using "test" dataset. Test points are more
     *   dense and are spread in [-0.9*Width, +0.9*Width] (reduced
     *   interval is used because RBF models are too bad near the
     *   boundaries).
     *
     * NOTE: we calculate maximum error for given function type
     *       and grid density over all modifications of the task,
     *       and only after that we perform comparison with tolerance
     *       level. It allows easier debugging.
     */
    for(functype=0; functype<=2; functype++)
    {
        for(densitytype=0; densitytype<=1; densitytype++)
        {
            
            /*
             * Select tolerance
             */
            lowprec = (double)(-999999);
            highprec = (double)(-999999);
            if( functype==0 )
            {
                lowprec = 1.0E-2;
                highprec = 1.0E-3;
            }
            else
            {
                if( functype==1 )
                {
                    lowprec = 1.0E-1;
                    highprec = 1.0E-2;
                }
                else
                {
                    if( functype==2 )
                    {
                        lowprec = 1.0E-3;
                        highprec = 1.0E-4;
                    }
                    else
                    {
                        ae_assert(ae_false, "Assertion failed", _state);
                    }
                }
            }
            if( densitytype==0 )
            {
                errtol = lowprec;
            }
            else
            {
                errtol = highprec;
            }
            
            /*
             * Test
             */
            maxerr = (double)(0);
            for(modeltype=0; modeltype<=1; modeltype++)
            {
                for(shaketype=0; shaketype<=1; shaketype++)
                {
                    
                    /*
                     * Generate grid
                     */
                    width = (double)(1);
                    fractionalerror = ae_false;
                    if( functype==0 )
                    {
                        
                        /*
                         * sin(x) on [-2*pi,+2*pi]
                         */
                        n = 17*ae_round(ae_pow((double)(4), (double)(densitytype), _state), _state);
                        width = ae_pi;
                        fractionalerror = ae_false;
                    }
                    else
                    {
                        if( functype==1 )
                        {
                            
                            /*
                             * exp(x) on [-3,+3]
                             */
                            n = 50*ae_round(ae_pow((double)(4), (double)(densitytype), _state), _state);
                            width = (double)(3);
                            fractionalerror = ae_true;
                        }
                        else
                        {
                            if( functype==2 )
                            {
                                
                                /*
                                 * 1/(1+x^2) on [-3,+3]
                                 */
                                n = 20*ae_round(ae_pow((double)(4), (double)(densitytype), _state), _state);
                                width = (double)(3);
                                fractionalerror = ae_false;
                            }
                            else
                            {
                                ae_assert(ae_false, "Assertion failed", _state);
                            }
                        }
                    }
                    ae_matrix_set_length(&xy, n, 2, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        v = shaketype*0.25*(ae_randomreal(_state)-0.5);
                        v = (i+v)/(n-1);
                        v = 2*v-1;
                        xy.ptr.pp_double[i][0] = width*v;
                    }
                    ntest = n*10;
                    ae_matrix_set_length(&xytest, ntest, 2, _state);
                    for(i=0; i<=ntest-1; i++)
                    {
                        xytest.ptr.pp_double[i][0] = 0.9*width*((double)(2*i)/(double)(ntest-1)-1);
                    }
                    
                    /*
                     * Evaluate function
                     */
                    if( functype==0 )
                    {
                        
                        /*
                         * sin(x)
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            xy.ptr.pp_double[i][1] = ae_sin(xy.ptr.pp_double[i][0], _state);
                        }
                        for(i=0; i<=ntest-1; i++)
                        {
                            xytest.ptr.pp_double[i][1] = ae_sin(xytest.ptr.pp_double[i][0], _state);
                        }
                    }
                    else
                    {
                        if( functype==1 )
                        {
                            
                            /*
                             * exp(x)
                             */
                            for(i=0; i<=n-1; i++)
                            {
                                xy.ptr.pp_double[i][1] = ae_exp(xy.ptr.pp_double[i][0], _state);
                            }
                            for(i=0; i<=ntest-1; i++)
                            {
                                xytest.ptr.pp_double[i][1] = ae_exp(xytest.ptr.pp_double[i][0], _state);
                            }
                        }
                        else
                        {
                            if( functype==2 )
                            {
                                
                                /*
                                 * 1/(1+x^2)
                                 */
                                for(i=0; i<=n-1; i++)
                                {
                                    xy.ptr.pp_double[i][1] = 1/(1+ae_sqr(xy.ptr.pp_double[i][0], _state));
                                }
                                for(i=0; i<=ntest-1; i++)
                                {
                                    xytest.ptr.pp_double[i][1] = 1/(1+ae_sqr(xytest.ptr.pp_double[i][0], _state));
                                }
                            }
                            else
                            {
                                ae_assert(ae_false, "Assertion failed", _state);
                            }
                        }
                    }
                    
                    /*
                     * Select model properties and precision
                     */
                    if( modeltype==0 )
                    {
                        rbase = 4.0*(2*width/n);
                        nlayers = 3;
                    }
                    else
                    {
                        if( modeltype==1 )
                        {
                            rbase = 16.0*(2*width/n);
                            nlayers = 6;
                        }
                        else
                        {
                            ae_assert(ae_false, "Assertion failed", _state);
                        }
                    }
                    
                    /*
                     * Build model
                     */
                    bf = ae_randominteger(2, _state);
                    rbfcreate(1, 1, &s, _state);
                    rbfsetv2bf(&s, bf, _state);
                    rbfsetalgohierarchical(&s, rbase, nlayers, 0.0, _state);
                    rbfsetpoints(&s, &xy, n, _state);
                    rbfbuildmodel(&s, &rep, _state);
                    if( rep.terminationtype<=0 )
                    {
                        ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3104");
                        ae_frame_leave(_state);
                        return result;
                    }
                    
                    /*
                     * Check
                     */
                    ae_vector_set_length(&x, 1, _state);
                    for(i=0; i<=ntest-1; i++)
                    {
                        x.ptr.p_double[0] = xytest.ptr.pp_double[i][0];
                        rbfcalc(&s, &x, &y, _state);
                        if( fractionalerror )
                        {
                            maxerr = ae_maxreal(maxerr, ae_fabs(y.ptr.p_double[0]-xytest.ptr.pp_double[i][1], _state)/ae_fabs(xytest.ptr.pp_double[i][1], _state), _state);
                        }
                        else
                        {
                            maxerr = ae_maxreal(maxerr, ae_fabs(y.ptr.p_double[0]-xytest.ptr.pp_double[i][1], _state), _state);
                        }
                    }
                }
            }
            
            /*
             * Check error
             */
            ae_set_error_flag(&result, ae_fp_greater(maxerr,errtol), __FILE__, __LINE__, "testrbfunit.ap:3126");
        }
    }
    
    /*
     * Scaling test - random problem, we test that after
     * scaling of all variables and radius by 2^K (for some K)
     * we will get exactly same results (up to the last bit of
     * mantissa).
     *
     * It is very strong requirement for algorithm stability,
     * but it is satisfiable in most implementations of RBFs,
     * because all operations involving spatial values are usually
     * followed by division by radius, and using multiplier which
     * is exactly power of 2 results in no changes in numbers
     * being returned.
     *
     * It allows to test different scale-related bugs
     * (say, situation when deep in kd-tree search code we compare
     * against R instead of R^2).
     *
     * All dataset points are located in unit cube on
     * regular grid.
     *
     * We do not use smoothing for this test.
     */
    for(nx=1; nx<=4; nx++)
    {
        
        /*
         * problem setup
         */
        n = 150;
        rbase = 0.33;
        nlayers = ae_randominteger(4, _state);
        scalefactor = ae_pow((double)(1024), (double)(2*ae_randominteger(2, _state)-1), _state);
        gridsize = ae_round(ae_pow((double)(n), (double)1/(double)nx, _state), _state)+1;
        ny = 1+ae_randominteger(3, _state);
        linterm = 1+ae_randominteger(3, _state);
        bf = ae_randominteger(2, _state);
        n = ae_round(ae_pow((double)(gridsize), (double)(nx), _state), _state);
        ae_matrix_set_length(&xy, n, nx+ny, _state);
        ae_matrix_set_length(&xy2, n, nx+ny, _state);
        ae_assert(gridsize>1, "Assertion failed", _state);
        ae_assert(ae_fp_eq((double)(n),ae_pow((double)(gridsize), (double)(nx), _state)), "Assertion failed", _state);
        for(i=0; i<=n-1; i++)
        {
            k = i;
            for(j=0; j<=nx-1; j++)
            {
                xy.ptr.pp_double[i][j] = (double)(k%gridsize)/(double)(gridsize-1);
                xy2.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]*scalefactor;
                k = k/gridsize;
            }
            for(j=0; j<=ny-1; j++)
            {
                xy.ptr.pp_double[i][nx+j] = ae_randomreal(_state)-0.5;
                xy2.ptr.pp_double[i][nx+j] = xy.ptr.pp_double[i][nx+j];
            }
        }
        
        /*
         * Build model 1
         */
        rbfcreate(nx, ny, &s, _state);
        rbfsetv2bf(&s, bf, _state);
        rbfsetalgohierarchical(&s, rbase, nlayers, 0.0, _state);
        if( linterm==1 )
        {
            rbfsetlinterm(&s, _state);
        }
        if( linterm==2 )
        {
            rbfsetconstterm(&s, _state);
        }
        if( linterm==3 )
        {
            rbfsetzeroterm(&s, _state);
        }
        rbfsetpoints(&s, &xy, n, _state);
        rbfbuildmodel(&s, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3202");
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Build model 2
         */
        rbfcreate(nx, ny, &s2, _state);
        rbfsetv2bf(&s2, bf, _state);
        rbfsetalgohierarchical(&s2, rbase*scalefactor, nlayers, 0.0, _state);
        if( linterm==1 )
        {
            rbfsetlinterm(&s2, _state);
        }
        if( linterm==2 )
        {
            rbfsetconstterm(&s2, _state);
        }
        if( linterm==3 )
        {
            rbfsetzeroterm(&s2, _state);
        }
        rbfsetpoints(&s2, &xy2, n, _state);
        rbfbuildmodel(&s2, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3222");
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Compare models
         */
        ae_vector_set_length(&x, nx, _state);
        ae_vector_set_length(&y, ny, _state);
        ae_vector_set_length(&y2, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=nx-1; j++)
            {
                x.ptr.p_double[j] = xy.ptr.pp_double[i][j];
            }
            rbfcalc(&s, &x, &y, _state);
            for(j=0; j<=nx-1; j++)
            {
                x.ptr.p_double[j] = xy2.ptr.pp_double[i][j];
            }
            rbfcalc(&s2, &x, &y2, _state);
            for(j=0; j<=ny-1; j++)
            {
                ae_set_error_flag(&result, ae_fp_neq(y.ptr.p_double[j],y2.ptr.p_double[j]), __FILE__, __LINE__, "testrbfunit.ap:3241");
            }
        }
    }
    
    /*
     * Test that passing scaled dataset automatically results in V2 model
     * being built (even when algorithm type is not set explicitly).
     */
    for(nx=1; nx<=4; nx++)
    {
        for(ny=1; ny<=3; ny++)
        {
            
            /*
             * problem setup
             */
            n = 10;
            ae_matrix_set_length(&xy, n, nx+ny, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx+ny-1; j++)
                {
                    xy.ptr.pp_double[i][j] = ae_randomreal(_state)-0.5;
                }
            }
            ae_vector_set_length(&scalevec, nx, _state);
            for(j=0; j<=nx-1; j++)
            {
                scalevec.ptr.p_double[j] = ae_pow((double)(2), 2*ae_randomreal(_state)-1, _state);
            }
            
            /*
             * prepare test problem
             */
            rbfcreate(nx, ny, &s, _state);
            rbfsetpointsandscales(&s, &xy, xy.rows, &scalevec, _state);
            
            /*
             * Build model, check model version
             */
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3277");
                ae_frame_leave(_state);
                return result;
            }
            ae_set_error_flag(&result, rbfgetmodelversion(&s, _state)!=2, __FILE__, __LINE__, "testrbfunit.ap:3280");
        }
    }
    
    /*
     * Test smoothing properties of RBF model: model with just one layer
     * and large initial radius will produce "average" value of neighbors.
     *
     * In order to test it we create regular grid, fill it with regular
     * +1/-1 pattern, and test model values in the inner points. Model
     * values should be around zero (we use handcrafted threshold to test
     * it). Radius is chosen to be several times larger than grid step.
     *
     * We perform test for 2D model, because same behavior is expected
     * regardless of dimensionality.
     */
    r0 = (double)(6);
    margin = 10;
    threshold = 0.005;
    gridsize = 2*margin+10;
    nx = 2;
    ny = 1;
    for(bf=0; bf<=1; bf++)
    {
        rbfcreate(nx, ny, &s, _state);
        rbfsetv2bf(&s, bf, _state);
        rbfsetalgohierarchical(&s, r0, 1, 1.0E-1, _state);
        rbfsetzeroterm(&s, _state);
        ae_matrix_set_length(&xy, gridsize*gridsize, nx+ny, _state);
        for(i=0; i<=gridsize-1; i++)
        {
            for(j=0; j<=gridsize-1; j++)
            {
                xy.ptr.pp_double[i*gridsize+j][0] = (double)(i);
                xy.ptr.pp_double[i*gridsize+j][1] = (double)(j);
                xy.ptr.pp_double[i*gridsize+j][2] = 0.01*(ae_randomreal(_state)-0.5)+(2*((i+j)%2)-1);
            }
        }
        rbfsetpoints(&s, &xy, gridsize*gridsize, _state);
        rbfbuildmodel(&s, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3320");
            ae_frame_leave(_state);
            return result;
        }
        v = 0.0;
        for(i=margin; i<=gridsize-margin-1; i++)
        {
            for(j=margin; j<=gridsize-margin-1; j++)
            {
                v = ae_maxreal(v, ae_fabs(rbfcalc2(&s, (double)(i), (double)(j), _state), _state), _state);
            }
        }
        ae_set_error_flag(&result, ae_fp_greater(v,threshold), __FILE__, __LINE__, "testrbfunit.ap:3327");
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing scaling-related functionality of RBF module with
hierarchical algorithm.

Returns True on failure (error flag is set).

  -- ALGLIB --
     Copyright 20.06.2016 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_scaledhrbftest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfmodel s2;
    rbfreport rep;
    rbfcalcbuffer tsbuf;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t linterm;
    ae_int_t bf;
    double rbase;
    ae_int_t nlayers;
    double errtol;
    ae_matrix xy;
    ae_matrix xy2;
    ae_vector x;
    ae_vector y;
    ae_vector y2;
    ae_vector xzero;
    ae_vector yref;
    ae_vector scalex;
    ae_vector scaley;
    ae_vector c0;
    ae_vector c1;
    ae_int_t n;
    ae_int_t gridsize;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t strictness;
    double lambdav;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&s2, 0, sizeof(s2));
    memset(&rep, 0, sizeof(rep));
    memset(&tsbuf, 0, sizeof(tsbuf));
    memset(&xy, 0, sizeof(xy));
    memset(&xy2, 0, sizeof(xy2));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&y2, 0, sizeof(y2));
    memset(&xzero, 0, sizeof(xzero));
    memset(&yref, 0, sizeof(yref));
    memset(&scalex, 0, sizeof(scalex));
    memset(&scaley, 0, sizeof(scaley));
    memset(&c0, 0, sizeof(c0));
    memset(&c1, 0, sizeof(c1));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfmodel_init(&s2, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    _rbfcalcbuffer_init(&tsbuf, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xzero, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yref, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scalex, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scaley, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c1, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    
    /*
     * First test - random problem, test that using scaling
     * does not change model significantly (except for
     * rounding-related errors).
     *
     * We test two kinds of scaling:
     * * "strict", which is scaling by some power of 2, and
     *   which should result in bit-to-bit equivalence of results
     * * "non-strict", which is scaling by random number, and
     *   which should result in approximate equivalence
     *
     * We also apply scaling to Y, in order to test that it
     * is correctly handled too.
     */
    for(strictness=0; strictness<=1; strictness++)
    {
        for(nx=1; nx<=2; nx++)
        {
            for(ny=1; ny<=2; ny++)
            {
                
                /*
                 * problem setup
                 */
                if( strictness==1 )
                {
                    errtol = (double)(0);
                    ae_vector_set_length(&scalex, nx, _state);
                    for(i=0; i<=nx-1; i++)
                    {
                        scalex.ptr.p_double[i] = ae_pow((double)(16), (double)(ae_randominteger(3, _state)-1), _state);
                    }
                    ae_vector_set_length(&scaley, ny, _state);
                    for(i=0; i<=ny-1; i++)
                    {
                        scaley.ptr.p_double[i] = ae_pow((double)(16), (double)(ae_randominteger(3, _state)-1), _state);
                    }
                }
                else
                {
                    errtol = 1.0E-3;
                    ae_vector_set_length(&scalex, nx, _state);
                    for(i=0; i<=nx-1; i++)
                    {
                        scalex.ptr.p_double[i] = ae_pow((double)(4), 2*ae_randomreal(_state)-1, _state);
                    }
                    ae_vector_set_length(&scaley, ny, _state);
                    for(i=0; i<=ny-1; i++)
                    {
                        scaley.ptr.p_double[i] = ae_pow((double)(4), 2*ae_randomreal(_state)-1, _state);
                    }
                }
                n = 150;
                rbase = 0.33;
                nlayers = 2;
                gridsize = ae_round(ae_pow((double)(n), (double)1/(double)nx, _state), _state)+1;
                linterm = 1+ae_randominteger(3, _state);
                bf = ae_randominteger(2, _state);
                lambdav = 1.0E-3*ae_randominteger(2, _state);
                n = ae_round(ae_pow((double)(gridsize), (double)(nx), _state), _state);
                ae_matrix_set_length(&xy, n, nx+ny, _state);
                ae_assert(gridsize>1, "Assertion failed", _state);
                ae_assert(ae_fp_eq((double)(n),ae_pow((double)(gridsize), (double)(nx), _state)), "Assertion failed", _state);
                ae_vector_set_length(&c0, nx, _state);
                for(j=0; j<=nx-1; j++)
                {
                    c0.ptr.p_double[j] = ae_randomreal(_state)-0.5;
                }
                ae_vector_set_length(&c1, ny, _state);
                for(j=0; j<=ny-1; j++)
                {
                    c1.ptr.p_double[j] = ae_randomreal(_state)-0.5;
                }
                for(i=0; i<=n-1; i++)
                {
                    k = i;
                    for(j=0; j<=nx-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = (double)(k%gridsize)/(double)(gridsize-1);
                        k = k/gridsize;
                    }
                    for(j=0; j<=ny-1; j++)
                    {
                        xy.ptr.pp_double[i][nx+j] = (double)(0);
                        for(k=0; k<=nx-1; k++)
                        {
                            xy.ptr.pp_double[i][nx+j] = xy.ptr.pp_double[i][nx+j]+c0.ptr.p_double[k]*ae_cos(ae_pi*(1+k)*xy.ptr.pp_double[i][k], _state);
                        }
                        xy.ptr.pp_double[i][nx+j] = c1.ptr.p_double[j]*xy.ptr.pp_double[i][nx+j];
                    }
                }
                ae_matrix_set_length(&xy2, n, nx+ny, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=nx-1; j++)
                    {
                        xy2.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]*scalex.ptr.p_double[j];
                    }
                    for(j=0; j<=ny-1; j++)
                    {
                        xy2.ptr.pp_double[i][nx+j] = xy.ptr.pp_double[i][nx+j]*scaley.ptr.p_double[j];
                    }
                }
                
                /*
                 * Build models
                 */
                rbfcreate(nx, ny, &s, _state);
                rbfsetv2bf(&s, bf, _state);
                rbfsetalgohierarchical(&s, rbase, nlayers, lambdav, _state);
                if( linterm==1 )
                {
                    rbfsetlinterm(&s, _state);
                }
                if( linterm==2 )
                {
                    rbfsetconstterm(&s, _state);
                }
                if( linterm==3 )
                {
                    rbfsetzeroterm(&s, _state);
                }
                rbfsetpoints(&s, &xy, n, _state);
                rbfbuildmodel(&s, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3470");
                    ae_frame_leave(_state);
                    return result;
                }
                rbfcreate(nx, ny, &s2, _state);
                rbfsetv2bf(&s2, bf, _state);
                rbfsetalgohierarchical(&s2, rbase, nlayers, lambdav, _state);
                if( linterm==1 )
                {
                    rbfsetlinterm(&s2, _state);
                }
                if( linterm==2 )
                {
                    rbfsetconstterm(&s2, _state);
                }
                if( linterm==3 )
                {
                    rbfsetzeroterm(&s2, _state);
                }
                rbfsetpointsandscales(&s2, &xy2, n, &scalex, _state);
                rbfbuildmodel(&s2, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3486");
                    ae_frame_leave(_state);
                    return result;
                }
                
                /*
                 * Compare model values in grid points
                 */
                ae_vector_set_length(&x, nx, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=nx-1; j++)
                    {
                        x.ptr.p_double[j] = xy.ptr.pp_double[i][j];
                    }
                    rbfcalc(&s, &x, &y, _state);
                    for(j=0; j<=nx-1; j++)
                    {
                        x.ptr.p_double[j] = xy2.ptr.pp_double[i][j];
                    }
                    rbfcalc(&s2, &x, &y2, _state);
                    for(j=0; j<=ny-1; j++)
                    {
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(y.ptr.p_double[j]-y2.ptr.p_double[j]/scaley.ptr.p_double[j], _state),errtol), __FILE__, __LINE__, "testrbfunit.ap:3503");
                    }
                }
                
                /*
                 * Compare model values in random points
                 */
                ae_vector_set_length(&x, nx, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=nx-1; j++)
                    {
                        x.ptr.p_double[j] = ae_randomreal(_state);
                    }
                    rbfcalc(&s, &x, &y, _state);
                    for(j=0; j<=nx-1; j++)
                    {
                        x.ptr.p_double[j] = x.ptr.p_double[j]*scalex.ptr.p_double[j];
                    }
                    rbfcalc(&s2, &x, &y2, _state);
                    for(j=0; j<=ny-1; j++)
                    {
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(y.ptr.p_double[j]-y2.ptr.p_double[j]/scaley.ptr.p_double[j], _state),errtol), __FILE__, __LINE__, "testrbfunit.ap:3519");
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Test special properties of hierarchical RBFs.
Returns True on errors.

  -- ALGLIB --
     Copyright 20.06.2016 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_spechrbftest(ae_state *_state)
{
    double d2;
    double vref;
    double vfunc;
    double maxerr;
    ae_bool result;


    result = ae_false;
    
    /*
     * Test properties of RBF basis functions - we compare values
     * returned by RBFV2BasisFunc() against analytic expressions
     * which are approximately modeled by RBFV2BasisFunc().
     */
    d2 = 0.0;
    while(ae_fp_less(d2,100.0))
    {
        vref = ae_exp(-d2, _state);
        vfunc = rbfv2basisfunc(0, d2, _state);
        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vref-vfunc, _state),1.0E-9*vref), __FILE__, __LINE__, "testrbfunit.ap:3550");
        d2 = d2+(double)1/(double)64;
    }
    d2 = 0.0;
    maxerr = 0.0;
    while(ae_fp_less(d2,16.0))
    {
        vref = ae_maxreal(1-d2/9, (double)(0), _state)*ae_exp(-d2, _state);
        vfunc = rbfv2basisfunc(1, d2, _state);
        maxerr = ae_maxreal(maxerr, ae_fabs(vref-vfunc, _state), _state);
        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(vref-vfunc, _state),0.005), __FILE__, __LINE__, "testrbfunit.ap:3560");
        d2 = d2+(double)1/(double)64;
    }
    return result;
}


/*************************************************************************
Test gridded evaluation of hierarchical RBFs.
Returns True on errors.

  -- ALGLIB --
     Copyright 20.06.2016 by Bochkanov Sergey
*************************************************************************/
static ae_bool testrbfunit_gridhrbftest(ae_state *_state)
{
    ae_frame _frame_block;
    rbfmodel s;
    rbfreport rep;
    ae_int_t linterm;
    ae_int_t bf;
    double rbase;
    ae_int_t nlayers;
    ae_int_t nx;
    ae_int_t ny;
    ae_bool hasscale;
    double errtol;
    ae_int_t n;
    ae_matrix xy;
    ae_matrix xy2;
    ae_matrix y2;
    ae_vector x0;
    ae_vector x1;
    ae_vector x2;
    ae_vector x02;
    ae_vector x12;
    ae_vector x22;
    ae_vector scalevec;
    ae_vector scalevec2;
    ae_vector needy;
    ae_vector rowflags;
    ae_int_t n0;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t nkind;
    double v;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t i0;
    ae_int_t i1;
    ae_int_t i2;
    ae_vector x;
    ae_vector y;
    ae_vector yv;
    ae_vector yv2;
    double scalefactor;
    double lambdav;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&xy, 0, sizeof(xy));
    memset(&xy2, 0, sizeof(xy2));
    memset(&y2, 0, sizeof(y2));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&x2, 0, sizeof(x2));
    memset(&x02, 0, sizeof(x02));
    memset(&x12, 0, sizeof(x12));
    memset(&x22, 0, sizeof(x22));
    memset(&scalevec, 0, sizeof(scalevec));
    memset(&scalevec2, 0, sizeof(scalevec2));
    memset(&needy, 0, sizeof(needy));
    memset(&rowflags, 0, sizeof(rowflags));
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&yv, 0, sizeof(yv));
    memset(&yv2, 0, sizeof(yv2));
    _rbfmodel_init(&s, _state, ae_true);
    _rbfreport_init(&rep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&y2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x02, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x12, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x22, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scalevec, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&scalevec2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&needy, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&rowflags, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yv2, 0, DT_REAL, _state, ae_true);

    result = ae_false;
    
    /*
     * Test 2-dimensional grid calculation
     */
    errtol = 1.0E-12;
    nx = 2;
    for(ny=1; ny<=4; ny++)
    {
        for(nkind=0; nkind<=2; nkind++)
        {
            
            /*
             * problem setup
             */
            n = 150;
            rbase = 0.10;
            nlayers = ae_randominteger(3, _state);
            linterm = 1+ae_randominteger(3, _state);
            lambdav = 1.0E-3*ae_randominteger(2, _state);
            bf = ae_randominteger(2, _state);
            ae_matrix_set_length(&xy, n, nx+ny, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                }
                for(j=0; j<=ny-1; j++)
                {
                    xy.ptr.pp_double[i][nx+j] = ae_randomreal(_state)-0.5;
                }
            }
            hasscale = ae_randominteger(2, _state)==0;
            if( hasscale )
            {
                ae_vector_set_length(&scalevec, nx, _state);
                for(j=0; j<=nx-1; j++)
                {
                    scalevec.ptr.p_double[j] = ae_pow((double)(2), 2*ae_randomreal(_state)-1, _state);
                }
            }
            
            /*
             * Build model
             */
            rbfcreate(nx, ny, &s, _state);
            rbfsetv2bf(&s, bf, _state);
            rbfsetalgohierarchical(&s, rbase, nlayers, lambdav, _state);
            if( linterm==1 )
            {
                rbfsetlinterm(&s, _state);
            }
            if( linterm==2 )
            {
                rbfsetconstterm(&s, _state);
            }
            if( linterm==3 )
            {
                rbfsetzeroterm(&s, _state);
            }
            if( hasscale )
            {
                rbfsetpointsandscales(&s, &xy, n, &scalevec, _state);
            }
            else
            {
                rbfsetpoints(&s, &xy, n, _state);
            }
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3655");
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Prepare grid to test
             */
            n0 = 1+ae_randominteger(50, _state);
            n1 = 1+ae_randominteger(50, _state);
            if( nkind==1 )
            {
                k = ae_randominteger(2, _state);
                if( k==0 )
                {
                    n0 = 1;
                }
                if( k==1 )
                {
                    n1 = 1;
                }
            }
            else
            {
                if( nkind==2 )
                {
                    n0 = 1;
                    n1 = 1;
                }
                else
                {
                    ae_assert(nkind==0, "Assertion failed", _state);
                }
            }
            ae_vector_set_length(&x0, n0, _state);
            x0.ptr.p_double[0] = ae_randomreal(_state);
            for(i=1; i<=n0-1; i++)
            {
                x0.ptr.p_double[i] = x0.ptr.p_double[i-1]+ae_randomreal(_state)/n0;
            }
            ae_vector_set_length(&x1, n1, _state);
            x1.ptr.p_double[0] = ae_randomreal(_state);
            for(i=1; i<=n1-1; i++)
            {
                x1.ptr.p_double[i] = x1.ptr.p_double[i-1]+ae_randomreal(_state)/n1;
            }
            ae_vector_set_length(&needy, n0*n1, _state);
            v = ae_pow((double)(10), -3*ae_randomreal(_state), _state);
            for(i=0; i<=n0*n1-1; i++)
            {
                needy.ptr.p_bool[i] = ae_fp_less(ae_randomreal(_state),v);
            }
            
            /*
             * Test at grid
             */
            ae_vector_set_length(&x, nx, _state);
            rbfgridcalc2v(&s, &x0, n0, &x1, n1, &yv, _state);
            for(i0=0; i0<=n0-1; i0++)
            {
                for(i1=0; i1<=n1-1; i1++)
                {
                    x.ptr.p_double[0] = x0.ptr.p_double[i0];
                    x.ptr.p_double[1] = x1.ptr.p_double[i1];
                    rbfcalc(&s, &x, &y, _state);
                    for(i=0; i<=ny-1; i++)
                    {
                        ae_set_error_flag(&result, ae_fp_greater(ae_fabs(y.ptr.p_double[i]-yv.ptr.p_double[i+ny*(i0+i1*n0)], _state),errtol), __FILE__, __LINE__, "testrbfunit.ap:3704");
                    }
                }
            }
            
            /*
             * Test calculation on subset of regular grid:
             * * Test 1: compare full and subset versions
             * * Test 2: check sparsity. Subset function may perform additional
             *   evaluations because it processes data micro-row by micro-row.
             *   So, we can't check that all elements which were not flagged
             *   are zero - some of them may become non-zero. However, if entire
             *   row is empty, we can reasonably expect (informal guarantee)
             *   that it is not processed. So, we check empty (completely
             *   unflagged) rows 
             *
             */
            unsetrealarray(&yv2, _state);
            rbfgridcalc2vsubset(&s, &x0, n0, &x1, n1, &needy, &yv2, _state);
            for(i=0; i<=ny*n0*n1-1; i++)
            {
                ae_set_error_flag(&result, needy.ptr.p_bool[i/ny]&&ae_fp_greater(ae_fabs(yv.ptr.p_double[i]-yv2.ptr.p_double[i], _state),errtol), __FILE__, __LINE__, "testrbfunit.ap:3723");
            }
            
            /*
             * Test legacy function
             */
            rbfgridcalc2(&s, &x0, n0, &x1, n1, &y2, _state);
            for(i=0; i<=n0*n1-1; i++)
            {
                if( ny==1 )
                {
                    ae_set_error_flag(&result, ae_fp_greater(ae_fabs(yv.ptr.p_double[i]-y2.ptr.pp_double[i%n0][i/n0], _state),errtol), __FILE__, __LINE__, "testrbfunit.ap:3733");
                }
                else
                {
                    ae_set_error_flag(&result, ae_fp_neq(y2.ptr.pp_double[i%n0][i/n0],(double)(0)), __FILE__, __LINE__, "testrbfunit.ap:3735");
                }
            }
            
            /*
             * Test that scaling RBase, XY, X0, X1 by some power of 2
             * does not change values at grid (quite a strict requirement, but
             * ALGLIB implementation of RBF may deal with it).
             */
            scalefactor = ae_pow((double)(1024), (double)(2*ae_randominteger(2, _state)-1), _state);
            ae_matrix_set_length(&xy2, n, nx+ny, _state);
            ae_vector_set_length(&x02, n0, _state);
            ae_vector_set_length(&x12, n1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xy2.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]*scalefactor;
                }
                for(j=0; j<=ny-1; j++)
                {
                    xy2.ptr.pp_double[i][nx+j] = xy.ptr.pp_double[i][nx+j];
                }
            }
            for(i=0; i<=n0-1; i++)
            {
                x02.ptr.p_double[i] = x0.ptr.p_double[i]*scalefactor;
            }
            for(i=0; i<=n1-1; i++)
            {
                x12.ptr.p_double[i] = x1.ptr.p_double[i]*scalefactor;
            }
            if( hasscale )
            {
                rbfsetpointsandscales(&s, &xy2, n, &scalevec, _state);
            }
            else
            {
                rbfsetpoints(&s, &xy2, n, _state);
            }
            rbfsetv2bf(&s, bf, _state);
            rbfsetalgohierarchical(&s, rbase*scalefactor, nlayers, lambdav, _state);
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3767");
                ae_frame_leave(_state);
                return result;
            }
            unsetrealarray(&yv2, _state);
            rbfgridcalc2v(&s, &x02, n0, &x12, n1, &yv2, _state);
            for(i=0; i<=ny*n0*n1-1; i++)
            {
                ae_set_error_flag(&result, ae_fp_neq(yv.ptr.p_double[i],yv2.ptr.p_double[i]), __FILE__, __LINE__, "testrbfunit.ap:3773");
            }
            
            /*
             * Test that scaling RBase and scale vector by some power of 2
             * (increase RBase and decreasing scale, or vice versa) does not
             * change values at grid (quite a strict requirement, but
             * ALGLIB implementation of RBF may deal with it).
             */
            scalefactor = ae_pow((double)(1024), (double)(2*ae_randominteger(2, _state)-1), _state);
            ae_vector_set_length(&scalevec2, nx, _state);
            for(i=0; i<=nx-1; i++)
            {
                if( hasscale )
                {
                    scalevec2.ptr.p_double[i] = scalevec.ptr.p_double[i];
                }
                else
                {
                    scalevec2.ptr.p_double[i] = 1.0;
                }
                scalevec2.ptr.p_double[i] = scalevec2.ptr.p_double[i]/scalefactor;
            }
            rbfsetpointsandscales(&s, &xy, n, &scalevec2, _state);
            rbfsetv2bf(&s, bf, _state);
            rbfsetalgohierarchical(&s, rbase*scalefactor, nlayers, lambdav, _state);
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3797");
                ae_frame_leave(_state);
                return result;
            }
            unsetrealarray(&yv2, _state);
            rbfgridcalc2v(&s, &x0, n0, &x1, n1, &yv2, _state);
            for(i=0; i<=ny*n0*n1-1; i++)
            {
                ae_set_error_flag(&result, ae_fp_greater(ae_fabs(yv.ptr.p_double[i]-yv2.ptr.p_double[i], _state),100*ae_machineepsilon*rmax3(ae_fabs(yv.ptr.p_double[i], _state), ae_fabs(yv2.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testrbfunit.ap:3803");
            }
        }
    }
    
    /*
     * Test 3-dimensional grid calculation
     */
    errtol = 1.0E-12;
    nx = 3;
    for(ny=1; ny<=4; ny++)
    {
        for(nkind=0; nkind<=2; nkind++)
        {
            
            /*
             * problem setup
             */
            n = 150;
            rbase = 0.10;
            lambdav = 1.0E-3*ae_randominteger(2, _state);
            nlayers = ae_randominteger(3, _state);
            linterm = 1+ae_randominteger(3, _state);
            bf = ae_randominteger(2, _state);
            ae_matrix_set_length(&xy, n, nx+ny, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                }
                for(j=0; j<=ny-1; j++)
                {
                    xy.ptr.pp_double[i][nx+j] = ae_randomreal(_state)-0.5;
                }
            }
            hasscale = ae_randominteger(2, _state)==0;
            if( hasscale )
            {
                ae_vector_set_length(&scalevec, nx, _state);
                for(j=0; j<=nx-1; j++)
                {
                    scalevec.ptr.p_double[j] = ae_pow((double)(2), 2*ae_randomreal(_state)-1, _state);
                }
            }
            
            /*
             * Build model
             */
            rbfcreate(nx, ny, &s, _state);
            rbfsetv2bf(&s, bf, _state);
            rbfsetalgohierarchical(&s, rbase, nlayers, lambdav, _state);
            if( linterm==1 )
            {
                rbfsetlinterm(&s, _state);
            }
            if( linterm==2 )
            {
                rbfsetconstterm(&s, _state);
            }
            if( linterm==3 )
            {
                rbfsetzeroterm(&s, _state);
            }
            if( hasscale )
            {
                rbfsetpointsandscales(&s, &xy, n, &scalevec, _state);
            }
            else
            {
                rbfsetpoints(&s, &xy, n, _state);
            }
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3858");
                ae_frame_leave(_state);
                return result;
            }
            
            /*
             * Prepare grid to test
             */
            n0 = 1+ae_randominteger(50, _state);
            n1 = 1+ae_randominteger(50, _state);
            n2 = 1+ae_randominteger(50, _state);
            if( nkind==1 )
            {
                k = ae_randominteger(3, _state);
                if( k==0 )
                {
                    n0 = 1;
                }
                if( k==1 )
                {
                    n1 = 1;
                }
                if( k==2 )
                {
                    n2 = 1;
                }
            }
            else
            {
                if( nkind==2 )
                {
                    n0 = 1;
                    n1 = 1;
                    n2 = 1;
                }
                else
                {
                    ae_assert(nkind==0, "Assertion failed", _state);
                }
            }
            ae_vector_set_length(&x0, n0, _state);
            x0.ptr.p_double[0] = ae_randomreal(_state);
            for(i=1; i<=n0-1; i++)
            {
                x0.ptr.p_double[i] = x0.ptr.p_double[i-1]+ae_randomreal(_state)/n0;
            }
            ae_vector_set_length(&x1, n1, _state);
            x1.ptr.p_double[0] = ae_randomreal(_state);
            for(i=1; i<=n1-1; i++)
            {
                x1.ptr.p_double[i] = x1.ptr.p_double[i-1]+ae_randomreal(_state)/n1;
            }
            ae_vector_set_length(&x2, n2, _state);
            x2.ptr.p_double[0] = ae_randomreal(_state);
            for(i=1; i<=n2-1; i++)
            {
                x2.ptr.p_double[i] = x2.ptr.p_double[i-1]+ae_randomreal(_state)/n2;
            }
            ae_vector_set_length(&needy, n0*n1*n2, _state);
            v = ae_pow((double)(10), -3*ae_randomreal(_state), _state);
            for(i=0; i<=n0*n1*n2-1; i++)
            {
                needy.ptr.p_bool[i] = ae_fp_less(ae_randomreal(_state),v);
            }
            
            /*
             * Test at grid
             */
            ae_vector_set_length(&x, nx, _state);
            rbfgridcalc3v(&s, &x0, n0, &x1, n1, &x2, n2, &yv, _state);
            for(i0=0; i0<=n0-1; i0++)
            {
                for(i1=0; i1<=n1-1; i1++)
                {
                    for(i2=0; i2<=n2-1; i2++)
                    {
                        x.ptr.p_double[0] = x0.ptr.p_double[i0];
                        x.ptr.p_double[1] = x1.ptr.p_double[i1];
                        x.ptr.p_double[2] = x2.ptr.p_double[i2];
                        rbfcalc(&s, &x, &y, _state);
                        for(i=0; i<=ny-1; i++)
                        {
                            ae_set_error_flag(&result, ae_fp_greater(ae_fabs(y.ptr.p_double[i]-yv.ptr.p_double[i+ny*(i0+i1*n0+i2*n0*n1)], _state),errtol), __FILE__, __LINE__, "testrbfunit.ap:3917");
                        }
                    }
                }
            }
            
            /*
             * Test calculation on subset of regular grid:
             * * Test 1: compare full and subset versions
             * * Test 2: check sparsity. Subset function may perform additional
             *   evaluations because it processes data micro-row by micro-row.
             *   So, we can't check that all elements which were not flagged
             *   are zero - some of them may become non-zero. However, if entire
             *   row is empty, we can reasonably expect (informal guarantee)
             *   that it is not processed. So, we check empty (completely
             *   unflagged) rows 
             *
             */
            ae_vector_set_length(&rowflags, n1*n2, _state);
            for(i=0; i<=n1*n2-1; i++)
            {
                rowflags.ptr.p_bool[i] = ae_false;
            }
            for(i=0; i<=n0*n1*n2-1; i++)
            {
                if( needy.ptr.p_bool[i] )
                {
                    rowflags.ptr.p_bool[i/n0] = ae_true;
                }
            }
            unsetrealarray(&yv2, _state);
            rbfgridcalc3vsubset(&s, &x0, n0, &x1, n1, &x2, n2, &needy, &yv2, _state);
            for(i=0; i<=ny*n0*n1*n2-1; i++)
            {
                ae_set_error_flag(&result, needy.ptr.p_bool[i/ny]&&ae_fp_greater(ae_fabs(yv.ptr.p_double[i]-yv2.ptr.p_double[i], _state),errtol), __FILE__, __LINE__, "testrbfunit.ap:3944");
                ae_set_error_flag(&result, !rowflags.ptr.p_bool[i/(ny*n0)]&&ae_fp_neq(yv2.ptr.p_double[i],0.0), __FILE__, __LINE__, "testrbfunit.ap:3945");
            }
            
            /*
             * Test that scaling RBase, XY, X0, X1 and X2 by some power of 2
             * does not change values at grid (quite a strict requirement, but
             * ALGLIB implementation of RBF may deal with it).
             */
            scalefactor = ae_pow((double)(1024), (double)(2*ae_randominteger(2, _state)-1), _state);
            ae_matrix_set_length(&xy2, n, nx+ny, _state);
            ae_vector_set_length(&x02, n0, _state);
            ae_vector_set_length(&x12, n1, _state);
            ae_vector_set_length(&x22, n2, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xy2.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]*scalefactor;
                }
                for(j=0; j<=ny-1; j++)
                {
                    xy2.ptr.pp_double[i][nx+j] = xy.ptr.pp_double[i][nx+j];
                }
            }
            for(i=0; i<=n0-1; i++)
            {
                x02.ptr.p_double[i] = x0.ptr.p_double[i]*scalefactor;
            }
            for(i=0; i<=n1-1; i++)
            {
                x12.ptr.p_double[i] = x1.ptr.p_double[i]*scalefactor;
            }
            for(i=0; i<=n2-1; i++)
            {
                x22.ptr.p_double[i] = x2.ptr.p_double[i]*scalefactor;
            }
            if( hasscale )
            {
                rbfsetpointsandscales(&s, &xy2, n, &scalevec, _state);
            }
            else
            {
                rbfsetpoints(&s, &xy2, n, _state);
            }
            rbfsetv2bf(&s, bf, _state);
            rbfsetalgohierarchical(&s, rbase*scalefactor, nlayers, lambdav, _state);
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:3980");
                ae_frame_leave(_state);
                return result;
            }
            unsetrealarray(&yv2, _state);
            rbfgridcalc3v(&s, &x02, n0, &x12, n1, &x22, n2, &yv2, _state);
            for(i=0; i<=ny*n0*n1*n2-1; i++)
            {
                ae_set_error_flag(&result, ae_fp_neq(yv.ptr.p_double[i],yv2.ptr.p_double[i]), __FILE__, __LINE__, "testrbfunit.ap:3986");
            }
            
            /*
             * Test that scaling RBase and scale vector by some power of 2
             * (increase RBase and decreasing scale, or vice versa) does not
             * change values at grid (quite a strict requirement, but
             * ALGLIB implementation of RBF may deal with it).
             */
            scalefactor = ae_pow((double)(1024), (double)(2*ae_randominteger(2, _state)-1), _state);
            ae_vector_set_length(&scalevec2, nx, _state);
            for(i=0; i<=nx-1; i++)
            {
                if( hasscale )
                {
                    scalevec2.ptr.p_double[i] = scalevec.ptr.p_double[i];
                }
                else
                {
                    scalevec2.ptr.p_double[i] = 1.0;
                }
                scalevec2.ptr.p_double[i] = scalevec2.ptr.p_double[i]/scalefactor;
            }
            rbfsetpointsandscales(&s, &xy, n, &scalevec2, _state);
            rbfsetv2bf(&s, bf, _state);
            rbfsetalgohierarchical(&s, rbase*scalefactor, nlayers, lambdav, _state);
            rbfbuildmodel(&s, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                ae_set_error_flag(&result, ae_true, __FILE__, __LINE__, "testrbfunit.ap:4010");
                ae_frame_leave(_state);
                return result;
            }
            unsetrealarray(&yv2, _state);
            rbfgridcalc3v(&s, &x0, n0, &x1, n1, &x2, n2, &yv2, _state);
            for(i=0; i<=ny*n0*n1*n2-1; i++)
            {
                ae_set_error_flag(&result, ae_fp_greater(ae_fabs(yv.ptr.p_double[i]-yv2.ptr.p_double[i], _state),100*ae_machineepsilon*rmax3(ae_fabs(yv.ptr.p_double[i], _state), ae_fabs(yv2.ptr.p_double[i], _state), 1.0, _state)), __FILE__, __LINE__, "testrbfunit.ap:4016");
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}








ae_bool testhermite(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double sumerr;
    double cerr;
    double threshold;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t pass;
    ae_vector c;
    double x;
    double v;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);

    err = (double)(0);
    sumerr = (double)(0);
    cerr = (double)(0);
    threshold = 1.0E-9;
    waserrors = ae_false;
    
    /*
     * Testing Hermite polynomials
     */
    n = 0;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)-1, _state), _state);
    n = 1;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)-2, _state), _state);
    n = 2;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)-2, _state), _state);
    n = 3;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)+4, _state), _state);
    n = 4;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)+20, _state), _state);
    n = 5;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)+8, _state), _state);
    n = 6;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)-184, _state), _state);
    n = 7;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)-464, _state), _state);
    n = 11;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)-230848, _state), _state);
    n = 12;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, (double)(1), _state)-280768, _state), _state);
    
    /*
     * Testing Clenshaw summation
     */
    maxn = 10;
    ae_vector_set_length(&c, maxn+1, _state);
    for(pass=1; pass<=10; pass++)
    {
        x = 2*ae_randomreal(_state)-1;
        v = (double)(0);
        for(n=0; n<=maxn; n++)
        {
            c.ptr.p_double[n] = 2*ae_randomreal(_state)-1;
            v = v+hermitecalculate(n, x, _state)*c.ptr.p_double[n];
            sumerr = ae_maxreal(sumerr, ae_fabs(v-hermitesum(&c, n, x, _state), _state), _state);
        }
    }
    
    /*
     * Testing coefficients
     */
    hermitecoefficients(0, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    hermitecoefficients(1, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-2, _state), _state);
    hermitecoefficients(2, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]+2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-4, _state), _state);
    hermitecoefficients(3, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+12, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-8, _state), _state);
    hermitecoefficients(4, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-12, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]+48, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-16, _state), _state);
    hermitecoefficients(5, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+160, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]-32, _state), _state);
    hermitecoefficients(6, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]+120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]+480, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[6]-64, _state), _state);
    
    /*
     * Reporting
     */
    waserrors = (ae_fp_greater(err,threshold)||ae_fp_greater(sumerr,threshold))||ae_fp_greater(cerr,threshold);
    if( !silent )
    {
        printf("TESTING CALCULATION OF THE HERMITE POLYNOMIALS\n");
        printf("Max error                                 %5.2e\n",
            (double)(err));
        printf("Summation error                           %5.2e\n",
            (double)(sumerr));
        printf("Coefficients error                        %5.2e\n",
            (double)(cerr));
        printf("Threshold                                 %5.2e\n",
            (double)(threshold));
        if( !waserrors )
        {
            printf("TEST PASSED\n");
        }
        else
        {
            printf("TEST FAILED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testlaguerre(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double sumerr;
    double cerr;
    double threshold;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t pass;
    ae_vector c;
    double x;
    double v;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);

    err = (double)(0);
    sumerr = (double)(0);
    cerr = (double)(0);
    threshold = 1.0E-9;
    waserrors = ae_false;
    
    /*
     * Testing Laguerre polynomials
     */
    n = 0;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)-1.0000000000, _state), _state);
    n = 1;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)-0.5000000000, _state), _state);
    n = 2;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)-0.1250000000, _state), _state);
    n = 3;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.1458333333, _state), _state);
    n = 4;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.3307291667, _state), _state);
    n = 5;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.4455729167, _state), _state);
    n = 6;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.5041449653, _state), _state);
    n = 7;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.5183392237, _state), _state);
    n = 8;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.4983629984, _state), _state);
    n = 9;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.4529195204, _state), _state);
    n = 10;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.3893744141, _state), _state);
    n = 11;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.3139072988, _state), _state);
    n = 12;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.2316496389, _state), _state);
    
    /*
     * Testing Clenshaw summation
     */
    maxn = 20;
    ae_vector_set_length(&c, maxn+1, _state);
    for(pass=1; pass<=10; pass++)
    {
        x = 2*ae_randomreal(_state)-1;
        v = (double)(0);
        for(n=0; n<=maxn; n++)
        {
            c.ptr.p_double[n] = 2*ae_randomreal(_state)-1;
            v = v+laguerrecalculate(n, x, _state)*c.ptr.p_double[n];
            sumerr = ae_maxreal(sumerr, ae_fabs(v-laguerresum(&c, n, x, _state), _state), _state);
        }
    }
    
    /*
     * Testing coefficients
     */
    laguerrecoefficients(0, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    laguerrecoefficients(1, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+1, _state), _state);
    laguerrecoefficients(2, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)2/(double)2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)4/(double)2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)1/(double)2, _state), _state);
    laguerrecoefficients(3, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)6/(double)6, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)18/(double)6, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)9/(double)6, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)1/(double)6, _state), _state);
    laguerrecoefficients(4, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)24/(double)24, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)96/(double)24, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)72/(double)24, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)16/(double)24, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-(double)1/(double)24, _state), _state);
    laguerrecoefficients(5, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)120/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)600/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)600/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)200/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-(double)25/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]+(double)1/(double)120, _state), _state);
    laguerrecoefficients(6, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)720/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)4320/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)5400/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)2400/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-(double)450/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]+(double)36/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[6]-(double)1/(double)720, _state), _state);
    
    /*
     * Reporting
     */
    waserrors = (ae_fp_greater(err,threshold)||ae_fp_greater(sumerr,threshold))||ae_fp_greater(cerr,threshold);
    if( !silent )
    {
        printf("TESTING CALCULATION OF THE LAGUERRE POLYNOMIALS\n");
        printf("Max error                                 %5.2e\n",
            (double)(err));
        printf("Summation error                           %5.2e\n",
            (double)(sumerr));
        printf("Coefficients error                        %5.2e\n",
            (double)(cerr));
        printf("Threshold                                 %5.2e\n",
            (double)(threshold));
        if( !waserrors )
        {
            printf("TEST PASSED\n");
        }
        else
        {
            printf("TEST FAILED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testlegendre(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double sumerr;
    double cerr;
    double threshold;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t pass;
    ae_vector c;
    double x;
    double v;
    double t;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);

    err = (double)(0);
    sumerr = (double)(0);
    cerr = (double)(0);
    threshold = 1.0E-9;
    waserrors = ae_false;
    
    /*
     * Testing Legendre polynomials values
     */
    for(n=0; n<=10; n++)
    {
        legendrecoefficients(n, &c, _state);
        for(pass=1; pass<=10; pass++)
        {
            x = 2*ae_randomreal(_state)-1;
            v = legendrecalculate(n, x, _state);
            t = (double)(1);
            for(i=0; i<=n; i++)
            {
                v = v-c.ptr.p_double[i]*t;
                t = t*x;
            }
            err = ae_maxreal(err, ae_fabs(v, _state), _state);
        }
    }
    
    /*
     * Testing Clenshaw summation
     */
    maxn = 20;
    ae_vector_set_length(&c, maxn+1, _state);
    for(pass=1; pass<=10; pass++)
    {
        x = 2*ae_randomreal(_state)-1;
        v = (double)(0);
        for(n=0; n<=maxn; n++)
        {
            c.ptr.p_double[n] = 2*ae_randomreal(_state)-1;
            v = v+legendrecalculate(n, x, _state)*c.ptr.p_double[n];
            sumerr = ae_maxreal(sumerr, ae_fabs(v-legendresum(&c, n, x, _state), _state), _state);
        }
    }
    
    /*
     * Testing coefficients
     */
    legendrecoefficients(0, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    legendrecoefficients(1, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-1, _state), _state);
    legendrecoefficients(2, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]+(double)1/(double)2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)3/(double)2, _state), _state);
    legendrecoefficients(3, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)3/(double)2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-(double)5/(double)2, _state), _state);
    legendrecoefficients(4, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)3/(double)8, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]+(double)30/(double)8, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-(double)35/(double)8, _state), _state);
    legendrecoefficients(9, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-(double)315/(double)128, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)4620/(double)128, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]-(double)18018/(double)128, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[6]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[7]+(double)25740/(double)128, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[8]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[9]-(double)12155/(double)128, _state), _state);
    
    /*
     * Reporting
     */
    waserrors = (ae_fp_greater(err,threshold)||ae_fp_greater(sumerr,threshold))||ae_fp_greater(cerr,threshold);
    if( !silent )
    {
        printf("TESTING CALCULATION OF THE LEGENDRE POLYNOMIALS\n");
        printf("Max error                                 %5.2e\n",
            (double)(err));
        printf("Summation error                           %5.2e\n",
            (double)(sumerr));
        printf("Coefficients error                        %5.2e\n",
            (double)(cerr));
        printf("Threshold                                 %5.2e\n",
            (double)(threshold));
        if( !waserrors )
        {
            printf("TEST PASSED\n");
        }
        else
        {
            printf("TEST FAILED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testchebyshev(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double sumerr;
    double cerr;
    double ferr;
    double threshold;
    double x;
    double v;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t maxn;
    ae_vector c;
    ae_vector p1;
    ae_vector p2;
    ae_matrix a;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&c, 0, sizeof(c));
    memset(&p1, 0, sizeof(p1));
    memset(&p2, 0, sizeof(p2));
    memset(&a, 0, sizeof(a));
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    err = (double)(0);
    sumerr = (double)(0);
    cerr = (double)(0);
    ferr = (double)(0);
    threshold = 1.0E-9;
    waserrors = ae_false;
    
    /*
     * Testing Chebyshev polynomials of the first kind
     */
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 0, 0.00, _state)-1, _state), _state);
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 0, 0.33, _state)-1, _state), _state);
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 0, -0.42, _state)-1, _state), _state);
    x = 0.2;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-0.2, _state), _state);
    x = 0.4;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-0.4, _state), _state);
    x = 0.6;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-0.6, _state), _state);
    x = 0.8;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-0.8, _state), _state);
    x = 1.0;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-1.0, _state), _state);
    x = 0.2;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)+0.92, _state), _state);
    x = 0.4;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)+0.68, _state), _state);
    x = 0.6;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)+0.28, _state), _state);
    x = 0.8;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)-0.28, _state), _state);
    x = 1.0;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)-1.00, _state), _state);
    n = 10;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, n, 0.2, _state)-0.4284556288, _state), _state);
    n = 11;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, n, 0.2, _state)+0.7996160205, _state), _state);
    n = 12;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, n, 0.2, _state)+0.7483020370, _state), _state);
    
    /*
     * Testing Chebyshev polynomials of the second kind
     */
    n = 0;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)-1.0000000000, _state), _state);
    n = 1;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)-0.4000000000, _state), _state);
    n = 2;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)+0.8400000000, _state), _state);
    n = 3;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)+0.7360000000, _state), _state);
    n = 4;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)-0.5456000000, _state), _state);
    n = 10;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)-0.6128946176, _state), _state);
    n = 11;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)+0.6770370970, _state), _state);
    n = 12;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)+0.8837094564, _state), _state);
    
    /*
     * Testing Clenshaw summation
     */
    maxn = 20;
    ae_vector_set_length(&c, maxn+1, _state);
    for(k=1; k<=2; k++)
    {
        for(pass=1; pass<=10; pass++)
        {
            x = 2*ae_randomreal(_state)-1;
            v = (double)(0);
            for(n=0; n<=maxn; n++)
            {
                c.ptr.p_double[n] = 2*ae_randomreal(_state)-1;
                v = v+chebyshevcalculate(k, n, x, _state)*c.ptr.p_double[n];
                sumerr = ae_maxreal(sumerr, ae_fabs(v-chebyshevsum(&c, k, n, x, _state), _state), _state);
            }
        }
    }
    
    /*
     * Testing coefficients
     */
    chebyshevcoefficients(0, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    chebyshevcoefficients(1, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-1, _state), _state);
    chebyshevcoefficients(2, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]+1, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-2, _state), _state);
    chebyshevcoefficients(3, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+3, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-4, _state), _state);
    chebyshevcoefficients(4, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]+8, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-8, _state), _state);
    chebyshevcoefficients(9, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-9, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]-432, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[6]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[7]+576, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[8]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[9]-256, _state), _state);
    
    /*
     * Testing FromChebyshev
     */
    maxn = 10;
    ae_matrix_set_length(&a, maxn+1, maxn+1, _state);
    for(i=0; i<=maxn; i++)
    {
        for(j=0; j<=maxn; j++)
        {
            a.ptr.pp_double[i][j] = (double)(0);
        }
        chebyshevcoefficients(i, &c, _state);
        ae_v_move(&a.ptr.pp_double[i][0], 1, &c.ptr.p_double[0], 1, ae_v_len(0,i));
    }
    ae_vector_set_length(&c, maxn+1, _state);
    ae_vector_set_length(&p1, maxn+1, _state);
    for(n=0; n<=maxn; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            for(i=0; i<=n; i++)
            {
                p1.ptr.p_double[i] = (double)(0);
            }
            for(i=0; i<=n; i++)
            {
                c.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                v = c.ptr.p_double[i];
                ae_v_addd(&p1.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,i), v);
            }
            fromchebyshev(&c, n, &p2, _state);
            for(i=0; i<=n; i++)
            {
                ferr = ae_maxreal(ferr, ae_fabs(p1.ptr.p_double[i]-p2.ptr.p_double[i], _state), _state);
            }
        }
    }
    
    /*
     * Reporting
     */
    waserrors = ((ae_fp_greater(err,threshold)||ae_fp_greater(sumerr,threshold))||ae_fp_greater(cerr,threshold))||ae_fp_greater(ferr,threshold);
    if( !silent )
    {
        printf("TESTING CALCULATION OF THE CHEBYSHEV POLYNOMIALS\n");
        printf("Max error against table                   %5.2e\n",
            (double)(err));
        printf("Summation error                           %5.2e\n",
            (double)(sumerr));
        printf("Coefficients error                        %5.2e\n",
            (double)(cerr));
        printf("FrobChebyshev error                       %5.2e\n",
            (double)(ferr));
        printf("Threshold                                 %5.2e\n",
            (double)(threshold));
        if( !waserrors )
        {
            printf("TEST PASSED\n");
        }
        else
        {
            printf("TEST FAILED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testwsr(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_vector xa;
    ae_int_t n;
    ae_int_t i;
    double taill;
    double tailr;
    double tailb;
    double taillprev;
    double tailrprev;
    double ebase;
    double eshift;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&xa, 0, sizeof(xa));
    ae_vector_init(&xa, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    
    /*
     * Test monotonicity of tail values for moving value of E
     */
    for(n=5; n<=50; n++)
    {
        
        /*
         * Generate uniform and sorted X spanning [0,1]
         */
        ae_vector_set_length(&xa, n, _state);
        for(i=0; i<=n-1; i++)
        {
            xa.ptr.p_double[i] = (double)i/(double)(n-1);
        }
        
        /*
         * Test N+1 values of E
         */
        ebase = -0.5/(n-1);
        eshift = (double)1/(double)(n-1);
        tailrprev = (double)(0);
        taillprev = (double)(1);
        for(i=0; i<=n; i++)
        {
            wilcoxonsignedranktest(&xa, n, ebase+eshift*i, &tailb, &taill, &tailr, _state);
            ae_set_error_flag(&waserrors, ae_fp_neq(tailb,2*ae_minreal(taill, tailr, _state)), __FILE__, __LINE__, "testwsrunit.ap:42");
            ae_set_error_flag(&waserrors, ae_fp_greater(tailrprev,tailr), __FILE__, __LINE__, "testwsrunit.ap:43");
            ae_set_error_flag(&waserrors, ae_fp_less(taillprev,taill), __FILE__, __LINE__, "testwsrunit.ap:44");
            tailrprev = tailr;
            taillprev = taill;
        }
    }
    
    /*
     * Test for integer overflow in the function: if one crucial
     * calculation step is performed in 32-bit integer arithmetics,
     * it will return incorrect results.
     *
     * We use special handcrafted N, such that in 32-bit integer
     * arithmetics int32(N*N)<0. Such negative N leads to domain
     * error in the sqrt() function.
     */
    n = 50000;
    ae_vector_set_length(&xa, n, _state);
    for(i=0; i<=n-1; i++)
    {
        xa.ptr.p_double[i] = ae_sin((double)(10*i), _state);
    }
    wilcoxonsignedranktest(&xa, n, 0.0, &tailb, &taill, &tailr, _state);
    ae_set_error_flag(&waserrors, !ae_isfinite(tailb, _state), __FILE__, __LINE__, "testwsrunit.ap:64");
    ae_set_error_flag(&waserrors, !ae_isfinite(taill, _state), __FILE__, __LINE__, "testwsrunit.ap:65");
    ae_set_error_flag(&waserrors, !ae_isfinite(tailr, _state), __FILE__, __LINE__, "testwsrunit.ap:66");
    
    /*
     *
     */
    if( !silent )
    {
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool teststest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    double taill;
    double tailr;
    double tailb;
    ae_bool waserrors;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    eps = 1.0E-3;
    
    /*
     * Test 1
     */
    ae_vector_set_length(&x, 6, _state);
    x.ptr.p_double[0] = (double)(-3);
    x.ptr.p_double[1] = (double)(-2);
    x.ptr.p_double[2] = (double)(-1);
    x.ptr.p_double[3] = (double)(1);
    x.ptr.p_double[4] = (double)(2);
    x.ptr.p_double[5] = (double)(3);
    onesamplesigntest(&x, 6, 0.0, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.65625, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.65625, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-1.00000, _state),eps);
    onesamplesigntest(&x, 6, -1.0, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.81250, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.50000, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-1.00000, _state),eps);
    onesamplesigntest(&x, 6, -1.5, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.89062, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.34375, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-0.68750, _state),eps);
    onesamplesigntest(&x, 6, -3.0, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-1.00000, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.03125, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-0.06250, _state),eps);
    
    /*
     * Test 2
     */
    ae_vector_set_length(&x, 3, _state);
    x.ptr.p_double[0] = (double)(2);
    x.ptr.p_double[1] = (double)(2);
    x.ptr.p_double[2] = (double)(2);
    onesamplesigntest(&x, 3, 2.0, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    
    /*
     * Final report
     */
    if( !silent )
    {
        printf("SIGN TEST:                               ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool teststudentttests(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    double eps;
    ae_vector x;
    ae_vector y;
    ae_vector xa;
    ae_vector ya;
    ae_vector xb;
    ae_vector yb;
    ae_int_t n;
    ae_int_t i;
    double taill;
    double tailr;
    double tailb;
    double taill1;
    double tailr1;
    double tailb1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&xa, 0, sizeof(xa));
    memset(&ya, 0, sizeof(ya));
    memset(&xb, 0, sizeof(xb));
    memset(&yb, 0, sizeof(yb));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xa, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ya, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yb, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    eps = 0.001;
    
    /*
     * 1-sample test
     */
    n = 8;
    ae_vector_set_length(&x, 8, _state);
    x.ptr.p_double[0] = -3.0;
    x.ptr.p_double[1] = -1.5;
    x.ptr.p_double[2] = -1.0;
    x.ptr.p_double[3] = -0.5;
    x.ptr.p_double[4] = 0.5;
    x.ptr.p_double[5] = 1.0;
    x.ptr.p_double[6] = 1.5;
    x.ptr.p_double[7] = 3.0;
    studentttest1(&x, n, 0.0, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-1.00000, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.50000, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.50000, _state),eps);
    studentttest1(&x, n, 1.0, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-0.17816, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.08908, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.91092, _state),eps);
    studentttest1(&x, n, -1.0, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-0.17816, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.91092, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.08908, _state),eps);
    x.ptr.p_double[0] = 1.1;
    x.ptr.p_double[1] = 1.1;
    x.ptr.p_double[2] = 1.1;
    x.ptr.p_double[3] = 1.1;
    x.ptr.p_double[4] = 1.1;
    x.ptr.p_double[5] = 1.1;
    x.ptr.p_double[6] = 1.1;
    x.ptr.p_double[7] = 1.1;
    studentttest1(&x, n, 1.1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest1(&x, n, 0.0, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(0));
    x.ptr.p_double[7] = 1.1;
    studentttest1(&x, 1, 1.1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest1(&x, 1, 0.0, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(0));
    
    /*
     * 2-sample pooled (equal variance) test
     */
    n = 8;
    ae_vector_set_length(&x, 8, _state);
    ae_vector_set_length(&y, 8, _state);
    x.ptr.p_double[0] = -3.0;
    x.ptr.p_double[1] = -1.5;
    x.ptr.p_double[2] = -1.0;
    x.ptr.p_double[3] = -0.5;
    x.ptr.p_double[4] = 0.5;
    x.ptr.p_double[5] = 1.0;
    x.ptr.p_double[6] = 1.5;
    x.ptr.p_double[7] = 3.0;
    y.ptr.p_double[0] = -2.0;
    y.ptr.p_double[1] = -0.5;
    y.ptr.p_double[2] = 0.0;
    y.ptr.p_double[3] = 0.5;
    y.ptr.p_double[4] = 1.5;
    y.ptr.p_double[5] = 2.0;
    y.ptr.p_double[6] = 2.5;
    y.ptr.p_double[7] = 4.0;
    studentttest2(&x, n, &y, n, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-0.30780, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.15390, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.84610, _state),eps);
    studentttest2(&x, n, &y, n-1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-0.53853, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.26927, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.73074, _state),eps);
    studentttest2(&x, n-1, &y, n, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-0.13829, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.06915, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.93086, _state),eps);
    x.ptr.p_double[0] = -1.0;
    x.ptr.p_double[1] = -1.0;
    x.ptr.p_double[2] = -1.0;
    x.ptr.p_double[3] = -1.0;
    x.ptr.p_double[4] = -1.0;
    x.ptr.p_double[5] = -1.0;
    x.ptr.p_double[6] = -1.0;
    x.ptr.p_double[7] = -1.0;
    y.ptr.p_double[0] = 1.0;
    y.ptr.p_double[1] = 1.0;
    y.ptr.p_double[2] = 1.0;
    y.ptr.p_double[3] = 1.0;
    y.ptr.p_double[4] = 1.0;
    y.ptr.p_double[5] = 1.0;
    y.ptr.p_double[6] = 1.0;
    y.ptr.p_double[7] = 1.0;
    studentttest2(&x, n, &y, n, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(0));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, n, &y, n-1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(0));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, n, &y, 1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(0));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, n-1, &y, n, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(0));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, 1, &y, n, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(0));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, 1, &y, 1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(0));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&y, 1, &x, 1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(0));
    x.ptr.p_double[0] = 1.1;
    x.ptr.p_double[1] = 1.1;
    x.ptr.p_double[2] = 1.1;
    x.ptr.p_double[3] = 1.1;
    x.ptr.p_double[4] = 1.1;
    x.ptr.p_double[5] = 1.1;
    x.ptr.p_double[6] = 1.1;
    x.ptr.p_double[7] = 1.1;
    y.ptr.p_double[0] = 1.1;
    y.ptr.p_double[1] = 1.1;
    y.ptr.p_double[2] = 1.1;
    y.ptr.p_double[3] = 1.1;
    y.ptr.p_double[4] = 1.1;
    y.ptr.p_double[5] = 1.1;
    y.ptr.p_double[6] = 1.1;
    y.ptr.p_double[7] = 1.1;
    studentttest2(&x, n, &y, n, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, n, &y, n-1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, n, &y, 1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, n-1, &y, n, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, 1, &y, n, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    studentttest2(&x, 1, &y, 1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    
    /*
     * 2-sample unpooled (unequal variance) test:
     * * test on two non-constant samples
     * * tests on different combinations of non-constant and constant samples
     */
    n = 8;
    ae_vector_set_length(&xa, 8, _state);
    ae_vector_set_length(&ya, 8, _state);
    ae_vector_set_length(&xb, 8, _state);
    ae_vector_set_length(&yb, 8, _state);
    xa.ptr.p_double[0] = -3.0;
    xa.ptr.p_double[1] = -1.5;
    xa.ptr.p_double[2] = -1.0;
    xa.ptr.p_double[3] = -0.5;
    xa.ptr.p_double[4] = 0.5;
    xa.ptr.p_double[5] = 1.0;
    xa.ptr.p_double[6] = 1.5;
    xa.ptr.p_double[7] = 3.0;
    ya.ptr.p_double[0] = -1.0;
    ya.ptr.p_double[1] = -0.5;
    ya.ptr.p_double[2] = 0.0;
    ya.ptr.p_double[3] = 0.5;
    ya.ptr.p_double[4] = 1.5;
    ya.ptr.p_double[5] = 2.0;
    ya.ptr.p_double[6] = 2.5;
    ya.ptr.p_double[7] = 3.0;
    xb.ptr.p_double[0] = -1.1;
    xb.ptr.p_double[1] = -1.1;
    xb.ptr.p_double[2] = -1.1;
    xb.ptr.p_double[3] = -1.1;
    xb.ptr.p_double[4] = -1.1;
    xb.ptr.p_double[5] = -1.1;
    xb.ptr.p_double[6] = -1.1;
    xb.ptr.p_double[7] = -1.1;
    yb.ptr.p_double[0] = 1.1;
    yb.ptr.p_double[1] = 1.1;
    yb.ptr.p_double[2] = 1.1;
    yb.ptr.p_double[3] = 1.1;
    yb.ptr.p_double[4] = 1.1;
    yb.ptr.p_double[5] = 1.1;
    yb.ptr.p_double[6] = 1.1;
    yb.ptr.p_double[7] = 1.1;
    unequalvariancettest(&xa, n, &ya, n, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-0.25791, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-0.12896, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-0.87105, _state),eps);
    unequalvariancettest(&xa, n, &yb, n, &tailb, &taill, &tailr, _state);
    studentttest1(&xa, n, 1.1, &tailb1, &taill1, &tailr1, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-tailb1, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-taill1, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-tailr1, _state),eps);
    unequalvariancettest(&xa, n, &yb, 1, &tailb, &taill, &tailr, _state);
    studentttest1(&xa, n, 1.1, &tailb1, &taill1, &tailr1, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-tailb1, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-taill1, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-tailr1, _state),eps);
    unequalvariancettest(&xb, n, &ya, n, &tailb, &taill, &tailr, _state);
    studentttest1(&ya, n, -1.1, &tailb1, &taill1, &tailr1, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-tailb1, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-tailr1, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-taill1, _state),eps);
    unequalvariancettest(&xb, 1, &ya, n, &tailb, &taill, &tailr, _state);
    studentttest1(&ya, n, -1.1, &tailb1, &taill1, &tailr1, _state);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailb-tailb1, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(taill-tailr1, _state),eps);
    waserrors = waserrors||ae_fp_greater(ae_fabs(tailr-taill1, _state),eps);
    unequalvariancettest(&xb, 1, &yb, 1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(0));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    unequalvariancettest(&yb, 1, &xb, 1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(0));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(0));
    unequalvariancettest(&xb, 1, &xb, 1, &tailb, &taill, &tailr, _state);
    waserrors = waserrors||ae_fp_neq(tailb,(double)(1));
    waserrors = waserrors||ae_fp_neq(taill,(double)(1));
    waserrors = waserrors||ae_fp_neq(tailr,(double)(1));
    
    /*
     * Test for integer overflow in the function: if one crucial
     * calculation step is performed in 32-bit integer arithmetics,
     * it will return incorrect results.
     *
     * We use special handcrafted N, such that in 32-bit integer
     * arithmetics int32(N*N)<0. Such negative N leads to domain
     * error in the incomplete beta function.
     */
    n = 50000;
    ae_vector_set_length(&xa, n, _state);
    ae_vector_set_length(&ya, n, _state);
    for(i=0; i<=n-1; i++)
    {
        xa.ptr.p_double[i] = ae_randomreal(_state);
        ya.ptr.p_double[i] = ae_randomreal(_state);
    }
    unequalvariancettest(&xa, n, &ya, n, &tailb, &taill, &tailr, _state);
    
    /*
     *
     */
    if( !silent )
    {
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testmannwhitneyu(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_vector x;
    ae_vector y;
    ae_int_t testmin;
    ae_int_t testmax;
    ae_int_t testcnt;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t k;
    double taill;
    double tailr;
    double tailb;
    double taill1;
    double tailr1;
    double tailb1;
    double taillprev;
    double tailrprev;
    double ebase;
    double eshift;
    ae_int_t ecnt;
    double worsterr;
    double v;
    hqrndstate rs;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&y, 0, sizeof(y));
    memset(&rs, 0, sizeof(rs));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    waserrors = ae_false;
    hqrndrandomize(&rs, _state);
    
    /*
     * Test monotonicity of tail values for monotinically moving distributions.
     */
    for(n=5; n<=20; n++)
    {
        for(m=5; m<=20; m++)
        {
            
            /*
             * Generate uniform and sorted X/Y spanning [0,1]
             */
            ae_vector_set_length(&x, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (double)i/(double)(n-1)+100*ae_machineepsilon*hqrndnormal(&rs, _state);
            }
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                y.ptr.p_double[i] = (double)i/(double)(m-1)+100*ae_machineepsilon*hqrndnormal(&rs, _state);
            }
            
            /*
             * Test 100 values of E
             */
            ecnt = 100;
            ebase = -1.1;
            eshift = -2*ebase/(ecnt-1);
            tailrprev = (double)(0);
            taillprev = (double)(1);
            for(i=0; i<=m-1; i++)
            {
                y.ptr.p_double[i] = y.ptr.p_double[i]+ebase;
            }
            for(k=0; k<=ecnt-1; k++)
            {
                mannwhitneyutest(&x, n, &y, m, &tailb, &taill, &tailr, _state);
                ae_set_error_flag(&waserrors, ae_fp_neq(tailb,2*ae_minreal(taill, tailr, _state)), __FILE__, __LINE__, "testmannwhitneyuunit.ap:57");
                ae_set_error_flag(&waserrors, ae_fp_greater(tailrprev,tailr), __FILE__, __LINE__, "testmannwhitneyuunit.ap:58");
                ae_set_error_flag(&waserrors, ae_fp_less(taillprev,taill), __FILE__, __LINE__, "testmannwhitneyuunit.ap:59");
                tailrprev = tailr;
                taillprev = taill;
                for(i=0; i<=m-1; i++)
                {
                    y.ptr.p_double[i] = y.ptr.p_double[i]+eshift;
                }
            }
        }
    }
    
    /*
     * Test frequency of p-value 0.05
     */
    testmin = 5;
    testmax = 50;
    testcnt = 10000;
    worsterr = 0.0;
    for(n=testmin; n<=testmax; n++)
    {
        m = n+hqrnduniformi(&rs, testmax-n+1, _state);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&y, m, _state);
        
        /*
         * Generate two uniformly distributed values, calculate p-value for both-tails, repeat
         */
        k = 0;
        for(pass=0; pass<=testcnt-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = hqrnduniformr(&rs, _state);
            }
            for(i=0; i<=m-1; i++)
            {
                y.ptr.p_double[i] = hqrnduniformr(&rs, _state);
            }
            mannwhitneyutest(&x, n, &y, m, &tailb, &taill, &tailr, _state);
            if( ae_fp_less(tailb,0.05) )
            {
                k = k+1;
            }
        }
        v = ae_fabs((double)k/(double)testcnt-0.05, _state);
        worsterr = ae_maxreal(worsterr, v, _state);
        
        /*
         * Test error in quantile; for different N's we have different tolerances
         */
        if( n<10 )
        {
            ae_set_error_flag(&waserrors, ae_fp_greater(v,0.030), __FILE__, __LINE__, "testmannwhitneyuunit.ap:101");
        }
        else
        {
            if( n<15 )
            {
                ae_set_error_flag(&waserrors, ae_fp_greater(v,0.020), __FILE__, __LINE__, "testmannwhitneyuunit.ap:103");
            }
            else
            {
                if( n<30 )
                {
                    ae_set_error_flag(&waserrors, ae_fp_greater(v,0.015), __FILE__, __LINE__, "testmannwhitneyuunit.ap:105");
                }
                else
                {
                    ae_set_error_flag(&waserrors, ae_fp_greater(v,0.010), __FILE__, __LINE__, "testmannwhitneyuunit.ap:107");
                }
            }
        }
    }
    
    /*
     * Test symmetry properties
     */
    for(n=5; n<=50; n++)
    {
        for(m=5; m<=50; m++)
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = hqrnduniformr(&rs, _state);
            }
            for(i=0; i<=m-1; i++)
            {
                y.ptr.p_double[i] = hqrnduniformr(&rs, _state);
            }
            mannwhitneyutest(&x, n, &y, m, &tailb, &taill, &tailr, _state);
            mannwhitneyutest(&y, m, &x, n, &tailb1, &taill1, &tailr1, _state);
            ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(tailb-tailb1, _state),1.0E-12), __FILE__, __LINE__, "testmannwhitneyuunit.ap:126");
            ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(taill-tailr1, _state),1.0E-12), __FILE__, __LINE__, "testmannwhitneyuunit.ap:127");
            ae_set_error_flag(&waserrors, ae_fp_greater(ae_fabs(tailr-taill1, _state),1.0E-12), __FILE__, __LINE__, "testmannwhitneyuunit.ap:128");
        }
    }
    
    /*
     * Test for integer overflow in the function: if one crucial
     * calculation step is performed in 32-bit integer arithmetics,
     * it will return incorrect results.
     *
     * We use special handcrafted N, such that in 32-bit integer
     * arithmetics int32(N*N)<0. Such negative N leads to domain
     * error in the sqrt() function.
     */
    n = 50000;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&y, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = ae_sin((double)(10*i), _state);
        y.ptr.p_double[i] = ae_sin((double)(13*i), _state);
    }
    mannwhitneyutest(&x, n, &y, n, &tailb, &taill, &tailr, _state);
    ae_set_error_flag(&waserrors, !ae_isfinite(tailb, _state), __FILE__, __LINE__, "testmannwhitneyuunit.ap:149");
    ae_set_error_flag(&waserrors, !ae_isfinite(taill, _state), __FILE__, __LINE__, "testmannwhitneyuunit.ap:150");
    ae_set_error_flag(&waserrors, !ae_isfinite(tailr, _state), __FILE__, __LINE__, "testmannwhitneyuunit.ap:151");
    
    /*
     *
     */
    if( !silent )
    {
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testschurunit_testschurproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double* materr,
     double* orterr,
     ae_bool* errstruct,
     ae_bool* wfailed,
     ae_state *_state);





/*************************************************************************
Testing Schur decomposition subroutine
*************************************************************************/
ae_bool testschur(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool waserrors;
    ae_bool errstruct;
    ae_bool wfailed;
    double materr;
    double orterr;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    materr = (double)(0);
    orterr = (double)(0);
    errstruct = ae_false;
    wfailed = ae_false;
    waserrors = ae_false;
    maxn = 70;
    passcount = 1;
    threshold = 5*100*ae_machineepsilon;
    ae_matrix_set_length(&a, maxn-1+1, maxn-1+1, _state);
    
    /*
     * zero matrix, several cases
     */
    for(i=0; i<=maxn-1; i++)
    {
        for(j=0; j<=maxn-1; j++)
        {
            a.ptr.pp_double[i][j] = (double)(0);
        }
    }
    for(n=1; n<=maxn; n++)
    {
        if( n>30&&n%2==0 )
        {
            continue;
        }
        testschurunit_testschurproblem(&a, n, &materr, &orterr, &errstruct, &wfailed, _state);
    }
    
    /*
     * Dense matrix
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            if( n>30&&n%2==0 )
            {
                continue;
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
            }
            testschurunit_testschurproblem(&a, n, &materr, &orterr, &errstruct, &wfailed, _state);
        }
    }
    
    /*
     * report
     */
    waserrors = ((ae_fp_greater(materr,threshold)||ae_fp_greater(orterr,threshold))||errstruct)||wfailed;
    if( !silent )
    {
        printf("TESTING SCHUR DECOMPOSITION\n");
        printf("Schur decomposition error:               %5.3e\n",
            (double)(materr));
        printf("Schur orthogonality error:               %5.3e\n",
            (double)(orterr));
        printf("T matrix structure:                      ");
        if( !errstruct )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Always converged:                        ");
        if( !wfailed )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testschurunit_testschurproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double* materr,
     double* orterr,
     ae_bool* errstruct,
     ae_bool* wfailed,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix s;
    ae_matrix t;
    ae_vector sr;
    ae_vector astc;
    ae_vector sastc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double locerr;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&t, 0, sizeof(t));
    memset(&sr, 0, sizeof(sr));
    memset(&astc, 0, sizeof(astc));
    memset(&sastc, 0, sizeof(sastc));
    ae_matrix_init(&s, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sr, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&astc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sastc, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&sr, n-1+1, _state);
    ae_vector_set_length(&astc, n-1+1, _state);
    ae_vector_set_length(&sastc, n-1+1, _state);
    
    /*
     * Schur decomposition, convergence test
     */
    ae_matrix_set_length(&t, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
    if( !rmatrixschur(&t, n, &s, _state) )
    {
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * decomposition error
     */
    locerr = (double)(0);
    for(j=0; j<=n-1; j++)
    {
        ae_v_move(&sr.ptr.p_double[0], 1, &s.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
        for(k=0; k<=n-1; k++)
        {
            v = ae_v_dotproduct(&t.ptr.pp_double[k][0], 1, &sr.ptr.p_double[0], 1, ae_v_len(0,n-1));
            astc.ptr.p_double[k] = v;
        }
        for(k=0; k<=n-1; k++)
        {
            v = ae_v_dotproduct(&s.ptr.pp_double[k][0], 1, &astc.ptr.p_double[0], 1, ae_v_len(0,n-1));
            sastc.ptr.p_double[k] = v;
        }
        for(k=0; k<=n-1; k++)
        {
            locerr = ae_maxreal(locerr, ae_fabs(sastc.ptr.p_double[k]-a->ptr.pp_double[k][j], _state), _state);
        }
    }
    *materr = ae_maxreal(*materr, locerr, _state);
    
    /*
     * orthogonality error
     */
    locerr = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&s.ptr.pp_double[0][i], s.stride, &s.ptr.pp_double[0][j], s.stride, ae_v_len(0,n-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(v, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(v-1, _state), _state);
            }
        }
    }
    *orterr = ae_maxreal(*orterr, locerr, _state);
    
    /*
     * T matrix structure
     */
    for(j=0; j<=n-1; j++)
    {
        for(i=j+2; i<=n-1; i++)
        {
            if( ae_fp_neq(t.ptr.pp_double[i][j],(double)(0)) )
            {
                *errstruct = ae_true;
            }
        }
    }
    ae_frame_leave(_state);
}








/*************************************************************************
Testing bidiagonal SVD decomposition subroutine
*************************************************************************/
ae_bool testspdgevd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_int_t atask;
    ae_int_t btask;
    ae_vector d;
    ae_vector t1;
    ae_matrix a;
    ae_matrix b;
    ae_matrix afull;
    ae_matrix bfull;
    ae_matrix l;
    ae_matrix z;
    ae_bool isuppera;
    ae_bool isupperb;
    ae_int_t i;
    ae_int_t j;
    ae_int_t minij;
    double v;
    double v1;
    double v2;
    double err;
    double valerr;
    double threshold;
    ae_bool waserrors;
    ae_bool wfailed;
    ae_bool wnsorted;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&d, 0, sizeof(d));
    memset(&t1, 0, sizeof(t1));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&afull, 0, sizeof(afull));
    memset(&bfull, 0, sizeof(bfull));
    memset(&l, 0, sizeof(l));
    memset(&z, 0, sizeof(z));
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&afull, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&bfull, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&l, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);

    threshold = 10000*ae_machineepsilon;
    valerr = (double)(0);
    wfailed = ae_false;
    wnsorted = ae_false;
    maxn = 20;
    passcount = 5;
    
    /*
     * Main cycle
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            for(atask=0; atask<=1; atask++)
            {
                for(btask=0; btask<=1; btask++)
                {
                    isuppera = atask==0;
                    isupperb = btask==0;
                    
                    /*
                     * Initialize A, B, AFull, BFull
                     */
                    ae_vector_set_length(&t1, n-1+1, _state);
                    ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
                    ae_matrix_set_length(&b, n-1+1, n-1+1, _state);
                    ae_matrix_set_length(&afull, n-1+1, n-1+1, _state);
                    ae_matrix_set_length(&bfull, n-1+1, n-1+1, _state);
                    ae_matrix_set_length(&l, n-1+1, n-1+1, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            afull.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                            afull.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=i+1; j<=n-1; j++)
                        {
                            l.ptr.pp_double[i][j] = ae_randomreal(_state);
                            l.ptr.pp_double[j][i] = l.ptr.pp_double[i][j];
                        }
                        l.ptr.pp_double[i][i] = 1.5+ae_randomreal(_state);
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            minij = ae_minint(i, j, _state);
                            v = ae_v_dotproduct(&l.ptr.pp_double[i][0], 1, &l.ptr.pp_double[0][j], l.stride, ae_v_len(0,minij));
                            b.ptr.pp_double[i][j] = v;
                            b.ptr.pp_double[j][i] = v;
                            bfull.ptr.pp_double[i][j] = v;
                            bfull.ptr.pp_double[j][i] = v;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( isuppera )
                            {
                                if( j<i )
                                {
                                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                }
                            }
                            else
                            {
                                if( i<j )
                                {
                                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                }
                            }
                            if( isupperb )
                            {
                                if( j<i )
                                {
                                    b.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                }
                            }
                            else
                            {
                                if( i<j )
                                {
                                    b.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                }
                            }
                        }
                    }
                    
                    /*
                     * Problem 1
                     */
                    if( !smatrixgevd(&a, n, isuppera, &b, isupperb, 1, 1, &d, &z, _state) )
                    {
                        wfailed = ae_true;
                        continue;
                    }
                    err = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            v1 = ae_v_dotproduct(&afull.ptr.pp_double[i][0], 1, &z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1));
                            v2 = ae_v_dotproduct(&bfull.ptr.pp_double[i][0], 1, &z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1));
                            err = ae_maxreal(err, ae_fabs(v1-d.ptr.p_double[j]*v2, _state), _state);
                        }
                    }
                    valerr = ae_maxreal(err, valerr, _state);
                    
                    /*
                     * Problem 2
                     */
                    if( !smatrixgevd(&a, n, isuppera, &b, isupperb, 1, 2, &d, &z, _state) )
                    {
                        wfailed = ae_true;
                        continue;
                    }
                    err = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            v1 = ae_v_dotproduct(&bfull.ptr.pp_double[i][0], 1, &z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1));
                            t1.ptr.p_double[i] = v1;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v2 = ae_v_dotproduct(&afull.ptr.pp_double[i][0], 1, &t1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            err = ae_maxreal(err, ae_fabs(v2-d.ptr.p_double[j]*z.ptr.pp_double[i][j], _state), _state);
                        }
                    }
                    valerr = ae_maxreal(err, valerr, _state);
                    
                    /*
                     * Test problem 3
                     */
                    if( !smatrixgevd(&a, n, isuppera, &b, isupperb, 1, 3, &d, &z, _state) )
                    {
                        wfailed = ae_true;
                        continue;
                    }
                    err = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            v1 = ae_v_dotproduct(&afull.ptr.pp_double[i][0], 1, &z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1));
                            t1.ptr.p_double[i] = v1;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v2 = ae_v_dotproduct(&bfull.ptr.pp_double[i][0], 1, &t1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            err = ae_maxreal(err, ae_fabs(v2-d.ptr.p_double[j]*z.ptr.pp_double[i][j], _state), _state);
                        }
                    }
                    valerr = ae_maxreal(err, valerr, _state);
                }
            }
        }
    }
    
    /*
     * report
     */
    waserrors = (ae_fp_greater(valerr,threshold)||wfailed)||wnsorted;
    if( !silent )
    {
        printf("TESTING SYMMETRIC GEVD\n");
        printf("Av-lambdav error (generalized):          %5.3e\n",
            (double)(valerr));
        printf("Eigen values order:                      ");
        if( !wnsorted )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Always converged:                        ");
        if( !wfailed )
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
        }
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testinverseupdateunit_makeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testinverseupdateunit_matlu(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Integer */ ae_vector* pivots,
     ae_state *_state);
static void testinverseupdateunit_generaterandomorthogonalmatrix(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state);
static void testinverseupdateunit_generaterandommatrixcond(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     double c,
     ae_state *_state);
static ae_bool testinverseupdateunit_invmattr(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static ae_bool testinverseupdateunit_invmatlu(/* Real    */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state);
static ae_bool testinverseupdateunit_invmat(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static double testinverseupdateunit_matrixdiff(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* b,
     ae_int_t m,
     ae_int_t n,
     ae_state *_state);
static ae_bool testinverseupdateunit_updandinv(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* u,
     /* Real    */ ae_vector* v,
     ae_int_t n,
     ae_state *_state);





ae_bool testinverseupdate(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix inva;
    ae_matrix b1;
    ae_matrix b2;
    ae_vector u;
    ae_vector v;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t updrow;
    ae_int_t updcol;
    double val;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool waserrors;
    double threshold;
    double c;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&inva, 0, sizeof(inva));
    memset(&b1, 0, sizeof(b1));
    memset(&b2, 0, sizeof(b2));
    memset(&u, 0, sizeof(u));
    memset(&v, 0, sizeof(v));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&u, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&v, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    maxn = 10;
    passcount = 100;
    threshold = 1.0E-6;
    
    /*
     * process
     */
    for(n=1; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        ae_matrix_set_length(&b1, n-1+1, n-1+1, _state);
        ae_matrix_set_length(&b2, n-1+1, n-1+1, _state);
        ae_vector_set_length(&u, n-1+1, _state);
        ae_vector_set_length(&v, n-1+1, _state);
        for(pass=1; pass<=passcount; pass++)
        {
            c = ae_exp(ae_randomreal(_state)*ae_log((double)(10), _state), _state);
            testinverseupdateunit_generaterandommatrixcond(&a, n, c, _state);
            testinverseupdateunit_makeacopy(&a, n, n, &inva, _state);
            if( !testinverseupdateunit_invmat(&inva, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            
            /*
             * Test simple update
             */
            updrow = ae_randominteger(n, _state);
            updcol = ae_randominteger(n, _state);
            val = 0.1*(2*ae_randomreal(_state)-1);
            for(i=0; i<=n-1; i++)
            {
                if( i==updrow )
                {
                    u.ptr.p_double[i] = val;
                }
                else
                {
                    u.ptr.p_double[i] = (double)(0);
                }
                if( i==updcol )
                {
                    v.ptr.p_double[i] = (double)(1);
                }
                else
                {
                    v.ptr.p_double[i] = (double)(0);
                }
            }
            testinverseupdateunit_makeacopy(&a, n, n, &b1, _state);
            if( !testinverseupdateunit_updandinv(&b1, &u, &v, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            testinverseupdateunit_makeacopy(&inva, n, n, &b2, _state);
            rmatrixinvupdatesimple(&b2, n, updrow, updcol, val, _state);
            waserrors = waserrors||ae_fp_greater(testinverseupdateunit_matrixdiff(&b1, &b2, n, n, _state),threshold);
            
            /*
             * Test row update
             */
            updrow = ae_randominteger(n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( i==updrow )
                {
                    u.ptr.p_double[i] = (double)(1);
                }
                else
                {
                    u.ptr.p_double[i] = (double)(0);
                }
                v.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
            }
            testinverseupdateunit_makeacopy(&a, n, n, &b1, _state);
            if( !testinverseupdateunit_updandinv(&b1, &u, &v, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            testinverseupdateunit_makeacopy(&inva, n, n, &b2, _state);
            rmatrixinvupdaterow(&b2, n, updrow, &v, _state);
            waserrors = waserrors||ae_fp_greater(testinverseupdateunit_matrixdiff(&b1, &b2, n, n, _state),threshold);
            
            /*
             * Test column update
             */
            updcol = ae_randominteger(n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( i==updcol )
                {
                    v.ptr.p_double[i] = (double)(1);
                }
                else
                {
                    v.ptr.p_double[i] = (double)(0);
                }
                u.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
            }
            testinverseupdateunit_makeacopy(&a, n, n, &b1, _state);
            if( !testinverseupdateunit_updandinv(&b1, &u, &v, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            testinverseupdateunit_makeacopy(&inva, n, n, &b2, _state);
            rmatrixinvupdatecolumn(&b2, n, updcol, &u, _state);
            waserrors = waserrors||ae_fp_greater(testinverseupdateunit_matrixdiff(&b1, &b2, n, n, _state),threshold);
            
            /*
             * Test full update
             */
            for(i=0; i<=n-1; i++)
            {
                v.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
                u.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
            }
            testinverseupdateunit_makeacopy(&a, n, n, &b1, _state);
            if( !testinverseupdateunit_updandinv(&b1, &u, &v, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            testinverseupdateunit_makeacopy(&inva, n, n, &b2, _state);
            rmatrixinvupdateuv(&b2, n, &u, &v, _state);
            waserrors = waserrors||ae_fp_greater(testinverseupdateunit_matrixdiff(&b1, &b2, n, n, _state),threshold);
        }
    }
    
    /*
     * report
     */
    if( !silent )
    {
        printf("TESTING INVERSE UPDATE (REAL)\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testinverseupdateunit_makeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
LU decomposition
*************************************************************************/
static void testinverseupdateunit_matlu(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Integer */ ae_vector* pivots,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jp;
    ae_vector t1;
    double s;

    ae_frame_make(_state, &_frame_block);
    memset(&t1, 0, sizeof(t1));
    ae_vector_clear(pivots);
    ae_vector_init(&t1, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(pivots, ae_minint(m-1, n-1, _state)+1, _state);
    ae_vector_set_length(&t1, ae_maxint(m-1, n-1, _state)+1, _state);
    ae_assert(m>=0&&n>=0, "Error in LUDecomposition: incorrect function arguments", _state);
    
    /*
     * Quick return if possible
     */
    if( m==0||n==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    for(j=0; j<=ae_minint(m-1, n-1, _state); j++)
    {
        
        /*
         * Find pivot and test for singularity.
         */
        jp = j;
        for(i=j+1; i<=m-1; i++)
        {
            if( ae_fp_greater(ae_fabs(a->ptr.pp_double[i][j], _state),ae_fabs(a->ptr.pp_double[jp][j], _state)) )
            {
                jp = i;
            }
        }
        pivots->ptr.p_int[j] = jp;
        if( ae_fp_neq(a->ptr.pp_double[jp][j],(double)(0)) )
        {
            
            /*
             *Apply the interchange to rows
             */
            if( jp!=j )
            {
                ae_v_move(&t1.ptr.p_double[0], 1, &a->ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                ae_v_move(&a->ptr.pp_double[j][0], 1, &a->ptr.pp_double[jp][0], 1, ae_v_len(0,n-1));
                ae_v_move(&a->ptr.pp_double[jp][0], 1, &t1.ptr.p_double[0], 1, ae_v_len(0,n-1));
            }
            
            /*
             *Compute elements J+1:M of J-th column.
             */
            if( j+1<m )
            {
                jp = j+1;
                s = 1/a->ptr.pp_double[j][j];
                ae_v_muld(&a->ptr.pp_double[jp][j], a->stride, ae_v_len(jp,m-1), s);
            }
        }
        if( j<ae_minint(m, n, _state)-1 )
        {
            
            /*
             *Update trailing submatrix.
             */
            jp = j+1;
            for(i=j+1; i<=m-1; i++)
            {
                s = a->ptr.pp_double[i][j];
                ae_v_subd(&a->ptr.pp_double[i][jp], 1, &a->ptr.pp_double[j][jp], 1, ae_v_len(jp,n-1), s);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Generate matrix with given condition number C (2-norm)
*************************************************************************/
static void testinverseupdateunit_generaterandomorthogonalmatrix(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    double t;
    double lambdav;
    ae_int_t s;
    ae_int_t i;
    ae_int_t j;
    double u1;
    double u2;
    ae_vector w;
    ae_vector v;
    ae_matrix a;
    double sm;

    ae_frame_make(_state, &_frame_block);
    memset(&w, 0, sizeof(w));
    memset(&v, 0, sizeof(v));
    memset(&a, 0, sizeof(a));
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&v, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    if( n<=0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_vector_set_length(&w, n+1, _state);
    ae_vector_set_length(&v, n+1, _state);
    ae_matrix_set_length(&a, n+1, n+1, _state);
    ae_matrix_set_length(a0, n-1+1, n-1+1, _state);
    
    /*
     * Prepare A
     */
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            if( i==j )
            {
                a.ptr.pp_double[i][j] = (double)(1);
            }
            else
            {
                a.ptr.pp_double[i][j] = (double)(0);
            }
        }
    }
    
    /*
     * Calculate A using Stewart algorithm
     */
    for(s=2; s<=n; s++)
    {
        
        /*
         * Prepare v and Lambda = v'*v
         */
        do
        {
            i = 1;
            while(i<=s)
            {
                u1 = 2*ae_randomreal(_state)-1;
                u2 = 2*ae_randomreal(_state)-1;
                sm = u1*u1+u2*u2;
                if( ae_fp_eq(sm,(double)(0))||ae_fp_greater(sm,(double)(1)) )
                {
                    continue;
                }
                sm = ae_sqrt(-2*ae_log(sm, _state)/sm, _state);
                v.ptr.p_double[i] = u1*sm;
                if( i+1<=s )
                {
                    v.ptr.p_double[i+1] = u2*sm;
                }
                i = i+2;
            }
            lambdav = ae_v_dotproduct(&v.ptr.p_double[1], 1, &v.ptr.p_double[1], 1, ae_v_len(1,s));
        }
        while(ae_fp_eq(lambdav,(double)(0)));
        lambdav = 2/lambdav;
        
        /*
         * A * (I - 2 vv'/v'v ) =
         *   = A - (2/v'v) * A * v * v' =
         *   = A - (2/v'v) * w * v'
         *  where w = Av
         */
        for(i=1; i<=s; i++)
        {
            t = ae_v_dotproduct(&a.ptr.pp_double[i][1], 1, &v.ptr.p_double[1], 1, ae_v_len(1,s));
            w.ptr.p_double[i] = t;
        }
        for(i=1; i<=s; i++)
        {
            t = w.ptr.p_double[i]*lambdav;
            ae_v_subd(&a.ptr.pp_double[i][1], 1, &v.ptr.p_double[1], 1, ae_v_len(1,s), t);
        }
    }
    
    /*
     *
     */
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a0->ptr.pp_double[i-1][j-1] = a.ptr.pp_double[i][j];
        }
    }
    ae_frame_leave(_state);
}


static void testinverseupdateunit_generaterandommatrixcond(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     double c,
     ae_state *_state)
{
    ae_frame _frame_block;
    double l1;
    double l2;
    ae_matrix q1;
    ae_matrix q2;
    ae_vector cc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;

    ae_frame_make(_state, &_frame_block);
    memset(&q1, 0, sizeof(q1));
    memset(&q2, 0, sizeof(q2));
    memset(&cc, 0, sizeof(cc));
    ae_matrix_init(&q1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cc, 0, DT_REAL, _state, ae_true);

    testinverseupdateunit_generaterandomorthogonalmatrix(&q1, n, _state);
    testinverseupdateunit_generaterandomorthogonalmatrix(&q2, n, _state);
    ae_vector_set_length(&cc, n-1+1, _state);
    l1 = (double)(0);
    l2 = ae_log(1/c, _state);
    cc.ptr.p_double[0] = ae_exp(l1, _state);
    for(i=1; i<=n-2; i++)
    {
        cc.ptr.p_double[i] = ae_exp(ae_randomreal(_state)*(l2-l1)+l1, _state);
    }
    cc.ptr.p_double[n-1] = ae_exp(l2, _state);
    ae_matrix_set_length(a0, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a0->ptr.pp_double[i][j] = (double)(0);
            for(k=0; k<=n-1; k++)
            {
                a0->ptr.pp_double[i][j] = a0->ptr.pp_double[i][j]+q1.ptr.pp_double[i][k]*cc.ptr.p_double[k]*q2.ptr.pp_double[j][k];
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
triangular inverse
*************************************************************************/
static ae_bool testinverseupdateunit_invmattr(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    double v;
    double ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&t, 0, sizeof(t));
    ae_vector_init(&t, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = (double)(-1);
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_move(&t.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i<j-1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][i+1], 1, &t.ptr.p_double[i+1], 1, ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = (double)(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],(double)(0)) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = (double)(-1);
            }
            if( j<n-1 )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_move(&t.ptr.p_double[j+1], 1, &a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &t.ptr.p_double[j+1], 1, ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = (double)(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
LU inverse
*************************************************************************/
static ae_bool testinverseupdateunit_invmatlu(/* Real    */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector work;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jp;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&work, 0, sizeof(work));
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    
    /*
     * Quick return if possible
     */
    if( n==0 )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_vector_set_length(&work, n-1+1, _state);
    
    /*
     * Form inv(U)
     */
    if( !testinverseupdateunit_invmattr(a, n, ae_true, ae_false, _state) )
    {
        result = ae_false;
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Solve the equation inv(A)*L = inv(U) for inv(A).
     */
    for(j=n-1; j>=0; j--)
    {
        
        /*
         * Copy current column of L to WORK and replace with zeros.
         */
        for(i=j+1; i<=n-1; i++)
        {
            work.ptr.p_double[i] = a->ptr.pp_double[i][j];
            a->ptr.pp_double[i][j] = (double)(0);
        }
        
        /*
         * Compute current column of inv(A).
         */
        if( j<n-1 )
        {
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &work.ptr.p_double[j+1], 1, ae_v_len(j+1,n-1));
                a->ptr.pp_double[i][j] = a->ptr.pp_double[i][j]-v;
            }
        }
    }
    
    /*
     * Apply column interchanges.
     */
    for(j=n-2; j>=0; j--)
    {
        jp = pivots->ptr.p_int[j];
        if( jp!=j )
        {
            ae_v_move(&work.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
            ae_v_move(&a->ptr.pp_double[0][j], a->stride, &a->ptr.pp_double[0][jp], a->stride, ae_v_len(0,n-1));
            ae_v_move(&a->ptr.pp_double[0][jp], a->stride, &work.ptr.p_double[0], 1, ae_v_len(0,n-1));
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Matrix inverse
*************************************************************************/
static ae_bool testinverseupdateunit_invmat(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector pivots;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&pivots, 0, sizeof(pivots));
    ae_vector_init(&pivots, 0, DT_INT, _state, ae_true);

    testinverseupdateunit_matlu(a, n, n, &pivots, _state);
    result = testinverseupdateunit_invmatlu(a, &pivots, n, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Diff
*************************************************************************/
static double testinverseupdateunit_matrixdiff(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* b,
     ae_int_t m,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double result;


    result = (double)(0);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            result = ae_maxreal(result, ae_fabs(b->ptr.pp_double[i][j]-a->ptr.pp_double[i][j], _state), _state);
        }
    }
    return result;
}


/*************************************************************************
Update and inverse
*************************************************************************/
static ae_bool testinverseupdateunit_updandinv(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* u,
     /* Real    */ ae_vector* v,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector pivots;
    ae_int_t i;
    double r;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&pivots, 0, sizeof(pivots));
    ae_vector_init(&pivots, 0, DT_INT, _state, ae_true);

    for(i=0; i<=n-1; i++)
    {
        r = u->ptr.p_double[i];
        ae_v_addd(&a->ptr.pp_double[i][0], 1, &v->ptr.p_double[0], 1, ae_v_len(0,n-1), r);
    }
    testinverseupdateunit_matlu(a, n, n, &pivots, _state);
    result = testinverseupdateunit_invmatlu(a, &pivots, n, _state);
    ae_frame_leave(_state);
    return result;
}








/*************************************************************************
Test
*************************************************************************/
ae_bool testpolynomialsolver(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool wereerrors;
    ae_vector a;
    ae_vector x;
    double eps;
    ae_int_t n;
    polynomialsolverreport rep;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&x, 0, sizeof(x));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_COMPLEX, _state, ae_true);
    _polynomialsolverreport_init(&rep, _state, ae_true);

    wereerrors = ae_false;
    
    /*
     * Basic tests
     */
    eps = 1.0E-6;
    n = 1;
    ae_vector_set_length(&a, n+1, _state);
    a.ptr.p_double[0] = (double)(2);
    a.ptr.p_double[1] = (double)(3);
    polynomialsolve(&a, n, &x, &rep, _state);
    ae_set_error_flag(&wereerrors, ae_fp_greater(ae_fabs(x.ptr.p_complex[0].x+(double)2/(double)3, _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:33");
    ae_set_error_flag(&wereerrors, ae_fp_neq(x.ptr.p_complex[0].y,(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:34");
    ae_set_error_flag(&wereerrors, ae_fp_greater(rep.maxerr,100*ae_machineepsilon), __FILE__, __LINE__, "testpolynomialsolverunit.ap:35");
    n = 2;
    ae_vector_set_length(&a, n+1, _state);
    a.ptr.p_double[0] = (double)(1);
    a.ptr.p_double[1] = (double)(-2);
    a.ptr.p_double[2] = (double)(1);
    polynomialsolve(&a, n, &x, &rep, _state);
    ae_set_error_flag(&wereerrors, ae_fp_greater(ae_c_abs(ae_c_sub_d(x.ptr.p_complex[0],1), _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:43");
    ae_set_error_flag(&wereerrors, ae_fp_greater(ae_c_abs(ae_c_sub_d(x.ptr.p_complex[1],1), _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:44");
    ae_set_error_flag(&wereerrors, ae_fp_greater(rep.maxerr,100*ae_machineepsilon), __FILE__, __LINE__, "testpolynomialsolverunit.ap:45");
    n = 2;
    ae_vector_set_length(&a, n+1, _state);
    a.ptr.p_double[0] = (double)(2);
    a.ptr.p_double[1] = (double)(-3);
    a.ptr.p_double[2] = (double)(1);
    polynomialsolve(&a, n, &x, &rep, _state);
    if( ae_fp_less(x.ptr.p_complex[0].x,x.ptr.p_complex[1].x) )
    {
        ae_set_error_flag(&wereerrors, ae_fp_greater(ae_fabs(x.ptr.p_complex[0].x-1, _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:55");
        ae_set_error_flag(&wereerrors, ae_fp_greater(ae_fabs(x.ptr.p_complex[1].x-2, _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:56");
    }
    else
    {
        ae_set_error_flag(&wereerrors, ae_fp_greater(ae_fabs(x.ptr.p_complex[0].x-2, _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:60");
        ae_set_error_flag(&wereerrors, ae_fp_greater(ae_fabs(x.ptr.p_complex[1].x-1, _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:61");
    }
    ae_set_error_flag(&wereerrors, ae_fp_neq(x.ptr.p_complex[0].y,(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:63");
    ae_set_error_flag(&wereerrors, ae_fp_neq(x.ptr.p_complex[1].y,(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:64");
    ae_set_error_flag(&wereerrors, ae_fp_greater(rep.maxerr,100*ae_machineepsilon), __FILE__, __LINE__, "testpolynomialsolverunit.ap:65");
    n = 2;
    ae_vector_set_length(&a, n+1, _state);
    a.ptr.p_double[0] = (double)(1);
    a.ptr.p_double[1] = (double)(0);
    a.ptr.p_double[2] = (double)(1);
    polynomialsolve(&a, n, &x, &rep, _state);
    ae_set_error_flag(&wereerrors, ae_fp_greater(ae_c_abs(ae_c_add_d(ae_c_mul(x.ptr.p_complex[0],x.ptr.p_complex[0]),(double)(1)), _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:73");
    ae_set_error_flag(&wereerrors, ae_fp_greater(rep.maxerr,100*ae_machineepsilon), __FILE__, __LINE__, "testpolynomialsolverunit.ap:74");
    n = 4;
    ae_vector_set_length(&a, n+1, _state);
    a.ptr.p_double[0] = (double)(0);
    a.ptr.p_double[1] = (double)(0);
    a.ptr.p_double[2] = (double)(0);
    a.ptr.p_double[3] = (double)(0);
    a.ptr.p_double[4] = (double)(1);
    polynomialsolve(&a, n, &x, &rep, _state);
    ae_set_error_flag(&wereerrors, ae_c_neq_d(x.ptr.p_complex[0],(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:84");
    ae_set_error_flag(&wereerrors, ae_c_neq_d(x.ptr.p_complex[1],(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:85");
    ae_set_error_flag(&wereerrors, ae_c_neq_d(x.ptr.p_complex[2],(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:86");
    ae_set_error_flag(&wereerrors, ae_c_neq_d(x.ptr.p_complex[3],(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:87");
    ae_set_error_flag(&wereerrors, ae_fp_greater(rep.maxerr,100*ae_machineepsilon), __FILE__, __LINE__, "testpolynomialsolverunit.ap:88");
    n = 2;
    ae_vector_set_length(&a, n+1, _state);
    a.ptr.p_double[0] = (double)(0);
    a.ptr.p_double[1] = (double)(3);
    a.ptr.p_double[2] = (double)(2);
    polynomialsolve(&a, n, &x, &rep, _state);
    if( ae_fp_greater(x.ptr.p_complex[0].x,x.ptr.p_complex[1].x) )
    {
        ae_set_error_flag(&wereerrors, ae_c_neq_d(x.ptr.p_complex[0],(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:98");
        ae_set_error_flag(&wereerrors, ae_fp_greater(ae_fabs(x.ptr.p_complex[1].x+(double)3/(double)2, _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:99");
        ae_set_error_flag(&wereerrors, ae_fp_neq(x.ptr.p_complex[1].y,(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:100");
    }
    else
    {
        ae_set_error_flag(&wereerrors, ae_c_neq_d(x.ptr.p_complex[1],(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:104");
        ae_set_error_flag(&wereerrors, ae_fp_greater(ae_fabs(x.ptr.p_complex[0].x+(double)3/(double)2, _state),eps), __FILE__, __LINE__, "testpolynomialsolverunit.ap:105");
        ae_set_error_flag(&wereerrors, ae_fp_neq(x.ptr.p_complex[0].y,(double)(0)), __FILE__, __LINE__, "testpolynomialsolverunit.ap:106");
    }
    ae_set_error_flag(&wereerrors, ae_fp_greater(rep.maxerr,100*ae_machineepsilon), __FILE__, __LINE__, "testpolynomialsolverunit.ap:108");
    if( !silent )
    {
        printf("TESTING POLYNOMIAL SOLVER\n");
        if( wereerrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !wereerrors;
    ae_frame_leave(_state);
    return result;
}



static void testnlequnit_testfunchbm(nleqstate* state, ae_state *_state);
static void testnlequnit_testfunchb1(nleqstate* state, ae_state *_state);
static void testnlequnit_testfuncshbm(nleqstate* state, ae_state *_state);





ae_bool testnleq(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool basicserrors;
    ae_bool converror;
    ae_bool othererrors;
    ae_int_t n;
    ae_vector x;
    ae_int_t i;
    ae_int_t k;
    double v;
    double flast;
    ae_vector xlast;
    ae_bool firstrep;
    ae_int_t nfunc;
    ae_int_t njac;
    ae_int_t itcnt;
    nleqstate state;
    nleqreport rep;
    ae_int_t pass;
    ae_int_t passcount;
    double epsf;
    double stpmax;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&x, 0, sizeof(x));
    memset(&xlast, 0, sizeof(xlast));
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    _nleqstate_init(&state, _state, ae_true);
    _nleqreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    basicserrors = ae_false;
    converror = ae_false;
    othererrors = ae_false;
    
    /*
     * Basic tests
     *
     * Test with Himmelblau's function (M):
     * * ability to find correct result
     * * ability to work after soft restart (restart after finish)
     * * ability to work after hard restart (restart in the middle of optimization)
     */
    passcount = 100;
    for(pass=0; pass<=passcount-1; pass++)
    {
        
        /*
         * Ability to find correct result
         */
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqcreatelm(2, 2, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            basicserrors = basicserrors||ae_fp_greater(ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state),ae_sqr(epsf, _state));
        }
        else
        {
            basicserrors = ae_true;
        }
        
        /*
         * Ability to work after soft restart
         */
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqcreatelm(2, 2, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqrestartfrom(&state, &x, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            basicserrors = basicserrors||ae_fp_greater(ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state),ae_sqr(epsf, _state));
        }
        else
        {
            basicserrors = ae_true;
        }
        
        /*
         * Ability to work after hard restart:
         * * stopping condition: small F
         * * StpMax is so small that we need about 10000 iterations to
         *   find solution (steps are small)
         * * choose random K significantly less that 9999
         * * iterate for some time, then break, restart optimization
         */
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = (double)(100);
        x.ptr.p_double[1] = (double)(100);
        nleqcreatelm(2, 2, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        nleqsetstpmax(&state, 0.01, _state);
        k = 1+ae_randominteger(100, _state);
        for(i=0; i<=k-1; i++)
        {
            if( !nleqiteration(&state, _state) )
            {
                break;
            }
            testnlequnit_testfunchbm(&state, _state);
        }
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqrestartfrom(&state, &x, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            basicserrors = basicserrors||ae_fp_greater(ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state),ae_sqr(epsf, _state));
        }
        else
        {
            basicserrors = ae_true;
        }
    }
    
    /*
     * Basic tests
     *
     * Test with Himmelblau's function (1):
     * * ability to find correct result
     */
    passcount = 100;
    for(pass=0; pass<=passcount-1; pass++)
    {
        
        /*
         * Ability to find correct result
         */
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqcreatelm(2, 1, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchb1(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            basicserrors = basicserrors||ae_fp_greater(ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state),epsf);
        }
        else
        {
            basicserrors = ae_true;
        }
    }
    
    /*
     * Basic tests
     *
     * Ability to detect situation when we can't find minimum
     */
    passcount = 100;
    for(pass=0; pass<=passcount-1; pass++)
    {
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqcreatelm(2, 3, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfuncshbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        basicserrors = basicserrors||rep.terminationtype!=-4;
    }
    
    /*
     * Test correctness of intermediate reports and final report:
     * * first report is starting point
     * * function value decreases on subsequent reports
     * * function value is correctly reported
     * * last report is final point
     * * NFunc and NJac are compared with values counted directly
     * * IterationsCount is compared with value counter directly
     */
    n = 2;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&xlast, n, _state);
    x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
    x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
    xlast.ptr.p_double[0] = ae_maxrealnumber;
    xlast.ptr.p_double[1] = ae_maxrealnumber;
    nleqcreatelm(n, 2, &x, &state, _state);
    nleqsetcond(&state, 1.0E-6, 0, _state);
    nleqsetxrep(&state, ae_true, _state);
    firstrep = ae_true;
    flast = ae_maxrealnumber;
    nfunc = 0;
    njac = 0;
    itcnt = 0;
    while(nleqiteration(&state, _state))
    {
        if( state.xupdated )
        {
            
            /*
             * first report must be starting point
             */
            if( firstrep )
            {
                for(i=0; i<=n-1; i++)
                {
                    othererrors = othererrors||ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]);
                }
                firstrep = ae_false;
            }
            
            /*
             * function value must decrease
             */
            othererrors = othererrors||ae_fp_greater(state.f,flast);
            
            /*
             * check correctness of function value
             */
            v = ae_sqr(state.x.ptr.p_double[0]*state.x.ptr.p_double[0]+state.x.ptr.p_double[1]-11, _state)+ae_sqr(state.x.ptr.p_double[0]+state.x.ptr.p_double[1]*state.x.ptr.p_double[1]-7, _state);
            othererrors = othererrors||ae_fp_greater(ae_fabs(v-state.f, _state)/ae_maxreal(v, (double)(1), _state),100*ae_machineepsilon);
            
            /*
             * update info and continue
             */
            ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            flast = state.f;
            itcnt = itcnt+1;
            continue;
        }
        if( state.needf )
        {
            nfunc = nfunc+1;
        }
        if( state.needfij )
        {
            nfunc = nfunc+1;
            njac = njac+1;
        }
        testnlequnit_testfunchbm(&state, _state);
    }
    nleqresults(&state, &x, &rep, _state);
    if( rep.terminationtype>0 )
    {
        othererrors = (othererrors||ae_fp_neq(xlast.ptr.p_double[0],x.ptr.p_double[0]))||ae_fp_neq(xlast.ptr.p_double[1],x.ptr.p_double[1]);
        v = ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state);
        othererrors = othererrors||ae_fp_greater(ae_fabs(flast-v, _state)/ae_maxreal(v, (double)(1), _state),100*ae_machineepsilon);
    }
    else
    {
        converror = ae_true;
    }
    othererrors = othererrors||rep.nfunc!=nfunc;
    othererrors = othererrors||rep.njac!=njac;
    othererrors = othererrors||rep.iterationscount!=itcnt-1;
    
    /*
     * Test ability to set limit on algorithm steps
     */
    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&xlast, 2, _state);
    x.ptr.p_double[0] = 20*ae_randomreal(_state)+20;
    x.ptr.p_double[1] = 20*ae_randomreal(_state)+20;
    xlast.ptr.p_double[0] = x.ptr.p_double[0];
    xlast.ptr.p_double[1] = x.ptr.p_double[1];
    stpmax = 0.1+0.1*ae_randomreal(_state);
    epsf = 1.0E-9;
    nleqcreatelm(2, 3, &x, &state, _state);
    nleqsetstpmax(&state, stpmax, _state);
    nleqsetcond(&state, epsf, 0, _state);
    nleqsetxrep(&state, ae_true, _state);
    while(nleqiteration(&state, _state))
    {
        if( state.needf||state.needfij )
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        if( (state.needf||state.needfij)||state.xupdated )
        {
            othererrors = othererrors||ae_fp_greater(ae_sqrt(ae_sqr(state.x.ptr.p_double[0]-xlast.ptr.p_double[0], _state)+ae_sqr(state.x.ptr.p_double[1]-xlast.ptr.p_double[1], _state), _state),1.00001*stpmax);
        }
        if( state.xupdated )
        {
            xlast.ptr.p_double[0] = state.x.ptr.p_double[0];
            xlast.ptr.p_double[1] = state.x.ptr.p_double[1];
        }
    }
    
    /*
     * end
     */
    waserrors = (basicserrors||converror)||othererrors;
    if( !silent )
    {
        printf("TESTING NLEQ SOLVER\n");
        printf("BASIC FUNCTIONALITY:                      ");
        if( basicserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE:                              ");
        if( converror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Himmelblau's function

    F = (x^2+y-11)^2 + (x+y^2-7)^2

posed as system of M functions:

    f0 = x^2+y-11
    f1 = x+y^2-7

*************************************************************************/
static void testnlequnit_testfunchbm(nleqstate* state, ae_state *_state)
{
    double x;
    double y;


    ae_assert(state->needf||state->needfij, "TestNLEQUnit: internal error!", _state);
    x = state->x.ptr.p_double[0];
    y = state->x.ptr.p_double[1];
    if( state->needf )
    {
        state->f = ae_sqr(x*x+y-11, _state)+ae_sqr(x+y*y-7, _state);
        return;
    }
    if( state->needfij )
    {
        state->fi.ptr.p_double[0] = x*x+y-11;
        state->fi.ptr.p_double[1] = x+y*y-7;
        state->j.ptr.pp_double[0][0] = 2*x;
        state->j.ptr.pp_double[0][1] = (double)(1);
        state->j.ptr.pp_double[1][0] = (double)(1);
        state->j.ptr.pp_double[1][1] = 2*y;
        return;
    }
}


/*************************************************************************
Himmelblau's function

    F = (x^2+y-11)^2 + (x+y^2-7)^2

posed as system of 1 function
*************************************************************************/
static void testnlequnit_testfunchb1(nleqstate* state, ae_state *_state)
{
    double x;
    double y;


    ae_assert(state->needf||state->needfij, "TestNLEQUnit: internal error!", _state);
    x = state->x.ptr.p_double[0];
    y = state->x.ptr.p_double[1];
    if( state->needf )
    {
        state->f = ae_sqr(ae_sqr(x*x+y-11, _state)+ae_sqr(x+y*y-7, _state), _state);
        return;
    }
    if( state->needfij )
    {
        state->fi.ptr.p_double[0] = ae_sqr(x*x+y-11, _state)+ae_sqr(x+y*y-7, _state);
        state->j.ptr.pp_double[0][0] = 2*(x*x+y-11)*2*x+2*(x+y*y-7);
        state->j.ptr.pp_double[0][1] = 2*(x*x+y-11)+2*(x+y*y-7)*2*y;
        return;
    }
}


/*************************************************************************
Shifted Himmelblau's function

    F = (x^2+y-11)^2 + (x+y^2-7)^2 + 1

posed as system of M functions:

    f0 = x^2+y-11
    f1 = x+y^2-7
    f2 = 1

This function is used to test algorithm on problem which has no solution.
*************************************************************************/
static void testnlequnit_testfuncshbm(nleqstate* state, ae_state *_state)
{
    double x;
    double y;


    ae_assert(state->needf||state->needfij, "TestNLEQUnit: internal error!", _state);
    x = state->x.ptr.p_double[0];
    y = state->x.ptr.p_double[1];
    if( state->needf )
    {
        state->f = ae_sqr(x*x+y-11, _state)+ae_sqr(x+y*y-7, _state)+1;
        return;
    }
    if( state->needfij )
    {
        state->fi.ptr.p_double[0] = x*x+y-11;
        state->fi.ptr.p_double[1] = x+y*y-7;
        state->fi.ptr.p_double[2] = (double)(1);
        state->j.ptr.pp_double[0][0] = 2*x;
        state->j.ptr.pp_double[0][1] = (double)(1);
        state->j.ptr.pp_double[1][0] = (double)(1);
        state->j.ptr.pp_double[1][1] = 2*y;
        state->j.ptr.pp_double[2][0] = (double)(0);
        state->j.ptr.pp_double[2][1] = (double)(0);
        return;
    }
}



static void testdirectsparsesolversunit_testsks(ae_bool* errorflag,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testdirectsparsesolvers(ae_bool silent, ae_state *_state)
{
    ae_bool rskserrors;
    ae_bool wereerrors;
    ae_bool result;


    rskserrors = ae_false;
    testdirectsparsesolversunit_testsks(&rskserrors, _state);
    wereerrors = rskserrors;
    if( !silent )
    {
        printf("TESTING DIRECT SPARSE SOLVERS:\n");
        printf("* SKS (real)                              ");
        if( rskserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( wereerrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !wereerrors;
    return result;
}


/*************************************************************************
SPD SKS test
*************************************************************************/
static void testdirectsparsesolversunit_testsks(ae_bool* errorflag,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t maxn;
    double threshold;
    ae_int_t bw;
    ae_bool isupper;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_matrix a;
    ae_vector xe;
    ae_vector b;
    ae_vector xs;
    sparsematrix sa;
    sparsematrix sa2;
    sparsesolverreport rep;
    hqrndstate rs;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&xe, 0, sizeof(xe));
    memset(&b, 0, sizeof(b));
    memset(&xs, 0, sizeof(xs));
    memset(&sa, 0, sizeof(sa));
    memset(&sa2, 0, sizeof(sa2));
    memset(&rep, 0, sizeof(rep));
    memset(&rs, 0, sizeof(rs));
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    _sparsematrix_init(&sa2, _state, ae_true);
    _sparsesolverreport_init(&rep, _state, ae_true);
    _hqrndstate_init(&rs, _state, ae_true);

    passcount = 10;
    maxn = 30;
    threshold = 1.0E-6;
    hqrndrandomize(&rs, _state);
    
    /*
     * Well conditioned SPD problems solved with SKS solver
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * 1. generate random well conditioned matrix A.
             * 2. generate random solution vector xe
             * 3. generate right part b=A*xe
             */
            isupper = ae_fp_greater(hqrndnormal(&rs, _state),0.5);
            bw = hqrnduniformi(&rs, n+1, _state);
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = (double)(0);
                }
            }
            sparsecreate(n, n, 0, &sa, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 1+hqrnduniformr(&rs, _state);
                sparseset(&sa, i, i, a.ptr.pp_double[i][i], _state);
                for(j=i+1; j<=ae_minint(i+bw, n-1, _state); j++)
                {
                    a.ptr.pp_double[i][j] = (hqrnduniformr(&rs, _state)-0.5)/n;
                    a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                    if( isupper )
                    {
                        sparseset(&sa, i, j, a.ptr.pp_double[i][j], _state);
                    }
                    else
                    {
                        sparseset(&sa, j, i, a.ptr.pp_double[i][j], _state);
                    }
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j<i&&isupper)||(j>i&&!isupper) )
                    {
                        if( ae_fp_less(hqrnduniformr(&rs, _state),0.25) )
                        {
                            sparseset(&sa, i, j, hqrndnormal(&rs, _state), _state);
                        }
                    }
                }
            }
            sparseconvertto(&sa, hqrnduniformi(&rs, 3, _state), _state);
            ae_vector_set_length(&xe, n, _state);
            for(i=0; i<=n-1; i++)
            {
                xe.ptr.p_double[i] = hqrndnormal(&rs, _state);
            }
            ae_vector_set_length(&b, n, _state);
            rmatrixgemv(n, n, 1.0, &a, 0, 0, 0, &xe, 0, 0.0, &b, 0, _state);
            
            /*
             * Test solver #1
             */
            ae_vector_set_length(&xs, 0, _state);
            sparsesolvesks(&sa, n, isupper, &b, &rep, &xs, _state);
            ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testdirectsparsesolversunit.ap:98");
            ae_set_error_flag(errorflag, xs.cnt!=n, __FILE__, __LINE__, "testdirectsparsesolversunit.ap:99");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xe.ptr.p_double[i]-xs.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testdirectsparsesolversunit.ap:103");
            }
            
            /*
             * Test solver #2
             */
            sparsecopytosks(&sa, &sa2, _state);
            if( !sparsecholeskyskyline(&sa2, n, isupper, _state) )
            {
                ae_set_error_flag(errorflag, ae_true, __FILE__, __LINE__, "testdirectsparsesolversunit.ap:112");
                ae_frame_leave(_state);
                return;
            }
            ae_vector_set_length(&xs, 0, _state);
            sparsecholeskysolvesks(&sa2, n, isupper, &b, &rep, &xs, _state);
            ae_set_error_flag(errorflag, rep.terminationtype<=0, __FILE__, __LINE__, "testdirectsparsesolversunit.ap:117");
            ae_set_error_flag(errorflag, xs.cnt!=n, __FILE__, __LINE__, "testdirectsparsesolversunit.ap:118");
            if( *errorflag )
            {
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                ae_set_error_flag(errorflag, ae_fp_greater(ae_fabs(xe.ptr.p_double[i]-xs.ptr.p_double[i], _state),threshold), __FILE__, __LINE__, "testdirectsparsesolversunit.ap:122");
            }
        }
    }
    ae_frame_leave(_state);
}



static double testlincgunit_e0 = 1.0E-6;
static double testlincgunit_maxcond = 30;
static ae_bool testlincgunit_complextest(ae_bool silent, ae_state *_state);
static ae_bool testlincgunit_complexres(ae_bool silent, ae_state *_state);
static ae_bool testlincgunit_basictestx(ae_bool silent, ae_state *_state);
static ae_bool testlincgunit_testrcorrectness(ae_bool silent,
     ae_state *_state);
static ae_bool testlincgunit_basictestiters(ae_bool silent,
     ae_state *_state);
static ae_bool testlincgunit_krylovsubspacetest(ae_bool silent,
     ae_state *_state);
static ae_bool testlincgunit_sparsetest(ae_bool silent, ae_state *_state);
static ae_bool testlincgunit_precondtest(ae_bool silent, ae_state *_state);
static void testlincgunit_gramshmidtortnorm(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_int_t k,
     double eps,
     /* Real    */ ae_matrix* b,
     ae_int_t* k2,
     ae_state *_state);
static ae_bool testlincgunit_frombasis(/* Real    */ ae_vector* x,
     /* Real    */ ae_matrix* basis,
     ae_int_t n,
     ae_int_t k,
     double eps,
     ae_state *_state);





ae_bool testlincg(ae_bool silent, ae_state *_state)
{
    ae_bool basictestxerrors;
    ae_bool basictestiterserr;
    ae_bool complexreserrors;
    ae_bool complexerrors;
    ae_bool rcorrectness;
    ae_bool krylovsubspaceerr;
    ae_bool sparseerrors;
    ae_bool preconderrors;
    ae_bool waserrors;
    ae_bool result;


    basictestxerrors = testlincgunit_basictestx(ae_true, _state);
    basictestiterserr = testlincgunit_basictestiters(ae_true, _state);
    complexreserrors = testlincgunit_complexres(ae_true, _state);
    complexerrors = testlincgunit_complextest(ae_true, _state);
    rcorrectness = testlincgunit_testrcorrectness(ae_true, _state);
    krylovsubspaceerr = testlincgunit_krylovsubspacetest(ae_true, _state);
    sparseerrors = testlincgunit_sparsetest(ae_true, _state);
    preconderrors = testlincgunit_precondtest(ae_true, _state);
    
    /*
     * report
     */
    waserrors = ((((((basictestxerrors||complexreserrors)||complexerrors)||rcorrectness)||basictestiterserr)||krylovsubspaceerr)||sparseerrors)||preconderrors;
    if( !silent )
    {
        printf("TESTING LinCG\n");
        printf("BasicTestX:                                   ");
        if( basictestxerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("BasicTestIters:                               ");
        if( basictestiterserr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("ComplexResTest:                               ");
        if( complexreserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("ComplexTest:                                  ");
        if( complexerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("R2 correctness:                               ");
        if( rcorrectness )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("KrylovSubSpaceTest:                           ");
        if( krylovsubspaceerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("SparseTest:                                   ");
        if( sparseerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("PrecondTest:                                  ");
        if( preconderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        
        /*
         *was errors?
         */
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Function for testing LinCGIteration function(custom option), which to solve
Ax=b(here A is random positive definite matrix NxN, b is random vector). It
uses  the  default  stopping criterion(RNorm<FEps=10^-6). If algorithm does
more iterations than size  of  the problem, then  some errors are possible.
The test verifies the following propirties:
    1. (A*pk,pm)=0 for any m<>k;
    2. (rk,rm)=0 for any m<>k;
    3. (rk,pm)=0 for any m<>k;

INPUT:
    Silent   -   if true then function output report

  -- ALGLIB --
     Copyright 14.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlincgunit_complextest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    lincgstate state;
    lincgreport rep;
    ae_matrix a;
    ae_vector b;
    ae_int_t n;
    double c;
    ae_vector x0;
    ae_vector residual;
    double normofresidual;
    double sclr;
    double na;
    double nv0;
    double nv1;
    ae_int_t sz;
    double mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    double tmp;
    ae_matrix mtx;
    ae_matrix mtp;
    ae_matrix mtr;
    double getrnorm;
    ae_int_t numofit;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&state, 0, sizeof(state));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    memset(&residual, 0, sizeof(residual));
    memset(&mtx, 0, sizeof(mtx));
    memset(&mtp, 0, sizeof(mtp));
    memset(&mtr, 0, sizeof(mtr));
    _lincgstate_init(&state, _state, ae_true);
    _lincgreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&residual, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mtx, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mtp, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mtr, 0, 0, DT_REAL, _state, ae_true);

    mx = (double)(100);
    n = 5;
    for(sz=1; sz<=n; sz++)
    {
        
        /*
         * Generate:
         * * random A with norm NA (equal to 1.0),
         * * random right part B whose elements are uniformly distributed in [-MX,+MX]
         * * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
         */
        c = 15+15*ae_randomreal(_state);
        spdmatrixrndcond(sz, c, &a, _state);
        na = (double)(1);
        ae_vector_set_length(&b, sz, _state);
        for(i=0; i<=sz-1; i++)
        {
            b.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
        }
        ae_vector_set_length(&x0, sz, _state);
        for(i=0; i<=sz-1; i++)
        {
            x0.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
        }
        ae_matrix_set_length(&mtx, sz+1, sz, _state);
        
        /*
         * Start optimization, record its progress for further analysis
         * NOTE: we set update frequency of R to 2 in order to test that R is updated correctly
         */
        lincgcreate(sz, &state, _state);
        lincgsetxrep(&state, ae_true, _state);
        lincgsetb(&state, &b, _state);
        lincgsetstartingpoint(&state, &x0, _state);
        lincgsetcond(&state, (double)(0), sz, _state);
        lincgsetrupdatefreq(&state, 2, _state);
        numofit = 0;
        getrnorm = ae_maxrealnumber;
        while(lincgiteration(&state, _state))
        {
            if( state.needmv )
            {
                for(i=0; i<=sz-1; i++)
                {
                    state.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=sz-1; j++)
                    {
                        state.mv.ptr.p_double[i] = state.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                    }
                }
            }
            if( state.needvmv )
            {
                state.vmv = (double)(0);
                for(i=0; i<=sz-1; i++)
                {
                    state.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=sz-1; j++)
                    {
                        state.mv.ptr.p_double[i] = state.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*state.x.ptr.p_double[j];
                    }
                    state.vmv = state.vmv+state.mv.ptr.p_double[i]*state.x.ptr.p_double[i];
                }
            }
            if( state.needprec )
            {
                for(i=0; i<=sz-1; i++)
                {
                    state.pv.ptr.p_double[i] = state.x.ptr.p_double[i];
                }
            }
            if( state.xupdated )
            {
                
                /*
                 * Save current point to MtX, it will be used later for additional tests
                 */
                if( numofit>=mtx.rows )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=sz-1; i++)
                {
                    mtx.ptr.pp_double[numofit][i] = state.x.ptr.p_double[i];
                }
                getrnorm = state.r2;
                numofit = numofit+1;
            }
        }
        lincgresults(&state, &x0, &rep, _state);
        if( ae_fp_neq(getrnorm,rep.r2) )
        {
            if( !silent )
            {
                printf("IterationsCount=%0d;\nNMV=%0d;\nTerminationType=%0d;\n",
                    (int)(rep.iterationscount),
                    (int)(rep.nmv),
                    (int)(rep.terminationtype));
                printf("Size=%0d;\nCond=%0.5f;\nComplexTest::Fail::GetRNorm<>Rep.R2!(%0.2e<>%0.2e)\n",
                    (int)(sz),
                    (double)(c),
                    (double)(getrnorm),
                    (double)(rep.r2));
            }
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Calculate residual, check result
         */
        ae_vector_set_length(&residual, sz, _state);
        for(i=0; i<=sz-1; i++)
        {
            tmp = (double)(0);
            for(j=0; j<=sz-1; j++)
            {
                tmp = tmp+a.ptr.pp_double[i][j]*x0.ptr.p_double[j];
            }
            residual.ptr.p_double[i] = b.ptr.p_double[i]-tmp;
        }
        normofresidual = (double)(0);
        for(i=0; i<=sz-1; i++)
        {
            if( ae_fp_greater(ae_fabs(residual.ptr.p_double[i], _state),testlincgunit_e0) )
            {
                if( !silent )
                {
                    printf("IterationsCount=%0d;\nNMV=%0d;\nTerminationType=%0d;\n",
                        (int)(rep.iterationscount),
                        (int)(rep.nmv),
                        (int)(rep.terminationtype));
                    printf("Size=%0d;\nCond=%0.5f;\nComplexTest::Fail::Discripancy[%0d]>E0!(%0.2e>%0.2e)\n",
                        (int)(sz),
                        (double)(c),
                        (int)(i),
                        (double)(residual.ptr.p_double[i]),
                        (double)(testlincgunit_e0));
                }
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
            normofresidual = normofresidual+residual.ptr.p_double[i]*residual.ptr.p_double[i];
        }
        if( ae_fp_greater(ae_fabs(normofresidual-rep.r2, _state),testlincgunit_e0) )
        {
            if( !silent )
            {
                printf("IterationsCount=%0d;\nNMV=%0d;\nTerminationType=%0d;\n",
                    (int)(rep.iterationscount),
                    (int)(rep.nmv),
                    (int)(rep.terminationtype));
                printf("Size=%0d;\nCond=%0.5f;\nComplexTest::Fail::||NormOfResidual-Rep.R2||>E0!(%0.2e>%0.2e)\n",
                    (int)(sz),
                    (double)(c),
                    (double)(ae_fabs(normofresidual-rep.r2, _state)),
                    (double)(testlincgunit_e0));
                printf("NormOfResidual=%0.2e; Rep.R2=%0.2e\n",
                    (double)(normofresidual),
                    (double)(rep.r2));
            }
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Check algorithm properties (conjugacy/orthogonality).
         * Here we use MtX which was filled during algorithm progress towards solution.
         *
         * NOTE: this test is skipped when algorithm converged in less than SZ iterations.
         */
        if( sz>1&&rep.iterationscount==sz )
        {
            ae_matrix_set_length(&mtp, sz, sz, _state);
            ae_matrix_set_length(&mtr, sz, sz, _state);
            for(i=0; i<=sz-1; i++)
            {
                for(j=0; j<=sz-1; j++)
                {
                    mtp.ptr.pp_double[i][j] = mtx.ptr.pp_double[i+1][j]-mtx.ptr.pp_double[i][j];
                    tmp = (double)(0);
                    for(k=0; k<=sz-1; k++)
                    {
                        tmp = tmp+a.ptr.pp_double[j][k]*mtx.ptr.pp_double[i][k];
                    }
                    mtr.ptr.pp_double[i][j] = b.ptr.p_double[j]-tmp;
                }
            }
            
            /*
             *(Api,pj)=0?
             */
            sclr = (double)(0);
            nv0 = (double)(0);
            nv1 = (double)(0);
            for(i=0; i<=sz-1; i++)
            {
                for(j=0; j<=sz-1; j++)
                {
                    if( i==j )
                    {
                        continue;
                    }
                    for(k=0; k<=sz-1; k++)
                    {
                        tmp = (double)(0);
                        for(l=0; l<=sz-1; l++)
                        {
                            tmp = tmp+a.ptr.pp_double[k][l]*mtp.ptr.pp_double[i][l];
                        }
                        sclr = sclr+tmp*mtp.ptr.pp_double[j][k];
                        nv0 = nv0+mtp.ptr.pp_double[i][k]*mtp.ptr.pp_double[i][k];
                        nv1 = nv1+mtp.ptr.pp_double[j][k]*mtp.ptr.pp_double[j][k];
                    }
                    nv0 = ae_sqrt(nv0, _state);
                    nv1 = ae_sqrt(nv1, _state);
                    if( ae_fp_greater(ae_fabs(sclr, _state),testlincgunit_e0*na*nv0*nv1) )
                    {
                        if( !silent )
                        {
                            printf("IterationsCount=%0d;\nNMV=%0d;\nTerminationType=%0d;\n",
                                (int)(rep.iterationscount),
                                (int)(rep.nmv),
                                (int)(rep.terminationtype));
                            printf("Size=%0d;\nCond=%0.5f;\nComplexTest::Fail::(Ap%0d,p%0d)!=0\n{Sclr=%0.15f; NA=%0.15f NV0=%0.15f NV1=%0.15f;}\n",
                                (int)(sz),
                                (double)(c),
                                (int)(i),
                                (int)(j),
                                (double)(sclr),
                                (double)(na),
                                (double)(nv0),
                                (double)(nv1));
                        }
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
            
            /*
             *(ri,pj)=0?
             */
            for(i=1; i<=sz-1; i++)
            {
                for(j=0; j<=i-1; j++)
                {
                    sclr = (double)(0);
                    nv0 = (double)(0);
                    nv1 = (double)(0);
                    for(k=0; k<=sz-1; k++)
                    {
                        sclr = sclr+mtr.ptr.pp_double[i][k]*mtp.ptr.pp_double[j][k];
                        nv0 = nv0+mtr.ptr.pp_double[i][k]*mtr.ptr.pp_double[i][k];
                        nv1 = nv1+mtp.ptr.pp_double[j][k]*mtp.ptr.pp_double[j][k];
                    }
                    nv0 = ae_sqrt(nv0, _state);
                    nv1 = ae_sqrt(nv1, _state);
                    if( ae_fp_greater(ae_fabs(sclr, _state),testlincgunit_e0*nv0*nv1) )
                    {
                        if( !silent )
                        {
                            printf("IterationsCount=%0d;\nNMV=%0d;\nTerminationType=%0d;\n",
                                (int)(rep.iterationscount),
                                (int)(rep.nmv),
                                (int)(rep.terminationtype));
                            printf("Size=%0d;\nCond=%0.5f;\nComplexTest::Fail::(r%0d,p%0d)!=0\n{Sclr=%0.15f; NV0=%0.15f NV1=%0.15f;}\n",
                                (int)(sz),
                                (double)(c),
                                (int)(i),
                                (int)(j),
                                (double)(sclr),
                                (double)(nv0),
                                (double)(nv1));
                        }
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
            
            /*
             *(ri,rj)=0?
             */
            for(i=0; i<=sz-1; i++)
            {
                for(j=i+1; j<=sz-1; j++)
                {
                    sclr = (double)(0);
                    nv0 = (double)(0);
                    nv1 = (double)(0);
                    for(k=0; k<=sz-1; k++)
                    {
                        sclr = sclr+mtr.ptr.pp_double[i][k]*mtr.ptr.pp_double[j][k];
                        nv0 = nv0+mtr.ptr.pp_double[i][k]*mtr.ptr.pp_double[i][k];
                        nv1 = nv1+mtr.ptr.pp_double[j][k]*mtr.ptr.pp_double[j][k];
                    }
                    nv0 = ae_sqrt(nv0, _state);
                    nv1 = ae_sqrt(nv1, _state);
                    if( ae_fp_greater(ae_fabs(sclr, _state),testlincgunit_e0*nv0*nv1) )
                    {
                        if( !silent )
                        {
                            printf("IterationsCount=%0d;\nNMV=%0d;\nTerminationType=%0d;\n",
                                (int)(rep.iterationscount),
                                (int)(rep.nmv),
                                (int)(rep.terminationtype));
                            printf("Size=%0d;\nCond=%0.5f;\nComplexTest::Fail::(rm,rk)!=0\n{Sclr=%0.15f; NV0=%0.15f NV1=%0.15f;}\n",
                                (int)(sz),
                                (double)(c),
                                (double)(sclr),
                                (double)(nv0),
                                (double)(nv1));
                        }
                        result = ae_true;
                        ae_frame_leave(_state);
                        return result;
                    }
                }
            }
        }
    }
    if( !silent )
    {
        printf("ComplexTest::Ok\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function prepare problem with a known solution 'Xs'(A*Xs-b=0). There
b is A*Xs. After, function check algorithm result and 'Xs'.
There used two stopping criterions:
    1. achieved the required precision(StCrit=0);
    2. execution of the required number of iterations(StCrit=1).

  -- ALGLIB --
     Copyright 14.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlincgunit_complexres(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    lincgstate s;
    lincgreport rep;
    ae_matrix a;
    ae_vector b;
    ae_vector xs;
    ae_vector x0;
    double err;
    ae_int_t n;
    ae_int_t sz;
    double c;
    ae_int_t i;
    ae_int_t j;
    ae_int_t stcrit;
    double mx;
    double tmp;
    double eps;
    ae_int_t xp;
    ae_int_t nxp;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&xs, 0, sizeof(xs));
    memset(&x0, 0, sizeof(x0));
    _lincgstate_init(&s, _state, ae_true);
    _lincgreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);

    sz = 5;
    mx = (double)(100);
    nxp = 100;
    for(xp=0; xp<=nxp-1; xp++)
    {
        for(n=1; n<=sz; n++)
        {
            for(stcrit=0; stcrit<=1; stcrit++)
            {
                
                /*
                 * Generate:
                 * * random A with norm NA (equal to 1.0),
                 * * random solution XS whose elements are uniformly distributed in [-MX,+MX]
                 * * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
                 * * B = A*Xs
                 */
                c = (testlincgunit_maxcond-1)*ae_randomreal(_state)+1;
                spdmatrixrndcond(n, c, &a, _state);
                ae_vector_set_length(&b, n, _state);
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&xs, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    x0.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
                    xs.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
                }
                eps = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    b.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        b.ptr.p_double[i] = b.ptr.p_double[i]+a.ptr.pp_double[i][j]*xs.ptr.p_double[j];
                    }
                    eps = eps+b.ptr.p_double[i]*b.ptr.p_double[i];
                }
                eps = 1.0E-6*ae_sqrt(eps, _state);
                
                /*
                 * Solve with different stopping criteria
                 */
                lincgcreate(n, &s, _state);
                lincgsetb(&s, &b, _state);
                lincgsetstartingpoint(&s, &x0, _state);
                lincgsetxrep(&s, ae_true, _state);
                if( stcrit==0 )
                {
                    lincgsetcond(&s, 1.0E-6, 0, _state);
                }
                else
                {
                    lincgsetcond(&s, (double)(0), n, _state);
                }
                while(lincgiteration(&s, _state))
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv.ptr.p_double[i] = (double)(0);
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                            }
                        }
                    }
                    if( s.needvmv )
                    {
                        s.vmv = (double)(0);
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv.ptr.p_double[i] = (double)(0);
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                            }
                            s.vmv = s.vmv+s.mv.ptr.p_double[i]*s.x.ptr.p_double[i];
                        }
                    }
                    if( s.needprec )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.pv.ptr.p_double[i] = s.x.ptr.p_double[i];
                        }
                    }
                }
                lincgresults(&s, &x0, &rep, _state);
                
                /*
                 * Check result
                 */
                err = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    tmp = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        tmp = tmp+a.ptr.pp_double[i][j]*x0.ptr.p_double[j];
                    }
                    err = err+ae_sqr(b.ptr.p_double[i]-tmp, _state);
                }
                err = ae_sqrt(err, _state);
                if( ae_fp_greater(err,eps) )
                {
                    if( !silent )
                    {
                        printf("ComplexRes::fail\n");
                        printf("IterationsCount=%0d\n",
                            (int)(rep.iterationscount));
                        printf("NMV=%0d\n",
                            (int)(rep.nmv));
                        printf("TerminationType=%0d\n",
                            (int)(rep.terminationtype));
                        printf("X and Xs...\n");
                        for(j=0; j<=n-1; j++)
                        {
                            printf("x[%0d]=%0.10f; xs[%0d]=%0.10f\n",
                                (int)(j),
                                (double)(x0.ptr.p_double[j]),
                                (int)(j),
                                (double)(xs.ptr.p_double[j]));
                        }
                    }
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    
    /*
     *test has been passed
     */
    if( !silent )
    {
        printf("ComplexRes::Ok\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function check, that XUpdated return State.X=X0 at zero iteration and
State.X=X(algorithm result) at last.

  -- ALGLIB --
     Copyright 14.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlincgunit_basictestx(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    lincgstate s;
    lincgreport rep;
    ae_matrix a;
    ae_vector b;
    ae_vector x0;
    ae_vector x00;
    ae_vector x01;
    ae_int_t n;
    ae_int_t sz;
    double c;
    ae_int_t i;
    ae_int_t j;
    double mx;
    ae_int_t iters;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    memset(&x00, 0, sizeof(x00));
    memset(&x01, 0, sizeof(x01));
    _lincgstate_init(&s, _state, ae_true);
    _lincgreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x00, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x01, 0, DT_REAL, _state, ae_true);

    sz = 5;
    mx = (double)(100);
    for(n=1; n<=sz; n++)
    {
        
        /*
         * Generate:
         * * random A with norm NA (equal to 1.0),
         * * random right part B whose elements are uniformly distributed in [-MX,+MX]
         * * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
         */
        c = (testlincgunit_maxcond-1)*ae_randomreal(_state)+1;
        spdmatrixrndcond(n, c, &a, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&x00, n, _state);
        ae_vector_set_length(&x01, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
            b.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
        }
        
        /*
         * Solve, save first and last reported points to x00 and x01
         */
        lincgcreate(n, &s, _state);
        lincgsetb(&s, &b, _state);
        lincgsetstartingpoint(&s, &x0, _state);
        lincgsetxrep(&s, ae_true, _state);
        lincgsetcond(&s, (double)(0), n, _state);
        iters = 0;
        while(lincgiteration(&s, _state))
        {
            if( s.needmv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                }
            }
            if( s.needvmv )
            {
                s.vmv = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                    s.vmv = s.vmv+s.mv.ptr.p_double[i]*s.x.ptr.p_double[i];
                }
            }
            if( s.needprec )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.pv.ptr.p_double[i] = s.x.ptr.p_double[i];
                }
            }
            if( s.xupdated )
            {
                if( iters==0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x00.ptr.p_double[i] = s.x.ptr.p_double[i];
                    }
                }
                if( iters==n )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x01.ptr.p_double[i] = s.x.ptr.p_double[i];
                    }
                }
                iters = iters+1;
            }
        }
        
        /*
         * Check first and last points
         */
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_neq(x00.ptr.p_double[i],x0.ptr.p_double[i]) )
            {
                if( !silent )
                {
                    printf("BasicTestX::fail\n");
                    printf("IterationsCount=%0d\n",
                        (int)(rep.iterationscount));
                    printf("NMV=%0d\n",
                        (int)(rep.nmv));
                    printf("TerminationType=%0d\n",
                        (int)(rep.terminationtype));
                    for(j=0; j<=n-1; j++)
                    {
                        printf("x0=%0.5f; x00=%0.5f;\n",
                            (double)(x0.ptr.p_double[j]),
                            (double)(x00.ptr.p_double[j]));
                    }
                }
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
        lincgresults(&s, &x0, &rep, _state);
        for(i=0; i<=n-1; i++)
        {
            if( ae_fp_neq(x01.ptr.p_double[i],x0.ptr.p_double[i]) )
            {
                if( !silent )
                {
                    printf("BasicTestX::fail\n");
                    printf("IterationsCount=%0d\n",
                        (int)(rep.iterationscount));
                    printf("NMV=%0d\n",
                        (int)(rep.nmv));
                    printf("TerminationType=%0d\n",
                        (int)(rep.terminationtype));
                    for(j=0; j<=n-1; j++)
                    {
                        printf("x0=%0.5f; x01=%0.5f;\n",
                            (double)(x0.ptr.p_double[j]),
                            (double)(x01.ptr.p_double[j]));
                    }
                }
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    
    /*
     *test has been passed
     */
    if( !silent )
    {
        printf("BasicTestIters::Ok\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function checks that XUpdated returns correct State.R2. It creates
large badly conditioned problem (N=50), which should be large enough and
ill-conditioned enough to cause periodic recalculation of R.

  -- ALGLIB --
     Copyright 14.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlincgunit_testrcorrectness(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    lincgstate s;
    lincgreport rep;
    ae_matrix a;
    ae_vector b;
    ae_int_t n;
    double c;
    ae_int_t i;
    ae_int_t j;
    double r2;
    double v;
    double rtol;
    ae_int_t maxits;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    _lincgstate_init(&s, _state, ae_true);
    _lincgreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);

    rtol = 1.0E6*ae_machineepsilon;
    n = 50;
    maxits = n/2;
    c = (double)(10000);
    spdmatrixrndcond(n, c, &a, _state);
    ae_vector_set_length(&b, n, _state);
    for(i=0; i<=n-1; i++)
    {
        b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
    }
    lincgcreate(n, &s, _state);
    lincgsetb(&s, &b, _state);
    lincgsetxrep(&s, ae_true, _state);
    lincgsetcond(&s, (double)(0), maxits, _state);
    while(lincgiteration(&s, _state))
    {
        if( s.needmv )
        {
            for(i=0; i<=n-1; i++)
            {
                s.mv.ptr.p_double[i] = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                }
            }
        }
        if( s.needvmv )
        {
            s.vmv = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                s.mv.ptr.p_double[i] = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                }
                s.vmv = s.vmv+s.mv.ptr.p_double[i]*s.x.ptr.p_double[i];
            }
        }
        if( s.needprec )
        {
            for(i=0; i<=n-1; i++)
            {
                s.pv.ptr.p_double[i] = s.x.ptr.p_double[i];
            }
        }
        if( s.xupdated )
        {
            
            /*
             * calculate R2, compare with value returned in state.R2
             */
            r2 = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                v = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    v = v+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                }
                r2 = r2+ae_sqr(v-b.ptr.p_double[i], _state);
            }
            if( ae_fp_greater(ae_fabs(r2-s.r2, _state),rtol) )
            {
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    lincgresults(&s, &b, &rep, _state);
    if( rep.iterationscount!=maxits )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function check, that number of iterations are't more than MaxIts.

  -- ALGLIB --
     Copyright 14.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlincgunit_basictestiters(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    lincgstate s;
    lincgreport rep;
    ae_matrix a;
    ae_vector b;
    ae_vector x0;
    ae_int_t n;
    ae_int_t sz;
    double c;
    ae_int_t i;
    ae_int_t j;
    double mx;
    ae_int_t iters;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    _lincgstate_init(&s, _state, ae_true);
    _lincgreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);

    sz = 5;
    mx = (double)(100);
    for(n=1; n<=sz; n++)
    {
        
        /*
         * Generate:
         * * random A with norm NA (equal to 1.0),
         * * random right part B whose elements are uniformly distributed in [-MX,+MX]
         * * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
         */
        c = (testlincgunit_maxcond-1)*ae_randomreal(_state)+1;
        spdmatrixrndcond(n, c, &a, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
            b.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
        }
        
        /*
         * Solve
         */
        lincgcreate(n, &s, _state);
        lincgsetb(&s, &b, _state);
        lincgsetstartingpoint(&s, &x0, _state);
        lincgsetxrep(&s, ae_true, _state);
        lincgsetcond(&s, (double)(0), n, _state);
        iters = 0;
        while(lincgiteration(&s, _state))
        {
            if( s.needmv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                }
            }
            if( s.needvmv )
            {
                s.vmv = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                    s.vmv = s.vmv+s.mv.ptr.p_double[i]*s.x.ptr.p_double[i];
                }
            }
            if( s.needprec )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.pv.ptr.p_double[i] = s.x.ptr.p_double[i];
                }
            }
            if( s.xupdated )
            {
                iters = iters+1;
            }
        }
        lincgresults(&s, &x0, &rep, _state);
        
        /*
         * Check
         */
        if( iters!=rep.iterationscount+1||iters>n+1 )
        {
            if( !silent )
            {
                printf("BasicTestIters::fail\n");
                printf("IterationsCount=%0d\n",
                    (int)(rep.iterationscount));
                printf("NMV=%0d\n",
                    (int)(rep.nmv));
                printf("TerminationType=%0d\n",
                    (int)(rep.terminationtype));
                printf("Iters=%0d\n",
                    (int)(iters));
            }
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Restart problem
         */
        c = (testlincgunit_maxcond-1)*ae_randomreal(_state)+1;
        spdmatrixrndcond(n, c, &a, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
            b.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
        }
        lincgsetstartingpoint(&s, &x0, _state);
        lincgrestart(&s, _state);
        lincgsetb(&s, &b, _state);
        iters = 0;
        while(lincgiteration(&s, _state))
        {
            if( s.needmv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                }
            }
            if( s.needvmv )
            {
                s.vmv = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                    s.vmv = s.vmv+s.mv.ptr.p_double[i]*s.x.ptr.p_double[i];
                }
            }
            if( s.needprec )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.pv.ptr.p_double[i] = s.x.ptr.p_double[i];
                }
            }
            if( s.xupdated )
            {
                iters = iters+1;
            }
        }
        lincgresults(&s, &x0, &rep, _state);
        
        /*
         *check
         */
        if( iters!=rep.iterationscount+1||iters>n+1 )
        {
            if( !silent )
            {
                printf("BasicTestIters::fail\n");
                printf("IterationsCount=%0d\n",
                    (int)(rep.iterationscount));
                printf("NMV=%0d\n",
                    (int)(rep.nmv));
                printf("TerminationType=%0d\n",
                    (int)(rep.terminationtype));
                printf("Iters=%0d\n",
                    (int)(iters));
            }
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     *test has been passed
     */
    if( !silent )
    {
        printf("BasicTestIters::Ok\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
This function check, that programmed method is Krylov subspace methed.

  -- ALGLIB --
     Copyright 14.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlincgunit_krylovsubspacetest(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    lincgstate s;
    lincgreport rep;
    ae_matrix a;
    ae_vector b;
    ae_vector x0;
    ae_matrix ksr;
    ae_vector r0;
    ae_vector tarray;
    ae_matrix mtx;
    ae_int_t n;
    ae_int_t sz;
    double c;
    ae_int_t i;
    ae_int_t j;
    ae_int_t l;
    ae_int_t m;
    double mx;
    double tmp;
    double normr0;
    ae_int_t numofit;
    ae_int_t maxits;
    ae_int_t k2;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&x0, 0, sizeof(x0));
    memset(&ksr, 0, sizeof(ksr));
    memset(&r0, 0, sizeof(r0));
    memset(&tarray, 0, sizeof(tarray));
    memset(&mtx, 0, sizeof(mtx));
    _lincgstate_init(&s, _state, ae_true);
    _lincgreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ksr, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tarray, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mtx, 0, 0, DT_REAL, _state, ae_true);

    eps = 1.0E-6;
    maxits = 3;
    sz = 5;
    mx = (double)(100);
    for(n=1; n<=sz; n++)
    {
        
        /*
         * Generate:
         * * random A with unit norm
         * * cond(A) in [0.5*MaxCond, 1.0*MaxCond]
         * * random x0 and b such that |A*x0-b| is large enough for algorithm to make at least one iteration.
         *
         * IMPORTANT: it is very important to have cond(A) both (1) not very large and
         *            (2) not very small. Large cond(A) will make our problem ill-conditioned,
         *            thus analytic properties won't hold. Small cond(A), from the other side,
         *            will give us rapid convergence of the algorithm - in fact, too rapid.
         *            Krylov basis will be dominated by numerical noise and test may fail.
         */
        c = testlincgunit_maxcond*(0.5*ae_randomreal(_state)+0.5);
        spdmatrixrndcond(n, c, &a, _state);
        ae_matrix_set_length(&mtx, n+1, n, _state);
        ae_matrix_set_length(&ksr, n, n, _state);
        ae_vector_set_length(&r0, n, _state);
        ae_vector_set_length(&tarray, n, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&x0, n, _state);
        do
        {
            for(i=0; i<=n-1; i++)
            {
                x0.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
                b.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
            }
            normr0 = (double)(0);
            for(i=0; i<=n-1; i++)
            {
                tmp = (double)(0);
                for(j=0; j<=n-1; j++)
                {
                    tmp = tmp+a.ptr.pp_double[i][j]*x0.ptr.p_double[j];
                }
                r0.ptr.p_double[i] = b.ptr.p_double[i]-tmp;
                normr0 = normr0+r0.ptr.p_double[i]*r0.ptr.p_double[i];
            }
        }
        while(ae_fp_less_eq(ae_sqrt(normr0, _state),eps));
        
        /*
         * Fill KSR by {r0, A*r0, A^2*r0, ... }
         */
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ksr.ptr.pp_double[i][j] = r0.ptr.p_double[j];
            }
            for(j=0; j<=i-1; j++)
            {
                for(l=0; l<=n-1; l++)
                {
                    tarray.ptr.p_double[l] = (double)(0);
                    for(m=0; m<=n-1; m++)
                    {
                        tarray.ptr.p_double[l] = tarray.ptr.p_double[l]+a.ptr.pp_double[l][m]*ksr.ptr.pp_double[i][m];
                    }
                }
                for(l=0; l<=n-1; l++)
                {
                    ksr.ptr.pp_double[i][l] = tarray.ptr.p_double[l];
                }
            }
        }
        
        /*
         * Solve system, record intermediate points for futher analysis.
         * NOTE: we set update frequency of R to 2 in order to test that R is updated correctly
         */
        lincgcreate(n, &s, _state);
        lincgsetb(&s, &b, _state);
        lincgsetstartingpoint(&s, &x0, _state);
        lincgsetxrep(&s, ae_true, _state);
        lincgsetcond(&s, (double)(0), n, _state);
        lincgsetrupdatefreq(&s, 2, _state);
        numofit = 0;
        while(lincgiteration(&s, _state))
        {
            if( s.needmv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                }
            }
            if( s.needvmv )
            {
                s.vmv = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                    s.vmv = s.vmv+s.mv.ptr.p_double[i]*s.x.ptr.p_double[i];
                }
            }
            if( s.needprec )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.pv.ptr.p_double[i] = s.x.ptr.p_double[i];
                }
            }
            if( s.xupdated )
            {
                for(i=0; i<=n-1; i++)
                {
                    mtx.ptr.pp_double[numofit][i] = s.x.ptr.p_double[i];
                }
                numofit = numofit+1;
            }
        }
        
        /*
         * Check that I-th step S_i=X[I+1]-X[i] belongs to I-th Krylov subspace.
         * Checks are done for first K2 steps, with K2 small enough to avoid
         * numerical errors.
         */
        if( n<=maxits )
        {
            k2 = n;
        }
        else
        {
            k2 = maxits;
        }
        for(i=0; i<=k2-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                tarray.ptr.p_double[j] = mtx.ptr.pp_double[i+1][j]-mtx.ptr.pp_double[i][j];
            }
            if( !testlincgunit_frombasis(&tarray, &ksr, n, i+1, testlincgunit_e0, _state) )
            {
                if( !silent )
                {
                    printf("KrylovSubspaceTest::FAIL\n");
                    printf("Size=%0d; Iters=%0d;\n",
                        (int)(n),
                        (int)(i));
                }
                result = ae_true;
                ae_frame_leave(_state);
                return result;
            }
        }
    }
    if( !silent )
    {
        printf("KrylovSubspaceTest::OK\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing  LinCgSolveSparse. This function  prepare problem with
a known solution 'Xs'(A*Xs-b=0). There b is A*Xs. After, function calculate
result by LinCGSolveSparse and compares it with 'Xs'.

  -- ALGLIB --
     Copyright 14.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlincgunit_sparsetest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    lincgstate s;
    lincgreport rep;
    ae_matrix a;
    ae_vector b;
    ae_vector xs;
    ae_vector x0;
    ae_vector x1;
    sparsematrix uppera;
    sparsematrix lowera;
    double err;
    ae_int_t n;
    ae_int_t sz;
    double c;
    ae_int_t i;
    ae_int_t j;
    double mx;
    double eps;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&b, 0, sizeof(b));
    memset(&xs, 0, sizeof(xs));
    memset(&x0, 0, sizeof(x0));
    memset(&x1, 0, sizeof(x1));
    memset(&uppera, 0, sizeof(uppera));
    memset(&lowera, 0, sizeof(lowera));
    _lincgstate_init(&s, _state, ae_true);
    _lincgreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&uppera, _state, ae_true);
    _sparsematrix_init(&lowera, _state, ae_true);

    sz = 5;
    mx = (double)(100);
    for(n=1; n<=sz; n++)
    {
        
        /*
         * Generate:
         * * random A with unit norm
         * * random X0 (starting point) and XS (known solution)
         * Copy dense A to sparse SA
         */
        c = (testlincgunit_maxcond-1)*ae_randomreal(_state)+1;
        spdmatrixrndcond(n, c, &a, _state);
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&xs, n, _state);
        for(i=0; i<=n-1; i++)
        {
            xs.ptr.p_double[i] = mx*(2*ae_randomreal(_state)-1);
        }
        eps = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            b.ptr.p_double[i] = (double)(0);
            for(j=0; j<=n-1; j++)
            {
                b.ptr.p_double[i] = b.ptr.p_double[i]+a.ptr.pp_double[i][j]*xs.ptr.p_double[j];
            }
            eps = eps+b.ptr.p_double[i]*b.ptr.p_double[i];
        }
        eps = 1.0E-6*ae_sqrt(eps, _state);
        sparsecreate(n, n, 0, &uppera, _state);
        sparsecreate(n, n, 0, &lowera, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( j>=i )
                {
                    sparseset(&uppera, i, j, a.ptr.pp_double[i][j], _state);
                }
                if( j<=i )
                {
                    sparseset(&lowera, i, j, a.ptr.pp_double[i][j], _state);
                }
            }
        }
        sparseconverttocrs(&uppera, _state);
        sparseconverttocrs(&lowera, _state);
        
        /*
         * Test upper triangle
         */
        lincgcreate(n, &s, _state);
        lincgsetcond(&s, (double)(0), n, _state);
        lincgsolvesparse(&s, &uppera, ae_true, &b, _state);
        lincgresults(&s, &x0, &rep, _state);
        err = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            err = err+ae_sqr(x0.ptr.p_double[i]-xs.ptr.p_double[i], _state);
        }
        err = ae_sqrt(err, _state);
        if( ae_fp_greater(err,eps) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
        
        /*
         * Test lower triangle
         */
        lincgcreate(n, &s, _state);
        lincgsetcond(&s, (double)(0), n, _state);
        lincgsolvesparse(&s, &lowera, ae_false, &b, _state);
        lincgresults(&s, &x1, &rep, _state);
        err = (double)(0);
        for(i=0; i<=n-1; i++)
        {
            err = err+ae_sqr(x1.ptr.p_double[i]-xs.ptr.p_double[i], _state);
        }
        err = ae_sqrt(err, _state);
        if( ae_fp_greater(err,eps) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Function for testing  the preconditioned conjugate gradient method.

  -- ALGLIB --
     Copyright 14.11.2011 by Bochkanov Sergey
*************************************************************************/
static ae_bool testlincgunit_precondtest(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    lincgstate s;
    lincgreport rep;
    ae_matrix a;
    ae_matrix ta;
    sparsematrix sa;
    ae_vector m;
    ae_matrix mtx;
    ae_matrix mtprex;
    ae_vector de;
    ae_vector rde;
    ae_vector b;
    ae_vector tb;
    ae_vector d;
    ae_vector xe;
    ae_vector x0;
    ae_vector tx0;
    ae_vector err;
    ae_int_t n;
    ae_int_t sz;
    ae_int_t numofit;
    double c;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double eps;
    ae_bool bflag;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&s, 0, sizeof(s));
    memset(&rep, 0, sizeof(rep));
    memset(&a, 0, sizeof(a));
    memset(&ta, 0, sizeof(ta));
    memset(&sa, 0, sizeof(sa));
    memset(&m, 0, sizeof(m));
    memset(&mtx, 0, sizeof(mtx));
    memset(&mtprex, 0, sizeof(mtprex));
    memset(&de, 0, sizeof(de));
    memset(&rde, 0, sizeof(rde));
    memset(&b, 0, sizeof(b));
    memset(&tb, 0, sizeof(tb));
    memset(&d, 0, sizeof(d));
    memset(&xe, 0, sizeof(xe));
    memset(&x0, 0, sizeof(x0));
    memset(&tx0, 0, sizeof(tx0));
    memset(&err, 0, sizeof(err));
    _lincgstate_init(&s, _state, ae_true);
    _lincgreport_init(&rep, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ta, 0, 0, DT_REAL, _state, ae_true);
    _sparsematrix_init(&sa, _state, ae_true);
    ae_vector_init(&m, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mtx, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mtprex, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&de, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rde, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&err, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test 1.
     * 
     * Preconditioned CG for A*x=b with preconditioner M=E*E' is algebraically 
     * equivalent to non-preconditioned CG for (inv(E)*A*inv(E'))*z = inv(E)*b
     * with z=E'*x.
     * 
     * We test it by generating random preconditioner, running algorithm twice -
     * one  time  for  original  problem  with  preconditioner , another one  for
     * modified problem without preconditioner.
     */
    sz = 5;
    for(n=1; n<=sz; n++)
    {
        
        /*
         * Generate:
         * * random A with unit norm
         * * random positive definite diagonal preconditioner M
         * * dE=sqrt(M)
         * * rdE=dE^(-1)
         * * tA = rdE*A*rdE
         * * random x0 and b - for original preconditioned problem
         * * tx0 and tb - for modified problem
         */
        c = (testlincgunit_maxcond-1)*ae_randomreal(_state)+1;
        spdmatrixrndcond(n, c, &a, _state);
        ae_matrix_set_length(&ta, n, n, _state);
        ae_matrix_set_length(&mtx, n+1, n, _state);
        ae_matrix_set_length(&mtprex, n+1, n, _state);
        ae_vector_set_length(&m, n, _state);
        ae_vector_set_length(&de, n, _state);
        ae_vector_set_length(&rde, n, _state);
        for(i=0; i<=n-1; i++)
        {
            m.ptr.p_double[i] = ae_randomreal(_state)+0.5;
            de.ptr.p_double[i] = ae_sqrt(m.ptr.p_double[i], _state);
            rde.ptr.p_double[i] = 1/de.ptr.p_double[i];
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ta.ptr.pp_double[i][j] = rde.ptr.p_double[i]*a.ptr.pp_double[i][j]*rde.ptr.p_double[j];
            }
        }
        ae_vector_set_length(&b, n, _state);
        ae_vector_set_length(&tb, n, _state);
        ae_vector_set_length(&x0, n, _state);
        ae_vector_set_length(&tx0, n, _state);
        ae_vector_set_length(&err, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        eps = 1.0E-5;
        for(i=0; i<=n-1; i++)
        {
            tx0.ptr.p_double[i] = de.ptr.p_double[i]*x0.ptr.p_double[i];
            tb.ptr.p_double[i] = rde.ptr.p_double[i]*b.ptr.p_double[i];
        }
        
        /*
         * Solve two problems, intermediate points are saved to MtX and MtPreX
         */
        lincgcreate(n, &s, _state);
        lincgsetb(&s, &b, _state);
        lincgsetstartingpoint(&s, &x0, _state);
        lincgsetxrep(&s, ae_true, _state);
        lincgsetcond(&s, (double)(0), n, _state);
        numofit = 0;
        while(lincgiteration(&s, _state))
        {
            if( s.needmv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                }
            }
            if( s.needvmv )
            {
                s.vmv = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+a.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                    s.vmv = s.vmv+s.mv.ptr.p_double[i]*s.x.ptr.p_double[i];
                }
            }
            if( s.needprec )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.pv.ptr.p_double[i] = s.x.ptr.p_double[i]/m.ptr.p_double[i];
                }
            }
            if( s.xupdated )
            {
                if( numofit>=mtx.rows )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    mtx.ptr.pp_double[numofit][i] = s.x.ptr.p_double[i];
                }
                numofit = numofit+1;
            }
        }
        lincgsetstartingpoint(&s, &tx0, _state);
        lincgsetb(&s, &tb, _state);
        lincgrestart(&s, _state);
        numofit = 0;
        while(lincgiteration(&s, _state))
        {
            if( s.needmv )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+ta.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                }
            }
            if( s.needvmv )
            {
                s.vmv = (double)(0);
                for(i=0; i<=n-1; i++)
                {
                    s.mv.ptr.p_double[i] = (double)(0);
                    for(j=0; j<=n-1; j++)
                    {
                        s.mv.ptr.p_double[i] = s.mv.ptr.p_double[i]+ta.ptr.pp_double[i][j]*s.x.ptr.p_double[j];
                    }
                    s.vmv = s.vmv+s.mv.ptr.p_double[i]*s.x.ptr.p_double[i];
                }
            }
            if( s.needprec )
            {
                for(i=0; i<=n-1; i++)
                {
                    s.pv.ptr.p_double[i] = s.x.ptr.p_double[i];
                }
            }
            if( s.xupdated )
            {
                if( numofit>=mtprex.rows )
                {
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    mtprex.ptr.pp_double[numofit][i] = s.x.ptr.p_double[i];
                }
                numofit = numofit+1;
            }
        }
        
        /*
         * Compare results - sequence of points generated when solving original problem with
         * points generated by modified problem.
         */
        for(i=0; i<=numofit-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( ae_fp_greater(ae_fabs(mtx.ptr.pp_double[i][j]-rde.ptr.p_double[j]*mtprex.ptr.pp_double[i][j], _state),eps) )
                {
                    if( !silent )
                    {
                        printf("PrecondTest::fail\n");
                        printf("Size=%0d\n",
                            (int)(n));
                        printf("IterationsCount=%0d\n",
                            (int)(rep.iterationscount));
                        printf("NMV=%0d\n",
                            (int)(rep.nmv));
                        printf("TerminationType=%0d\n",
                            (int)(rep.terminationtype));
                        printf("X and X^...\n");
                        for(k=0; k<=n-1; k++)
                        {
                            printf("I=%0d; mtx[%0d]=%0.10f; mtx^[%0d]=%0.10f\n",
                                (int)(i),
                                (int)(k),
                                (double)(mtx.ptr.pp_double[i][k]),
                                (int)(k),
                                (double)(mtprex.ptr.pp_double[i][k]));
                        }
                    }
                    result = ae_true;
                    ae_frame_leave(_state);
                    return result;
                }
            }
        }
    }
    
    /*
     * Test 2.
     *
     * We test automatic diagonal preconditioning used by SolveSparse.
     * In order to do so we:
     * 1. generate 20*20 matrix A0 with condition number equal to 1.0E1
     * 2. generate random "exact" solution xe and right part b=A0*xe
     * 3. generate random ill-conditioned diagonal scaling matrix D with
     *    condition number equal to 1.0E50:
     * 4. transform A*x=b into badly scaled problem:
     *    A0*x0=b0
     *    A0*D*(inv(D)*x0)=b0
     *    (D*A0*D)*(inv(D)*x0)=(D*b0)
     *    finally we got new problem A*x=b with A=D*A0*D, b=D*b0, x=inv(D)*x0
     *
     * Then we solve A*x=b:
     * 1. with default preconditioner
     * 2. with explicitly activayed diagonal preconditioning
     * 3. with unit preconditioner.
     * 1st and 2nd solutions must be close to xe, 3rd solution must be very
     * far from the true one.
     */
    n = 20;
    spdmatrixrndcond(n, 1.0E1, &ta, _state);
    ae_vector_set_length(&xe, n, _state);
    for(i=0; i<=n-1; i++)
    {
        xe.ptr.p_double[i] = randomnormal(_state);
    }
    ae_vector_set_length(&b, n, _state);
    for(i=0; i<=n-1; i++)
    {
        b.ptr.p_double[i] = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            b.ptr.p_double[i] = b.ptr.p_double[i]+ta.ptr.pp_double[i][j]*xe.ptr.p_double[j];
        }
    }
    ae_vector_set_length(&d, n, _state);
    for(i=0; i<=n-1; i++)
    {
        d.ptr.p_double[i] = ae_pow((double)(10), 100*ae_randomreal(_state)-50, _state);
    }
    ae_matrix_set_length(&a, n, n, _state);
    sparsecreate(n, n, n*n, &sa, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a.ptr.pp_double[i][j] = d.ptr.p_double[i]*ta.ptr.pp_double[i][j]*d.ptr.p_double[j];
            sparseset(&sa, i, j, a.ptr.pp_double[i][j], _state);
        }
        b.ptr.p_double[i] = b.ptr.p_double[i]*d.ptr.p_double[i];
        xe.ptr.p_double[i] = xe.ptr.p_double[i]/d.ptr.p_double[i];
    }
    sparseconverttocrs(&sa, _state);
    lincgcreate(n, &s, _state);
    lincgsetcond(&s, (double)(0), 2*n, _state);
    lincgsolvesparse(&s, &sa, ae_true, &b, _state);
    lincgresults(&s, &x0, &rep, _state);
    if( rep.terminationtype<=0 )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(ae_fabs(xe.ptr.p_double[i]-x0.ptr.p_double[i], _state),5.0E-2/d.ptr.p_double[i]) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    lincgsetprecunit(&s, _state);
    lincgsolvesparse(&s, &sa, ae_true, &b, _state);
    lincgresults(&s, &x0, &rep, _state);
    if( rep.terminationtype>0 )
    {
        bflag = ae_false;
        for(i=0; i<=n-1; i++)
        {
            bflag = bflag||ae_fp_greater(ae_fabs(xe.ptr.p_double[i]-x0.ptr.p_double[i], _state),5.0E-2/d.ptr.p_double[i]);
        }
        if( !bflag )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    lincgsetprecdiag(&s, _state);
    lincgsolvesparse(&s, &sa, ae_true, &b, _state);
    lincgresults(&s, &x0, &rep, _state);
    if( rep.terminationtype<=0 )
    {
        result = ae_true;
        ae_frame_leave(_state);
        return result;
    }
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(ae_fabs(xe.ptr.p_double[i]-x0.ptr.p_double[i], _state),5.0E-2/d.ptr.p_double[i]) )
        {
            result = ae_true;
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     *test has been passed
     */
    if( !silent )
    {
        printf("PrecondTest::Ok\n");
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Orthogonalization by Gram-Shmidt method.
*************************************************************************/
static void testlincgunit_gramshmidtortnorm(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_int_t k,
     double eps,
     /* Real    */ ae_matrix* b,
     ae_int_t* k2,
     ae_state *_state)
{
    double scaling;
    double tmp;
    double e;
    ae_int_t i;
    ae_int_t j;
    ae_int_t l;
    ae_int_t m;
    double sc;

    ae_matrix_clear(b);
    *k2 = 0;

    *k2 = 0;
    scaling = (double)(0);
    ae_matrix_set_length(b, k, n, _state);
    for(i=0; i<=k-1; i++)
    {
        tmp = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            tmp = tmp+a->ptr.pp_double[i][j]*a->ptr.pp_double[i][j];
        }
        if( ae_fp_greater(tmp,scaling) )
        {
            scaling = tmp;
        }
    }
    scaling = ae_sqrt(scaling, _state);
    e = eps*scaling;
    for(i=0; i<=k-1; i++)
    {
        tmp = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[*k2][j] = a->ptr.pp_double[i][j];
            tmp = tmp+a->ptr.pp_double[i][j]*a->ptr.pp_double[i][j];
        }
        tmp = ae_sqrt(tmp, _state);
        if( ae_fp_less_eq(tmp,e) )
        {
            continue;
        }
        for(j=0; j<=*k2-1; j++)
        {
            sc = (double)(0);
            for(m=0; m<=n-1; m++)
            {
                sc = sc+b->ptr.pp_double[*k2][m]*b->ptr.pp_double[j][m];
            }
            for(l=0; l<=n-1; l++)
            {
                b->ptr.pp_double[*k2][l] = b->ptr.pp_double[*k2][l]-sc*b->ptr.pp_double[j][l];
            }
        }
        tmp = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            tmp = tmp+b->ptr.pp_double[*k2][j]*b->ptr.pp_double[*k2][j];
        }
        tmp = ae_sqrt(tmp, _state);
        if( ae_fp_less_eq(tmp,e) )
        {
            continue;
        }
        else
        {
            for(j=0; j<=n-1; j++)
            {
                b->ptr.pp_double[*k2][j] = b->ptr.pp_double[*k2][j]/tmp;
            }
        }
        *k2 = *k2+1;
    }
}


/*************************************************************************
Checks that a vector belongs to the basis.
*************************************************************************/
static ae_bool testlincgunit_frombasis(/* Real    */ ae_vector* x,
     /* Real    */ ae_matrix* basis,
     ae_int_t n,
     ae_int_t k,
     double eps,
     ae_state *_state)
{
    ae_frame _frame_block;
    double normx;
    ae_matrix ortnormbasis;
    ae_int_t k2;
    ae_int_t i;
    ae_int_t j;
    double alpha;
    ae_vector alphas;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&ortnormbasis, 0, sizeof(ortnormbasis));
    memset(&alphas, 0, sizeof(alphas));
    ae_matrix_init(&ortnormbasis, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&alphas, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&alphas, k, _state);
    
    /*
     *calculating NORM for X
     */
    normx = (double)(0);
    for(i=0; i<=n-1; i++)
    {
        normx = normx+x->ptr.p_double[i]*x->ptr.p_double[i];
    }
    normx = ae_sqrt(normx, _state);
    
    /*
     *Gram-Shmidt method
     */
    testlincgunit_gramshmidtortnorm(basis, n, k, eps, &ortnormbasis, &k2, _state);
    for(i=0; i<=k2-1; i++)
    {
        alpha = (double)(0);
        for(j=0; j<=n-1; j++)
        {
            alpha = alpha+x->ptr.p_double[j]*ortnormbasis.ptr.pp_double[i][j];
        }
        alphas.ptr.p_double[i] = alpha;
    }
    
    /*
     *check
     */
    for(i=0; i<=n-1; i++)
    {
        alpha = (double)(0);
        for(j=0; j<=k2-1; j++)
        {
            alpha = alpha+alphas.ptr.p_double[j]*ortnormbasis.ptr.pp_double[j][i];
        }
        if( ae_fp_greater(ae_fabs(x->ptr.p_double[i]-alpha, _state),normx*eps) )
        {
            result = ae_false;
            ae_frame_leave(_state);
            return result;
        }
    }
    result = ae_true;
    ae_frame_leave(_state);
    return result;
}



static ae_bool testalglibbasicsunit_testcomplexarithmetics(ae_bool silent,
     ae_state *_state);
static ae_bool testalglibbasicsunit_testieeespecial(ae_bool silent,
     ae_state *_state);
static ae_bool testalglibbasicsunit_testswapfunctions(ae_bool silent,
     ae_state *_state);
static ae_bool testalglibbasicsunit_teststandardfunctions(ae_bool silent,
     ae_state *_state);
static ae_bool testalglibbasicsunit_testserializationfunctions(ae_bool silent,
     ae_state *_state);
static void testalglibbasicsunit_createpoolandrecords(poolrec2* seedrec2,
     poolrec2* seedrec2copy,
     ae_shared_pool* pool,
     ae_state *_state);
static ae_bool testalglibbasicsunit_sharedpoolerrors(ae_state *_state);
static ae_bool testalglibbasicsunit_testsharedpool(ae_bool silent,
     ae_state *_state);
static void testalglibbasicsunit_testsort0func(/* Integer */ ae_vector* a,
     /* Integer */ ae_vector* buf,
     ae_int_t idx0,
     ae_int_t idx2,
     ae_state *_state);
ae_bool _trypexec_testalglibbasicsunit_testsort0func(/* Integer */ ae_vector* a,
    /* Integer */ ae_vector* buf,
    ae_int_t idx0,
    ae_int_t idx2, ae_state *_state);
static ae_bool testalglibbasicsunit_performtestsort0(ae_state *_state);
static void testalglibbasicsunit_testsort1func(/* Integer */ ae_vector* a,
     /* Integer */ ae_vector* buf,
     ae_int_t idx0,
     ae_int_t idx2,
     ae_bool usesmp,
     ae_state *_state);
ae_bool _trypexec_testalglibbasicsunit_testsort1func(/* Integer */ ae_vector* a,
    /* Integer */ ae_vector* buf,
    ae_int_t idx0,
    ae_int_t idx2,
    ae_bool usesmp, ae_state *_state);
static ae_bool testalglibbasicsunit_performtestsort1(ae_state *_state);
static void testalglibbasicsunit_testsort2func(/* Integer */ ae_vector* a,
     /* Integer */ ae_vector* buf,
     ae_int_t idx0,
     ae_int_t idx2,
     ae_state *_state);
ae_bool _trypexec_testalglibbasicsunit_testsort2func(/* Integer */ ae_vector* a,
    /* Integer */ ae_vector* buf,
    ae_int_t idx0,
    ae_int_t idx2, ae_state *_state);
static ae_bool testalglibbasicsunit_performtestsort2(ae_state *_state);
static ae_bool testalglibbasicsunit_performtestpoolsum(ae_state *_state);
static void testalglibbasicsunit_parallelpoolsum(ae_shared_pool* sumpool,
     ae_int_t ind0,
     ae_int_t ind1,
     ae_state *_state);
ae_bool _trypexec_testalglibbasicsunit_parallelpoolsum(ae_shared_pool* sumpool,
    ae_int_t ind0,
    ae_int_t ind1, ae_state *_state);
static void testalglibbasicsunit_mergesortedarrays(/* Integer */ ae_vector* a,
     /* Integer */ ae_vector* buf,
     ae_int_t idx0,
     ae_int_t idx1,
     ae_int_t idx2,
     ae_state *_state);
ae_bool _trypexec_testalglibbasicsunit_mergesortedarrays(/* Integer */ ae_vector* a,
    /* Integer */ ae_vector* buf,
    ae_int_t idx0,
    ae_int_t idx1,
    ae_int_t idx2, ae_state *_state);
static ae_bool testalglibbasicsunit_testsmp(ae_bool silent,
     ae_state *_state);





void rec4serializationalloc(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state)
{
    ae_int_t i;


    
    /*
     * boolean fields
     */
    ae_serializer_alloc_entry(s);
    for(i=0; i<=v->b.cnt-1; i++)
    {
        ae_serializer_alloc_entry(s);
    }
    
    /*
     * integer fields
     */
    ae_serializer_alloc_entry(s);
    for(i=0; i<=v->i.cnt-1; i++)
    {
        ae_serializer_alloc_entry(s);
    }
    
    /*
     * real fields
     */
    ae_serializer_alloc_entry(s);
    for(i=0; i<=v->r.cnt-1; i++)
    {
        ae_serializer_alloc_entry(s);
    }
}


void rec4serializationserialize(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state)
{
    ae_int_t i;


    
    /*
     * boolean fields
     */
    ae_serializer_serialize_int(s, v->b.cnt, _state);
    for(i=0; i<=v->b.cnt-1; i++)
    {
        ae_serializer_serialize_bool(s, v->b.ptr.p_bool[i], _state);
    }
    
    /*
     * integer fields
     */
    ae_serializer_serialize_int(s, v->i.cnt, _state);
    for(i=0; i<=v->i.cnt-1; i++)
    {
        ae_serializer_serialize_int(s, v->i.ptr.p_int[i], _state);
    }
    
    /*
     * real fields
     */
    ae_serializer_serialize_int(s, v->r.cnt, _state);
    for(i=0; i<=v->r.cnt-1; i++)
    {
        ae_serializer_serialize_double(s, v->r.ptr.p_double[i], _state);
    }
}


void rec4serializationunserialize(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;
    ae_bool bv;
    ae_int_t iv;
    double rv;

    _rec4serialization_clear(v);

    
    /*
     * boolean fields
     */
    ae_serializer_unserialize_int(s, &k, _state);
    if( k>0 )
    {
        ae_vector_set_length(&v->b, k, _state);
        for(i=0; i<=k-1; i++)
        {
            ae_serializer_unserialize_bool(s, &bv, _state);
            v->b.ptr.p_bool[i] = bv;
        }
    }
    
    /*
     * integer fields
     */
    ae_serializer_unserialize_int(s, &k, _state);
    if( k>0 )
    {
        ae_vector_set_length(&v->i, k, _state);
        for(i=0; i<=k-1; i++)
        {
            ae_serializer_unserialize_int(s, &iv, _state);
            v->i.ptr.p_int[i] = iv;
        }
    }
    
    /*
     * real fields
     */
    ae_serializer_unserialize_int(s, &k, _state);
    if( k>0 )
    {
        ae_vector_set_length(&v->r, k, _state);
        for(i=0; i<=k-1; i++)
        {
            ae_serializer_unserialize_double(s, &rv, _state);
            v->r.ptr.p_double[i] = rv;
        }
    }
}


ae_bool testalglibbasics(ae_bool silent, ae_state *_state)
{
    ae_bool result;


    result = ae_true;
    result = result&&testalglibbasicsunit_testcomplexarithmetics(silent, _state);
    result = result&&testalglibbasicsunit_testieeespecial(silent, _state);
    result = result&&testalglibbasicsunit_testswapfunctions(silent, _state);
    result = result&&testalglibbasicsunit_teststandardfunctions(silent, _state);
    result = result&&testalglibbasicsunit_testserializationfunctions(silent, _state);
    result = result&&testalglibbasicsunit_testsharedpool(silent, _state);
    result = result&&testalglibbasicsunit_testsmp(silent, _state);
    if( !silent )
    {
        printf("\n\n");
    }
    return result;
}


/*************************************************************************
Complex arithmetics test
*************************************************************************/
static ae_bool testalglibbasicsunit_testcomplexarithmetics(ae_bool silent,
     ae_state *_state)
{
    ae_bool absc;
    ae_bool addcc;
    ae_bool addcr;
    ae_bool addrc;
    ae_bool subcc;
    ae_bool subcr;
    ae_bool subrc;
    ae_bool mulcc;
    ae_bool mulcr;
    ae_bool mulrc;
    ae_bool divcc;
    ae_bool divcr;
    ae_bool divrc;
    ae_complex ca;
    ae_complex cb;
    ae_complex res;
    double ra;
    double rb;
    double threshold;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool result;


    threshold = 100*ae_machineepsilon;
    passcount = 1000;
    result = ae_true;
    absc = ae_true;
    addcc = ae_true;
    addcr = ae_true;
    addrc = ae_true;
    subcc = ae_true;
    subcr = ae_true;
    subrc = ae_true;
    mulcc = ae_true;
    mulcr = ae_true;
    mulrc = ae_true;
    divcc = ae_true;
    divcr = ae_true;
    divrc = ae_true;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Test AbsC
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        ra = ae_c_abs(ca, _state);
        absc = absc&&ae_fp_less(ae_fabs(ra-ae_sqrt(ae_sqr(ca.x, _state)+ae_sqr(ca.y, _state), _state), _state),threshold);
        
        /*
         * test Add
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        cb.x = 2*ae_randomreal(_state)-1;
        cb.y = 2*ae_randomreal(_state)-1;
        ra = 2*ae_randomreal(_state)-1;
        rb = 2*ae_randomreal(_state)-1;
        res = ae_c_add(ca,cb);
        addcc = (addcc&&ae_fp_less(ae_fabs(res.x-ca.x-cb.x, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y-cb.y, _state),threshold);
        res = ae_c_add_d(ca,rb);
        addcr = (addcr&&ae_fp_less(ae_fabs(res.x-ca.x-rb, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y, _state),threshold);
        res = ae_c_add_d(cb,ra);
        addrc = (addrc&&ae_fp_less(ae_fabs(res.x-ra-cb.x, _state),threshold))&&ae_fp_less(ae_fabs(res.y-cb.y, _state),threshold);
        
        /*
         * test Sub
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        cb.x = 2*ae_randomreal(_state)-1;
        cb.y = 2*ae_randomreal(_state)-1;
        ra = 2*ae_randomreal(_state)-1;
        rb = 2*ae_randomreal(_state)-1;
        res = ae_c_sub(ca,cb);
        subcc = (subcc&&ae_fp_less(ae_fabs(res.x-(ca.x-cb.x), _state),threshold))&&ae_fp_less(ae_fabs(res.y-(ca.y-cb.y), _state),threshold);
        res = ae_c_sub_d(ca,rb);
        subcr = (subcr&&ae_fp_less(ae_fabs(res.x-(ca.x-rb), _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y, _state),threshold);
        res = ae_c_d_sub(ra,cb);
        subrc = (subrc&&ae_fp_less(ae_fabs(res.x-(ra-cb.x), _state),threshold))&&ae_fp_less(ae_fabs(res.y+cb.y, _state),threshold);
        
        /*
         * test Mul
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        cb.x = 2*ae_randomreal(_state)-1;
        cb.y = 2*ae_randomreal(_state)-1;
        ra = 2*ae_randomreal(_state)-1;
        rb = 2*ae_randomreal(_state)-1;
        res = ae_c_mul(ca,cb);
        mulcc = (mulcc&&ae_fp_less(ae_fabs(res.x-(ca.x*cb.x-ca.y*cb.y), _state),threshold))&&ae_fp_less(ae_fabs(res.y-(ca.x*cb.y+ca.y*cb.x), _state),threshold);
        res = ae_c_mul_d(ca,rb);
        mulcr = (mulcr&&ae_fp_less(ae_fabs(res.x-ca.x*rb, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y*rb, _state),threshold);
        res = ae_c_mul_d(cb,ra);
        mulrc = (mulrc&&ae_fp_less(ae_fabs(res.x-ra*cb.x, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ra*cb.y, _state),threshold);
        
        /*
         * test Div
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        do
        {
            cb.x = 2*ae_randomreal(_state)-1;
            cb.y = 2*ae_randomreal(_state)-1;
        }
        while(ae_fp_less_eq(ae_c_abs(cb, _state),0.5));
        ra = 2*ae_randomreal(_state)-1;
        do
        {
            rb = 2*ae_randomreal(_state)-1;
        }
        while(ae_fp_less_eq(ae_fabs(rb, _state),0.5));
        res = ae_c_div(ca,cb);
        divcc = (divcc&&ae_fp_less(ae_fabs(ae_c_mul(res,cb).x-ca.x, _state),threshold))&&ae_fp_less(ae_fabs(ae_c_mul(res,cb).y-ca.y, _state),threshold);
        res = ae_c_div_d(ca,rb);
        divcr = (divcr&&ae_fp_less(ae_fabs(res.x-ca.x/rb, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y/rb, _state),threshold);
        res = ae_c_d_div(ra,cb);
        divrc = (divrc&&ae_fp_less(ae_fabs(ae_c_mul(res,cb).x-ra, _state),threshold))&&ae_fp_less(ae_fabs(ae_c_mul(res,cb).y, _state),threshold);
    }
    
    /*
     * summary
     */
    result = result&&absc;
    result = result&&addcc;
    result = result&&addcr;
    result = result&&addrc;
    result = result&&subcc;
    result = result&&subcr;
    result = result&&subrc;
    result = result&&mulcc;
    result = result&&mulcr;
    result = result&&mulrc;
    result = result&&divcc;
    result = result&&divcr;
    result = result&&divrc;
    if( !silent )
    {
        if( result )
        {
            printf("COMPLEX ARITHMETICS:                     OK\n");
        }
        else
        {
            printf("COMPLEX ARITHMETICS:                     FAILED\n");
            printf("* AddCC - - - - - - - - - - - - - - - -  ");
            if( addcc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* AddCR - - - - - - - - - - - - - - - -  ");
            if( addcr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* AddRC - - - - - - - - - - - - - - - -  ");
            if( addrc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* SubCC - - - - - - - - - - - - - - - -  ");
            if( subcc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* SubCR - - - - - - - - - - - - - - - -  ");
            if( subcr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* SubRC - - - - - - - - - - - - - - - -  ");
            if( subrc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* MulCC - - - - - - - - - - - - - - - -  ");
            if( mulcc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* MulCR - - - - - - - - - - - - - - - -  ");
            if( mulcr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* MulRC - - - - - - - - - - - - - - - -  ");
            if( mulrc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* DivCC - - - - - - - - - - - - - - - -  ");
            if( divcc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* DivCR - - - - - - - - - - - - - - - -  ");
            if( divcr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* DivRC - - - - - - - - - - - - - - - -  ");
            if( divrc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
        }
    }
    return result;
}


/*************************************************************************
Tests for IEEE special quantities
*************************************************************************/
static ae_bool testalglibbasicsunit_testieeespecial(ae_bool silent,
     ae_state *_state)
{
    ae_bool oknan;
    ae_bool okinf;
    ae_bool okother;
    double v1;
    double v2;
    ae_bool result;


    result = ae_true;
    oknan = ae_true;
    okinf = ae_true;
    okother = ae_true;
    
    /*
     * Test classification functions
     */
    okother = okother&&!ae_isinf(_state->v_nan, _state);
    okother = okother&&ae_isinf(_state->v_posinf, _state);
    okother = okother&&!ae_isinf(ae_maxrealnumber, _state);
    okother = okother&&!ae_isinf(1.0, _state);
    okother = okother&&!ae_isinf(ae_minrealnumber, _state);
    okother = okother&&!ae_isinf(0.0, _state);
    okother = okother&&!ae_isinf(-ae_minrealnumber, _state);
    okother = okother&&!ae_isinf(-1.0, _state);
    okother = okother&&!ae_isinf(-ae_maxrealnumber, _state);
    okother = okother&&ae_isinf(_state->v_neginf, _state);
    okother = okother&&!ae_isposinf(_state->v_nan, _state);
    okother = okother&&ae_isposinf(_state->v_posinf, _state);
    okother = okother&&!ae_isposinf(ae_maxrealnumber, _state);
    okother = okother&&!ae_isposinf(1.0, _state);
    okother = okother&&!ae_isposinf(ae_minrealnumber, _state);
    okother = okother&&!ae_isposinf(0.0, _state);
    okother = okother&&!ae_isposinf(-ae_minrealnumber, _state);
    okother = okother&&!ae_isposinf(-1.0, _state);
    okother = okother&&!ae_isposinf(-ae_maxrealnumber, _state);
    okother = okother&&!ae_isposinf(_state->v_neginf, _state);
    okother = okother&&!ae_isneginf(_state->v_nan, _state);
    okother = okother&&!ae_isneginf(_state->v_posinf, _state);
    okother = okother&&!ae_isneginf(ae_maxrealnumber, _state);
    okother = okother&&!ae_isneginf(1.0, _state);
    okother = okother&&!ae_isneginf(ae_minrealnumber, _state);
    okother = okother&&!ae_isneginf(0.0, _state);
    okother = okother&&!ae_isneginf(-ae_minrealnumber, _state);
    okother = okother&&!ae_isneginf(-1.0, _state);
    okother = okother&&!ae_isneginf(-ae_maxrealnumber, _state);
    okother = okother&&ae_isneginf(_state->v_neginf, _state);
    okother = okother&&ae_isnan(_state->v_nan, _state);
    okother = okother&&!ae_isnan(_state->v_posinf, _state);
    okother = okother&&!ae_isnan(ae_maxrealnumber, _state);
    okother = okother&&!ae_isnan(1.0, _state);
    okother = okother&&!ae_isnan(ae_minrealnumber, _state);
    okother = okother&&!ae_isnan(0.0, _state);
    okother = okother&&!ae_isnan(-ae_minrealnumber, _state);
    okother = okother&&!ae_isnan(-1.0, _state);
    okother = okother&&!ae_isnan(-ae_maxrealnumber, _state);
    okother = okother&&!ae_isnan(_state->v_neginf, _state);
    okother = okother&&!ae_isfinite(_state->v_nan, _state);
    okother = okother&&!ae_isfinite(_state->v_posinf, _state);
    okother = okother&&ae_isfinite(ae_maxrealnumber, _state);
    okother = okother&&ae_isfinite(1.0, _state);
    okother = okother&&ae_isfinite(ae_minrealnumber, _state);
    okother = okother&&ae_isfinite(0.0, _state);
    okother = okother&&ae_isfinite(-ae_minrealnumber, _state);
    okother = okother&&ae_isfinite(-1.0, _state);
    okother = okother&&ae_isfinite(-ae_maxrealnumber, _state);
    okother = okother&&!ae_isfinite(_state->v_neginf, _state);
    
    /*
     * Test NAN
     */
    v1 = _state->v_nan;
    v2 = _state->v_nan;
    oknan = oknan&&ae_isnan(v1, _state);
    oknan = oknan&&ae_fp_neq(v1,v2);
    oknan = oknan&&!ae_fp_eq(v1,v2);
    
    /*
     * Test INF:
     * * basic properties
     * * comparisons involving PosINF on one of the sides
     * * comparisons involving NegINF on one of the sides
     */
    v1 = _state->v_posinf;
    v2 = _state->v_neginf;
    okinf = okinf&&ae_isinf(_state->v_posinf, _state);
    okinf = okinf&&ae_isinf(v1, _state);
    okinf = okinf&&ae_isinf(_state->v_neginf, _state);
    okinf = okinf&&ae_isinf(v2, _state);
    okinf = okinf&&ae_isposinf(_state->v_posinf, _state);
    okinf = okinf&&ae_isposinf(v1, _state);
    okinf = okinf&&!ae_isposinf(_state->v_neginf, _state);
    okinf = okinf&&!ae_isposinf(v2, _state);
    okinf = okinf&&!ae_isneginf(_state->v_posinf, _state);
    okinf = okinf&&!ae_isneginf(v1, _state);
    okinf = okinf&&ae_isneginf(_state->v_neginf, _state);
    okinf = okinf&&ae_isneginf(v2, _state);
    okinf = okinf&&ae_fp_eq(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&ae_fp_eq(_state->v_posinf,v1);
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,v2);
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,(double)(0));
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,1.2);
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,-1.2);
    okinf = okinf&&ae_fp_eq(v1,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(v2,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq((double)(0),_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(-1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_neq(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&!ae_fp_neq(_state->v_posinf,v1);
    okinf = okinf&&ae_fp_neq(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&ae_fp_neq(_state->v_posinf,v2);
    okinf = okinf&&ae_fp_neq(_state->v_posinf,(double)(0));
    okinf = okinf&&ae_fp_neq(_state->v_posinf,1.2);
    okinf = okinf&&ae_fp_neq(_state->v_posinf,-1.2);
    okinf = okinf&&!ae_fp_neq(v1,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(v2,_state->v_posinf);
    okinf = okinf&&ae_fp_neq((double)(0),_state->v_posinf);
    okinf = okinf&&ae_fp_neq(1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(-1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,v1);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,v2);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,(double)(0));
    okinf = okinf&&!ae_fp_less(_state->v_posinf,1.2);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,-1.2);
    okinf = okinf&&!ae_fp_less(v1,_state->v_posinf);
    okinf = okinf&&ae_fp_less(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_less(v2,_state->v_posinf);
    okinf = okinf&&ae_fp_less((double)(0),_state->v_posinf);
    okinf = okinf&&ae_fp_less(1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_less(-1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(_state->v_posinf,v1);
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,v2);
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,(double)(0));
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,1.2);
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,-1.2);
    okinf = okinf&&ae_fp_less_eq(v1,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(v2,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq((double)(0),_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(-1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(_state->v_posinf,v1);
    okinf = okinf&&ae_fp_greater(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&ae_fp_greater(_state->v_posinf,v2);
    okinf = okinf&&ae_fp_greater(_state->v_posinf,(double)(0));
    okinf = okinf&&ae_fp_greater(_state->v_posinf,1.2);
    okinf = okinf&&ae_fp_greater(_state->v_posinf,-1.2);
    okinf = okinf&&!ae_fp_greater(v1,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(v2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater((double)(0),_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(-1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,v1);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,v2);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,(double)(0));
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,1.2);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,-1.2);
    okinf = okinf&&ae_fp_greater_eq(v1,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(v2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq((double)(0),_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(-1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,v1);
    okinf = okinf&&ae_fp_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_eq(_state->v_neginf,v2);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,(double)(0));
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,1.2);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,-1.2);
    okinf = okinf&&!ae_fp_eq(v1,_state->v_neginf);
    okinf = okinf&&ae_fp_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_eq(v2,_state->v_neginf);
    okinf = okinf&&!ae_fp_eq((double)(0),_state->v_neginf);
    okinf = okinf&&!ae_fp_eq(1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_eq(-1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,v1);
    okinf = okinf&&!ae_fp_neq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_neq(_state->v_neginf,v2);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,(double)(0));
    okinf = okinf&&ae_fp_neq(_state->v_neginf,1.2);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,-1.2);
    okinf = okinf&&ae_fp_neq(v1,_state->v_neginf);
    okinf = okinf&&!ae_fp_neq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_neq(v2,_state->v_neginf);
    okinf = okinf&&ae_fp_neq((double)(0),_state->v_neginf);
    okinf = okinf&&ae_fp_neq(1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_neq(-1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_less(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_less(_state->v_neginf,v1);
    okinf = okinf&&!ae_fp_less(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(_state->v_neginf,v2);
    okinf = okinf&&ae_fp_less(_state->v_neginf,(double)(0));
    okinf = okinf&&ae_fp_less(_state->v_neginf,1.2);
    okinf = okinf&&ae_fp_less(_state->v_neginf,-1.2);
    okinf = okinf&&!ae_fp_less(v1,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(v2,_state->v_neginf);
    okinf = okinf&&!ae_fp_less((double)(0),_state->v_neginf);
    okinf = okinf&&!ae_fp_less(1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(-1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,v1);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,v2);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,(double)(0));
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,1.2);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,-1.2);
    okinf = okinf&&!ae_fp_less_eq(v1,_state->v_neginf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_less_eq(v2,_state->v_neginf);
    okinf = okinf&&!ae_fp_less_eq((double)(0),_state->v_neginf);
    okinf = okinf&&!ae_fp_less_eq(1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_less_eq(-1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,v1);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,v2);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,(double)(0));
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,1.2);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,-1.2);
    okinf = okinf&&ae_fp_greater(v1,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater(v2,_state->v_neginf);
    okinf = okinf&&ae_fp_greater((double)(0),_state->v_neginf);
    okinf = okinf&&ae_fp_greater(1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_greater(-1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,v1);
    okinf = okinf&&ae_fp_greater_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_neginf,v2);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,(double)(0));
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,1.2);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,-1.2);
    okinf = okinf&&ae_fp_greater_eq(v1,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(v2,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq((double)(0),_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(-1.2,_state->v_neginf);
    
    /*
     * summary
     */
    result = result&&oknan;
    result = result&&okinf;
    result = result&&okother;
    if( !silent )
    {
        if( result )
        {
            printf("IEEE SPECIAL VALUES:                     OK\n");
        }
        else
        {
            printf("IEEE SPECIAL VALUES:                     FAILED\n");
            printf("* NAN - - - - - - - - - - - - - - - - -  ");
            if( oknan )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* INF - - - - - - - - - - - - - - - - -  ");
            if( okinf )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* FUNCTIONS - - - - - - - - - - - - - -  ");
            if( okother )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
        }
    }
    return result;
}


/*************************************************************************
Tests for swapping functions
*************************************************************************/
static ae_bool testalglibbasicsunit_testswapfunctions(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool okb1;
    ae_bool okb2;
    ae_bool oki1;
    ae_bool oki2;
    ae_bool okr1;
    ae_bool okr2;
    ae_bool okc1;
    ae_bool okc2;
    ae_vector b11;
    ae_vector b12;
    ae_vector i11;
    ae_vector i12;
    ae_vector r11;
    ae_vector r12;
    ae_vector c11;
    ae_vector c12;
    ae_matrix b21;
    ae_matrix b22;
    ae_matrix i21;
    ae_matrix i22;
    ae_matrix r21;
    ae_matrix r22;
    ae_matrix c21;
    ae_matrix c22;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&b11, 0, sizeof(b11));
    memset(&b12, 0, sizeof(b12));
    memset(&i11, 0, sizeof(i11));
    memset(&i12, 0, sizeof(i12));
    memset(&r11, 0, sizeof(r11));
    memset(&r12, 0, sizeof(r12));
    memset(&c11, 0, sizeof(c11));
    memset(&c12, 0, sizeof(c12));
    memset(&b21, 0, sizeof(b21));
    memset(&b22, 0, sizeof(b22));
    memset(&i21, 0, sizeof(i21));
    memset(&i22, 0, sizeof(i22));
    memset(&r21, 0, sizeof(r21));
    memset(&r22, 0, sizeof(r22));
    memset(&c21, 0, sizeof(c21));
    memset(&c22, 0, sizeof(c22));
    ae_vector_init(&b11, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&b12, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&i11, 0, DT_INT, _state, ae_true);
    ae_vector_init(&i12, 0, DT_INT, _state, ae_true);
    ae_vector_init(&r11, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r12, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c11, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&c12, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b21, 0, 0, DT_BOOL, _state, ae_true);
    ae_matrix_init(&b22, 0, 0, DT_BOOL, _state, ae_true);
    ae_matrix_init(&i21, 0, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&i22, 0, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&r21, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r22, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c21, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&c22, 0, 0, DT_COMPLEX, _state, ae_true);

    result = ae_true;
    okb1 = ae_true;
    okb2 = ae_true;
    oki1 = ae_true;
    oki2 = ae_true;
    okr1 = ae_true;
    okr2 = ae_true;
    okc1 = ae_true;
    okc2 = ae_true;
    
    /*
     * Test B1 swaps
     */
    ae_vector_set_length(&b11, 1, _state);
    ae_vector_set_length(&b12, 2, _state);
    b11.ptr.p_bool[0] = ae_true;
    b12.ptr.p_bool[0] = ae_false;
    b12.ptr.p_bool[1] = ae_true;
    ae_swap_vectors(&b11, &b12);
    if( b11.cnt==2&&b12.cnt==1 )
    {
        okb1 = okb1&&!b11.ptr.p_bool[0];
        okb1 = okb1&&b11.ptr.p_bool[1];
        okb1 = okb1&&b12.ptr.p_bool[0];
    }
    else
    {
        okb1 = ae_false;
    }
    
    /*
     * Test I1 swaps
     */
    ae_vector_set_length(&i11, 1, _state);
    ae_vector_set_length(&i12, 2, _state);
    i11.ptr.p_int[0] = 1;
    i12.ptr.p_int[0] = 2;
    i12.ptr.p_int[1] = 3;
    ae_swap_vectors(&i11, &i12);
    if( i11.cnt==2&&i12.cnt==1 )
    {
        oki1 = oki1&&i11.ptr.p_int[0]==2;
        oki1 = oki1&&i11.ptr.p_int[1]==3;
        oki1 = oki1&&i12.ptr.p_int[0]==1;
    }
    else
    {
        oki1 = ae_false;
    }
    
    /*
     * Test R1 swaps
     */
    ae_vector_set_length(&r11, 1, _state);
    ae_vector_set_length(&r12, 2, _state);
    r11.ptr.p_double[0] = 1.5;
    r12.ptr.p_double[0] = 2.5;
    r12.ptr.p_double[1] = 3.5;
    ae_swap_vectors(&r11, &r12);
    if( r11.cnt==2&&r12.cnt==1 )
    {
        okr1 = okr1&&ae_fp_eq(r11.ptr.p_double[0],2.5);
        okr1 = okr1&&ae_fp_eq(r11.ptr.p_double[1],3.5);
        okr1 = okr1&&ae_fp_eq(r12.ptr.p_double[0],1.5);
    }
    else
    {
        okr1 = ae_false;
    }
    
    /*
     * Test C1 swaps
     */
    ae_vector_set_length(&c11, 1, _state);
    ae_vector_set_length(&c12, 2, _state);
    c11.ptr.p_complex[0] = ae_complex_from_i(1);
    c12.ptr.p_complex[0] = ae_complex_from_i(2);
    c12.ptr.p_complex[1] = ae_complex_from_i(3);
    ae_swap_vectors(&c11, &c12);
    if( c11.cnt==2&&c12.cnt==1 )
    {
        okc1 = okc1&&ae_c_eq_d(c11.ptr.p_complex[0],(double)(2));
        okc1 = okc1&&ae_c_eq_d(c11.ptr.p_complex[1],(double)(3));
        okc1 = okc1&&ae_c_eq_d(c12.ptr.p_complex[0],(double)(1));
    }
    else
    {
        okc1 = ae_false;
    }
    
    /*
     * Test B2 swaps
     */
    ae_matrix_set_length(&b21, 1, 2, _state);
    ae_matrix_set_length(&b22, 2, 1, _state);
    b21.ptr.pp_bool[0][0] = ae_true;
    b21.ptr.pp_bool[0][1] = ae_false;
    b22.ptr.pp_bool[0][0] = ae_false;
    b22.ptr.pp_bool[1][0] = ae_true;
    ae_swap_matrices(&b21, &b22);
    if( ((b21.rows==2&&b21.cols==1)&&b22.rows==1)&&b22.cols==2 )
    {
        okb2 = okb2&&!b21.ptr.pp_bool[0][0];
        okb2 = okb2&&b21.ptr.pp_bool[1][0];
        okb2 = okb2&&b22.ptr.pp_bool[0][0];
        okb2 = okb2&&!b22.ptr.pp_bool[0][1];
    }
    else
    {
        okb2 = ae_false;
    }
    
    /*
     * Test I2 swaps
     */
    ae_matrix_set_length(&i21, 1, 2, _state);
    ae_matrix_set_length(&i22, 2, 1, _state);
    i21.ptr.pp_int[0][0] = 1;
    i21.ptr.pp_int[0][1] = 2;
    i22.ptr.pp_int[0][0] = 3;
    i22.ptr.pp_int[1][0] = 4;
    ae_swap_matrices(&i21, &i22);
    if( ((i21.rows==2&&i21.cols==1)&&i22.rows==1)&&i22.cols==2 )
    {
        oki2 = oki2&&i21.ptr.pp_int[0][0]==3;
        oki2 = oki2&&i21.ptr.pp_int[1][0]==4;
        oki2 = oki2&&i22.ptr.pp_int[0][0]==1;
        oki2 = oki2&&i22.ptr.pp_int[0][1]==2;
    }
    else
    {
        oki2 = ae_false;
    }
    
    /*
     * Test R2 swaps
     */
    ae_matrix_set_length(&r21, 1, 2, _state);
    ae_matrix_set_length(&r22, 2, 1, _state);
    r21.ptr.pp_double[0][0] = (double)(1);
    r21.ptr.pp_double[0][1] = (double)(2);
    r22.ptr.pp_double[0][0] = (double)(3);
    r22.ptr.pp_double[1][0] = (double)(4);
    ae_swap_matrices(&r21, &r22);
    if( ((r21.rows==2&&r21.cols==1)&&r22.rows==1)&&r22.cols==2 )
    {
        okr2 = okr2&&ae_fp_eq(r21.ptr.pp_double[0][0],(double)(3));
        okr2 = okr2&&ae_fp_eq(r21.ptr.pp_double[1][0],(double)(4));
        okr2 = okr2&&ae_fp_eq(r22.ptr.pp_double[0][0],(double)(1));
        okr2 = okr2&&ae_fp_eq(r22.ptr.pp_double[0][1],(double)(2));
    }
    else
    {
        okr2 = ae_false;
    }
    
    /*
     * Test C2 swaps
     */
    ae_matrix_set_length(&c21, 1, 2, _state);
    ae_matrix_set_length(&c22, 2, 1, _state);
    c21.ptr.pp_complex[0][0] = ae_complex_from_i(1);
    c21.ptr.pp_complex[0][1] = ae_complex_from_i(2);
    c22.ptr.pp_complex[0][0] = ae_complex_from_i(3);
    c22.ptr.pp_complex[1][0] = ae_complex_from_i(4);
    ae_swap_matrices(&c21, &c22);
    if( ((c21.rows==2&&c21.cols==1)&&c22.rows==1)&&c22.cols==2 )
    {
        okc2 = okc2&&ae_c_eq_d(c21.ptr.pp_complex[0][0],(double)(3));
        okc2 = okc2&&ae_c_eq_d(c21.ptr.pp_complex[1][0],(double)(4));
        okc2 = okc2&&ae_c_eq_d(c22.ptr.pp_complex[0][0],(double)(1));
        okc2 = okc2&&ae_c_eq_d(c22.ptr.pp_complex[0][1],(double)(2));
    }
    else
    {
        okc2 = ae_false;
    }
    
    /*
     * summary
     */
    result = result&&okb1;
    result = result&&okb2;
    result = result&&oki1;
    result = result&&oki2;
    result = result&&okr1;
    result = result&&okr2;
    result = result&&okc1;
    result = result&&okc2;
    if( !silent )
    {
        if( result )
        {
            printf("SWAPPING FUNCTIONS:                      OK\n");
        }
        else
        {
            printf("SWAPPING FUNCTIONS:                      FAILED\n");
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Tests for standard functions
*************************************************************************/
static ae_bool testalglibbasicsunit_teststandardfunctions(ae_bool silent,
     ae_state *_state)
{
    ae_bool result;


    result = ae_true;
    
    /*
     * Test Sign()
     */
    result = result&&ae_sign(1.2, _state)==1;
    result = result&&ae_sign((double)(0), _state)==0;
    result = result&&ae_sign(-1.2, _state)==-1;
    
    /*
     * summary
     */
    if( !silent )
    {
        if( result )
        {
            printf("STANDARD FUNCTIONS:                      OK\n");
        }
        else
        {
            printf("STANDARD FUNCTIONS:                      FAILED\n");
        }
    }
    return result;
}


/*************************************************************************
Tests for serualization functions
*************************************************************************/
static ae_bool testalglibbasicsunit_testserializationfunctions(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool okb;
    ae_bool oki;
    ae_bool okr;
    ae_int_t nb;
    ae_int_t ni;
    ae_int_t nr;
    ae_int_t i;
    rec4serialization r0;
    rec4serialization r1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&r0, 0, sizeof(r0));
    memset(&r1, 0, sizeof(r1));
    _rec4serialization_init(&r0, _state, ae_true);
    _rec4serialization_init(&r1, _state, ae_true);

    result = ae_true;
    okb = ae_true;
    oki = ae_true;
    okr = ae_true;
    for(nb=1; nb<=4; nb++)
    {
        for(ni=1; ni<=4; ni++)
        {
            for(nr=1; nr<=4; nr++)
            {
                ae_vector_set_length(&r0.b, nb, _state);
                for(i=0; i<=nb-1; i++)
                {
                    r0.b.ptr.p_bool[i] = ae_randominteger(2, _state)!=0;
                }
                ae_vector_set_length(&r0.i, ni, _state);
                for(i=0; i<=ni-1; i++)
                {
                    r0.i.ptr.p_int[i] = ae_randominteger(10, _state)-5;
                }
                ae_vector_set_length(&r0.r, nr, _state);
                for(i=0; i<=nr-1; i++)
                {
                    r0.r.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    rec4serializationalloc(&_local_serializer, &r0, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    memset(&_local_dynamic_block, 0, sizeof(_local_dynamic_block));
                    ae_db_init(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    rec4serializationserialize(&_local_serializer, &r0, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    rec4serializationunserialize(&_local_serializer, &r1, _state);
                    ae_serializer_stop(&_local_serializer, _state);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
                if( (r0.b.cnt==r1.b.cnt&&r0.i.cnt==r1.i.cnt)&&r0.r.cnt==r1.r.cnt )
                {
                    for(i=0; i<=nb-1; i++)
                    {
                        okb = okb&&((r0.b.ptr.p_bool[i]&&r1.b.ptr.p_bool[i])||(!r0.b.ptr.p_bool[i]&&!r1.b.ptr.p_bool[i]));
                    }
                    for(i=0; i<=ni-1; i++)
                    {
                        oki = oki&&r0.i.ptr.p_int[i]==r1.i.ptr.p_int[i];
                    }
                    for(i=0; i<=nr-1; i++)
                    {
                        okr = okr&&ae_fp_eq(r0.r.ptr.p_double[i],r1.r.ptr.p_double[i]);
                    }
                }
                else
                {
                    oki = ae_false;
                }
            }
        }
    }
    
    /*
     * summary
     */
    result = result&&okb;
    result = result&&oki;
    result = result&&okr;
    if( !silent )
    {
        if( result )
        {
            printf("SERIALIZATION FUNCTIONS:                 OK\n");
        }
        else
        {
            printf("SERIALIZATION FUNCTIONS:                 FAILED\n");
            printf("* BOOLEAN - - - - - - - - - - - - - - -  ");
            if( okb )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* INTEGER - - - - - - - - - - - - - - -  ");
            if( oki )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* REAL  - - - - - - - - - - - - - - - -  ");
            if( okr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Tests for pool functions
*************************************************************************/
static void testalglibbasicsunit_createpoolandrecords(poolrec2* seedrec2,
     poolrec2* seedrec2copy,
     ae_shared_pool* pool,
     ae_state *_state)
{

    _poolrec2_clear(seedrec2);
    _poolrec2_clear(seedrec2copy);
    ae_shared_pool_clear(pool);

    seedrec2->bval = ae_fp_greater(ae_randomreal(_state),0.5);
    seedrec2->recval.bval = ae_fp_greater(ae_randomreal(_state),0.5);
    seedrec2->recval.ival = ae_randominteger(10, _state);
    seedrec2->recval.rval = ae_randomreal(_state);
    seedrec2->recval.cval.x = ae_randomreal(_state);
    seedrec2->recval.cval.y = ae_randomreal(_state);
    ae_vector_set_length(&seedrec2->recval.i1val, 3, _state);
    seedrec2->recval.i1val.ptr.p_int[0] = ae_randominteger(10, _state);
    seedrec2->recval.i1val.ptr.p_int[1] = ae_randominteger(10, _state);
    seedrec2->recval.i1val.ptr.p_int[2] = ae_randominteger(10, _state);
    seedrec2copy->bval = seedrec2->bval;
    seedrec2copy->recval.bval = seedrec2->recval.bval;
    seedrec2copy->recval.ival = seedrec2->recval.ival;
    seedrec2copy->recval.rval = seedrec2->recval.rval;
    seedrec2copy->recval.cval = seedrec2->recval.cval;
    ae_vector_set_length(&seedrec2copy->recval.i1val, 3, _state);
    seedrec2copy->recval.i1val.ptr.p_int[0] = seedrec2->recval.i1val.ptr.p_int[0];
    seedrec2copy->recval.i1val.ptr.p_int[1] = seedrec2->recval.i1val.ptr.p_int[1];
    seedrec2copy->recval.i1val.ptr.p_int[2] = seedrec2->recval.i1val.ptr.p_int[2];
    ae_shared_pool_set_seed(pool, seedrec2, sizeof(*seedrec2), _poolrec2_init, _poolrec2_init_copy, _poolrec2_destroy, _state);
}


static ae_bool testalglibbasicsunit_sharedpoolerrors(ae_state *_state)
{
    ae_frame _frame_block;
    poolrec1 seedrec1;
    poolrec2 seedrec2;
    poolrec2 seedrec2copy;
    ae_shared_pool pool;
    ae_shared_pool pool2;
    poolrec2 *prec2;
    ae_smart_ptr _prec2;
    poolrec2 *p0;
    ae_smart_ptr _p0;
    poolrec2 *p1;
    ae_smart_ptr _p1;
    poolrec2 *p2;
    ae_smart_ptr _p2;
    poolrec1 *q0;
    ae_smart_ptr _q0;
    poolrec1 *q1;
    ae_smart_ptr _q1;
    ae_shared_pool *ppool0;
    ae_smart_ptr _ppool0;
    ae_shared_pool *ppool1;
    ae_smart_ptr _ppool1;
    ae_int_t val100cnt;
    ae_int_t val101cnt;
    ae_int_t val102cnt;
    ae_int_t tmpval;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&seedrec1, 0, sizeof(seedrec1));
    memset(&seedrec2, 0, sizeof(seedrec2));
    memset(&seedrec2copy, 0, sizeof(seedrec2copy));
    memset(&pool, 0, sizeof(pool));
    memset(&pool2, 0, sizeof(pool2));
    memset(&_prec2, 0, sizeof(_prec2));
    memset(&_p0, 0, sizeof(_p0));
    memset(&_p1, 0, sizeof(_p1));
    memset(&_p2, 0, sizeof(_p2));
    memset(&_q0, 0, sizeof(_q0));
    memset(&_q1, 0, sizeof(_q1));
    memset(&_ppool0, 0, sizeof(_ppool0));
    memset(&_ppool1, 0, sizeof(_ppool1));
    _poolrec1_init(&seedrec1, _state, ae_true);
    _poolrec2_init(&seedrec2, _state, ae_true);
    _poolrec2_init(&seedrec2copy, _state, ae_true);
    ae_shared_pool_init(&pool, _state, ae_true);
    ae_shared_pool_init(&pool2, _state, ae_true);
    ae_smart_ptr_init(&_prec2, (void**)&prec2, _state, ae_true);
    ae_smart_ptr_init(&_p0, (void**)&p0, _state, ae_true);
    ae_smart_ptr_init(&_p1, (void**)&p1, _state, ae_true);
    ae_smart_ptr_init(&_p2, (void**)&p2, _state, ae_true);
    ae_smart_ptr_init(&_q0, (void**)&q0, _state, ae_true);
    ae_smart_ptr_init(&_q1, (void**)&q1, _state, ae_true);
    ae_smart_ptr_init(&_ppool0, (void**)&ppool0, _state, ae_true);
    ae_smart_ptr_init(&_ppool1, (void**)&ppool1, _state, ae_true);

    result = ae_true;
    
    /*
     * Test 1: test that:
     * a) smart pointer is null by default
     * b) "conventional local" is valid by default
     * b) unitinitialized shared pool is "not initialized"
     */
    if( prec2!=NULL )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( !(&seedrec1!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( ae_shared_pool_is_initialized(&pool) )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test 2: basic copying of complex structures
     * * check that pool is recognized as "initialized"
     * * change original seed record,
     * * retrieve value from pool,
     * * check that it is valid
     * * and it is unchanged.
     */
    testalglibbasicsunit_createpoolandrecords(&seedrec2, &seedrec2copy, &pool, _state);
    if( !ae_shared_pool_is_initialized(&pool) )
    {
        ae_frame_leave(_state);
        return result;
    }
    seedrec2.bval = !seedrec2.bval;
    seedrec2.recval.i1val.ptr.p_int[0] = seedrec2.recval.i1val.ptr.p_int[0]+1;
    ae_shared_pool_retrieve(&pool, &_prec2, _state);
    if( !(prec2!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( (seedrec2copy.bval&&!prec2->bval)||(prec2->bval&&!seedrec2copy.bval) )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( seedrec2copy.recval.i1val.ptr.p_int[0]!=prec2->recval.i1val.ptr.p_int[0] )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test 3: unrecycled values are lost
     * * retrieve value from pool,
     * * change it,
     * * retrieve one more time,
     * * check that it is unchanged.
     */
    testalglibbasicsunit_createpoolandrecords(&seedrec2, &seedrec2copy, &pool, _state);
    ae_shared_pool_retrieve(&pool, &_prec2, _state);
    prec2->recval.ival = prec2->recval.ival+1;
    ae_shared_pool_retrieve(&pool, &_prec2, _state);
    if( prec2->recval.ival!=seedrec2copy.recval.ival )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test 4: recycled values are reused, PoolClearRecycled() removes recycled values
     * * retrieve value from pool,
     * * change it,
     * * recycle,
     * * check that recycled pointer is null
     * * retrieve one more time,
     * * check that modified value was returned,
     * * recycle,
     * * clear pool,
     * * retrieve one more time,
     * * check that unmodified value was returned,
     */
    testalglibbasicsunit_createpoolandrecords(&seedrec2, &seedrec2copy, &pool, _state);
    ae_shared_pool_retrieve(&pool, &_prec2, _state);
    prec2->recval.ival = prec2->recval.ival+1;
    ae_shared_pool_recycle(&pool, &_prec2, _state);
    if( prec2!=NULL )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_retrieve(&pool, &_prec2, _state);
    if( !(prec2!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( prec2->recval.ival!=seedrec2copy.recval.ival+1 )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_recycle(&pool, &_prec2, _state);
    ae_shared_pool_clear_recycled(&pool, _state);
    ae_shared_pool_retrieve(&pool, &_prec2, _state);
    if( !(prec2!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( prec2->recval.ival!=seedrec2copy.recval.ival )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test 5: basic enumeration
     * * retrieve 3 values from pool
     * * fill RecVal.iVal by 100, 101, 102
     * * recycle values
     * * enumerate, check that each iVal occurs only once during enumeration
     * * repeat enumeration to make sure that it can be repeated
     */
    testalglibbasicsunit_createpoolandrecords(&seedrec2, &seedrec2copy, &pool, _state);
    ae_shared_pool_retrieve(&pool, &_p0, _state);
    ae_shared_pool_retrieve(&pool, &_p1, _state);
    ae_shared_pool_retrieve(&pool, &_p2, _state);
    p0->recval.ival = 100;
    p1->recval.ival = 101;
    p2->recval.ival = 102;
    ae_shared_pool_recycle(&pool, &_p1, _state);
    ae_shared_pool_recycle(&pool, &_p2, _state);
    ae_shared_pool_recycle(&pool, &_p0, _state);
    val100cnt = 0;
    val101cnt = 0;
    val102cnt = 0;
    ae_shared_pool_first_recycled(&pool, &_prec2, _state);
    while(prec2!=NULL)
    {
        if( prec2->recval.ival==100 )
        {
            val100cnt = val100cnt+1;
        }
        if( prec2->recval.ival==101 )
        {
            val101cnt = val101cnt+1;
        }
        if( prec2->recval.ival==102 )
        {
            val102cnt = val102cnt+1;
        }
        ae_shared_pool_next_recycled(&pool, &_prec2, _state);
    }
    if( (val100cnt!=1||val101cnt!=1)||val102cnt!=1 )
    {
        ae_frame_leave(_state);
        return result;
    }
    val100cnt = 0;
    val101cnt = 0;
    val102cnt = 0;
    ae_shared_pool_first_recycled(&pool, &_prec2, _state);
    while(prec2!=NULL)
    {
        if( prec2->recval.ival==100 )
        {
            val100cnt = val100cnt+1;
        }
        if( prec2->recval.ival==101 )
        {
            val101cnt = val101cnt+1;
        }
        if( prec2->recval.ival==102 )
        {
            val102cnt = val102cnt+1;
        }
        ae_shared_pool_next_recycled(&pool, &_prec2, _state);
    }
    if( (val100cnt!=1||val101cnt!=1)||val102cnt!=1 )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test 6: pool itself can be pooled
     * * pool can be seeded with another pool
     * * smart pointers to pool are correctly handled
     * * pool correctly returns different references on "retrieve":
     *   * we retrieve, modify and recycle back to PPool0
     *   * we retrieve from PPool1 - unmodified value is returned
     *   * we retrievefrom PPool0  - modified value is returned
     */
    testalglibbasicsunit_createpoolandrecords(&seedrec2, &seedrec2copy, &pool, _state);
    ae_shared_pool_set_seed(&pool2, &pool, sizeof(pool), ae_shared_pool_init, ae_shared_pool_init_copy, ae_shared_pool_destroy, _state);
    if( ppool0!=NULL||ppool1!=NULL )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_retrieve(&pool2, &_ppool0, _state);
    ae_shared_pool_retrieve(&pool2, &_ppool1, _state);
    if( !(ppool0!=NULL&&ppool1!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_retrieve(ppool0, &_p0, _state);
    p0->recval.ival = p0->recval.ival+1;
    tmpval = p0->recval.ival;
    ae_shared_pool_recycle(ppool0, &_p0, _state);
    ae_shared_pool_retrieve(ppool1, &_p1, _state);
    if( p1->recval.ival==tmpval )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_recycle(ppool1, &_p1, _state);
    ae_shared_pool_retrieve(ppool0, &_p0, _state);
    if( p0->recval.ival!=tmpval )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test 7: pools which are fields of records are correctly handled
     * * pool can be seeded with record which has initialized pool as its field
     * * when record is retrieved from pool, its fields are correctly copied (including
     *   fields which are pools)
     */
    testalglibbasicsunit_createpoolandrecords(&seedrec2, &seedrec2copy, &pool, _state);
    tmpval = 99;
    seedrec1.ival = tmpval;
    ae_shared_pool_set_seed(&seedrec2.pool, &seedrec1, sizeof(seedrec1), _poolrec1_init, _poolrec1_init_copy, _poolrec1_destroy, _state);
    ae_shared_pool_set_seed(&pool, &seedrec2, sizeof(seedrec2), _poolrec2_init, _poolrec2_init_copy, _poolrec2_destroy, _state);
    ae_shared_pool_retrieve(&pool, &_p0, _state);
    ae_shared_pool_retrieve(&p0->pool, &_q0, _state);
    q0->ival = tmpval-1;
    ae_shared_pool_recycle(&p0->pool, &_q0, _state);
    ae_shared_pool_retrieve(&pool, &_p1, _state);
    ae_shared_pool_retrieve(&p1->pool, &_q1, _state);
    if( q1->ival!=tmpval )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_recycle(&p1->pool, &_q1, _state);
    ae_shared_pool_retrieve(&p0->pool, &_q0, _state);
    if( q0->ival!=tmpval-1 )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test 8: after call to PoolReset(), call to PoolFirstRecycled() returns null references
     */
    testalglibbasicsunit_createpoolandrecords(&seedrec2, &seedrec2copy, &pool, _state);
    ae_shared_pool_retrieve(&pool, &_p0, _state);
    ae_shared_pool_retrieve(&pool, &_p1, _state);
    ae_shared_pool_retrieve(&pool, &_p2, _state);
    ae_shared_pool_recycle(&pool, &_p1, _state);
    ae_shared_pool_recycle(&pool, &_p2, _state);
    ae_shared_pool_recycle(&pool, &_p0, _state);
    ae_shared_pool_first_recycled(&pool, &_p0, _state);
    if( !(p0!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_next_recycled(&pool, &_p0, _state);
    if( !(p0!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_next_recycled(&pool, &_p0, _state);
    if( !(p0!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_next_recycled(&pool, &_p0, _state);
    if( p0!=NULL )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_reset(&pool, _state);
    ae_shared_pool_first_recycled(&pool, &_p0, _state);
    if( p0!=NULL )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_next_recycled(&pool, &_p0, _state);
    if( p0!=NULL )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_next_recycled(&pool, &_p0, _state);
    if( p0!=NULL )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_next_recycled(&pool, &_p0, _state);
    if( p0!=NULL )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test 9: invalid pointer is recognized as non-null (we do not reference it, just test)
     */
    testalglibbasicsunit_createpoolandrecords(&seedrec2, &seedrec2copy, &pool, _state);
    ae_shared_pool_retrieve(&pool, &_p0, _state);
    ae_shared_pool_retrieve(&pool, &_p1, _state);
    ae_shared_pool_retrieve(&pool, &_p2, _state);
    ae_shared_pool_recycle(&pool, &_p1, _state);
    ae_shared_pool_recycle(&pool, &_p2, _state);
    ae_shared_pool_recycle(&pool, &_p0, _state);
    ae_shared_pool_first_recycled(&pool, &_p0, _state);
    if( !(p0!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_shared_pool_clear_recycled(&pool, _state);
    if( !(p0!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Test 9: non-null pointer is nulled by calling SetNull()
     */
    testalglibbasicsunit_createpoolandrecords(&seedrec2, &seedrec2copy, &pool, _state);
    ae_shared_pool_retrieve(&pool, &_p0, _state);
    if( !(p0!=NULL) )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_smart_ptr_assign(&_p0, NULL, ae_false, ae_false, NULL);
    if( p0!=NULL )
    {
        ae_frame_leave(_state);
        return result;
    }
    result = ae_false;
    ae_frame_leave(_state);
    return result;
}


static ae_bool testalglibbasicsunit_testsharedpool(ae_bool silent,
     ae_state *_state)
{
    ae_bool result;


    result = !testalglibbasicsunit_sharedpoolerrors(_state);
    if( !silent )
    {
        if( result )
        {
            printf("SHARED POOL:                             OK\n");
        }
        else
        {
            printf("SHARED POOL:                             FAILED\n");
        }
    }
    return result;
}


/*************************************************************************
Tests for SMP functions

testSort0: sort function
*************************************************************************/
static void testalglibbasicsunit_testsort0func(/* Integer */ ae_vector* a,
     /* Integer */ ae_vector* buf,
     ae_int_t idx0,
     ae_int_t idx2,
     ae_state *_state)
{
    ae_int_t idx1;


    if( idx2<=idx0+1 )
    {
        return;
    }
    idx1 = (idx0+idx2)/2;
    testalglibbasicsunit_testsort0func(a, buf, idx0, idx1, _state);
    testalglibbasicsunit_testsort0func(a, buf, idx1, idx2, _state);
    testalglibbasicsunit_mergesortedarrays(a, buf, idx0, idx1, idx2, _state);
}


/*************************************************************************
Serial stub for GPL edition.
*************************************************************************/
ae_bool _trypexec_testalglibbasicsunit_testsort0func(/* Integer */ ae_vector* a,
    /* Integer */ ae_vector* buf,
    ae_int_t idx0,
    ae_int_t idx2,
    ae_state *_state)
{
    return ae_false;
}


/*************************************************************************
testSort0: recursive sorting by splitting array into two subarrays.
Returns True on success, False on failure.
*************************************************************************/
static ae_bool testalglibbasicsunit_performtestsort0(ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector a;
    ae_vector buf;
    ae_int_t i;
    ae_int_t k;
    ae_int_t t;
    ae_int_t n;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&buf, 0, sizeof(buf));
    ae_vector_init(&a, 0, DT_INT, _state, ae_true);
    ae_vector_init(&buf, 0, DT_INT, _state, ae_true);

    n = 100000;
    ae_vector_set_length(&a, n, _state);
    ae_vector_set_length(&buf, n, _state);
    for(i=0; i<=n-1; i++)
    {
        a.ptr.p_int[i] = i;
    }
    for(i=0; i<=n-1; i++)
    {
        k = ae_randominteger(n, _state);
        if( k!=i )
        {
            t = a.ptr.p_int[i];
            a.ptr.p_int[i] = a.ptr.p_int[k];
            a.ptr.p_int[k] = t;
        }
    }
    testalglibbasicsunit_testsort0func(&a, &buf, 0, n, _state);
    result = ae_true;
    for(i=0; i<=n-1; i++)
    {
        result = result&&a.ptr.p_int[i]==i;
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
TestSort0: sort function
*************************************************************************/
static void testalglibbasicsunit_testsort1func(/* Integer */ ae_vector* a,
     /* Integer */ ae_vector* buf,
     ae_int_t idx0,
     ae_int_t idx2,
     ae_bool usesmp,
     ae_state *_state)
{
    ae_int_t idxa;
    ae_int_t idxb;
    ae_int_t idxc;
    ae_int_t cnt4;


    if( idx2<=idx0+1 )
    {
        return;
    }
    if( idx2==idx0+2 )
    {
        testalglibbasicsunit_mergesortedarrays(a, buf, idx0, idx0+1, idx0+2, _state);
        return;
    }
    if( idx2==idx0+3 )
    {
        testalglibbasicsunit_mergesortedarrays(a, buf, idx0+0, idx0+1, idx0+2, _state);
        testalglibbasicsunit_mergesortedarrays(a, buf, idx0+0, idx0+2, idx0+3, _state);
        return;
    }
    if( idx2==idx0+4 )
    {
        testalglibbasicsunit_mergesortedarrays(a, buf, idx0+0, idx0+1, idx0+2, _state);
        testalglibbasicsunit_mergesortedarrays(a, buf, idx0+2, idx0+3, idx0+4, _state);
        testalglibbasicsunit_mergesortedarrays(a, buf, idx0+0, idx0+2, idx0+4, _state);
        return;
    }
    cnt4 = (idx2-idx0)/4;
    idxa = idx0+cnt4;
    idxb = idx0+2*cnt4;
    idxc = idx0+3*cnt4;
    testalglibbasicsunit_testsort1func(a, buf, idx0, idxa, usesmp, _state);
    testalglibbasicsunit_testsort1func(a, buf, idxa, idxb, usesmp, _state);
    testalglibbasicsunit_testsort1func(a, buf, idxb, idxc, usesmp, _state);
    testalglibbasicsunit_testsort1func(a, buf, idxc, idx2, usesmp, _state);
    testalglibbasicsunit_mergesortedarrays(a, buf, idx0, idxa, idxb, _state);
    testalglibbasicsunit_mergesortedarrays(a, buf, idxb, idxc, idx2, _state);
    testalglibbasicsunit_mergesortedarrays(a, buf, idx0, idxb, idx2, _state);
}


/*************************************************************************
Serial stub for GPL edition.
*************************************************************************/
ae_bool _trypexec_testalglibbasicsunit_testsort1func(/* Integer */ ae_vector* a,
    /* Integer */ ae_vector* buf,
    ae_int_t idx0,
    ae_int_t idx2,
    ae_bool usesmp,
    ae_state *_state)
{
    return ae_false;
}


/*************************************************************************
TestSort0: recursive sorting by splitting array into 4 subarrays.

Sorting is performed in three rounds:
* parallel sorting of randomly permuted array
* result is randomly shuffled and sequentially sorted
* result is randomly shuffled (again) and sorted in parallel mode (again)

The idea of such "multitry sort" is that we test ability of  SMP  core  to
interleave highly parallel parts of code with long sequential parts.

Returns True on success, False on failure.
*************************************************************************/
static ae_bool testalglibbasicsunit_performtestsort1(ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector a;
    ae_vector buf;
    ae_int_t i;
    ae_int_t k;
    ae_int_t t;
    ae_int_t n;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&buf, 0, sizeof(buf));
    ae_vector_init(&a, 0, DT_INT, _state, ae_true);
    ae_vector_init(&buf, 0, DT_INT, _state, ae_true);

    
    /*
     * Generate array
     */
    n = 100000;
    ae_vector_set_length(&a, n, _state);
    ae_vector_set_length(&buf, n, _state);
    for(i=0; i<=n-1; i++)
    {
        a.ptr.p_int[i] = i;
    }
    
    /*
     * round 0: parallel sorting of randomly permuted array
     */
    for(i=0; i<=n-1; i++)
    {
        k = ae_randominteger(n, _state);
        if( k!=i )
        {
            t = a.ptr.p_int[i];
            a.ptr.p_int[i] = a.ptr.p_int[k];
            a.ptr.p_int[k] = t;
        }
    }
    testalglibbasicsunit_testsort1func(&a, &buf, 0, n, ae_true, _state);
    
    /*
     * round 1: result is randomly shuffled and sequentially sorted
     */
    for(i=0; i<=n-1; i++)
    {
        k = ae_randominteger(n, _state);
        if( k!=i )
        {
            t = a.ptr.p_int[i];
            a.ptr.p_int[i] = a.ptr.p_int[k];
            a.ptr.p_int[k] = t;
        }
    }
    testalglibbasicsunit_testsort1func(&a, &buf, 0, n, ae_false, _state);
    
    /*
     * round 2: result is randomly shuffled (again) and sorted in parallel mode (again)
     */
    for(i=0; i<=n-1; i++)
    {
        k = ae_randominteger(n, _state);
        if( k!=i )
        {
            t = a.ptr.p_int[i];
            a.ptr.p_int[i] = a.ptr.p_int[k];
            a.ptr.p_int[k] = t;
        }
    }
    testalglibbasicsunit_testsort1func(&a, &buf, 0, n, ae_true, _state);
    
    /*
     * Test
     */
    result = ae_true;
    for(i=0; i<=n-1; i++)
    {
        result = result&&a.ptr.p_int[i]==i;
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Tests for SMP functions

testSort2: sort function
*************************************************************************/
static void testalglibbasicsunit_testsort2func(/* Integer */ ae_vector* a,
     /* Integer */ ae_vector* buf,
     ae_int_t idx0,
     ae_int_t idx2,
     ae_state *_state)
{
    ae_int_t idx1;


    if( idx2<=idx0+1 )
    {
        return;
    }
    idx1 = idx0+1+ae_randominteger(idx2-idx0-1, _state);
    testalglibbasicsunit_testsort0func(a, buf, idx0, idx1, _state);
    testalglibbasicsunit_testsort0func(a, buf, idx1, idx2, _state);
    testalglibbasicsunit_mergesortedarrays(a, buf, idx0, idx1, idx2, _state);
}


/*************************************************************************
Serial stub for GPL edition.
*************************************************************************/
ae_bool _trypexec_testalglibbasicsunit_testsort2func(/* Integer */ ae_vector* a,
    /* Integer */ ae_vector* buf,
    ae_int_t idx0,
    ae_int_t idx2,
    ae_state *_state)
{
    return ae_false;
}


/*************************************************************************
testSort2: recursive sorting by splitting array into two subarrays of
different length (main difference from testsort0).
Returns True on success, False on failure.
*************************************************************************/
static ae_bool testalglibbasicsunit_performtestsort2(ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector a;
    ae_vector buf;
    ae_int_t i;
    ae_int_t k;
    ae_int_t t;
    ae_int_t n;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&a, 0, sizeof(a));
    memset(&buf, 0, sizeof(buf));
    ae_vector_init(&a, 0, DT_INT, _state, ae_true);
    ae_vector_init(&buf, 0, DT_INT, _state, ae_true);

    n = 100000;
    ae_vector_set_length(&a, n, _state);
    ae_vector_set_length(&buf, n, _state);
    for(i=0; i<=n-1; i++)
    {
        a.ptr.p_int[i] = i;
    }
    for(i=0; i<=n-1; i++)
    {
        k = ae_randominteger(n, _state);
        if( k!=i )
        {
            t = a.ptr.p_int[i];
            a.ptr.p_int[i] = a.ptr.p_int[k];
            a.ptr.p_int[k] = t;
        }
    }
    testalglibbasicsunit_testsort2func(&a, &buf, 0, n, _state);
    result = ae_true;
    for(i=0; i<=n-1; i++)
    {
        result = result&&a.ptr.p_int[i]==i;
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
TestPoolSum: summation with pool

We perform summation of 500000 numbers (each of them is equal to 1) in the
recurrent manner, by accumulation of result in the pool.

This test checks pool ability to handle continuous stream of operations.

Returns True on success, False on failure.
*************************************************************************/
static ae_bool testalglibbasicsunit_performtestpoolsum(ae_state *_state)
{
    ae_frame _frame_block;
    ae_shared_pool pool;
    poolsummand *ptr;
    ae_smart_ptr _ptr;
    poolsummand seed;
    ae_int_t n;
    ae_int_t sum;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    memset(&pool, 0, sizeof(pool));
    memset(&_ptr, 0, sizeof(_ptr));
    memset(&seed, 0, sizeof(seed));
    ae_shared_pool_init(&pool, _state, ae_true);
    ae_smart_ptr_init(&_ptr, (void**)&ptr, _state, ae_true);
    _poolsummand_init(&seed, _state, ae_true);

    n = 500000;
    seed.val = 0;
    ae_shared_pool_set_seed(&pool, &seed, sizeof(seed), _poolsummand_init, _poolsummand_init_copy, _poolsummand_destroy, _state);
    testalglibbasicsunit_parallelpoolsum(&pool, 0, n, _state);
    sum = 0;
    ae_shared_pool_first_recycled(&pool, &_ptr, _state);
    while(ptr!=NULL)
    {
        sum = sum+ptr->val;
        ae_shared_pool_next_recycled(&pool, &_ptr, _state);
    }
    result = sum==n;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Summation routune for parallel summation test.
*************************************************************************/
static void testalglibbasicsunit_parallelpoolsum(ae_shared_pool* sumpool,
     ae_int_t ind0,
     ae_int_t ind1,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    poolsummand *ptr;
    ae_smart_ptr _ptr;

    ae_frame_make(_state, &_frame_block);
    memset(&_ptr, 0, sizeof(_ptr));
    ae_smart_ptr_init(&_ptr, (void**)&ptr, _state, ae_true);

    if( ind1-ind0<=2 )
    {
        ae_shared_pool_retrieve(sumpool, &_ptr, _state);
        ptr->val = ptr->val+ind1-ind0;
        ae_shared_pool_recycle(sumpool, &_ptr, _state);
    }
    else
    {
        i = (ind0+ind1)/2;
        testalglibbasicsunit_parallelpoolsum(sumpool, ind0, i, _state);
        testalglibbasicsunit_parallelpoolsum(sumpool, i, ind1, _state);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Serial stub for GPL edition.
*************************************************************************/
ae_bool _trypexec_testalglibbasicsunit_parallelpoolsum(ae_shared_pool* sumpool,
    ae_int_t ind0,
    ae_int_t ind1,
    ae_state *_state)
{
    return ae_false;
}


/*************************************************************************
This function merges sorted A[Idx0,Idx1) and A[Idx1,Idx2) into sorted array
A[Idx0,Idx2) using corresponding elements of Buf.
*************************************************************************/
static void testalglibbasicsunit_mergesortedarrays(/* Integer */ ae_vector* a,
     /* Integer */ ae_vector* buf,
     ae_int_t idx0,
     ae_int_t idx1,
     ae_int_t idx2,
     ae_state *_state)
{
    ae_int_t srcleft;
    ae_int_t srcright;
    ae_int_t dst;


    srcleft = idx0;
    srcright = idx1;
    dst = idx0;
    for(;;)
    {
        if( srcleft==idx1&&srcright==idx2 )
        {
            break;
        }
        if( srcleft==idx1 )
        {
            buf->ptr.p_int[dst] = a->ptr.p_int[srcright];
            srcright = srcright+1;
            dst = dst+1;
            continue;
        }
        if( srcright==idx2 )
        {
            buf->ptr.p_int[dst] = a->ptr.p_int[srcleft];
            srcleft = srcleft+1;
            dst = dst+1;
            continue;
        }
        if( a->ptr.p_int[srcleft]<a->ptr.p_int[srcright] )
        {
            buf->ptr.p_int[dst] = a->ptr.p_int[srcleft];
            srcleft = srcleft+1;
            dst = dst+1;
        }
        else
        {
            buf->ptr.p_int[dst] = a->ptr.p_int[srcright];
            srcright = srcright+1;
            dst = dst+1;
        }
    }
    for(dst=idx0; dst<=idx2-1; dst++)
    {
        a->ptr.p_int[dst] = buf->ptr.p_int[dst];
    }
}


/*************************************************************************
Serial stub for GPL edition.
*************************************************************************/
ae_bool _trypexec_testalglibbasicsunit_mergesortedarrays(/* Integer */ ae_vector* a,
    /* Integer */ ae_vector* buf,
    ae_int_t idx0,
    ae_int_t idx1,
    ae_int_t idx2,
    ae_state *_state)
{
    return ae_false;
}


static ae_bool testalglibbasicsunit_testsmp(ae_bool silent,
     ae_state *_state)
{
    ae_bool t0;
    ae_bool t1;
    ae_bool t2;
    ae_bool ts;
    ae_bool result;


    t0 = testalglibbasicsunit_performtestsort0(_state);
    t1 = testalglibbasicsunit_performtestsort1(_state);
    t2 = testalglibbasicsunit_performtestsort2(_state);
    ts = testalglibbasicsunit_performtestpoolsum(_state);
    result = ((t0&&t1)&&t2)&&ts;
    if( !silent )
    {
        if( result )
        {
            printf("SMP FUNCTIONS:                           OK\n");
        }
        else
        {
            printf("SMP FUNCTIONS:                           FAILED\n");
            printf("* TEST SORT0 (sorting, split-2)          ");
            if( t0 )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* TEST SORT1 (sorting, split-4)          ");
            if( t1 )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* TEST SORT2 (sorting, split-2, unequal) ");
            if( t2 )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* TEST POOLSUM (accumulation with pool)  ");
            if( ts )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
        }
    }
    return result;
}


void _rec1_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    rec1 *p = (rec1*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->b1field, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->r1field, 0, DT_REAL, _state, make_automatic);
    ae_vector_init(&p->i1field, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->c1field, 0, DT_COMPLEX, _state, make_automatic);
    ae_matrix_init(&p->b2field, 0, 0, DT_BOOL, _state, make_automatic);
    ae_matrix_init(&p->r2field, 0, 0, DT_REAL, _state, make_automatic);
    ae_matrix_init(&p->i2field, 0, 0, DT_INT, _state, make_automatic);
    ae_matrix_init(&p->c2field, 0, 0, DT_COMPLEX, _state, make_automatic);
}


void _rec1_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
{
    rec1 *dst = (rec1*)_dst;
    rec1 *src = (rec1*)_src;
    dst->bfield = src->bfield;
    dst->rfield = src->rfield;
    dst->ifield = src->ifield;
    dst->cfield = src->cfield;
    ae_vector_init_copy(&dst->b1field, &src->b1field, _state, make_automatic);
    ae_vector_init_copy(&dst->r1field, &src->r1field, _state, make_automatic);
    ae_vector_init_copy(&dst->i1field, &src->i1field, _state, make_automatic);
    ae_vector_init_copy(&dst->c1field, &src->c1field, _state, make_automatic);
    ae_matrix_init_copy(&dst->b2field, &src->b2field, _state, make_automatic);
    ae_matrix_init_copy(&dst->r2field, &src->r2field, _state, make_automatic);
    ae_matrix_init_copy(&dst->i2field, &src->i2field, _state, make_automatic);
    ae_matrix_init_copy(&dst->c2field, &src->c2field, _state, make_automatic);
}


void _rec1_clear(void* _p)
{
    rec1 *p = (rec1*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->b1field);
    ae_vector_clear(&p->r1field);
    ae_vector_clear(&p->i1field);
    ae_vector_clear(&p->c1field);
    ae_matrix_clear(&p->b2field);
    ae_matrix_clear(&p->r2field);
    ae_matrix_clear(&p->i2field);
    ae_matrix_clear(&p->c2field);
}


void _rec1_destroy(void* _p)
{
    rec1 *p = (rec1*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->b1field);
    ae_vector_destroy(&p->r1field);
    ae_vector_destroy(&p->i1field);
    ae_vector_destroy(&p->c1field);
    ae_matrix_destroy(&p->b2field);
    ae_matrix_destroy(&p->r2field);
    ae_matrix_destroy(&p->i2field);
    ae_matrix_destroy(&p->c2field);
}


void _rec4serialization_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    rec4serialization *p = (rec4serialization*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->b, 0, DT_BOOL, _state, make_automatic);
    ae_vector_init(&p->i, 0, DT_INT, _state, make_automatic);
    ae_vector_init(&p->r, 0, DT_REAL, _state, make_automatic);
}


void _rec4serialization_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
{
    rec4serialization *dst = (rec4serialization*)_dst;
    rec4serialization *src = (rec4serialization*)_src;
    ae_vector_init_copy(&dst->b, &src->b, _state, make_automatic);
    ae_vector_init_copy(&dst->i, &src->i, _state, make_automatic);
    ae_vector_init_copy(&dst->r, &src->r, _state, make_automatic);
}


void _rec4serialization_clear(void* _p)
{
    rec4serialization *p = (rec4serialization*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->b);
    ae_vector_clear(&p->i);
    ae_vector_clear(&p->r);
}


void _rec4serialization_destroy(void* _p)
{
    rec4serialization *p = (rec4serialization*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->b);
    ae_vector_destroy(&p->i);
    ae_vector_destroy(&p->r);
}


void _poolrec1_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    poolrec1 *p = (poolrec1*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_init(&p->i1val, 0, DT_INT, _state, make_automatic);
}


void _poolrec1_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
{
    poolrec1 *dst = (poolrec1*)_dst;
    poolrec1 *src = (poolrec1*)_src;
    dst->cval = src->cval;
    dst->rval = src->rval;
    dst->ival = src->ival;
    dst->bval = src->bval;
    ae_vector_init_copy(&dst->i1val, &src->i1val, _state, make_automatic);
}


void _poolrec1_clear(void* _p)
{
    poolrec1 *p = (poolrec1*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_clear(&p->i1val);
}


void _poolrec1_destroy(void* _p)
{
    poolrec1 *p = (poolrec1*)_p;
    ae_touch_ptr((void*)p);
    ae_vector_destroy(&p->i1val);
}


void _poolrec2_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    poolrec2 *p = (poolrec2*)_p;
    ae_touch_ptr((void*)p);
    _poolrec1_init(&p->recval, _state, make_automatic);
    ae_shared_pool_init(&p->pool, _state, make_automatic);
}


void _poolrec2_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
{
    poolrec2 *dst = (poolrec2*)_dst;
    poolrec2 *src = (poolrec2*)_src;
    dst->bval = src->bval;
    _poolrec1_init_copy(&dst->recval, &src->recval, _state, make_automatic);
    ae_shared_pool_init_copy(&dst->pool, &src->pool, _state, make_automatic);
}


void _poolrec2_clear(void* _p)
{
    poolrec2 *p = (poolrec2*)_p;
    ae_touch_ptr((void*)p);
    _poolrec1_clear(&p->recval);
    ae_shared_pool_clear(&p->pool);
}


void _poolrec2_destroy(void* _p)
{
    poolrec2 *p = (poolrec2*)_p;
    ae_touch_ptr((void*)p);
    _poolrec1_destroy(&p->recval);
    ae_shared_pool_destroy(&p->pool);
}


void _poolsummand_init(void* _p, ae_state *_state, ae_bool make_automatic)
{
    poolsummand *p = (poolsummand*)_p;
    ae_touch_ptr((void*)p);
}


void _poolsummand_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
{
    poolsummand *dst = (poolsummand*)_dst;
    poolsummand *src = (poolsummand*)_src;
    dst->val = src->val;
}


void _poolsummand_clear(void* _p)
{
    poolsummand *p = (poolsummand*)_p;
    ae_touch_ptr((void*)p);
}


void _poolsummand_destroy(void* _p)
{
    poolsummand *p = (poolsummand*)_p;
    ae_touch_ptr((void*)p);
}



#if (AE_OS==AE_WINDOWS) || defined(AE_DEBUG4WINDOWS)
#include <windows.h>
#endif
#if (AE_OS==AE_POSIX) || defined(AE_DEBUG4POSIX)
#include <unistd.h>
#include <pthread.h>
#endif
#define AE_SINGLECORE           1
#define AE_SEQUENTIAL_MULTICORE 2
#define AE_PARALLEL_SINGLECORE 3
#define AE_PARALLEL_MULTICORE 4
#define AE_SKIP_TEST 5
unsigned seed;
int global_failure_flag = 0;
ae_bool use_smp = ae_false;
#if (AE_OS==AE_WINDOWS) || defined(AE_DEBUG4WINDOWS)
CRITICAL_SECTION tests_lock;
CRITICAL_SECTION print_lock;
#elif (AE_OS==AE_POSIX) || defined(AE_DEBUG4POSIX)
pthread_mutex_t tests_lock;
pthread_mutex_t print_lock;
#else
void *tests_lock = NULL;
void *print_lock = NULL;
#endif
typedef struct
{
    const char *name;
    ae_bool (*testfunc)(ae_bool, ae_state*);
    
} _s_testrecord;
int unittests_processed = 0;
_s_testrecord unittests[] =
{
    {"tsort",testtsort},
    {"nearestneighbor",testnearestneighbor},
    {"hqrnd",testhqrnd},
    {"odesolver",testodesolver},
    {"sparse",testsparse},
    {"ablas",testablas},
    {"creflections",testcreflections},
    {"matgen",testmatgen},
    {"trfac",testtrfac},
    {"trlinsolve",testtrlinsolve},
    {"safesolve",testsafesolve},
    {"rcond",testrcond},
    {"matinv",testmatinv},
    {"hblas",testhblas},
    {"sblas",testsblas},
    {"ortfac",testortfac},
    {"fbls",testfbls},
    {"cqmodels",testcqmodels},
    {"bdsvd",testbdsvd},
    {"blas",testblas},
    {"svd",testsvd},
    {"optserv",testoptserv},
    {"snnls",testsnnls},
    {"sactivesets",testsactivesets},
    {"linmin",testlinmin},
    {"minlbfgs",testminlbfgs},
    {"xblas",testxblas},
    {"directdensesolvers",testdirectdensesolvers},
    {"normestimator",testnormestimator},
    {"linlsqr",testlinlsqr},
    {"mincg",testmincg},
    {"minbleic",testminbleic},
    {"minqp",testminqp},
    {"minbc",testminbc},
    {"minnlc",testminnlc},
    {"minns",testminns},
    {"minlm",testminlm},
    {"evd",testevd},
    {"basestat",testbasestat},
    {"pca",testpca},
    {"bdss",testbdss},
    {"mlpbase",testmlpbase},
    {"lda",testlda},
    {"ssa",testssa},
    {"gammafunc",testgammafunc},
    {"linreg",testlinreg},
    {"filters",testfilters},
    {"mcpd",testmcpd},
    {"mlpe",testmlpe},
    {"mlptrain",testmlptrain},
    {"clustering",testclustering},
    {"dforest",testdforest},
    {"gq",testgq},
    {"gkq",testgkq},
    {"autogk",testautogk},
    {"fft",testfft},
    {"fht",testfht},
    {"conv",testconv},
    {"corr",testcorr},
    {"idwint",testidwint},
    {"ratint",testratint},
    {"fitsphere",testfitsphere},
    {"spline1d",testspline1d},
    {"parametric",testparametric},
    {"spline3d",testspline3d},
    {"polint",testpolint},
    {"lsfit",testlsfit},
    {"spline2d",testspline2d},
    {"rbf",testrbf},
    {"hermite",testhermite},
    {"laguerre",testlaguerre},
    {"legendre",testlegendre},
    {"chebyshev",testchebyshev},
    {"wsr",testwsr},
    {"stest",teststest},
    {"studentttests",teststudentttests},
    {"mannwhitneyu",testmannwhitneyu},
    {"schur",testschur},
    {"spdgevd",testspdgevd},
    {"inverseupdate",testinverseupdate},
    {"polynomialsolver",testpolynomialsolver},
    {"nleq",testnleq},
    {"directsparsesolvers",testdirectsparsesolvers},
    {"lincg",testlincg},
    {"alglibbasics",testalglibbasics},

    {NULL, NULL}
};

#if (AE_OS==AE_WINDOWS) || defined(AE_DEBUG4WINDOWS)
void acquire_lock(CRITICAL_SECTION *p_lock)
{
    EnterCriticalSection(p_lock);
}
void release_lock(CRITICAL_SECTION *p_lock)
{
    LeaveCriticalSection(p_lock);
}
#elif (AE_OS==AE_POSIX) || defined(AE_DEBUG4POSIX)
void acquire_lock(pthread_mutex_t *p_lock)
{
    pthread_mutex_lock(p_lock);
}
void release_lock(pthread_mutex_t *p_lock)
{
    pthread_mutex_unlock(p_lock);
}
#else
void acquire_lock(void **p_lock)
{
}
void release_lock(void **p_lock)
{
}
#endif

ae_bool call_unittest(
    ae_bool(*testfunc)(ae_bool, ae_state*),
    int *psticky)
{
#ifndef AE_USE_CPP_ERROR_HANDLING
    ae_state _alglib_env_state;
    ae_frame _frame_block;
    jmp_buf _break_jump;
    ae_bool result;
    
    ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
        *psticky = 1;
        return ae_false;
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    if( use_smp )
        ae_state_set_flags(&_alglib_env_state, _ALGLIB_FLG_THREADING_PARALLEL);
    ae_frame_make(&_alglib_env_state, &_frame_block);
    result = testfunc(ae_true, &_alglib_env_state);
    ae_state_clear(&_alglib_env_state);
    if( !result )
        *psticky = 1;
    return result;
#else
    try
    {
        ae_state _alglib_env_state;
        ae_frame _frame_block;
        ae_bool result;
        
        ae_state_init(&_alglib_env_state);
        ae_frame_make(&_alglib_env_state, &_frame_block);
        if( use_smp )
            ae_state_set_flags(&_alglib_env_state, _ALGLIB_FLG_THREADING_PARALLEL);
        result = testfunc(ae_true, &_alglib_env_state);
        ae_state_clear(&_alglib_env_state);
        if( !result )
            *psticky = 1;
        return result;
    }
    catch(...)
    {
        *psticky = 1;
        return ae_false;
    }
#endif
}

#if (AE_OS==AE_WINDOWS) || defined(AE_DEBUG4WINDOWS)
DWORD WINAPI tester_function(LPVOID T)
#elif AE_OS==AE_POSIX   || defined(AE_DEBUG4POSIX)
void* tester_function(void *T)
#else
void tester_function(void *T)
#endif
{
    int idx;
    ae_bool status;
    for(;;)
    {
        /*
         * try to acquire test record
         */
        acquire_lock(&tests_lock);
        if( unittests[unittests_processed].name==NULL )
        {
            release_lock(&tests_lock);
            break;
        
        }
        idx = unittests_processed;
        unittests_processed++;
        release_lock(&tests_lock);
        
        /*
         * Call unit test
         */
        status = call_unittest(unittests[idx].testfunc, &global_failure_flag);
        acquire_lock(&print_lock);
        if( status )
            printf("%-32s OK\n", unittests[idx].name);
        else
            printf("%-32s FAILED\n", unittests[idx].name);
        fflush(stdout);
        release_lock(&print_lock);
    }
#if AE_OS==AE_WINDOWS || defined(AE_DEBUG4WINDOWS)
    return 0;
#elif AE_OS==AE_POSIX || defined(AE_DEBUG4POSIX)
    return NULL;
#else
#endif    
}

int main(int argc, char **argv)
{
    time_t time_0, time_1;
    union
    {
        double a;
        ae_int32_t p[2];
    } u;
    if( argc==2 )
        seed = (unsigned)atoi(argv[1]);
    else
    {
        time_t t;
        seed = (unsigned)time(&t);
    }
#if (AE_OS==AE_WINDOWS) || defined(AE_DEBUG4WINDOWS)
    InitializeCriticalSection(&tests_lock);
    InitializeCriticalSection(&print_lock);
#elif (AE_OS==AE_POSIX) || defined(AE_DEBUG4POSIX)
    pthread_mutex_init(&tests_lock, NULL);
    pthread_mutex_init(&print_lock, NULL);
#endif
    
    /*
     * SMP settings
     */
#if AE_TEST==AE_PARALLEL_MULTICORE || AE_TEST==AE_SEQUENTIAL_MULTICORE
    use_smp = ae_true;
#else
    use_smp = ae_false;
#endif
    
    /* 
     * Seed
     */
    printf("SEED: %u\n", (unsigned int)seed);
    srand(seed);
    
    /* 
     * Compiler
     */
#if AE_COMPILER==AE_GNUC
    printf("COMPILER: GCC\n");
#elif AE_COMPILER==AE_SUNC
    printf("COMPILER: SunStudio\n");
#elif AE_COMPILER==AE_MSVC
    printf("COMPILER: MSVC\n");
#else
    printf("COMPILER: unknown\n");
#endif
    
    /*
     * Architecture
     */
    if( sizeof(void*)==4 )
        printf("HARDWARE: 32-bit\n");
    else if( sizeof(void*)==8 )
        printf("HARDWARE: 64-bit\n");
    else
        printf("HARDWARE: strange (non-32, non-64)\n");
    
    /* 
     * determine endianness of hardware.
     * 1983 is a good number - non-periodic double representation allow us to
     * easily distinguish between upper and lower halfs and to detect mixed endian hardware.
     */
    u.a = 1.0/1983.0; 
    if( u.p[1]==0x3f408642 )
        printf("HARDWARE: little-endian\n");
    else if( u.p[0]==0x3f408642 )
        printf("HARDWARE: big-endian\n");
    else
        printf("HARDWARE: mixed-endian\n");
    
    /* 
     * CPU (as defined)
     */
#if AE_CPU==AE_INTEL
    printf("CPU:   Intel\n");
#elif AE_CPU==AE_SPARC
    printf("CPU:   SPARC\n");
#else
    printf("CPU:   unknown\n");
#endif

    /* 
     * Cores count
     */
#ifdef _ALGLIB_HAS_WORKSTEALING
    printf("CORES: %d\n", (int)ae_cores_count());
#else
    printf("CORES: 1 (serial version)\n");
#endif

    /*
     * Support for vendor libraries
     */
#ifdef AE_MKL
    printf("LIBS:  MKL (Intel)\n");
#else
    printf("LIBS:  \n");
#endif

    /* 
     * CPUID results
     */
    printf("CPUID: %s\n", ae_cpuid()&CPU_SSE2 ? "sse2" : "");

    /* 
     * OS
     */
#if AE_OS==AE_WINDOWS
    printf("OS: Windows\n");
#elif AE_OS==AE_POSIX
    printf("OS: POSIX\n");
#else
    printf("OS: unknown\n");
#endif

    /* 
     * Testing mode
     */
#if (AE_TEST==0) || (AE_TEST==AE_SINGLECORE)
    printf("TESTING MODE: single core\n");
#elif AE_TEST==AE_PARALLEL_SINGLECORE
    printf("TESTING MODE: single core, parallel\n");
#elif AE_TEST==AE_SEQUENTIAL_MULTICORE
    printf("TESTING MODE: milti-core, sequential\n");
#elif AE_TEST==AE_PARALLEL_MULTICORE
    printf("TESTING MODE: milti-core, parallel\n");
#elif AE_TEST==AE_SKIP_TEST
    printf("TESTING MODE: just compiling\n");
    printf("Done in 0 seconds\n");
    return 0;
#else
#error Unknown AE_TEST being passed
#endif
    
    /*
     * now we are ready to test!
     */
    time(&time_0);
#ifdef _ALGLIB_HAS_WORKSTEALING
    if( ae_smpselftests() )
        printf("%-32s OK\n", "SMP self tests");
    else
    {
        printf("%-32s FAILED\n", "SMP self tests");
        return 1;
    }
#endif
    fflush(stdout);
#if AE_TEST==0 || AE_TEST==AE_SINGLECORE || AE_TEST==AE_SEQUENTIAL_MULTICORE || AE_TEST==AE_SKIP_TEST
    tester_function(NULL);
#elif AE_TEST==AE_PARALLEL_MULTICORE || AE_TEST==AE_PARALLEL_SINGLECORE
#ifdef _ALGLIB_HAS_WORKSTEALING
    ae_set_cores_to_use(0);
#endif
#if AE_OS==AE_WINDOWS || defined(AE_DEBUG4WINDOWS)
    {
        SYSTEM_INFO sysInfo;
        HANDLE  *hThreads = NULL;
        int idx;
        GetSystemInfo(&sysInfo);
        ae_assert(sysInfo.dwNumberOfProcessors>=1, "processors count is less than 1", NULL);
        hThreads = (HANDLE*)malloc(sizeof(HANDLE)*sysInfo.dwNumberOfProcessors);
        ae_assert(hThreads!=NULL, "malloc failure", NULL);
        for(idx=0; idx<sysInfo.dwNumberOfProcessors; idx++)
            hThreads[idx] = CreateThread(NULL, 0, tester_function, NULL, 0, NULL);
        WaitForMultipleObjects(sysInfo.dwNumberOfProcessors, hThreads, TRUE, INFINITE);
    }
#elif AE_OS==AE_POSIX || defined(AE_DEBUG4POSIX)
    {
        long cpu_cnt;
        pthread_t  *threads = NULL;
        int idx;
        cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN);
        ae_assert(cpu_cnt>=1, "processors count is less than 1", NULL);
        threads = (pthread_t*)malloc(sizeof(pthread_t)*cpu_cnt);
        ae_assert(threads!=NULL, "malloc failure", NULL);
        for(idx=0; idx<cpu_cnt; idx++)
        {
            int status = pthread_create(&threads[idx], NULL, tester_function, NULL);
            if( status!=0 )
            {
                printf("Failed to create thread\n");
                abort();
            }
        }
        for(idx=0; idx<cpu_cnt; idx++)
            pthread_join(threads[idx], NULL);
    }
#else
#error Unable to determine OS (define AE_OS, AE_DEBUG4WINDOWS or AE_DEBUG4POSIX)
#endif
#else
#error Unexpected test mode
#endif
    time(&time_1);
    printf("Done in %ld seconds\n", (long)difftime(time_1, time_0));
    if( global_failure_flag )
        printf("Last error at %s:%d (%s)\n", ae_get_last_error_file(), (int)ae_get_last_error_line(), ae_get_last_error_xdesc());

    /*
     * Free structures
     */
#if (AE_OS==AE_WINDOWS) || defined(AE_DEBUG4WINDOWS)
    DeleteCriticalSection(&tests_lock);
    DeleteCriticalSection(&print_lock);
#elif (AE_OS==AE_POSIX) || defined(AE_DEBUG4POSIX)
    pthread_mutex_destroy(&tests_lock);
    pthread_mutex_destroy(&print_lock);
#endif

    /*
     * Return result
     */
    return global_failure_flag;
}

